package main import ( "gestion/db" "gestion/routes" "gestion/services" "gestion/workers" "log" "net/http" "os" "time" "github.com/gin-contrib/cors" "github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions/cookie" "github.com/gin-gonic/gin" "github.com/joho/godotenv" ) func main() { if loc, err := time.LoadLocation("Europe/Paris"); err == nil { time.Local = loc } else { log.Printf("⚠️ Impossible de charger la timezone Europe/Paris: %v", err) } if err := godotenv.Load(); err != nil { log.Println("⚠️ Aucun fichier .env trouvé, utilisation des valeurs par défaut.") } database := db.InitDB() defer database.Close() log.Printf("✅ Database initialisée: %+v", database) db.InitRedis() defer db.Redis.Close() log.Println("✅ Redis initialisé avec succès") geoService := services.NewGeoService(db.Redis, db.RedisCtx) log.Println("✅ Service de géolocalisation initialisé") lbService := services.NewLBTelegramService() if lbService.IsConfigured() { log.Println("✅ Service LBTelegram initialisé") } else { log.Println("ℹ️ Service LBTelegram désactivé (LBTELEGRAM_URL non défini)") } telegramService := services.NewTelegramService() if telegramService.IsConfigured() { log.Println("✅ Service Telegram initialisé") if webhookURL := os.Getenv("TELEGRAM_WEBHOOK_URL"); webhookURL != "" { if err := telegramService.SetWebhook(webhookURL); err != nil { log.Printf("⚠️ [TELEGRAM] Erreur enregistrement webhook: %v", err) } } } else { log.Println("ℹ️ Service Telegram désactivé (TELEGRAM_BOT_TOKEN non défini)") } database.MigrateAddTelegramColumns() if dbSettings, err := database.GetSettings(); err == nil { telegramService.Reload(dbSettings.TelegramBotToken, dbSettings.TelegramBotUsername) telegramService.SetNotificationsEnabled(dbSettings.TelegramNotificationsEnabled) if dbSettings.TelegramBotToken != "" { log.Printf("✅ [TELEGRAM] Config chargée depuis la DB (username: %s)", dbSettings.TelegramBotUsername) if webhookURL := os.Getenv("TELEGRAM_WEBHOOK_URL"); webhookURL != "" { if err := telegramService.SetWebhook(webhookURL); err != nil { log.Printf("⚠️ [TELEGRAM] Erreur enregistrement webhook (DB reload): %v", err) } } } } // Ré-enrôler tous les comptes déjà liés dans lbtelegram (au cas où lbtelegram a redémarré) if lbService.IsConfigured() { go func() { accounts, err := database.GetAllLinkedTelegramAccounts() if err != nil { log.Printf("⚠️ [LB_SYNC] Erreur lecture comptes liés: %v", err) return } ok, fail := 0, 0 for _, a := range accounts { if err := lbService.EnrollUser(a.ChatID, a.Username, a.Role); err != nil { fail++ } else { ok++ } } log.Printf("✅ [LB_SYNC] Re-enrollment terminé: %d OK, %d échecs (total %d comptes)", ok, fail, len(accounts)) }() } s3Service, err := services.NewS3Service( os.Getenv("S3_REGION"), os.Getenv("S3_BUCKET"), os.Getenv("S3_ENDPOINT"), services.S3Credentials{ S3KeyId: os.Getenv("RUSTFS_ACCESS_KEY"), S3AccessKey: os.Getenv("RUSTFS_SECRET_KEY"), }, ) if err != nil { log.Fatalf("erreur init S3: %v", err) } var storage services.Storage switch os.Getenv("STORAGE_DRIVER") { case "s3": storage = services.NewS3Storage(s3Service) log.Println("✅ Storage driver: s3 (RustFS)") default: storage = services.NewLocalStorage("uploads") log.Println("✅ Storage driver: local") } log.Println("") log.Println("🧹 Démarrage du nettoyage des commandes invalides...") removed, err := database.CleanupInvalidQueueCommands() if err != nil { log.Printf("❌ Erreur lors du nettoyage initial: %v", err) } else { if removed > 0 { log.Printf("✅ Nettoyage initial terminé: %d commandes invalides supprimées", removed) } else { log.Println("✅ Nettoyage initial: Aucune commande invalide trouvée") } } go database.StartQueueCleanupScheduler() log.Println("✅ Scheduler de nettoyage démarré (toutes les 5 min)") if err := database.SyncAllDeliverymanStatuses(); err != nil { log.Printf("⚠️ Erreur synchronisation statuts: %v", err) } else { log.Println("✅ Synchronisation des statuts livreurs terminée") } go workers.StartAutoAssignmentCron(database, geoService) log.Println("✅ Cron job auto-assignation démarré (5 min)") go workers.StartRedisWorkers(database) log.Println("✅ Workers Redis démarrés") workers.StartDynamicPaymentChecker(database, func() *services.NowPaymentsClient { s, err := database.GetSettings() if err != nil || !s.CryptoPaymentEnabled || s.NowPaymentsAPIKey == "" { return nil } return services.NewNowPaymentsClient(s.NowPaymentsAPIKey, s.NowPaymentsIPNSecret) }, 2*time.Minute) log.Println("✅ Worker paiements crypto démarré (2 min)") gin.SetMode(gin.ReleaseMode) r := gin.Default() store := cookie.NewStore([]byte(os.Getenv("SESSION_SECRET"))) store.Options(sessions.Options{ Path: "/", Domain: "", MaxAge: 3600, HttpOnly: true, Secure: true, SameSite: http.SameSiteStrictMode, }) r.Use(sessions.Sessions("mysession", store)) r.Use(cors.New(cors.Config{ AllowOrigins: []string{"https://uber-stup.club", "https://5.181.0.112.nip.io", "https://5.181.0.112.nip.io:8080", "https://5.181.0.112.nip.io:8443", "https://mln-uber.club", "http://localhost:5173", "http://5.181.0.112"}, AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"}, AllowHeaders: []string{"Origin", "Content-Type", "Accept", "Authorization", "X-Request-ID"}, ExposeHeaders: []string{"Content-Length"}, AllowCredentials: true, })) r.Use(func(c *gin.Context) { c.Set("database", database) c.Set("geoService", geoService) c.Set("s3Service", s3Service) c.Set("storage", storage) c.Next() }) r.Use(func(c *gin.Context) { settings, err := database.GetSettings() if err == nil && settings.CryptoPaymentEnabled && settings.NowPaymentsAPIKey != "" { np := services.NewNowPaymentsClient(settings.NowPaymentsAPIKey, settings.NowPaymentsIPNSecret) c.Set("nowpayments", np) } c.Next() }) r.Static("/uploads", "./uploads") routes.SetupRoutes(r, database, geoService, s3Service) if err := r.Run(":8080"); err != nil { log.Fatalf("❌ Erreur au lancement du serveur : %v", err) } }