Files
chat_room/internal/router/router.go
T
mobiniqow c8e6f80c51 Add bio field and daily streak check-in for gamification
- User.Bio: a short (300 char) freeform profile bio, editable via
  PATCH /profile/bio, shown on discover cards and candidate detail (richer
  profiles beyond the fixed onboarding fields).
- User.StreakCount/LastActiveDate: POST /profile/checkin increments the
  streak on consecutive days, resets after a gap, no-ops if already
  checked in today. Backs a daily-return hook on the frontend.
2026-09-20 12:26:06 +03:30

141 lines
5.5 KiB
Go

package router
import (
"github.com/gin-gonic/gin"
"dating-app/internal/handler"
"dating-app/internal/middleware"
"dating-app/internal/repository"
"dating-app/internal/utils"
)
type Handlers struct {
Auth *handler.AuthHandler
Onboarding *handler.OnboardingHandler
Profile *handler.ProfileHandler
Admin *handler.AdminHandler
Discovery *handler.DiscoveryHandler
Chat *handler.ChatHandler
Monitoring *handler.MonitoringHandler
Backup *handler.BackupHandler
}
func Setup(
engine *gin.Engine,
h Handlers,
jwtManager *utils.JWTManager,
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"})
})
// فایل‌های عکس آپلودشده (خارج از /api/v1، چون فایل استاتیکه نه API)
engine.Static("/uploads", "./uploads")
api := engine.Group("/api/v1")
// -------- احراز هویت (عمومی) --------
// درخواست OTP هزینه‌ی پیامک داره و هدف مناسبی برای اسپم/brute-force هست؛
// حداکثر ۵ درخواست، بعد هر ثانیه یک توکن شارژ می‌شه (یعنی حدود ۵ تا در دقیقه‌ی اول و بعدش پیوسته).
otpRateLimit := middleware.RateLimiter(5, 1.0/12.0)
auth := api.Group("/auth")
{
auth.POST("/request-otp", otpRateLimit, h.Auth.RequestOTP)
auth.POST("/verify-otp", otpRateLimit, h.Auth.VerifyOTP)
auth.POST("/token/refresh", h.Auth.RefreshToken)
}
authRequired := middleware.AuthRequired(jwtManager, userRepo)
// -------- پروفایل --------
profile := api.Group("/profile", authRequired)
{
profile.GET("/me", h.Profile.Me)
profile.POST("/photos", h.Profile.UploadPhoto)
profile.DELETE("/photos/:id", h.Profile.DeletePhoto)
profile.PATCH("/location", h.Profile.UpdateLocation)
profile.PATCH("/bio", h.Profile.UpdateBio)
// استریک روزانه (گیمیفیکیشن): هر بار اپ باز می‌شه یه بار صدا زده می‌شه
profile.POST("/checkin", h.Profile.CheckIn)
}
// -------- آنبوردینگ (نیاز به ورود) --------
onboarding := api.Group("/onboarding", authRequired)
{
onboarding.GET("/status", h.Onboarding.Status)
onboarding.POST("/basic-info", h.Onboarding.BasicInfo)
onboarding.POST("/photos", h.Onboarding.Photos)
onboarding.POST("/body-info", h.Onboarding.BodyInfo)
onboarding.GET("/profile-questions", h.Onboarding.AvailableQuestions)
onboarding.POST("/profile-questions", h.Onboarding.SetProfileQuestions)
onboarding.POST("/education", h.Onboarding.Education)
onboarding.POST("/occupations", h.Onboarding.Occupations)
onboarding.POST("/relationship-importance", h.Onboarding.RelationshipImportance)
onboarding.POST("/interests", h.Onboarding.Interests)
onboarding.POST("/looking-for", h.Onboarding.LookingFor)
// لیست گزینه‌های فعال هر دسته، برای نمایش در فرم هر مرحله
// category یکی از: education_level, field_of_study, occupation,
// important_in_relationship, interest, looking_for
onboarding.GET("/options/:category", h.Onboarding.Options)
}
// -------- دوستیابی (نیاز به ورود) --------
api.GET("/discover", authRequired, h.Discovery.Feed)
api.GET("/discover/:id", authRequired, h.Discovery.CandidateDetail)
api.POST("/discover/:id/swipe", authRequired, h.Discovery.Swipe)
// -------- مچ‌ها و گفتگو (نیاز به ورود) --------
matches := api.Group("/matches", authRequired)
{
matches.GET("", h.Chat.ListMatches)
matches.GET("/:id/messages", h.Chat.ListMessages)
matches.POST("/:id/messages", h.Chat.SendMessage)
}
// -------- پنل ادمین (نیاز به ورود + نقش ادمین) --------
admin := api.Group("/admin", authRequired, middleware.AdminRequired())
{
admin.GET("/users", h.Admin.ListAllUsers)
admin.GET("/users/pending", h.Admin.ListPendingUsers)
admin.GET("/users/:id", h.Admin.GetUserDetail)
admin.POST("/users/:id/approve", h.Admin.ApproveUser)
admin.POST("/users/:id/reject", h.Admin.RejectUser)
admin.POST("/users/:id/block", h.Admin.BlockUser)
admin.POST("/users/:id/unblock", h.Admin.UnblockUser)
admin.POST("/users/:id/report", h.Admin.ReportUser)
admin.GET("/users/:id/reports", h.Admin.ListUserReports)
// رفقا (مچ‌ها)
admin.GET("/matches", h.Admin.ListAllMatches)
// نظارت بر عکس‌های آپلودشده
admin.GET("/photos", h.Admin.ListAllPhotos)
admin.DELETE("/photos/:id", h.Admin.DeletePhoto)
admin.GET("/categories/:key", h.Admin.GetCategory)
admin.PATCH("/categories/:key", h.Admin.UpdateCategory)
admin.GET("/categories/:key/options", h.Admin.ListOptions)
admin.POST("/categories/:key/options", h.Admin.CreateOption)
admin.PATCH("/options/:id", h.Admin.UpdateOption)
admin.DELETE("/options/:id", h.Admin.DeleteOption)
admin.GET("/profile-questions", h.Admin.ListQuestions)
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)
}
}