init
This commit is contained in:
137
server/server.go
Normal file
137
server/server.go
Normal file
@@ -0,0 +1,137 @@
|
||||
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
|
||||
}
|
||||
|
||||
// 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: 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)
|
||||
}
|
||||
Reference in New Issue
Block a user