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
+49
View File
@@ -0,0 +1,49 @@
package handler
import (
"net/http"
"github.com/gin-gonic/gin"
"dating-app/internal/service"
"dating-app/internal/utils"
)
type BackupHandler struct {
backupService *service.BackupService
}
func NewBackupHandler(backupService *service.BackupService) *BackupHandler {
return &BackupHandler{backupService: backupService}
}
// List GET /admin/backups
func (h *BackupHandler) List(c *gin.Context) {
backups, err := h.backupService.List()
if err != nil {
utils.Fail(c, http.StatusInternalServerError, "خطا در دریافت لیست بک‌آپ‌ها", nil)
return
}
utils.Success(c, http.StatusOK, "", backups)
}
// Create POST /admin/backups
func (h *BackupHandler) Create(c *gin.Context) {
info, err := h.backupService.Create()
if err != nil {
utils.Fail(c, http.StatusInternalServerError, "گرفتن بک‌آپ ناموفق بود: "+err.Error(), nil)
return
}
utils.Success(c, http.StatusCreated, "بک‌آپ با موفقیت ساخته شد", info)
}
// Download GET /admin/backups/:filename
func (h *BackupHandler) Download(c *gin.Context) {
filename := c.Param("filename")
path, err := h.backupService.ResolvePath(filename)
if err != nil {
utils.Fail(c, http.StatusNotFound, "فایل بک‌آپ یافت نشد", nil)
return
}
c.FileAttachment(path, filename)
}