sermoni

"Service monitor" / cronjob status service
Log | Files | Refs

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:
M.gitignore | 1+
Mcmd/sermoni/main.go | 7+++++--
Aui/package.json | 19+++++++++++++++++++
Aui/src/App.vue | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aui/src/Events.vue | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aui/src/Services.vue | 14++++++++++++++
Aui/src/sermoni.js | 7+++++++
Aui/webpack.config.js | 31+++++++++++++++++++++++++++++++
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">&gt; 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); + }); + }); + } + } + ] +}