First working? version with persistent state

This commit is contained in:
Bendodroid 2023-11-10 03:24:12 +01:00
parent 4eb1f18bf5
commit 8a1cf0456a
Signed by: bendodroid
GPG key ID: 3EEE19A0F73D5FFC
3 changed files with 102 additions and 1 deletions

18
main.go
View file

@ -3,6 +3,9 @@ package main
import ( import (
"log" "log"
"net/http" "net/http"
"os"
"os/signal"
"syscall"
"gitlab.hamburg.ccc.de/ccchh/spaceapid/handlers" "gitlab.hamburg.ccc.de/ccchh/spaceapid/handlers"
"gitlab.hamburg.ccc.de/ccchh/spaceapid/util" "gitlab.hamburg.ccc.de/ccchh/spaceapid/util"
@ -15,10 +18,23 @@ func main() {
log.Println("Reading initial SpaceAPI response from", config.TemplatePath) log.Println("Reading initial SpaceAPI response from", config.TemplatePath)
spaceApiResponse := util.ParseTemplate(config.TemplatePath) spaceApiResponse := util.ParseTemplate(config.TemplatePath)
// Merge old state if present
util.MergeOldState(&spaceApiResponse)
// Register HTTP handlers // Register HTTP handlers
http.HandleFunc("/", handlers.Root(&spaceApiResponse)) http.HandleFunc("/", handlers.Root(&spaceApiResponse))
http.HandleFunc("/state/open", handlers.StateOpen(config.BAUsername, config.BAPassword, &spaceApiResponse)) http.HandleFunc("/state/open", handlers.StateOpen(config.BAUsername, config.BAPassword, &spaceApiResponse))
// Start webserver
log.Println("Starting HTTP server...") log.Println("Starting HTTP server...")
log.Fatalln(http.ListenAndServe(":8080", nil)) go log.Fatalln(http.ListenAndServe(":8080", nil))
// Wait for exit signal
sc := make(chan os.Signal, 1)
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM)
<-sc
// Save state for next run
log.Println("Saving state and shutting down...")
util.SaveCurrentState(spaceApiResponse)
} }

View file

@ -38,3 +38,10 @@ type SpaceAPIResponseV14 struct {
URL string `json:"url"` URL string `json:"url"`
} `json:"links"` } `json:"links"`
} }
type PersistentStateV14 struct {
State struct {
Open bool `json:"open"`
LastChange int64 `json:"lastchange"`
} `json:"state"`
}

View file

@ -3,13 +3,17 @@ package util
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors"
"log" "log"
"os" "os"
"path"
"slices" "slices"
"gitlab.hamburg.ccc.de/ccchh/spaceapid/types" "gitlab.hamburg.ccc.de/ccchh/spaceapid/types"
) )
const savedStateJSONPath = "/var/lib/spaceapid/spaceapid-state.json"
// ParseTemplate parses the given file and // ParseTemplate parses the given file and
func ParseTemplate(file string) (resp types.SpaceAPIResponseV14) { func ParseTemplate(file string) (resp types.SpaceAPIResponseV14) {
@ -34,3 +38,77 @@ func ParseTemplate(file string) (resp types.SpaceAPIResponseV14) {
return return
} }
// MergeOldState merges a given SpaceAPIResponse with the state saved at the time of last program exit and then deletes
// the file containing the old state.
func MergeOldState(response *types.SpaceAPIResponseV14) {
var (
err error
oldState []byte
)
// Create state directory if not present
err = os.MkdirAll(path.Dir(savedStateJSONPath), 0750)
if err != nil {
log.Fatalln("Failed creating", savedStateJSONPath, ", aborting... error:", err)
}
// Check if state.json is present
_, err = os.Stat(savedStateJSONPath)
if err != nil {
log.Println("Old state json not accessible at", savedStateJSONPath, ", skipping merge... error:", err)
if errors.Is(err, os.ErrNotExist) {
return
}
goto removeOld
}
// Read file and merge
oldState, err = os.ReadFile(savedStateJSONPath)
if err != nil {
log.Println("Error reading old state from", savedStateJSONPath, ", skipping merge... error:", err)
goto removeOld
}
err = json.Unmarshal(oldState, response)
if err != nil {
log.Println(savedStateJSONPath, "doesn't seem to contain valid data... error:", err)
goto removeOld
}
// Delete old state json
removeOld:
err = os.RemoveAll(savedStateJSONPath)
if err != nil {
log.Println("Failed to remove", savedStateJSONPath, ", continuing... error:", err)
}
}
func SaveCurrentState(response types.SpaceAPIResponseV14) {
file, err := os.OpenFile(savedStateJSONPath, os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
log.Fatalln("Failed opening", savedStateJSONPath, "while trying to save current state... error:", err)
}
defer func(file *os.File) {
_ = file.Close()
}(file)
// Create persistent state
persistentStateV14 := types.PersistentStateV14{
State: struct {
Open bool `json:"open"`
LastChange int64 `json:"lastchange"`
}{Open: response.State.Open, LastChange: response.State.LastChange},
}
// Serialize persistent state
marshal, err := json.MarshalIndent(persistentStateV14, "", "\t")
if err != nil {
log.Fatalln("Failed serializing persistent state... error:", err)
}
// Write to file
_, err = file.Write(marshal)
if err != nil {
log.Fatalln("Failed writing persistent state to file", file.Name(), "... error:", err)
}
}