commit f4a707d5674f154a2eba0cb5fc796249fb282c4d
parent 84673d881008684688113e514c8989244fc3a901
Author: Vetle Haflan <vetle@haflan.dev>
Date: Sat, 11 Apr 2020 19:26:54 +0200
Start work on Vue frontend (ui/)
Diffstat:
8 files changed, 259 insertions(+), 2 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,2 +1,3 @@
sermoni.db
**/test.db
+node_modules/
diff --git a/cmd/sermoni/main.go b/cmd/sermoni/main.go
@@ -7,10 +7,13 @@ import (
"sermoni/internal/database"
)
+var (
+ port = flag.Int("p", 8080, "Port")
+ dbFile = flag.String("d", "sermoni.db", "Database file")
+)
+
func main() {
// TODO: Use getopt package instead of flags?
- port := flag.Int("p", 8080, "Port")
- dbFile := flag.String("d", "sermoni.db", "Database file")
//password := flag.String("w", "", "Password for the web interface")
flag.Parse()
if err := database.Init(*dbFile); err != nil {
diff --git a/ui/package.json b/ui/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "sermoni-ui",
+ "version": "0.0.0",
+ "scripts": {
+ "build": "webpack --config webpack.config.js --mode production",
+ "build-dev": "webpack --config webpack.config.js --mode development --watch"
+ },
+ "devDependencies": {
+ "vue": "^2.6.10",
+ "vue-loader": "^15.7.2",
+ "vue-template-compiler": "^2.6.10",
+ "@babel/core": "^7.7.5",
+ "@babel/preset-env": "^7.7.6",
+ "babel-loader": "^8.0.6",
+ "css-loader": "^3.3.0",
+ "webpack": "^4.41.2",
+ "webpack-cli": "^3.3.10"
+ }
+}
diff --git a/ui/src/App.vue b/ui/src/App.vue
@@ -0,0 +1,77 @@
+<template>
+ <div>
+ <header>
+ <div id="bar">
+ <div style="font-size: 1.5em; color: #bbf">> sermoni</div>
+ <!-- color #bbf instead? -->
+ <div style="margin-left: auto;">
+ <img src="dist/eye.png"
+ @click="togglePage"
+ style="height: 3em; opacity: 0.3;">
+ </div>
+ </div>
+ </header>
+ <main>
+ <component :is="page"/>
+ </main>
+ </div>
+</template>
+
+<script>
+ import Events from "./Events.vue";
+ import Services from "./Services.vue";
+ export default {
+ name: "App",
+ components: {Events, Services},
+ data() {
+ return {
+ page: Events,
+ };
+ },
+ methods: {
+ togglePage() {
+ // Should do nothing when on login page
+ if (this.page === Events) {
+ this.page = Services;
+ } else if (this.page === Services) {
+ this.page = Events;
+ }
+ }
+ }
+ }
+</script>
+
+<style>
+body {
+ font-family: Roboto, sans-serif;
+ background-color: #FAFAFA;
+ margin: 0;
+}
+header {
+ z-index: 1000;
+ background-color: #eef;
+ border-bottom: 1px solid rgba(0,0,0,.075);
+ -webkit-box-shadow: 0 0 10px rgba(0,0,0,.1);
+ box-shadow: 0 0 10px rgba(0,0,0,.1);
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ padding: 0;
+ height: 4em;
+ display: flex;
+ align-items: center;
+}
+#bar {
+ width: 100%;
+ display: flex;
+ margin: 1em;
+ align-items: center;
+}
+main {
+ margin: 4em 0 0 0;
+ width: calc(100%-1em);
+ min-height: 1em;
+ padding: 1em;
+}
+</style>
diff --git a/ui/src/Events.vue b/ui/src/Events.vue
@@ -0,0 +1,105 @@
+<template>
+ <div class="events-wrapper">
+ <div v-for="e in events">
+ <div class="event"
+ style="display: flex;"
+ :style="e.style">
+ <div class="event-field">{{ e.service }}</div>
+ <!-- TODO: Include VueMQ
+ <mq-layout mq="md+">
+ <div class="event-field">{{ e.title }}</div>
+ </mq-layout>
+ -->
+ </div>
+ <div v-show="false"> more info here </div>
+ </div>
+ </div>
+</template>
+
+<script>
+ export default {
+ name: "Events",
+ data() {
+ return {
+ testdata: [{
+ service: "backup-files @ haflan.dev",
+ timestamp: 1586459792925,
+ title: "Server reported success",
+ status: "ok"
+ },{
+ service: "backup-gitlab @ haflan.dev",
+ timestamp: 1586459793155,
+ title: "Server reported success",
+ status: "ok"
+ },{
+ service: "backup-qumps @ haflan.dev",
+ timestamp: 1586459793285,
+ status: "warning",
+ title: "Memory almost full",
+ message: "df -h reports that less than 1GB is available"
+ },{
+ service: "backup-offsite @ work-computer",
+ timestamp: 1586459794385,
+ status: "error",
+ title: "Expectation not met" // These are read from a default title thingy
+ },{
+ service: "ssh @ haflan.dev",
+ timestamp: 1586459794385,
+ status: "info",
+ title: "SSH server login",
+ message: "User vetle logged in from IP 192.168.10.105"
+ }],
+ statusStyling: {
+ "ok": { color: "#000", backgroundColor: "#c3e6cb" },
+ "warning": { color: "#000", backgroundColor: "#ffeeba" },
+ "error": { color: "#000", backgroundColor: "#f5c6cb" },
+ "info": { color: "#000", backgroundColor: "#fff" }
+ }
+ };
+ },
+ methods: {
+ statusStyle(status) {
+ const style = this.statusStyling[status];
+ if (style) {
+ return style;
+ } else {
+ return { color: "#fff", backgroundColor: "#000" };
+ }
+ }
+ },
+ computed: {
+ events() {
+ return this.testdata.map(e => {
+ return {
+ ...e,
+ style: this.statusStyle(e.status)
+ };
+ });
+ }
+ }
+ }
+
+</script>
+
+<style scoped>
+.event {
+ height: 3em;
+ /*
+ margin-bottom: 0.5em;
+ -webkit-box-shadow: 0 0 10px rgba(0,0,0,.1);
+ box-shadow: 0 0 10px rgba(0,0,0,.1);
+ */
+ padding: .75rem;
+ border: 1px solid rgba(0,0,0,.125);
+ box-sizing: border-box;
+ overflow-x: scroll;
+ white-space: nowrap;
+}
+.event-field {
+ flex: 1;
+}
+.events-wrapper {
+ margin: 0;
+ padding: 0;
+}
+</style>
diff --git a/ui/src/Services.vue b/ui/src/Services.vue
@@ -0,0 +1,14 @@
+<template>
+ <div class="services-wrapper">
+ nothing here yet
+ </div>
+</template>
+
+<script>
+ export default {
+ name: "Services"
+ }
+</script>
+
+<style scoped>
+</style>
diff --git a/ui/src/sermoni.js b/ui/src/sermoni.js
@@ -0,0 +1,7 @@
+import Vue from "vue";
+import App from "./App.vue";
+export const app = new Vue({
+ el: "#app",
+ render: h => h(App),
+});
+
diff --git a/ui/webpack.config.js b/ui/webpack.config.js
@@ -0,0 +1,31 @@
+const VueLoaderPlugin = require('vue-loader/lib/plugin')
+const exec = require("child_process").exec;
+
+module.exports = {
+ entry: "./src/sermoni.js",
+ output: {
+ filename: "sermoni.js"
+ },
+ module: {
+ rules: [
+ {test: /\.js$/, use: 'babel-loader'},
+ {test: /\.vue$/, use: 'vue-loader'},
+ {test: /\.css$/, use: ['vue-style-loader', 'css-loader']},
+ ]
+ },
+ plugins: [
+ new VueLoaderPlugin(),
+ // Ad-hoc plugin for running script automatically
+ // Thanks to https://stackoverflow.com/a/49786887
+ {
+ apply: (compiler) => {
+ compiler.hooks.afterEmit.tap("AfterEmitPlugin", (compilation) => {
+ exec("./generate.sh", (err, stdout, stderr) => {
+ if (stdout) process.stdout.write(stdout);
+ if (stderr) process.stderr.write(stderr);
+ });
+ });
+ }
+ }
+ ]
+}