sermoni

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

commit a5028d431073a0af539260308139f5f9e188d8d7
parent f4a707d5674f154a2eba0cb5fc796249fb282c4d
Author: Vetle Haflan <vetle@haflan.dev>
Date:   Sat, 11 Apr 2020 22:41:47 +0200

More fancy frontend work

Diffstat:
M.gitignore | 1+
Mcmd/sermoni/main.go | 4+++-
Ainternal/http/http.go | 19+++++++++++++++++++
Aui/dockerdev.sh | 13+++++++++++++
Aui/generate.sh | 27+++++++++++++++++++++++++++
Mui/src/App.vue | 25+++++++++++++++++--------
Aui/src/Eye.vue | 21+++++++++++++++++++++
Aui/src/Login.vue | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 173 insertions(+), 9 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,3 +1,4 @@ sermoni.db **/test.db node_modules/ +html.go diff --git a/cmd/sermoni/main.go b/cmd/sermoni/main.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "sermoni/internal/database" + smhttp "sermoni/internal/http" ) var ( @@ -16,9 +17,10 @@ func main() { // TODO: Use getopt package instead of flags? //password := flag.String("w", "", "Password for the web interface") flag.Parse() + defer database.Close() if err := database.Init(*dbFile); err != nil { log.Fatal(err) } - defer database.Close() fmt.Printf("Server running on port %v\n", *port) + smhttp.StartServer(*port) } diff --git a/internal/http/http.go b/internal/http/http.go @@ -0,0 +1,19 @@ +package http + +import ( + "fmt" + "log" + "net/http" +) + +// StartServer starts the server at the given port +func StartServer(port int) { + http.HandleFunc("/", staticHandler) + log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), nil)) +} + +func staticHandler(res http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + res.Write(websiteHTML) + return +} diff --git a/ui/dockerdev.sh b/ui/dockerdev.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# TODO: This is probably common for all slash apps, so it can be moved into +# qumpweb/dev or something + +DOCKERCMD="docker run -it -v $PWD:/vue -w /vue node:alpine /bin/sh -c " + +if [ ! -d "node_modules" ]; then + $DOCKERCMD "npm install; npm run build-dev" +else + $DOCKERCMD "npm run build-dev" +fi + diff --git a/ui/generate.sh b/ui/generate.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# index.html for development +cat <<EOF > index.html +<html> +<head> +<meta name="viewport" content="width=device-width,initial-scale=1"> +</head> +<body> +<div id="app"></div> +<script> +$(cat ./dist/sermoni.js) +</script> +<body> +</html> +EOF + +# html.go for production, ` must be replaced by `+"`"+` +cp index.html html.go +sed -i 's/`/`\+"`"\+`/g' html.go +cat <<EOF > html.go +package http + +var websiteHTML = []byte(\` +$(cat html.go) +\`) +EOF diff --git a/ui/src/App.vue b/ui/src/App.vue @@ -3,40 +3,49 @@ <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 @click="togglePage" style="margin-left: auto;"> + <Eye :service-view="this.serviceView"/> </div> </div> </header> <main> - <component :is="page"/> + <component :is="page" @login="login"/> </main> </div> </template> <script> + import Eye from "./Eye.vue"; + import Login from "./Login.vue"; import Events from "./Events.vue"; import Services from "./Services.vue"; export default { name: "App", - components: {Events, Services}, + components: {Login, Eye, Events, Services}, data() { return { - page: Events, + page: Login, + serviceView: false }; }, methods: { + login() { + this.page = Events; + }, togglePage() { // Should do nothing when on login page if (this.page === Events) { this.page = Services; + this.serviceView = true; } else if (this.page === Services) { this.page = Events; + this.serviceView = false; } } + }, + mounted() { + // TODO: Send request to server to figure out if an authenticated + // session is active } } </script> diff --git a/ui/src/Eye.vue b/ui/src/Eye.vue @@ -0,0 +1,21 @@ +<template> + <svg :stroke="color" :fill="color" width="24" height="24" xmlns="http://www.w3.org/2000/svg" :fill-rule="fillRule" clip-rule="evenodd"><path d="M12.01 20c-5.065 0-9.586-4.211-12.01-8.424 2.418-4.103 6.943-7.576 12.01-7.576 5.135 0 9.635 3.453 11.999 7.564-2.241 4.43-6.726 8.436-11.999 8.436zm-10.842-8.416c.843 1.331 5.018 7.416 10.842 7.416 6.305 0 10.112-6.103 10.851-7.405-.772-1.198-4.606-6.595-10.851-6.595-6.116 0-10.025 5.355-10.842 6.584zm10.832-4.584c2.76 0 5 2.24 5 5s-2.24 5-5 5-5-2.24-5-5 2.24-5 5-5zm0 1c2.208 0 4 1.792 4 4s-1.792 4-4 4-4-1.792-4-4 1.792-4 4-4z"/></svg> +</template> +<script> + export default { + name: "Eye", + props: { + serviceView: Boolean + }, + computed: { + // Modify the image if in service view + fillRule() { + // This works some times, don't know why + return this.serviceView ? "nonzero" : "evenodd" + }, + color() { + return this.serviceView ? "#77b" : "#bbf" + } + } + } +</script> diff --git a/ui/src/Login.vue b/ui/src/Login.vue @@ -0,0 +1,72 @@ +<template> + <div style="width: 100%;"> + <div style="max-width: 400px;"> + <input ref="ppinput" + v-model="passphrase" + type="password" + placeholder="passphrase"> + <button @click="enter">&gt;</button> + </div> + </div> +</template> + +<script> + export default { + name: "Login", + data() { + return { + passphrase: "" + } + }, + methods: { + enter() { + if (this.passphrase == "correct") { + this.$emit("login"); + } + } + }, + mounted() { + this.$refs.ppinput.focus(); + this.$refs.ppinput.addEventListener("keypress", (e) => { + if (e.keyCode === 13) { + e.preventDefault(); + this.enter(); + } + }); + } + } + +</script> + +<style scoped> +div { + display: flex; + align-items: center +} +input { + border-width: 0 0 1px; + border-color: #bbf; + background-color: inherit; + outline-width: 0; + flex: 1; + margin-bottom: 3px; + font-size: 2em; + width: 100%; + max-width: 400px; +} +input:focus { + outline-width: 0; +} +input::placeholder { + color: #bbf; +} +button { + background-color: inherit; + border: none; + border-radius: 3px; + color: #bbf; + outline-width: 0; + text-decoration: none; + text-align: center; +} +</style>