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 = { module.exports = {
root: true,
root: true, env: {
node: 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
}
}, },
{
files: [ extends: ['plugin:vue/essential', 'eslint:recommended', '@vue/typescript/recommended'],
'**/__tests__/*.{j,t}s?(x)', parserOptions: {
'**/tests/unit/**/*.spec.{j,t}s?(x)' ecmaVersion: 2020,
], project: ['./tsconfig.json', './tsconfig.eslint.json'],
env: { },
mocha: true 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> <template>
<v-app id="bitchmin"> <v-app id="bitchmin">
<TopBarNav v-if="isAuthenticated"/> <TopBarNav v-on:toggle-sidebar="drawerOpen = !drawerOpen" v-if="isAuthenticated" />
<SideBarNav v-if="isAuthenticated" /> <SideBarNav v-if="isAuthenticated" v-model="drawerOpen" />
<v-main> <v-main>
<v-container fluid> <v-container fluid>
<router-view></router-view> <router-view></router-view>
</v-container> </v-container>
</v-main> </v-main>
<Footer /> <Footer />
</v-app> </v-app>
</template> </template>
<script lang="ts"> <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 import Footer from '@/components/Footer.vue'; // @ is an alias to /src
@Component({ @Component({
components: { components: {
TopBarNav, TopBarNav,
SideBarNav, SideBarNav,
Footer, Footer,
}, },
}) })
export default class App extends Vue { export default class App extends Vue {
async mounted() { drawerOpen = true;
this.$store.dispatch('loadInitialState');
}
get isAuthenticated() { async mounted() {
return this.$store.getters.isLoggedIn; this.$store.dispatch('loadInitialState');
} }
get isAuthenticated() {
return this.$store.getters.isLoggedIn;
}
} }
</script> </script>
<style> <style>
#keep .v-navigation-drawer__border { #keep .v-navigation-drawer__border {
display: none; display: none;
} }
</style> </style>

View File

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

View File

@@ -34,7 +34,7 @@ import { DnsRecord } from '@/models/dnsRecord';
import { DataApiResult } from '@/api/apiResult'; import { DataApiResult } from '@/api/apiResult';
@Component({ @Component({
name: 'DnsUpdateForm', name: 'DnsUpdateForm',
}) })
export default class DnsUpdateForm extends Vue { export default class DnsUpdateForm extends Vue {
msg = ''; msg = '';
@@ -48,52 +48,55 @@ export default class DnsUpdateForm extends Vue {
hostName = ''; hostName = '';
hostNameRules = [ hostNameRules = [
(v: string) => !!v || 'Host name is required', (v: string) => !!v || 'Host name is required',
(v: string) => (v && v.length < 253) || 'Hostname cannot exceed 253 characters', (v: string) =>
(v && v.length < 253) || 'Hostname cannot exceed 253 characters',
]; ];
ipAddress = ''; ipAddress = '';
ipAddressRules = [ ipAddressRules = [
(v: string) => !!v || 'IP address is required', (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: string) =>
v, /^(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(
) || 'Invalid IP Address', v
(v: string) => (v && v.length < 16) || 'IP address cannot exceed 16 characters', ) || 'Invalid IP Address',
(v: string) =>
(v && v.length < 16) || 'IP address cannot exceed 16 characters',
]; ];
@PropSync('inrecords') @PropSync('inrecords')
public records!: DnsRecord[]; public records!: DnsRecord[];
validate() { validate() {
// this.$refs.form.validate(); // this.$refs.form.validate();
} }
processUpdate() { processUpdate() {
dnsApi dnsApi
.updateDnsRecord(this.hostName, this.ipAddress) .updateDnsRecord(this.hostName, this.ipAddress)
.then((r: DataApiResult<DnsRecord>) => { .then((r: DataApiResult<DnsRecord>) => {
if (r.status === 'success') { if (r.status === 'success') {
this.error = ''; this.error = '';
Vue.toasted.success('Update successful'); Vue.toasted.success('Update successful');
if (r.payload) { if (r.payload) {
this.records.unshift(r.payload); this.records.unshift(r.payload);
this.$emit('update:inrecords', this.records); this.$emit('update:inrecords', this.records);
} }
this.ipAddress = ''; this.ipAddress = '';
this.hostName = ''; this.hostName = '';
} else { } else {
this.error = 'Unable to add DNS record'; this.error = 'Unable to add DNS record';
} }
}) })
.catch((e: any) => { .catch((e: any) => {
console.log('DnsUpdateForm', 'error', e); console.log('DnsUpdateForm', 'error', e);
if (e.response && e.response.data.payload) { if (e.response && e.response.data.payload) {
this.error = e.response.data.payload; this.error = e.response.data.payload;
} else { } else {
this.error = e; this.error = e;
} }
}); });
} }
} }
</script> </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> <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"> <v-list dense class="grey lighten-4">
<template v-for="(item, i) in items"> <template v-for="(item, i) in items">
<v-row v-if="item.heading" :key="i" align="center"> <v-row v-if="item.heading" :key="i" align="center">
@@ -24,19 +27,21 @@
</v-navigation-drawer> </v-navigation-drawer>
</template> </template>
<script> <script lang="ts">
import { Component, Vue } from 'vue-property-decorator'; import { Component, Prop, Vue } from 'vue-property-decorator';
@Component @Component
export default class SideBarNav extends Vue { export default class SideBarNav extends Vue {
drawer = null; @Prop({ required: true, default: true })
value = true;
items = [ items = [
{ title: 'Debug', icon: 'mdi-bug', route: 'debug' }, { title: 'Debug', icon: 'mdi-bug', route: 'debug' },
{ title: 'DNS Config', icon: 'mdi-dns', route: 'bitchns' }, { title: 'DNS Config', icon: 'mdi-dns', route: 'bitchns' },
{ title: 'IP Tools', icon: 'mdi-ip', route: 'myip' }, { title: 'Media Stuff', icon: 'mdi-filmstrip', route: 'media' },
{ title: 'JWT Decoder', icon: 'mdi-code-array', route: 'jwt' }, { title: 'IP Tools', icon: 'mdi-ip', route: 'myip' },
{ title: 'Lights', icon: 'mdi-lightbulb', route: 'lights' }, { title: 'JWT Decoder', icon: 'mdi-code-array', route: 'jwt' },
{ title: 'Lights', icon: 'mdi-lightbulb', route: 'lights' },
]; ];
} }
</script> </script>

View File

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

View File

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

View File

@@ -9,16 +9,6 @@
</v-col> </v-col>
</v-row> </v-row>
</v-container> </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> </template>
<script lang="ts"> <script lang="ts">
@@ -30,17 +20,17 @@ import { DnsRecord } from '@/models';
import { dnsApi } from '@/api'; import { dnsApi } from '@/api';
@Component({ @Component({
components: { components: {
HelloWorld, HelloWorld,
DnsRecordsList, DnsRecordsList,
DnsUpdateForm, DnsUpdateForm,
}, },
}) })
export default class BitchNS extends Vue { export default class BitchNS extends Vue {
dnsRecords: DnsRecord[] = []; dnsRecords: DnsRecord[] = [];
async mounted() { async mounted() {
this.dnsRecords = await dnsApi.getDnsRecords(); this.dnsRecords = await dnsApi.getDnsRecords();
} }
} }
</script> </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, "skipLibCheck": true,
"esModuleInterop": true, "esModuleInterop": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"noImplicitAny": false,
"sourceMap": true, "sourceMap": true,
"baseUrl": ".", "baseUrl": ".",
"types": ["webpack-env", "mocha", "chai", "vuetify"], "types": ["webpack-env", "mocha", "chai", "vuetify"],

View File

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

View File

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

View File

@@ -26,7 +26,7 @@ def check_host_records():
except NoResultFound: except NoResultFound:
bind_state = BindState( bind_state = BindState(
nameserver_1_ip=platform_ip, 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.add(bind_state)
db.session.commit() db.session.commit()

View File

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