commit a55dd0f1080ed58a0a2dea4a11bed354ed27c424
parent 3d6bc0e631ba154d31263043b77d6d7b65d6b2d1
Author: Vetle Haflan <vetle@haflan.dev>
Date: Mon, 13 Apr 2020 14:58:09 +0200
More work on requests and request handlers
Including reportEvent and getEvents MVP and a bash script for sending
report requests with curl (pretty much the entire client application)
Diffstat:
7 files changed, 148 insertions(+), 53 deletions(-)
diff --git a/internal/events/events.go b/internal/events/events.go
@@ -5,7 +5,6 @@ import (
"fmt"
"log"
"sermoni/internal/database"
- "sermoni/internal/services"
"strconv"
"go.etcd.io/bbolt"
@@ -69,14 +68,9 @@ func Delete(idInt uint64) error {
})
}
-// Add a new service event if the token matches any services in the database
-// The event.Service will be set to the service ID automatically, given a valid token
-func Add(serviceToken string, event *Event) error {
+// Add persists a new event to database after generating an ID for it
+func Add(event *Event) error {
db := database.GetDB()
- service := services.GetByToken(serviceToken)
- if service == nil {
- return errors.New("no service found for the given token")
- }
return db.Update(func(tx *bbolt.Tx) error {
b := tx.Bucket(database.BucketKeyEvents)
if b == nil {
@@ -90,7 +84,6 @@ func Add(serviceToken string, event *Event) error {
}
id := []byte(strconv.FormatUint(idInt, 10))
event.ID = idInt
- event.Service = service.ID
// Create the event bucket and fill it with data from event
eb, err := b.CreateBucket(id)
diff --git a/internal/events/events_test.go b/internal/events/events_test.go
@@ -3,6 +3,7 @@ package events
import (
"fmt"
"os"
+ "sermoni/internal/config"
"sermoni/internal/database"
"sermoni/internal/services"
"testing"
@@ -79,12 +80,9 @@ func TestMain(m *testing.M) {
// (Re)create the test database
testDB := "test.db"
os.Remove(testDB)
- var err error
- if err = database.Init(testDB); err != nil {
- print("Couldn't initialize test database")
- os.Exit(1)
- }
- err = services.Add(serviceToken, &services.Service{
+ database.Open(testDB)
+ config.InitConfig()
+ err := services.Add(serviceToken, &services.Service{
Name: "test @ dev-laptop",
Description: "Service used for testing only",
})
diff --git a/internal/http/events.go b/internal/http/events.go
@@ -2,10 +2,16 @@ package http
import (
"encoding/json"
+ "fmt"
+ "io/ioutil"
+ "log"
"net/http"
"sermoni/internal/events"
+ "sermoni/internal/services"
)
+const headerServiceToken = "Service-Token"
+
func getEvents(w http.ResponseWriter, r *http.Request) {
// Create a mapping from service id to name
/* Eventually?
@@ -17,8 +23,8 @@ func getEvents(w http.ResponseWriter, r *http.Request) {
*/
events := events.GetAll()
- json.Marshal(events)
- return
+ b, _ := json.Marshal(events)
+ w.Write(b)
}
// TODO: This is still a placeholder!
@@ -29,9 +35,37 @@ func deleteEvent(w http.ResponseWriter, r *http.Request) {
err := events.Delete(id)
fmt.Println(err)
*/
- return
}
func reportEvent(w http.ResponseWriter, r *http.Request) {
- return
+ tokens := r.Header[headerServiceToken]
+ if len(tokens) == 0 {
+ w.WriteHeader(http.StatusUnauthorized)
+ msg := fmt.Sprintf("%v: No service token given\n", http.StatusUnauthorized)
+ w.Write([]byte(msg))
+ return
+ }
+ service := services.GetByToken(tokens[0])
+ if service == nil {
+ msg := fmt.Sprintf("%v: No service for the given token\n", http.StatusUnauthorized)
+ w.Write([]byte(msg))
+ return
+ }
+ content, err := ioutil.ReadAll(r.Body)
+ check(err)
+ // TODO: This should deffo not panic on error!
+ event := new(events.Event)
+ err = json.Unmarshal(content, event)
+ check(err)
+ event.Service = service.ID
+ err = events.Add(event)
+ check(err)
+ log.Printf("New event registered, id = %v\n", event.ID)
}
+
+// For later reference:
+// Instead of anonymous structs for each JSON object to be written,
+// a simple map can be used instead, something like (untested!):
+// b, _ := json.Marshal(&map[string]string{
+// "message": http.StatusUnauthorized + ": No valid token"
+// })
diff --git a/internal/http/http.go b/internal/http/http.go
@@ -33,8 +33,10 @@ func StartServer(port int) {
router.Handle("/events", auth(getEvents)).Methods(http.MethodGet)
router.Handle("/events/{id:[0-9]+}", auth(deleteEvent)).Methods(http.MethodDelete)
+ // POSTS to /events is how services should report events to the monitor
+ // This should not be accessible from the website
+ router.HandleFunc("/events", reportEvent).Methods(http.MethodPost)
- router.HandleFunc("/report", reportEvent).Methods(http.MethodPost)
http.Handle("/", router)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), nil))
}
diff --git a/internal/services/services_test.go b/internal/services/services_test.go
@@ -3,6 +3,7 @@ package services
import (
"fmt"
"os"
+ "sermoni/internal/config"
"sermoni/internal/database"
"strconv"
"testing"
@@ -134,10 +135,8 @@ func TestMain(m *testing.M) {
// (Re)create the test database
testDB := "test.db"
os.Remove(testDB)
- if err := database.Init(testDB); err != nil {
- print("Couldn't initialize test database")
- os.Exit(1)
- }
+ database.Open(testDB)
+ config.InitConfig()
defer database.Close()
os.Exit(m.Run())
}
diff --git a/report.sh b/report.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+# Script for reporting events to sermoni
+
+sermoni=http://localhost:8080
+
+token=$1
+status=$2
+title=$3
+details=$4
+timestamp="$(date +%s)"
+
+read -d '' JSONDATA << EOF
+{
+ \"status\": \"$status\",
+ \"timestamp\": $timestamp,
+ \"title\": \"$title\",
+ \"details\": \"$details\"
+}
+EOF
+
+curl -H "Service-Token: $token" -d "$JSONDATA" $sermoni/events
diff --git a/ui/src/requests.js b/ui/src/requests.js
@@ -1,4 +1,80 @@
+
+export default {
+ init,
+ login,
+ getServices,
+ postService,
+ deleteService,
+}
+
+/***** Login and authentication *****/
+
+var csrfToken;
+
+function init(successHandler, errorHandler) {
+ request({
+ url: "/init",
+ success: (data) => {
+ csrfToken = data.csrftoken;
+ if (successHandler) {
+ successHandler(data);
+ }
+ },
+ error: errorHandler
+ });
+}
+
+function login(passphrase, successHandler, errorHandler) {
+ request({
+ url: "/login",
+ method: "POST",
+ data: { passphrase: passphrase },
+ success: successHandler,
+ error: errorHandler
+ });
+}
+
+function logout(passphrase, successHandler, errorHandler) {
+ request({
+ url: "/logout",
+ method: "POST",
+ success: successHandler,
+ error: errorHandler
+ });
+}
+
+
+/***** Service management *****/
+
+function getServices() {
+
+}
+
+function postService(service) {
+
+}
+
+function deleteService(id) {
+
+}
+
+/***** Event management *****/
+
+// (TODO)
+
+function getEvents() {
+
+}
+
+function deleteEvent(id) {
+
+}
+
+
+
+/***** Request utils *****/
+
/**
* Create URL parameters from a JSON object
*/
@@ -17,8 +93,6 @@ function params(parameterObject) {
return parameters.toString();
}
-var csrfToken;
-
// Send a request to server. Takes request description, jsonRequest,
// on the following format
// {
@@ -66,30 +140,3 @@ function request(jsonRequest) {
}
}
-function init(successHandler, errorHandler) {
- request({
- url: "/init",
- success: (data) => {
- csrfToken = data.csrftoken;
- if (successHandler) {
- successHandler(data);
- }
- },
- error: errorHandler
- });
-}
-
-function login(passphrase, successHandler, errorHandler) {
- request({
- url: "/login",
- method: "POST",
- data: { passphrase: passphrase },
- success: successHandler,
- error: errorHandler
- });
-}
-
-export default {
- init,
- login,
-}