Fucking navbar

This commit is contained in:
Fergal Moran
2020-09-11 17:21:58 +01:00
parent 1a9ef9fcf8
commit 57c34c3fee
18 changed files with 278 additions and 195 deletions

View File

@@ -1,55 +1,40 @@
module.exports = {
root: true,
root: true,
env: {
node: true
},
extends: [
'plugin:vue/essential',
'eslint:recommended',
'@vue/typescript/recommended'
],
parserOptions: {
ecmaVersion: 2020
},
rules: {
/*
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
quotes: [2, 'single', { avoidEscape: true }],
'@typescript-eslint/quotes': [2, 'single', { avoidEscape: true }],
'prettier/prettier': 'error',
'max-len': [
'error',
{
'code': 80
}
], 'indent': [
'error',
2
]
*/
},
overrides: [
{
files: [
'**/__tests__/*.{j,t}s?(x)',
'**/tests/unit/**/*.spec.{j,t}s?(x)'
],
env: {
mocha: true
}
env: {
node: true,
},
{
files: [
'**/__tests__/*.{j,t}s?(x)',
'**/tests/unit/**/*.spec.{j,t}s?(x)'
],
env: {
mocha: true
}
}
]
extends: ['plugin:vue/essential', 'eslint:recommended', '@vue/typescript/recommended'],
parserOptions: {
ecmaVersion: 2020,
project: ['./tsconfig.json', './tsconfig.eslint.json'],
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
quotes: [2, 'single', { avoidEscape: true }],
'@typescript-eslint/quotes': [2, 'single', { avoidEscape: true }],
'max-len': [
'error',
{
code: 120,
},
],
indent: [2, 4],
},
overrides: [
{
files: ['**/__tests__/*.{j,t}s?(x)', '**/tests/unit/**/*.spec.{j,t}s?(x)'],
env: {
mocha: true,
},
},
{
files: ['**/__tests__/*.{j,t}s?(x)', '**/tests/unit/**/*.spec.{j,t}s?(x)'],
env: {
mocha: true,
},
},
],
};

1
client/externals.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
declare module 'vue-web-cam';

View File

@@ -1,14 +1,14 @@
<template>
<v-app id="bitchmin">
<TopBarNav v-if="isAuthenticated"/>
<SideBarNav v-if="isAuthenticated" />
<v-main>
<v-container fluid>
<router-view></router-view>
</v-container>
</v-main>
<Footer />
</v-app>
<v-app id="bitchmin">
<TopBarNav v-on:toggle-sidebar="drawerOpen = !drawerOpen" v-if="isAuthenticated" />
<SideBarNav v-if="isAuthenticated" v-model="drawerOpen" />
<v-main>
<v-container fluid>
<router-view></router-view>
</v-container>
</v-main>
<Footer />
</v-app>
</template>
<script lang="ts">
@@ -18,24 +18,26 @@ import SideBarNav from '@/components/SideBarNav.vue'; // @ is an alias to /src
import Footer from '@/components/Footer.vue'; // @ is an alias to /src
@Component({
components: {
TopBarNav,
SideBarNav,
Footer,
},
components: {
TopBarNav,
SideBarNav,
Footer,
},
})
export default class App extends Vue {
async mounted() {
this.$store.dispatch('loadInitialState');
}
drawerOpen = true;
get isAuthenticated() {
return this.$store.getters.isLoggedIn;
}
async mounted() {
this.$store.dispatch('loadInitialState');
}
get isAuthenticated() {
return this.$store.getters.isLoggedIn;
}
}
</script>
<style>
#keep .v-navigation-drawer__border {
display: none;
display: none;
}
</style>

View File

@@ -1,12 +1,15 @@
<template>
<v-card outlined>
<template slot="progress">
<v-progress-linear color="deep-purple" height="10" indeterminate></v-progress-linear>
<v-progress-linear color="deep-purple" height="10" indeterminate>
</v-progress-linear>
</template>
<v-card-title>Existing records</v-card-title>
<v-card-text>
<v-row>
<v-alert type="error" v-if="errorMessage">{{errorMessage}}</v-alert>
<v-alert type="error" v-if="errorMessage">
{{errorMessage}}
</v-alert>
<v-simple-table>
<template v-slot:default>
<thead>
@@ -23,14 +26,21 @@
<td>{{host.ip}}</td>
<td>{{host.created_on | formatDate}}</td>
<td>
<v-btn-toggle dense background-color="pink" rounded>
<v-btn icon color="pink" @click="refreshRecord(host)">
<v-btn-toggle dense
background-color="pink"
rounded>
<v-btn icon color="pink"
@click="refreshRecord(host)">
<v-icon dark>mdi-refresh</v-icon>
</v-btn>
<v-btn icon color="pink" @click="verifyRecord(host)">
<v-btn icon
color="pink"
@click="verifyRecord(host)">
<v-icon dark>mdi-eye-check</v-icon>
</v-btn>
<v-btn icon color="pink" @click="deleteRecord(host)">
<v-btn icon
color="pink"
@click="deleteRecord(host)">
<v-icon dark>mdi-delete</v-icon>
</v-btn>
</v-btn-toggle>
@@ -45,9 +55,7 @@
</template>
<script lang="ts">
import {
Component, Prop, PropSync, Vue,
} from 'vue-property-decorator';
import { Component, PropSync, Vue } from 'vue-property-decorator';
import { dnsApi } from '@/api';
import { DnsRecord } from '@/models/dnsRecord';
import dayjs from 'dayjs';
@@ -57,16 +65,16 @@ import relativeTime from 'dayjs/plugin/relativeTime';
import localizedFormat from 'dayjs/plugin/localizedFormat';
@Component({
name: 'DnsRecordsList',
filters: {
formatDate: (date: string) => {
if (!date) {
return null;
}
const d = dayjs(date);
return d.format('L LT');
name: 'DnsRecordsList',
filters: {
formatDate: (date: string) => {
if (!date) {
return null;
}
const d = dayjs(date);
return d.format('L LT');
},
},
},
})
export default class DnsRecordsList extends Vue {
@PropSync('inrecords')
@@ -77,41 +85,41 @@ export default class DnsRecordsList extends Vue {
errorMessage = '';
async refreshRecord(host: DnsRecord) {
this.callInProgress = true;
console.log('DnsRecordsList', 'refreshRecord', host);
const result = await dnsApi.refreshDnsRecord(host.host, host.ip);
if (result.status === 'success') {
Vue.toasted.success('Refreshed successfully');
}
this.callInProgress = false;
this.callInProgress = true;
console.log('DnsRecordsList', 'refreshRecord', host);
const result = await dnsApi.refreshDnsRecord(host.host, host.ip);
if (result.status === 'success') {
Vue.toasted.success('Refreshed successfully');
}
this.callInProgress = false;
}
async verifyRecord(record: DnsRecord) {
this.callInProgress = true;
const result = await dnsApi.verifyDnsRecord(record.host, record.ip);
if (result.status === 'success') {
Vue.toasted.success(result.payload || 'Record checks out');
} else {
this.errorMessage = result.payload || 'Error checking record';
Vue.toasted.error(this.errorMessage);
}
this.callInProgress = false;
this.callInProgress = true;
const result = await dnsApi.verifyDnsRecord(record.host, record.ip);
if (result.status === 'success') {
Vue.toasted.success(result.payload || 'Record checks out');
} else {
this.errorMessage = result.payload || 'Error checking record';
Vue.toasted.error(this.errorMessage);
}
this.callInProgress = false;
}
async deleteRecord(record: DnsRecord) {
this.callInProgress = true;
const result = await dnsApi.deleteDnsRecord(record.host);
if (result === 200) {
Vue.toasted.success('Record deleted successfully');
this.records = this.records.filter((t) => t.host !== record.host);
console.log('DnsRecordsList', 'delete', this.records);
}
this.callInProgress = false;
this.callInProgress = true;
const result = await dnsApi.deleteDnsRecord(record.host);
if (result === 200) {
Vue.toasted.success('Record deleted successfully');
this.records = this.records.filter((t) => t.host !== record.host);
console.log('DnsRecordsList', 'delete', this.records);
}
this.callInProgress = false;
}
mounted() {
dayjs.extend(localizedFormat);
console.log('DnsRecordsList', 'mounded_after', this.records);
dayjs.extend(localizedFormat);
console.log('DnsRecordsList', 'mounded_after', this.records);
}
}
</script>

View File

@@ -34,7 +34,7 @@ import { DnsRecord } from '@/models/dnsRecord';
import { DataApiResult } from '@/api/apiResult';
@Component({
name: 'DnsUpdateForm',
name: 'DnsUpdateForm',
})
export default class DnsUpdateForm extends Vue {
msg = '';
@@ -48,52 +48,55 @@ export default class DnsUpdateForm extends Vue {
hostName = '';
hostNameRules = [
(v: string) => !!v || 'Host name is required',
(v: string) => (v && v.length < 253) || 'Hostname cannot exceed 253 characters',
(v: string) => !!v || 'Host name is required',
(v: string) =>
(v && v.length < 253) || 'Hostname cannot exceed 253 characters',
];
ipAddress = '';
ipAddressRules = [
(v: string) => !!v || 'IP address is required',
(v: string) => /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(
v,
) || 'Invalid IP Address',
(v: string) => (v && v.length < 16) || 'IP address cannot exceed 16 characters',
(v: string) => !!v || 'IP address is required',
(v: string) =>
/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(
v
) || 'Invalid IP Address',
(v: string) =>
(v && v.length < 16) || 'IP address cannot exceed 16 characters',
];
@PropSync('inrecords')
public records!: DnsRecord[];
validate() {
// this.$refs.form.validate();
// this.$refs.form.validate();
}
processUpdate() {
dnsApi
.updateDnsRecord(this.hostName, this.ipAddress)
.then((r: DataApiResult<DnsRecord>) => {
if (r.status === 'success') {
this.error = '';
Vue.toasted.success('Update successful');
if (r.payload) {
this.records.unshift(r.payload);
this.$emit('update:inrecords', this.records);
}
this.ipAddress = '';
this.hostName = '';
} else {
this.error = 'Unable to add DNS record';
}
})
.catch((e: any) => {
console.log('DnsUpdateForm', 'error', e);
if (e.response && e.response.data.payload) {
this.error = e.response.data.payload;
} else {
this.error = e;
}
});
dnsApi
.updateDnsRecord(this.hostName, this.ipAddress)
.then((r: DataApiResult<DnsRecord>) => {
if (r.status === 'success') {
this.error = '';
Vue.toasted.success('Update successful');
if (r.payload) {
this.records.unshift(r.payload);
this.$emit('update:inrecords', this.records);
}
this.ipAddress = '';
this.hostName = '';
} else {
this.error = 'Unable to add DNS record';
}
})
.catch((e: any) => {
console.log('DnsUpdateForm', 'error', e);
if (e.response && e.response.data.payload) {
this.error = e.response.data.payload;
} else {
this.error = e;
}
});
}
}
</script>

View File

@@ -0,0 +1,47 @@
<template>
<div id="container">
<video id="video" width="640" height="480" autoplay></video>
<button id="snap"></button>
<canvas id="canvas" width="640" height="480"></canvas>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
@Component({
components: {},
})
export default class VideoTest extends Vue {
async mounted() {
console.log('VideoTest', 'mounted');
const video = document.getElementById('video') as HTMLVideoElement;
if (video !== null) {
if (navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices
.getUserMedia({ video: true })
.then(function (stream) {
video.srcObject = stream;
})
.catch(function (err0r) {
console.log('Something went wrong!');
});
}
}
}
}
</script>
<style scoped=true>
#container {
margin: 0px auto;
width: 500px;
height: 375px;
border: 10px #333 solid;
}
#videoElement {
width: 500px;
height: 375px;
background-color: #666;
}
</style>

View File

@@ -1,5 +1,8 @@
<template>
<v-navigation-drawer v-model="drawer" app clipped color="grey lighten-4">
<v-navigation-drawer
v-bind:value="value"
v-on:input="$emit('input', $event)"
app clipped color="grey lighten-4">
<v-list dense class="grey lighten-4">
<template v-for="(item, i) in items">
<v-row v-if="item.heading" :key="i" align="center">
@@ -24,19 +27,21 @@
</v-navigation-drawer>
</template>
<script>
import { Component, Vue } from 'vue-property-decorator';
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
@Component
export default class SideBarNav extends Vue {
drawer = null;
@Prop({ required: true, default: true })
value = true;
items = [
{ title: 'Debug', icon: 'mdi-bug', route: 'debug' },
{ title: 'DNS Config', icon: 'mdi-dns', route: 'bitchns' },
{ title: 'IP Tools', icon: 'mdi-ip', route: 'myip' },
{ title: 'JWT Decoder', icon: 'mdi-code-array', route: 'jwt' },
{ title: 'Lights', icon: 'mdi-lightbulb', route: 'lights' },
{ title: 'Debug', icon: 'mdi-bug', route: 'debug' },
{ title: 'DNS Config', icon: 'mdi-dns', route: 'bitchns' },
{ title: 'Media Stuff', icon: 'mdi-filmstrip', route: 'media' },
{ title: 'IP Tools', icon: 'mdi-ip', route: 'myip' },
{ title: 'JWT Decoder', icon: 'mdi-code-array', route: 'jwt' },
{ title: 'Lights', icon: 'mdi-lightbulb', route: 'lights' },
];
}
</script>

View File

@@ -1,23 +1,23 @@
<template>
<v-app-bar app clipped-left color="#90CAF9">
<v-app-bar-nav-icon @click="drawer = !drawer"></v-app-bar-nav-icon>
<span class="title ml-3 mr-5">
Bitch&nbsp;
<span class="font-weight-light">Mints</span>
</span>
<v-spacer></v-spacer>
<v-btn icon>
<v-icon @click="signout">mdi-exit-to-app</v-icon>
</v-btn>
</v-app-bar>
<v-app-bar app clipped-left color="#90CAF9">
<v-app-bar-nav-icon @click="$emit('toggle-sidebar')"></v-app-bar-nav-icon>
<span class="title ml-3 mr-5">
Bitch&nbsp;
<span class="font-weight-light">Mints</span>
</span>
<v-spacer></v-spacer>
<v-btn icon>
<v-icon @click="signout">mdi-exit-to-app</v-icon>
</v-btn>
</v-app-bar>
</template>
<script>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
@Component
export default class TopBarNav extends Vue {
signout() {
this.$store.dispatch('logout').then(() => this.$router.push('/login'));
}
signout() {
this.$store.dispatch('logout').then(() => this.$router.push('/login'));
}
}
</script>

View File

@@ -4,10 +4,12 @@ import store from '@/store';
import Home from '../views/Home.vue';
import Login from '../views/Login.vue';
import Lights from '../views/Lights.vue';
import About from '../views/About.vue';
import Debug from '../views/Debug.vue';
import BitchNS from '../views/BitchNS.vue';
import JwtDecoder from '../views/JwtDecoder.vue';
import MyIp from '../views/MyIp.vue';
import Media from '../views/Media.vue';
Vue.use(VueRouter);
@@ -28,6 +30,14 @@ const routes: Array<RouteConfig> = [
requiresAuth: false,
},
},
{
path: '/media',
name: 'Media',
component: Media,
meta: {
requiresAuth: false,
},
},
{
path: '/bitchns',
name: 'BitchNS',
@@ -66,7 +76,7 @@ const routes: Array<RouteConfig> = [
{
path: '/about',
name: 'About',
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
component: About,
meta: {
requiresAuth: true,
},

View File

@@ -9,16 +9,6 @@
</v-col>
</v-row>
</v-container>
<!-- <v-container fluid grid-list-xl>
<v-layout row wrap>
<v-flex sm4 xs4>
<DnsUpdateForm :inrecords="dnsRecords" />
</v-flex>
<v-flex d-flex lg4 sm6 xs12>
<DnsRecordsList :inrecords="dnsRecords" />
</v-flex>
</v-layout>
</v-container>-->
</template>
<script lang="ts">
@@ -30,17 +20,17 @@ import { DnsRecord } from '@/models';
import { dnsApi } from '@/api';
@Component({
components: {
HelloWorld,
DnsRecordsList,
DnsUpdateForm,
},
components: {
HelloWorld,
DnsRecordsList,
DnsUpdateForm,
},
})
export default class BitchNS extends Vue {
dnsRecords: DnsRecord[] = [];
async mounted() {
this.dnsRecords = await dnsApi.getDnsRecords();
this.dnsRecords = await dnsApi.getDnsRecords();
}
}
</script>

0
client/src/views/CnsViwq Normal file
View File

View File

@@ -0,0 +1,17 @@
<template>
<div class="about">
<h1>Media Stuff</h1>
<VideoTest />
</div>
</template>
<script>
import { Component, Vue } from 'vue-property-decorator';
import VideoTest from '@/components/Media/VideoTest';
@Component({
components: {
VideoTest
}
})
export default class Media extends Vue{}
</script>

View File

@@ -0,0 +1,12 @@
{
"extends": "./tsconfig.json",
"include": [
".eslintrc.js",
"babel.config.js"
],
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"noEmit": true
}
}

View File

@@ -10,6 +10,7 @@
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"noImplicitAny": false,
"sourceMap": true,
"baseUrl": ".",
"types": ["webpack-env", "mocha", "chai", "vuetify"],

View File

@@ -10,5 +10,5 @@ module.exports = {
},
configureWebpack: {
devtool: 'source-map'
}
},
};

View File

@@ -24,7 +24,7 @@ migrate = Migrate()
CELERY_TASK_LIST = [
'app.tasks.hosts',
]
import flask_monitoringdashboard as dashboard
def create_app(app_name='bitchmin', config_class=Config):
logger.info('Creating app {}'.format(app_name))

View File

@@ -26,7 +26,7 @@ def check_host_records():
except NoResultFound:
bind_state = BindState(
nameserver_1_ip=platform_ip,
nameserver_1_host=os.getenv('DNS_SERVER')
nameserver_1_host=os.getenv('DNSro_SERVER')
)
db.session.add(bind_state)
db.session.commit()

View File

@@ -18,4 +18,6 @@ flower
requests
IPy
pydig
twilio
tinder
twilio
flask_monitoringdashboard