package main import ( "context" "log" "log/slog" "net/http" "os" "os/signal" "syscall" "time" "gitea.leeworks.dev/0xwheatyz/gitea-mobile/internal/config" giteaclient "gitea.leeworks.dev/0xwheatyz/gitea-mobile/internal/gitea" "gitea.leeworks.dev/0xwheatyz/gitea-mobile/internal/handlers" "gitea.leeworks.dev/0xwheatyz/gitea-mobile/internal/middleware" ) func main() { // Set up structured logging. slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ Level: slog.LevelInfo, }))) cfg, err := config.Load() if err != nil { log.Fatalf("configuration error: %v", err) } // Create Gitea API client. client := giteaclient.NewClient(cfg.GiteaURL) // Create handler with all routes. mux := http.NewServeMux() h := handlers.NewHandler(cfg, client) h.RegisterRoutes(mux) // Apply middleware chain: logging -> auth. var handler http.Handler = mux handler = middleware.Auth(cfg.SessionSecret, cfg.GiteaToken)(handler) handler = middleware.Logging()(handler) srv := &http.Server{ Addr: cfg.ListenAddr, Handler: handler, } // Channel to receive shutdown signals. quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGTERM, syscall.SIGINT) // Start server in a goroutine. go func() { slog.Info("server starting", "addr", cfg.ListenAddr, "gitea_url", cfg.GiteaURL) if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("server error: %v", err) } }() // Block until a shutdown signal is received. sig := <-quit slog.Info("shutdown signal received, draining in-flight requests", "signal", sig.String()) // Give in-flight requests up to 15 seconds to complete. ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { slog.Error("server forced to shutdown", "error", err) os.Exit(1) } slog.Info("server stopped gracefully") }