Add production hardening: monitoring, backups, caching, and high-traffic resilience

- Recovery middleware (no more crash-on-panic), request metrics/monitoring
  store + admin API, in-memory TTL caching for read-heavy option/question/
  category lookups, gzip + rate-limiting middleware, sanitized chat message
  encoding, and pg_dump-based database backups with an admin API.
- Fix N+1 queries in the discover feed, candidate detail, and match list
  endpoints by batching selection/photo/user/last-message lookups into
  single queries instead of one-per-item loops.
- Tune DB connection pooling and add HTTP server timeouts + graceful
  shutdown so the API degrades gracefully instead of falling over under load.
- Fix a null (vs empty array) JSON response for candidates/matches without
  photos or interests, which crashed the web app's swipe card.
This commit is contained in:
2026-09-19 22:17:12 +03:30
parent 15583009e0
commit 769e76313f
29 changed files with 1377 additions and 72 deletions
+17 -2
View File
@@ -16,6 +16,8 @@ type Handlers struct {
Admin *handler.AdminHandler
Discovery *handler.DiscoveryHandler
Chat *handler.ChatHandler
Monitoring *handler.MonitoringHandler
Backup *handler.BackupHandler
}
func Setup(
@@ -25,6 +27,7 @@ func Setup(
userRepo *repository.UserRepository,
) {
engine.Use(middleware.CORS())
engine.Use(middleware.Gzip())
engine.GET("/health", func(c *gin.Context) {
c.JSON(200, gin.H{"status": "ok"})
@@ -36,10 +39,13 @@ func Setup(
api := engine.Group("/api/v1")
// -------- احراز هویت (عمومی) --------
// درخواست OTP هزینه‌ی پیامک داره و هدف مناسبی برای اسپم/brute-force هست؛
// حداکثر ۵ درخواست، بعد هر ثانیه یک توکن شارژ می‌شه (یعنی حدود ۵ تا در دقیقه‌ی اول و بعدش پیوسته).
otpRateLimit := middleware.RateLimiter(5, 1.0/12.0)
auth := api.Group("/auth")
{
auth.POST("/request-otp", h.Auth.RequestOTP)
auth.POST("/verify-otp", h.Auth.VerifyOTP)
auth.POST("/request-otp", otpRateLimit, h.Auth.RequestOTP)
auth.POST("/verify-otp", otpRateLimit, h.Auth.VerifyOTP)
auth.POST("/token/refresh", h.Auth.RefreshToken)
}
@@ -106,5 +112,14 @@ func Setup(
admin.POST("/profile-questions", h.Admin.CreateQuestion)
admin.PATCH("/profile-questions/:id", h.Admin.UpdateQuestion)
admin.DELETE("/profile-questions/:id", h.Admin.DeleteQuestion)
// مانیتورینگ: بار/زمان/ریسپانس درخواست‌های اخیر سرور
admin.GET("/monitoring/summary", h.Monitoring.Summary)
admin.GET("/monitoring/requests", h.Monitoring.RecentRequests)
// بک‌آپ دیتابیس
admin.GET("/backups", h.Backup.List)
admin.POST("/backups", h.Backup.Create)
admin.GET("/backups/:filename", h.Backup.Download)
}
}