diff --git a/backend/cmd/api/main.go b/backend/cmd/api/main.go index 5bc85409..f48467fd 100644 --- a/backend/cmd/api/main.go +++ b/backend/cmd/api/main.go @@ -255,7 +255,11 @@ func main() { cerb := cerberus.New(cfg.Security, db) // Pass config to routes for auth service and certificate service - if err := routes.RegisterWithDeps(router, db, cfg, caddyManager, cerb); err != nil { + // Lifecycle context cancelled on shutdown to stop background goroutines + appCtx, appCancel := context.WithCancel(context.Background()) + defer appCancel() + + if err := routes.RegisterWithDeps(appCtx, router, db, cfg, caddyManager, cerb); err != nil { log.Fatalf("register routes: %v", err) } @@ -291,6 +295,9 @@ func main() { sig := <-quit logger.Log().Infof("Received signal %v, initiating graceful shutdown...", sig) + // Cancel the app-wide context to stop background goroutines (e.g. cert expiry checker) + appCancel() + // Graceful shutdown with timeout ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() diff --git a/backend/internal/api/routes/endpoint_inventory_test.go b/backend/internal/api/routes/endpoint_inventory_test.go index e6c23314..6019c6fa 100644 --- a/backend/internal/api/routes/endpoint_inventory_test.go +++ b/backend/internal/api/routes/endpoint_inventory_test.go @@ -1,6 +1,7 @@ package routes_test import ( + "context" "testing" "github.com/gin-gonic/gin" @@ -20,7 +21,7 @@ func TestEndpointInventory_FrontendCanonicalSaveImportContractsExistInBackend(t require.NoError(t, err) router := gin.New() - require.NoError(t, routes.Register(router, db, config.Config{JWTSecret: "test-secret"})) + require.NoError(t, routes.Register(context.Background(), router, db, config.Config{JWTSecret: "test-secret"})) routes.RegisterImportHandler(router, db, config.Config{JWTSecret: "test-secret"}, "echo", "/tmp", "/import/Caddyfile") assertStrictMethodPathMatrix(t, router.Routes(), backendImportSaveInventoryCanonical(), "backend canonical save/import inventory") @@ -33,7 +34,7 @@ func TestEndpointInventory_FrontendParityMatchesCurrentContract(t *testing.T) { require.NoError(t, err) router := gin.New() - require.NoError(t, routes.Register(router, db, config.Config{JWTSecret: "test-secret"})) + require.NoError(t, routes.Register(context.Background(), router, db, config.Config{JWTSecret: "test-secret"})) routes.RegisterImportHandler(router, db, config.Config{JWTSecret: "test-secret"}, "echo", "/tmp", "/import/Caddyfile") assertStrictMethodPathMatrix(t, router.Routes(), frontendObservedImportSaveInventory(), "frontend observed save/import inventory") @@ -46,7 +47,7 @@ func TestEndpointInventory_FrontendParityDetectsActualMismatch(t *testing.T) { require.NoError(t, err) router := gin.New() - require.NoError(t, routes.Register(router, db, config.Config{JWTSecret: "test-secret"})) + require.NoError(t, routes.Register(context.Background(), router, db, config.Config{JWTSecret: "test-secret"})) routes.RegisterImportHandler(router, db, config.Config{JWTSecret: "test-secret"}, "echo", "/tmp", "/import/Caddyfile") contractWithMismatch := append([]endpointInventoryEntry{}, frontendObservedImportSaveInventory()...) diff --git a/backend/internal/api/routes/routes.go b/backend/internal/api/routes/routes.go index 0cb00e98..0a085c29 100644 --- a/backend/internal/api/routes/routes.go +++ b/backend/internal/api/routes/routes.go @@ -61,7 +61,7 @@ func migrateViewerToPassthrough(db *gorm.DB) { } // Register wires up API routes and performs automatic migrations. -func Register(router *gin.Engine, db *gorm.DB, cfg config.Config) error { +func Register(ctx context.Context, router *gin.Engine, db *gorm.DB, cfg config.Config) error { // Caddy Manager - created early so it can be used by settings handlers for config reload caddyClient := caddy.NewClient(cfg.CaddyAdminAPI) caddyManager := caddy.NewManager(caddyClient, db, cfg.CaddyConfigDir, cfg.FrontendDir, cfg.ACMEStaging, cfg.Security) @@ -69,11 +69,11 @@ func Register(router *gin.Engine, db *gorm.DB, cfg config.Config) error { // Cerberus middleware applies the optional security suite checks (WAF, ACL, CrowdSec) cerb := cerberus.New(cfg.Security, db) - return RegisterWithDeps(router, db, cfg, caddyManager, cerb) + return RegisterWithDeps(ctx, router, db, cfg, caddyManager, cerb) } // RegisterWithDeps wires up API routes and performs automatic migrations with prebuilt dependencies. -func RegisterWithDeps(router *gin.Engine, db *gorm.DB, cfg config.Config, caddyManager *caddy.Manager, cerb *cerberus.Cerberus) error { +func RegisterWithDeps(ctx context.Context, router *gin.Engine, db *gorm.DB, cfg config.Config, caddyManager *caddy.Manager, cerb *cerberus.Cerberus) error { // Emergency bypass must be registered FIRST. // When a valid X-Emergency-Token is present from an authorized source, // it sets an emergency context flag and strips the token header so downstream @@ -705,7 +705,7 @@ func RegisterWithDeps(router *gin.Engine, db *gorm.DB, cfg config.Config, caddyM if cfg.CertExpiryWarningDays > 0 { warningDays = cfg.CertExpiryWarningDays } - go certService.StartExpiryChecker(context.Background(), notificationService, warningDays) + go certService.StartExpiryChecker(ctx, notificationService, warningDays) // Proxy Hosts & Remote Servers proxyHostHandler := handlers.NewProxyHostHandler(db, caddyManager, notificationService, uptimeService) diff --git a/backend/internal/api/routes/routes_coverage_test.go b/backend/internal/api/routes/routes_coverage_test.go index 57939ce7..21b21d41 100644 --- a/backend/internal/api/routes/routes_coverage_test.go +++ b/backend/internal/api/routes/routes_coverage_test.go @@ -1,6 +1,7 @@ package routes import ( + "context" "errors" "testing" @@ -34,7 +35,7 @@ func TestRegister_NotifyOnlyProviderMigrationErrorReturns(t *testing.T) { cfg := config.Config{JWTSecret: "test-secret"} - err = Register(router, db, cfg) + err = Register(context.Background(), router, db, cfg) require.Error(t, err) require.Contains(t, err.Error(), "notify-only provider migration") } @@ -61,7 +62,7 @@ func TestRegister_LegacyMigrationErrorIsNonFatal(t *testing.T) { cfg := config.Config{JWTSecret: "test-secret"} - err = Register(router, db, cfg) + err = Register(context.Background(), router, db, cfg) require.NoError(t, err) hasHealth := false @@ -96,7 +97,7 @@ func TestRegister_UptimeFeatureFlagDefaultErrorIsNonFatal(t *testing.T) { cfg := config.Config{JWTSecret: "test-secret"} - err = Register(router, db, cfg) + err = Register(context.Background(), router, db, cfg) require.NoError(t, err) } @@ -122,6 +123,6 @@ func TestRegister_SecurityHeaderPresetInitErrorIsNonFatal(t *testing.T) { cfg := config.Config{JWTSecret: "test-secret"} - err = Register(router, db, cfg) + err = Register(context.Background(), router, db, cfg) require.NoError(t, err) } diff --git a/backend/internal/api/routes/routes_save_contract_test.go b/backend/internal/api/routes/routes_save_contract_test.go index 33e9afd4..e5be0484 100644 --- a/backend/internal/api/routes/routes_save_contract_test.go +++ b/backend/internal/api/routes/routes_save_contract_test.go @@ -1,6 +1,7 @@ package routes_test import ( + "context" "testing" "github.com/gin-gonic/gin" @@ -19,7 +20,7 @@ func TestRegister_StrictSaveRouteMatrixUsedByImportWorkflows(t *testing.T) { require.NoError(t, err) router := gin.New() - require.NoError(t, routes.Register(router, db, config.Config{JWTSecret: "test-secret"})) + require.NoError(t, routes.Register(context.Background(), router, db, config.Config{JWTSecret: "test-secret"})) assertStrictMethodPathMatrix(t, router.Routes(), saveRouteMatrixForImportWorkflows(), "save") } diff --git a/backend/internal/api/routes/routes_test.go b/backend/internal/api/routes/routes_test.go index 28a3ed16..d8714b0a 100644 --- a/backend/internal/api/routes/routes_test.go +++ b/backend/internal/api/routes/routes_test.go @@ -1,6 +1,7 @@ package routes import ( + "context" "io" "net/http" "net/http/httptest" @@ -41,7 +42,7 @@ func TestRegister(t *testing.T) { JWTSecret: "test-secret", } - err = Register(router, db, cfg) + err = Register(context.Background(), router, db, cfg) assert.NoError(t, err) // Verify some routes are registered @@ -70,7 +71,7 @@ func TestRegister_WithDevelopmentEnvironment(t *testing.T) { Environment: "development", } - err = Register(router, db, cfg) + err = Register(context.Background(), router, db, cfg) assert.NoError(t, err) } @@ -86,7 +87,7 @@ func TestRegister_WithProductionEnvironment(t *testing.T) { Environment: "production", } - err = Register(router, db, cfg) + err = Register(context.Background(), router, db, cfg) assert.NoError(t, err) } @@ -107,7 +108,7 @@ func TestRegister_AutoMigrateFailure(t *testing.T) { JWTSecret: "test-secret", } - err = Register(router, db, cfg) + err = Register(context.Background(), router, db, cfg) assert.Error(t, err) assert.Contains(t, err.Error(), "auto migrate") } @@ -148,7 +149,7 @@ func TestRegister_RoutesRegistration(t *testing.T) { JWTSecret: "test-secret", } - err = Register(router, db, cfg) + err = Register(context.Background(), router, db, cfg) require.NoError(t, err) routes := router.Routes() @@ -181,7 +182,7 @@ func TestRegister_ProxyHostsRequireAuth(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) req := httptest.NewRequest(http.MethodPost, "/api/v1/proxy-hosts", strings.NewReader(`{}`)) req.Header.Set("Content-Type", "application/json") @@ -200,7 +201,7 @@ func TestRegister_StateChangingRoutesDenyByDefaultWithExplicitAllowlist(t *testi require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) mutatingMethods := map[string]bool{ http.MethodPost: true, @@ -264,7 +265,7 @@ func TestRegister_DNSProviders_NotRegisteredWhenEncryptionKeyMissing(t *testing. require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret", EncryptionKey: ""} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) for _, r := range router.Routes() { assert.NotContains(t, r.Path, "/api/v1/dns-providers") @@ -279,7 +280,7 @@ func TestRegister_DNSProviders_NotRegisteredWhenEncryptionKeyInvalid(t *testing. require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret", EncryptionKey: "not-base64"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) for _, r := range router.Routes() { assert.NotContains(t, r.Path, "/api/v1/dns-providers") @@ -295,7 +296,7 @@ func TestRegister_DNSProviders_RegisteredWhenEncryptionKeyValid(t *testing.T) { // 32-byte all-zero key in base64 cfg := config.Config{JWTSecret: "test-secret", EncryptionKey: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) paths := make(map[string]bool) for _, r := range router.Routes() { @@ -317,7 +318,7 @@ func TestRegister_AllRoutesRegistered(t *testing.T) { JWTSecret: "test-secret", EncryptionKey: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", } - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) routes := router.Routes() routeMap := make(map[string][]string) // path -> methods @@ -384,7 +385,7 @@ func TestRegister_MiddlewareApplied(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) // Test that security headers middleware is applied w := httptest.NewRecorder() @@ -413,7 +414,7 @@ func TestRegister_AuthenticatedRoutes(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) // Test that protected routes require authentication protectedPaths := []struct { @@ -449,7 +450,7 @@ func TestRegister_StateChangingRoutesRequireAuthentication(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) stateChangingPaths := []struct { method string @@ -488,7 +489,7 @@ func TestRegister_AdminRoutes(t *testing.T) { JWTSecret: "test-secret", EncryptionKey: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", } - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) // Admin routes should exist and require auth adminPaths := []string{ @@ -513,7 +514,7 @@ func TestRegister_PublicRoutes(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) // Public routes should be accessible without auth (route exists, not 404) publicPaths := []struct { @@ -545,7 +546,7 @@ func TestRegister_HealthEndpoint(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) w := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/api/v1/health", nil) @@ -563,7 +564,7 @@ func TestRegister_MetricsEndpoint(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) w := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/metrics", nil) @@ -582,7 +583,7 @@ func TestRegister_DBHealthEndpoint(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) w := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/api/v1/health/db", nil) @@ -600,7 +601,7 @@ func TestRegister_LoginEndpoint(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) // Test login endpoint exists and accepts POST body := `{"username": "test", "password": "test"}` @@ -621,7 +622,7 @@ func TestRegister_SetupEndpoint(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) // GET /setup should return setup status w := httptest.NewRecorder() @@ -646,7 +647,7 @@ func TestRegister_WithEncryptionRoutes(t *testing.T) { JWTSecret: "test-secret", EncryptionKey: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", } - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) // Check if encryption routes are registered (may depend on env) routes := router.Routes() @@ -668,7 +669,7 @@ func TestRegister_UptimeCheckEndpoint(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) // Uptime check route should exist and require auth w := httptest.NewRecorder() @@ -687,7 +688,7 @@ func TestRegister_CrowdSecRoutes(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) // CrowdSec routes should exist routes := router.Routes() @@ -713,7 +714,7 @@ func TestRegister_SecurityRoutes(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) routes := router.Routes() routeMap := make(map[string]bool) @@ -740,7 +741,7 @@ func TestRegister_AccessListRoutes(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) routes := router.Routes() routeMap := make(map[string]bool) @@ -763,7 +764,7 @@ func TestRegister_CertificateRoutes(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) routes := router.Routes() routeMap := make(map[string]bool) @@ -792,7 +793,7 @@ func TestRegister_NilHandlers(t *testing.T) { EncryptionKey: "", // No encryption key - DNS providers won't be registered } - err = Register(router, db, cfg) + err = Register(context.Background(), router, db, cfg) assert.NoError(t, err) // Verify that routes still work without DNS provider features @@ -823,7 +824,7 @@ func TestRegister_MiddlewareOrder(t *testing.T) { Environment: "development", } - err = Register(router, db, cfg) + err = Register(context.Background(), router, db, cfg) require.NoError(t, err) // Test that security headers are applied (they should come first) @@ -848,7 +849,7 @@ func TestRegister_GzipCompression(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) // Request with Accept-Encoding: gzip w := httptest.NewRecorder() @@ -875,7 +876,7 @@ func TestRegister_CerberusMiddleware(t *testing.T) { }, } - err = Register(router, db, cfg) + err = Register(context.Background(), router, db, cfg) require.NoError(t, err) // API routes should have Cerberus middleware applied @@ -896,7 +897,7 @@ func TestRegister_FeatureFlagsEndpoint(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) // Feature flags should require auth w := httptest.NewRecorder() @@ -915,7 +916,7 @@ func TestRegister_WebSocketRoutes(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) routes := router.Routes() routeMap := make(map[string]bool) @@ -939,7 +940,7 @@ func TestRegister_NotificationRoutes(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) routes := router.Routes() routeMap := make(map[string]bool) @@ -967,7 +968,7 @@ func TestRegister_DomainRoutes(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) routes := router.Routes() routeMap := make(map[string]bool) @@ -989,7 +990,7 @@ func TestRegister_VerifyAuthEndpoint(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) // Verify endpoint is public (for Caddy forward auth) w := httptest.NewRecorder() @@ -1009,7 +1010,7 @@ func TestRegister_SMTPRoutes(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) routes := router.Routes() routeMap := make(map[string]bool) @@ -1064,7 +1065,7 @@ func TestRegister_EncryptionRoutesWithValidKey(t *testing.T) { JWTSecret: "test-secret", EncryptionKey: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", } - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) routes := router.Routes() routeMap := make(map[string]bool) @@ -1091,7 +1092,7 @@ func TestRegister_WAFExclusionRoutes(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) routes := router.Routes() routeMap := make(map[string]bool) @@ -1113,7 +1114,7 @@ func TestRegister_BreakGlassRoute(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) routes := router.Routes() routeMap := make(map[string]bool) @@ -1134,7 +1135,7 @@ func TestRegister_RateLimitPresetsRoute(t *testing.T) { require.NoError(t, err) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) routes := router.Routes() routeMap := make(map[string]bool) @@ -1166,7 +1167,7 @@ func TestEmergencyEndpoint_BypassACL(t *testing.T) { CerberusEnabled: true, }, } - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) // Note: We don't need to create ACL settings here because the emergency endpoint // bypass happens at middleware level before Cerberus checks @@ -1210,7 +1211,7 @@ func TestEmergencyBypass_MiddlewareOrder(t *testing.T) { ManagementCIDRs: []string{"127.0.0.0/8"}, }, } - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) // Request with emergency token should set bypass flag w := httptest.NewRecorder() @@ -1239,7 +1240,7 @@ func TestEmergencyBypass_InvalidToken(t *testing.T) { CerberusEnabled: true, }, } - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) // Request with WRONG emergency token w := httptest.NewRecorder() @@ -1271,7 +1272,7 @@ func TestEmergencyBypass_UnauthorizedIP(t *testing.T) { ManagementCIDRs: []string{"192.168.1.0/24"}, }, } - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) // Request from public IP (not in management network) w := httptest.NewRecorder() @@ -1295,7 +1296,7 @@ func TestRegister_CreatesAccessLogFileForLogWatcher(t *testing.T) { t.Setenv("CHARON_CADDY_ACCESS_LOG", logFilePath) cfg := config.Config{JWTSecret: "test-secret"} - require.NoError(t, Register(router, db, cfg)) + require.NoError(t, Register(context.Background(), router, db, cfg)) _, statErr := os.Stat(logFilePath) assert.NoError(t, statErr) @@ -1341,7 +1342,7 @@ func TestRegister_CleansLetsEncryptCertAssignments(t *testing.T) { require.NoError(t, db.Create(&host).Error) cfg := config.Config{JWTSecret: "test-secret"} - err = Register(router, db, cfg) + err = Register(context.Background(), router, db, cfg) require.NoError(t, err) var reloaded models.ProxyHost diff --git a/backend/internal/api/tests/integration_test.go b/backend/internal/api/tests/integration_test.go index 6cc21b9a..2488744d 100644 --- a/backend/internal/api/tests/integration_test.go +++ b/backend/internal/api/tests/integration_test.go @@ -2,6 +2,7 @@ package tests import ( + "context" "net/http" "net/http/httptest" "strings" @@ -33,7 +34,7 @@ func TestIntegration_WAF_BlockAndMonitor(t *testing.T) { } cfg.Security.WAFMode = mode r := gin.New() - if err := routes.Register(r, db, cfg); err != nil { + if err := routes.Register(context.Background(), r, db, cfg); err != nil { t.Fatalf("register: %v", err) } return r, db diff --git a/backend/internal/config/config.go b/backend/internal/config/config.go index bf1634df..fe09bce3 100644 --- a/backend/internal/config/config.go +++ b/backend/internal/config/config.go @@ -110,15 +110,8 @@ func Load() (Config, error) { Debug: getEnvAny("false", "CHARON_DEBUG", "CPM_DEBUG") == "true", } - // Certificate expiry warning days - if days := getEnvAny("30", "CHARON_CERT_EXPIRY_WARNING_DAYS"); days != "" { - if n, err := strconv.Atoi(days); err == nil && n > 0 { - cfg.CertExpiryWarningDays = n - } - } - - // Certificate expiry warning days - if days := getEnvAny("30", "CHARON_CERT_EXPIRY_WARNING_DAYS"); days != "" { + cfg.CertExpiryWarningDays = 30 + if days := getEnvAny("", "CHARON_CERT_EXPIRY_WARNING_DAYS"); days != "" { if n, err := strconv.Atoi(days); err == nil && n > 0 { cfg.CertExpiryWarningDays = n }