package api import ( "log" "net/http" "os" "time" ) // restartDelay gives the in-flight HTTP response time to flush before the process exits. const restartDelay = 500 * time.Millisecond // gracefulSelfRestart schedules a clean process exit after a short delay. The container // runs with `restart: unless-stopped`, so exiting 0 makes Docker start a fresh process // that re-reads controller.yaml. This is how a config-apply (e.g. a rotated Cloudflare // API token) and the manual restart button actually take effect: singletons such as the // Cloudflare client are built once at startup and are not reloaded in-process. func gracefulSelfRestart(logger *log.Logger) { go func() { time.Sleep(restartDelay) if logger != nil { logger.Println("[INFO] [api] Graceful self-restart: exiting (0) for container restart") } os.Exit(0) }() } // flushResponse flushes buffered output to the client if the writer supports it, so the // caller receives the response body before a subsequent self-restart kills the process. func flushResponse(w http.ResponseWriter) { if f, ok := w.(http.Flusher); ok { f.Flush() } } // selfRestart handles POST /api/selfrestart — a customer-facing self-serve restart so a // user can recover the controller without rebooting the whole guest. Auth + CSRF are // applied by the /api/ mux mount (same protection as every other state-changing endpoint). // Responds first, flushes, then triggers the graceful restart. func (r *Router) selfRestart(w http.ResponseWriter, _ *http.Request) { r.logger.Println("[INFO] [api] Manual controller restart requested") writeJSON(w, http.StatusOK, apiResponse{OK: true, Message: "Újraindítás folyamatban…"}) flushResponse(w) if r.restart != nil { r.restart() } }