Update für Persistenz
This commit is contained in:
86
main.go
86
main.go
@@ -13,10 +13,12 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -62,6 +64,8 @@ var (
|
|||||||
taskIndex = map[string]*Task{}
|
taskIndex = map[string]*Task{}
|
||||||
started time.Time
|
started time.Time
|
||||||
csrfKey []byte
|
csrfKey []byte
|
||||||
|
cfgPath = "tasks.json" // <- Dateipfad zentral
|
||||||
|
cfgMu sync.Mutex // <- schützt Speichervorgang
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed web/*
|
//go:embed web/*
|
||||||
@@ -329,6 +333,11 @@ func handleSetInterval(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.setInterval(interval, enable)
|
t.setInterval(interval, enable)
|
||||||
|
|
||||||
|
// PERSISTENZ
|
||||||
|
// auch die Werte im cfg.Tasks stecken bereits in t, also genügt:
|
||||||
|
persistOrLog()
|
||||||
|
|
||||||
io.WriteString(w, "OK")
|
io.WriteString(w, "OK")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,6 +353,10 @@ func handleToggle(w http.ResponseWriter, r *http.Request) {
|
|||||||
t.Enabled = enable
|
t.Enabled = enable
|
||||||
t.mutex.Unlock()
|
t.mutex.Unlock()
|
||||||
t.scheduleNext()
|
t.scheduleNext()
|
||||||
|
|
||||||
|
// PERSISTENZ
|
||||||
|
persistOrLog()
|
||||||
|
|
||||||
io.WriteString(w, "OK")
|
io.WriteString(w, "OK")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,16 +412,62 @@ func handleLogs(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
/* ====== Init/Load/Serve ====== */
|
/* ====== Init/Load/Serve ====== */
|
||||||
|
|
||||||
func loadConfig() {
|
func saveConfigAtomic() error {
|
||||||
f, err := os.Open("tasks.json")
|
cfgMu.Lock()
|
||||||
|
defer cfgMu.Unlock()
|
||||||
|
|
||||||
|
// Schön formatiert schreiben
|
||||||
|
b, err := json.MarshalIndent(cfg, "", " ")
|
||||||
if err != nil {
|
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()
|
defer f.Close()
|
||||||
dec := json.NewDecoder(f)
|
dec := json.NewDecoder(f)
|
||||||
dec.DisallowUnknownFields()
|
dec.DisallowUnknownFields()
|
||||||
if err := dec.Decode(&cfg); err != nil {
|
if err := dec.Decode(&cfg); err != nil {
|
||||||
log.Fatalf("tasks.json fehlerhaft: %v", err)
|
log.Fatalf("%s fehlerhaft: %v", cfgPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Listen == "" {
|
if cfg.Listen == "" {
|
||||||
@@ -495,8 +554,25 @@ func main() {
|
|||||||
Handler: basicAuth(mux),
|
Handler: basicAuth(mux),
|
||||||
ReadHeaderTimeout: 10 * time.Second,
|
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.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) ====== */
|
/* ====== Embedded HTML (Tailwind-lite via CDN) ====== */
|
||||||
|
|||||||
@@ -3,21 +3,19 @@
|
|||||||
"username": "admin",
|
"username": "admin",
|
||||||
"password": "change-me",
|
"password": "change-me",
|
||||||
"scripts_dir": "C:\\scripts",
|
"scripts_dir": "C:\\scripts",
|
||||||
"csrf_secret": "ersetzenMitEigenemSecret",
|
|
||||||
"tasks": [
|
"tasks": [
|
||||||
{
|
{
|
||||||
"name": "Nacht-Backup",
|
"name": "Nacht-Backup",
|
||||||
"path": "C:\\scripts\\backup.ps1",
|
"path": "C:\\scripts\\backup.ps1",
|
||||||
"interval": "24h",
|
"interval": "24h",
|
||||||
"enabled": true,
|
|
||||||
"timeout": "2h"
|
"timeout": "2h"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "IIS-Log-Rotation",
|
"name": "IIS-Log-Rotation",
|
||||||
"path": "C:\\scripts\\rotate.ps1",
|
"path": "C:\\scripts\\rotate.ps1",
|
||||||
"interval": "0",
|
"interval": "5m",
|
||||||
"enabled": false,
|
|
||||||
"timeout": "30m"
|
"timeout": "30m"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"csrf_secret": "ersetzenMitEigenemSecret"
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user