feat: config hot-reload — save restarts services automatically
This commit is contained in:
parent
e790855f69
commit
02ee23281c
4 changed files with 39 additions and 3 deletions
13
api.go
13
api.go
|
|
@ -138,6 +138,19 @@ func (s *Server) handleConfig(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
// handleConfigReload restarts internal services with the current config.
|
||||
// POST /api/config/reload
|
||||
func (s *Server) handleConfigReload(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
jsonResponse(w, http.StatusMethodNotAllowed, APIResponse{Error: "method not allowed"})
|
||||
return
|
||||
}
|
||||
if s.onReload != nil {
|
||||
s.onReload()
|
||||
}
|
||||
jsonResponse(w, http.StatusOK, APIResponse{Success: true, Data: "reloaded"})
|
||||
}
|
||||
|
||||
// handleStream proxies MJPEG streams from go2rtc to avoid cross-origin issues.
|
||||
// GET /stream/{cam_id}?type=sub (default: sub stream via go2rtc)
|
||||
func (s *Server) handleStream(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
|||
19
main.go
19
main.go
|
|
@ -77,6 +77,25 @@ func main() {
|
|||
|
||||
// Start the HTTP server (main port, with auth).
|
||||
app.server, _ = NewServer(cfg)
|
||||
app.server.onReload = func() {
|
||||
log.Println("Config reload — restarting services...")
|
||||
if app.go2rtc != nil { app.go2rtc.Stop() }
|
||||
if app.snapshots != nil { app.snapshots.StopAll() }
|
||||
if app.recorder != nil { app.recorder.StopAll() }
|
||||
// Restart with the updated config from the server's in-memory copy.
|
||||
liveCfg := app.server.appConfig
|
||||
if liveCfg.Go2RTC.Enabled {
|
||||
app.go2rtc = NewGo2RTCManager(liveCfg.Go2RTC)
|
||||
app.go2rtc.Start(liveCfg.Cameras)
|
||||
}
|
||||
snaps := NewSnapshotEngine(liveCfg.Storage)
|
||||
snaps.StartAll(liveCfg.Cameras)
|
||||
app.snapshots = snaps
|
||||
rec := NewRecorderManager(liveCfg.Storage)
|
||||
rec.StartAll(liveCfg.Cameras)
|
||||
app.recorder = rec
|
||||
log.Println("Config reload — services restarted.")
|
||||
}
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
errCh <- app.server.ListenAndServe()
|
||||
|
|
|
|||
|
|
@ -378,14 +378,16 @@ document.getElementById('btn-save').addEventListener('click', async () => {
|
|||
});
|
||||
const j = await r.json();
|
||||
if (j.success) {
|
||||
// Reload cameras from the server (in-memory config is now updated).
|
||||
// Reload cameras from the server.
|
||||
await loadCameras();
|
||||
renderLiveGrid();
|
||||
renderPlaybackCameras();
|
||||
renderCameraCards();
|
||||
status.textContent = '✅ Cameras saved! Switching to Live Wall...';
|
||||
status.textContent = '✅ Saved! Restarting services...';
|
||||
status.style.color = 'var(--green)';
|
||||
setTimeout(() => switchTab('live'), 800);
|
||||
// Trigger backend reload of cameras.
|
||||
try { await fetch(API + '/config/reload', { method: 'POST' }); } catch(e) {}
|
||||
setTimeout(() => switchTab('live'), 1500);
|
||||
} else {
|
||||
status.textContent = '❌ Save failed: ' + j.error;
|
||||
status.style.color = 'var(--red)';
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ type Server struct {
|
|||
appConfig *Config
|
||||
mu sync.RWMutex
|
||||
sessions *SessionStore
|
||||
onReload func() // called when config is saved via API
|
||||
}
|
||||
|
||||
// NewServer creates and configures the HTTP server.
|
||||
|
|
@ -116,6 +117,7 @@ func (s *Server) registerRoutes() {
|
|||
s.mux.HandleFunc("/api/cameras", s.handleCameras)
|
||||
s.mux.HandleFunc("/api/cameras/", s.handleCameraByID)
|
||||
s.mux.HandleFunc("/api/config", s.handleConfig)
|
||||
s.mux.HandleFunc("/api/config/reload", s.handleConfigReload)
|
||||
s.mux.HandleFunc("/api/scan", s.handleScan)
|
||||
s.mux.HandleFunc("/api/status", s.handleStatus)
|
||||
s.mux.HandleFunc("/api/recordings", s.handleRecordings)
|
||||
|
|
|
|||
Loading…
Reference in a new issue