package server import ( "context" "log" "net/http" "os" "os/signal" "sync" "syscall" "time" "AbstractWizard/audit" ) // Mode represents the server operating mode. type Mode int const ( ModeInit Mode = iota // init: read/write allowed ModeReadOnly // readonly: only reads allowed ) func (m Mode) String() string { switch m { case ModeInit: return "init" case ModeReadOnly: return "readonly" default: return "unknown" } } // ParseMode converts a string to a Mode. func ParseMode(s string) (Mode, bool) { switch s { case "init": return ModeInit, true case "readonly": return ModeReadOnly, true default: return 0, false } } // AppConfig holds environment-based configuration. type AppConfig struct { ConfigDir string ListenAddr string MaxBackups int CORSOrigins []string } // Server is the main HTTP server. type Server struct { cfg AppConfig audit *audit.Logger mode Mode modeMu sync.RWMutex srv *http.Server } // New creates a new Server. func New(cfg AppConfig, auditLog *audit.Logger) *Server { s := &Server{ cfg: cfg, audit: auditLog, mode: ModeInit, } mux := http.NewServeMux() s.registerRoutes(mux) s.srv = &http.Server{ Addr: cfg.ListenAddr, Handler: CORSMiddleware(cfg.CORSOrigins, LoggingMiddleware(mux)), ReadTimeout: 10 * time.Second, WriteTimeout: 30 * time.Second, IdleTimeout: 60 * time.Second, } return s } // registerRoutes sets up all API routes. func (s *Server) registerRoutes(mux *http.ServeMux) { mux.HandleFunc("GET /health", s.handleHealth) mux.HandleFunc("GET /api/v1/config/{path...}", s.handleGetConfig) mux.HandleFunc("PUT /api/v1/config/{path...}", s.handlePutConfig) mux.HandleFunc("PATCH /api/v1/config/{path...}", s.handlePatchConfig) mux.HandleFunc("GET /api/v1/backups/{path...}", s.handleListBackups) mux.HandleFunc("POST /api/v1/rollback/{path...}", s.handleRollback) mux.HandleFunc("GET /api/v1/mode", s.handleGetMode) mux.HandleFunc("PUT /api/v1/mode", s.handleSetMode) } // GetMode returns the current server mode. func (s *Server) GetMode() Mode { s.modeMu.RLock() defer s.modeMu.RUnlock() return s.mode } // SetMode changes the server mode. func (s *Server) SetMode(m Mode) { s.modeMu.Lock() defer s.modeMu.Unlock() s.mode = m } // ListenAndServe starts the HTTP server and blocks until shutdown. func (s *Server) ListenAndServe() error { stop := make(chan os.Signal, 1) signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM) errCh := make(chan error, 1) go func() { log.Printf("listening on %s", s.cfg.ListenAddr) if err := s.srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { errCh <- err } close(errCh) }() select { case sig := <-stop: log.Printf("received signal %v, shutting down", sig) case err := <-errCh: if err != nil { return err } } ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() return s.srv.Shutdown(ctx) }