2823fb45f5
Phone+OTP auth, staged onboarding, admin approval workflow, and a full discovery/swipe/match/chat system with photo upload and approximate distance (haversine) based on user location. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
90 lines
3.3 KiB
Go
90 lines
3.3 KiB
Go
package main
|
|
|
|
import (
|
|
"log"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"dating-app/internal/config"
|
|
"dating-app/internal/database"
|
|
"dating-app/internal/handler"
|
|
"dating-app/internal/repository"
|
|
"dating-app/internal/router"
|
|
"dating-app/internal/service"
|
|
"dating-app/internal/utils"
|
|
)
|
|
|
|
func main() {
|
|
cfg := config.Load()
|
|
|
|
db, err := database.Connect(cfg)
|
|
if err != nil {
|
|
log.Fatalf("اتصال به دیتابیس ناموفق بود: %v", err)
|
|
}
|
|
|
|
if err := database.AutoMigrate(db); err != nil {
|
|
log.Fatalf("migrate ناموفق بود: %v", err)
|
|
}
|
|
if err := database.SeedCategories(db); err != nil {
|
|
log.Fatalf("seed کردن دستهها ناموفق بود: %v", err)
|
|
}
|
|
if err := database.BootstrapAdmin(db, cfg.AdminBootstrapPhone); err != nil {
|
|
log.Fatalf("ساخت ادمین اولیه ناموفق بود: %v", err)
|
|
}
|
|
|
|
// -------- Repositories --------
|
|
userRepo := repository.NewUserRepository(db)
|
|
optionRepo := repository.NewOptionRepository(db)
|
|
questionRepo := repository.NewQuestionRepository(db)
|
|
photoRepo := repository.NewPhotoRepository(db)
|
|
swipeRepo := repository.NewSwipeRepository(db)
|
|
matchRepo := repository.NewMatchRepository(db)
|
|
messageRepo := repository.NewMessageRepository(db)
|
|
|
|
// -------- Utils / Infra --------
|
|
jwtManager := utils.NewJWTManager(
|
|
cfg.JWTAccessSecret,
|
|
cfg.JWTRefreshSecret,
|
|
cfg.JWTAccessTTL,
|
|
cfg.JWTRefreshTTL,
|
|
)
|
|
// برای اتصال به یک گیتوی پیامک واقعی، این خط رو با پیادهسازی خودت
|
|
// (که service.OTPSender رو implement میکنه) عوض کن.
|
|
otpSender := service.NewMockOTPSender(cfg.AppEnv != "production")
|
|
// برای اتصال به یک object storage واقعی، این خط رو با پیادهسازی خودت
|
|
// (که service.PhotoStorage رو implement میکنه) عوض کن.
|
|
photoStorage := service.NewLocalPhotoStorage("./uploads/photos", "/uploads/photos")
|
|
|
|
// -------- Services --------
|
|
authService := service.NewAuthService(
|
|
userRepo, otpSender, jwtManager,
|
|
cfg.OTPExpirySeconds, cfg.OTPLength, cfg.OTPHashSecret,
|
|
)
|
|
onboardingService := service.NewOnboardingService(userRepo, optionRepo, questionRepo)
|
|
adminService := service.NewAdminService(optionRepo, questionRepo, userRepo)
|
|
profileService := service.NewProfileService(photoRepo, userRepo, photoStorage)
|
|
discoveryService := service.NewDiscoveryService(userRepo, photoRepo, swipeRepo, matchRepo, optionRepo, questionRepo)
|
|
chatService := service.NewChatService(matchRepo, messageRepo, userRepo, photoRepo)
|
|
|
|
// -------- Handlers --------
|
|
handlers := router.Handlers{
|
|
Auth: handler.NewAuthHandler(authService),
|
|
Onboarding: handler.NewOnboardingHandler(onboardingService),
|
|
Profile: handler.NewProfileHandler(optionRepo, questionRepo, photoRepo, onboardingService, profileService),
|
|
Admin: handler.NewAdminHandler(adminService, optionRepo, questionRepo, photoRepo, onboardingService),
|
|
Discovery: handler.NewDiscoveryHandler(discoveryService),
|
|
Chat: handler.NewChatHandler(chatService),
|
|
}
|
|
|
|
if cfg.AppEnv == "production" {
|
|
gin.SetMode(gin.ReleaseMode)
|
|
}
|
|
engine := gin.Default()
|
|
router.Setup(engine, handlers, jwtManager, userRepo)
|
|
|
|
log.Printf("سرور روی پورت %s در حال اجراست (env=%s)", cfg.AppPort, cfg.AppEnv)
|
|
if err := engine.Run(":" + cfg.AppPort); err != nil {
|
|
log.Fatalf("اجرای سرور ناموفق بود: %v", err)
|
|
}
|
|
}
|