// ============================================ // main.go - VERSION SIMPLIFIÉE AVEC CLEANUP AUTO // ============================================ 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() { // Forcer la timezone Europe/Paris (UTC+1/+2) if loc, err := time.LoadLocation("Europe/Paris"); err == nil { time.Local = loc } else { log.Printf("⚠️ Impossible de charger la timezone Europe/Paris: %v", err) } // Chargement des variables d'environnement if err := godotenv.Load(); err != nil { log.Println("⚠️ Aucun fichier .env trouvé, utilisation des valeurs par défaut.") } // Initialisation de la base de données database := db.InitDB() defer database.Close() log.Printf("✅ Database initialisée: %+v", database) // Initialisation de Redis db.InitRedis() defer db.Redis.Close() log.Println("✅ Redis initialisé avec succès") // Initialisation du service de géolocalisation geoService := services.NewGeoService(db.Redis, db.RedisCtx) log.Println("✅ Service de géolocalisation initialisé") // ============================================ // 🧹 NETTOYAGE INITIAL DES COMMANDES INVALIDES // ============================================ 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") } } // ============================================ // 🔄 DÉMARRAGE DU SCHEDULER DE NETTOYAGE AUTO // ============================================ go database.StartQueueCleanupScheduler() log.Println("✅ Scheduler de nettoyage démarré (toutes les 5 min)") // ============================================ // 🔄 SYNCHRONISATION DES STATUTS LIVREURS // ============================================ if err := database.SyncAllDeliverymanStatuses(); err != nil { log.Printf("⚠️ Erreur synchronisation statuts: %v", err) } else { log.Println("✅ Synchronisation des statuts livreurs terminée") } // ============================================ // ⭐ DÉMARRAGE DU CRON JOB AUTO-ASSIGNATION // ============================================ go workers.StartAutoAssignmentCron(database, geoService) log.Println("✅ Cron job auto-assignation démarré (5 min)") // Démarrage des workers Redis en arrière-plan go workers.StartRedisWorkers(database) log.Println("✅ Workers Redis démarrés") // Worker de vérification des paiements crypto (recharge les clés dynamiquement) 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() // Configuration des sessions 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)) // Configuration CORS 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, })) // Middleware pour injecter la base de données et geoService r.Use(func(c *gin.Context) { c.Set("database", database) c.Set("geoService", geoService) c.Next() }) // Middleware NowPayments : injecte le client si crypto activé dans les settings 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() }) // Fichiers statiques r.Static("/uploads", "./uploads") // ============================================ // ENREGISTRER TOUTES LES ROUTES // ============================================ routes.SetupRoutes(r, database, geoService) // ============================================ // LANCEMENT DU SERVEUR // ============================================ printServerInfo() if err := r.Run(":8080"); err != nil { log.Fatalf("❌ Erreur au lancement du serveur : %v", err) } } // printServerInfo affiche les infos du serveur au démarrage func printServerInfo() { log.Println("") log.Println("═══════════════════════════════════════════════════════════") log.Println("🚀 Serveur lancé sur http://localhost:8080") log.Println("═══════════════════════════════════════════════════════════") log.Println("") log.Println("📡 API Endpoints disponibles:") log.Println("") log.Println(" 🌍 PUBLIC:") log.Println(" GET /api/v1/health - Health check") log.Println(" GET /api/v1/products - Liste produits") log.Println("") log.Println(" 👤 CLIENTS:") log.Println(" POST /api/v1/auth/register - Inscription") log.Println(" POST /api/v1/auth/login - Connexion") log.Println(" POST /api/v1/checkout - ✅ PASSER COMMANDE (avec auto-assign)") log.Println(" GET /api/v1/my-commands - Mes commandes avec suivi") log.Println(" GET /api/v1/commands/:id/status - ✅ Statut temps réel") log.Println(" GET /api/v1/commands/:id/tracking - ✅ Suivi détaillé") log.Println(" POST /api/v1/commands/:id/approve - Confirmer livraison") log.Println("") log.Println(" 🚗 LIVREURS:") log.Println(" GET /api/v1/livreur/deliveries - ✅ Mes livraisons (SANS téléphone client)") log.Println(" GET /api/v1/livreur/deliveries/:id - ✅ Détail livraison filtrée") log.Println(" PUT /api/v1/livreur/deliveries/:id/status - MAJ statut (avec GPS)") log.Println(" POST /api/v1/livreur/location/update - MAJ position GPS") log.Println(" GET /api/v1/livreur/queue - Ma queue de livraisons") log.Println("") log.Println(" 🔐 ADMIN:") log.Println(" POST /api/v2/admin/auth/login - Connexion admin") log.Println(" GET /api/v2/admin/protected/orders - Toutes les commandes") log.Println(" POST /api/v2/admin/protected/orders/:id/auto-assign - ✅ Auto-assign GPS") log.Println(" POST /api/v2/admin/protected/commands/auto-assign-all - ✅ Assigner toutes") log.Println(" GET /api/v2/admin/protected/delivery/queues - Queues livreurs") log.Println("") log.Println("⏰ Workers & Services actifs:") log.Println(" - ✅ AutoAssignmentCron (5min) - Assignation backup") log.Println(" - ✅ QueueCleanupScheduler (5min) - 🧹 Nettoyage commandes invalides") log.Println(" - ✅ NotificationWorker (30s) - Notifications ETA") log.Println(" - ✅ StockCleanupWorker (5min) - Nettoyage stock") log.Println("") log.Println("🎯 Fonctionnalités activées:") log.Println(" 1. ✅ Auto-assignation au checkout (immédiate)") log.Println(" 2. ✅ Cron job backup (toutes les 5 min)") log.Println(" 3. ✅ Validation stricte avant ajout à la queue") log.Println(" 4. ✅ Nettoyage automatique des commandes invalides") log.Println(" 5. ✅ Vue livreur filtrée (sans téléphone)") log.Println(" 6. ✅ Vue client avec suivi temps réel") log.Println(" 7. ✅ Gestion automatique statut BUSY (queue >= 10)") log.Println("") log.Println("🔒 Règles de validation automatique:") log.Println(" - Username non vide ✓") log.Println(" - Adresse de livraison non vide ✓") log.Println(" - Coordonnées GPS valides (lat != 0, lng != 0) ✓") log.Println(" - Date de création valide ✓") log.Println(" - Prix total > 0 (recommandé) ✓") log.Println("") log.Println("═══════════════════════════════════════════════════════════") log.Println("") }