Update für Persistenz

This commit is contained in:
2025-10-07 20:02:33 +02:00
parent 9f4c081e06
commit b0af9c1444
2 changed files with 85 additions and 11 deletions

86
main.go
View File

@@ -13,10 +13,12 @@ import (
"net/http"
"os"
"os/exec"
"os/signal"
"path/filepath"
"runtime"
"strings"
"sync"
"syscall"
"time"
)
@@ -62,6 +64,8 @@ var (
taskIndex = map[string]*Task{}
started time.Time
csrfKey []byte
cfgPath = "tasks.json" // <- Dateipfad zentral
cfgMu sync.Mutex // <- schützt Speichervorgang
)
//go:embed web/*
@@ -329,6 +333,11 @@ func handleSetInterval(w http.ResponseWriter, r *http.Request) {
return
}
t.setInterval(interval, enable)
// PERSISTENZ
// auch die Werte im cfg.Tasks stecken bereits in t, also genügt:
persistOrLog()
io.WriteString(w, "OK")
}
@@ -344,6 +353,10 @@ func handleToggle(w http.ResponseWriter, r *http.Request) {
t.Enabled = enable
t.mutex.Unlock()
t.scheduleNext()
// PERSISTENZ
persistOrLog()
io.WriteString(w, "OK")
}
@@ -399,16 +412,62 @@ func handleLogs(w http.ResponseWriter, r *http.Request) {
/* ====== Init/Load/Serve ====== */
func loadConfig() {
f, err := os.Open("tasks.json")
func saveConfigAtomic() error {
cfgMu.Lock()
defer cfgMu.Unlock()
// Schön formatiert schreiben
b, err := json.MarshalIndent(cfg, "", " ")
if err != nil {
log.Fatalf("tasks.json nicht gefunden: %v", err)
return err
}
// In dasselbe Verzeichnis wie cfgPath schreiben (wichtig für Rename-Atomizität)
dir := filepath.Dir(cfgPath)
base := filepath.Base(cfgPath)
tmp := filepath.Join(dir, "."+base+".tmp")
// Temp-Datei erzeugen, schreiben, flushen
f, err := os.OpenFile(tmp, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
if err != nil {
return err
}
if _, err := f.Write(b); err != nil {
f.Close()
return err
}
if err := f.Sync(); err != nil {
f.Close()
return err
}
if err := f.Close(); err != nil {
return err
}
// Atomisch ersetzen
if err := os.Rename(tmp, cfgPath); err != nil {
return err
}
return nil
}
// bequemer Helfer zum Loggen statt Abbrechen
func persistOrLog() {
if err := saveConfigAtomic(); err != nil {
log.Printf("WARN: Konnte %s nicht speichern: %v", cfgPath, err)
}
}
func loadConfig() {
f, err := os.Open(cfgPath)
if err != nil {
log.Fatalf("%s nicht gefunden: %v", cfgPath, err)
}
defer f.Close()
dec := json.NewDecoder(f)
dec.DisallowUnknownFields()
if err := dec.Decode(&cfg); err != nil {
log.Fatalf("tasks.json fehlerhaft: %v", err)
log.Fatalf("%s fehlerhaft: %v", cfgPath, err)
}
if cfg.Listen == "" {
@@ -495,8 +554,25 @@ func main() {
Handler: basicAuth(mux),
ReadHeaderTimeout: 10 * time.Second,
}
// GRACEFUL SHUTDOWN + PERSISTENZ
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
go func() {
<-stop
log.Println("Beende... speichere Konfiguration.")
persistOrLog()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_ = server.Shutdown(ctx)
}()
log.Printf("Weboberfläche: http://%s (Basic Auth aktiv)", addr)
log.Fatal(server.ListenAndServe())
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatal(err)
}
log.Println("Server gestoppt.")
}
/* ====== Embedded HTML (Tailwind-lite via CDN) ====== */