Files
uopi/internal/server/server.go
T
Martino Ferrari 9aa89cc0cf Initial commit
2026-04-24 15:09:14 +02:00

64 lines
1.4 KiB
Go

package server
import (
"context"
"io/fs"
"log/slog"
"net/http"
"time"
)
type Server struct {
httpServer *http.Server
log *slog.Logger
}
// New creates an HTTP server that serves the embedded frontend and a health
// check endpoint. Additional routes (API, WebSocket) will be registered in
// later phases.
func New(addr string, webFS fs.FS, log *slog.Logger) *Server {
mux := http.NewServeMux()
// Health check
mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("ok"))
})
// Serve embedded frontend for everything else
mux.Handle("/", http.FileServerFS(webFS))
return &Server{
httpServer: &http.Server{
Addr: addr,
Handler: mux,
ReadTimeout: 10 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 120 * time.Second,
},
log: log,
}
}
// Start listens and serves until ctx is cancelled.
func (s *Server) Start(ctx context.Context) error {
s.log.Info("listening", "addr", s.httpServer.Addr)
errCh := make(chan error, 1)
go func() {
if err := s.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
errCh <- err
}
}()
select {
case err := <-errCh:
return err
case <-ctx.Done():
shutCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
s.log.Info("shutting down")
return s.httpServer.Shutdown(shutCtx)
}
}