This commit is contained in:
Tomas Dvorak
2025-11-11 10:29:30 +01:00
parent d5b4faea61
commit 8762bde4bf
139 changed files with 7240 additions and 2870 deletions
+30 -17
View File
@@ -109,6 +109,7 @@ func main() {
&models.SweepstakeWinner{},
&models.UploadedFile{},
&models.FileUsage{},
&models.ErrorEvent{},
); err != nil {
log.Printf("Warning: AutoMigrate failed: %v", err)
}
@@ -151,9 +152,9 @@ func main() {
}
// Initialize Gin router (custom stack)
reporter := services.NewErrorReporter(config.AppConfig)
r := gin.New()
// Use custom recovery with request ID tracking (replaces gin.Recovery)
r.Use(middleware.CustomRecovery())
r.Use(middleware.CustomRecoveryWithReporter(reporter))
// Do not trust any proxies by default (prevent spoofed client IP)
if err := r.SetTrustedProxies(nil); err != nil {
log.Printf("Trusted proxies setup error: %v", err)
@@ -161,6 +162,7 @@ func main() {
// Lightweight hardening middlewares
r.Use(middleware.RequestID())
r.Use(middleware.RequestLogger())
r.Use(middleware.ErrorStatusReporter(reporter))
r.Use(middleware.SanitizeHeaders())
// Add database context with timeout to prevent hanging queries
r.Use(middleware.DBContext())
@@ -183,11 +185,17 @@ func main() {
// CORS: reflect the Origin only if it is allowed. In development, also allow localhost/127.0.0.1 any port.
origin := c.Request.Header.Get("Origin")
allowed := false
path := c.Request.URL.Path
if strings.HasPrefix(path, "/api/v1/admin/scoreboard") || strings.HasPrefix(path, "/api/v1/scoreboard") {
if origin != "" {
allowed = true
}
}
// 1) Explicit exact-origin allow list
for _, ao := range config.AppConfig.AllowedOrigins {
if ao == origin {
allowed = true
break
allowed = true
break
}
}
// 2) Wildcard support: ALLOWED_ORIGINS="*" means reflect any non-empty Origin
@@ -212,20 +220,23 @@ func main() {
allowed = true
}
}
if allowed && origin != "" {
c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
c.Writer.Header().Set("Vary", "Origin")
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
// Expose request id header for client-side diagnostics
c.Writer.Header().Set("Access-Control-Expose-Headers", "X-Request-ID")
}
c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, Authorization, Cache-Control, X-Requested-With, X-Session-Token, X-Admin-Token, X-Dev-Admin, X-CSRF-Token")
if allowed && origin != "" {
c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
c.Writer.Header().Set("Vary", "Origin")
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
// Expose request id header for client-side diagnostics
c.Writer.Header().Set("Access-Control-Expose-Headers", "X-Request-ID")
}
c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, Authorization, Cache-Control, X-Requested-With, X-Session-Token, X-Admin-Token, X-Dev-Admin, X-CSRF-Token")
if c.Request.Header.Get("Access-Control-Request-Private-Network") != "" {
c.Writer.Header().Set("Access-Control-Allow-Private-Network", "true")
}
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
})
@@ -267,6 +278,8 @@ func main() {
}
services.StartPrefetcher(prefetchTarget)
services.StartErrorReviewAutoRegister(dbInstance)
// Start newsletter scheduler (automated emails - legacy weekly)
services.StartNewsletterScheduler(dbInstance, emailSvc)