Core infrastructure: - Uber fx dependency injection - Chi router with middleware stack - SQLite database with embedded migrations - Embedded templates and static assets - Structured logging with slog Features implemented: - Authentication (login, logout, session management, argon2id hashing) - App management (create, edit, delete, list) - Deployment pipeline (clone, build, deploy, health check) - Webhook processing for Gitea - Notifications (ntfy, Slack) - Environment variables, labels, volumes per app - SSH key generation for deploy keys Server startup: - Server.Run() starts HTTP server on configured port - Server.Shutdown() for graceful shutdown - SetupRoutes() wires all handlers with chi router
122 lines
2.7 KiB
Go
122 lines
2.7 KiB
Go
// Package server provides the HTTP server.
|
|
package server
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"log/slog"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"go.uber.org/fx"
|
|
|
|
"git.eeqj.de/sneak/upaas/internal/config"
|
|
"git.eeqj.de/sneak/upaas/internal/globals"
|
|
"git.eeqj.de/sneak/upaas/internal/handlers"
|
|
"git.eeqj.de/sneak/upaas/internal/logger"
|
|
"git.eeqj.de/sneak/upaas/internal/middleware"
|
|
)
|
|
|
|
// Params contains dependencies for Server.
|
|
type Params struct {
|
|
fx.In
|
|
|
|
Logger *logger.Logger
|
|
Globals *globals.Globals
|
|
Config *config.Config
|
|
Middleware *middleware.Middleware
|
|
Handlers *handlers.Handlers
|
|
}
|
|
|
|
// shutdownTimeout is how long to wait for graceful shutdown.
|
|
const shutdownTimeout = 30 * time.Second
|
|
|
|
// readHeaderTimeout is the maximum duration for reading request headers.
|
|
const readHeaderTimeout = 10 * time.Second
|
|
|
|
// Server is the HTTP server.
|
|
type Server struct {
|
|
startupTime time.Time
|
|
port int
|
|
log *slog.Logger
|
|
router *chi.Mux
|
|
httpServer *http.Server
|
|
params Params
|
|
mw *middleware.Middleware
|
|
handlers *handlers.Handlers
|
|
}
|
|
|
|
// New creates a new Server instance.
|
|
func New(lifecycle fx.Lifecycle, params Params) (*Server, error) {
|
|
srv := &Server{
|
|
port: params.Config.Port,
|
|
log: params.Logger.Get(),
|
|
params: params,
|
|
mw: params.Middleware,
|
|
handlers: params.Handlers,
|
|
}
|
|
|
|
lifecycle.Append(fx.Hook{
|
|
OnStart: func(_ context.Context) error {
|
|
srv.startupTime = time.Now()
|
|
go srv.Run()
|
|
|
|
return nil
|
|
},
|
|
OnStop: func(ctx context.Context) error {
|
|
return srv.Shutdown(ctx)
|
|
},
|
|
})
|
|
|
|
return srv, nil
|
|
}
|
|
|
|
// Run starts the HTTP server.
|
|
func (s *Server) Run() {
|
|
s.SetupRoutes()
|
|
|
|
listenAddr := fmt.Sprintf(":%d", s.port)
|
|
s.httpServer = &http.Server{
|
|
Addr: listenAddr,
|
|
Handler: s,
|
|
ReadHeaderTimeout: readHeaderTimeout,
|
|
}
|
|
|
|
s.log.Info("http server starting", "addr", listenAddr)
|
|
|
|
err := s.httpServer.ListenAndServe()
|
|
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
s.log.Error("http server error", "error", err)
|
|
}
|
|
}
|
|
|
|
// Shutdown gracefully shuts down the server.
|
|
func (s *Server) Shutdown(ctx context.Context) error {
|
|
if s.httpServer == nil {
|
|
return nil
|
|
}
|
|
|
|
s.log.Info("shutting down http server")
|
|
|
|
shutdownCtx, cancel := context.WithTimeout(ctx, shutdownTimeout)
|
|
defer cancel()
|
|
|
|
err := s.httpServer.Shutdown(shutdownCtx)
|
|
if err != nil {
|
|
s.log.Error("http server shutdown error", "error", err)
|
|
|
|
return fmt.Errorf("shutting down http server: %w", err)
|
|
}
|
|
|
|
s.log.Info("http server stopped")
|
|
|
|
return nil
|
|
}
|
|
|
|
// ServeHTTP implements http.Handler.
|
|
func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
|
s.router.ServeHTTP(writer, request)
|
|
}
|