// cmd/server/main.go - ENHANCED WITH DEBUG LOGGING package main import ( "context" "log" "lost-and-found/internal/config" "lost-and-found/internal/middleware" "lost-and-found/internal/routes" "lost-and-found/internal/workers" "net/http" "os" "os/signal" "syscall" "time" "sync" "github.com/gin-gonic/gin" "github.com/joho/godotenv" "go.uber.org/zap" ) func main() { // Load .env file if err := godotenv.Load(); err != nil { log.Println("⚠️ No .env file found, using environment variables") } // ✅ Structured Logging logger, err := zap.NewProduction() if err != nil { log.Fatalf("❌ Failed to initialize logger: %v", err) } defer logger.Sync() logger.Info("🚀 Starting Lost & Found System...") // Initialize JWT config config.InitJWT() logger.Info("✅ JWT configuration initialized") // Initialize database logger.Info("📊 Initializing database...") if err := config.InitDB(); err != nil { logger.Fatal("❌ Failed to initialize database", zap.Error(err)) } defer func() { logger.Info("🗄️ Closing database connections...") if err := config.CloseDB(); err != nil { logger.Error("Failed to close database", zap.Error(err)) } else { logger.Info("✅ Database connections closed") } }() // Run migrations logger.Info("📋 Running migrations...") db := config.GetDB() if err := config.RunMigrations(db); err != nil { logger.Fatal("❌ Failed to run migrations", zap.Error(err)) } // Initialize Gin if config.IsProduction() { gin.SetMode(gin.ReleaseMode) } router := gin.Default() // Apply middleware router.Use(middleware.CORSMiddleware()) router.Use(middleware.LoggerMiddleware()) router.Use(middleware.RateLimiterMiddleware()) if config.IsDevelopment() { router.Use(func(c *gin.Context) { c.Writer.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") c.Writer.Header().Set("Pragma", "no-cache") c.Writer.Header().Set("Expires", "0") c.Next() }) } // Serve static files router.Static("/uploads", "./uploads") router.Static("/css", "./web/css") router.Static("/js", "./web/js") router.Static("/assets", "./web/assets") // Frontend routes router.GET("/", func(c *gin.Context) { c.File("./web/index.html") }) router.GET("/login", func(c *gin.Context) { c.File("./web/login.html") }) router.GET("/login.html", func(c *gin.Context) { c.File("./web/login.html") }) // ✅ Tambahkan ini router.GET("/register", func(c *gin.Context) { c.File("./web/register.html") }) router.GET("/register.html", func(c *gin.Context) { c.File("./web/register.html") }) // ✅ Tambahkan ini router.GET("/admin", func(c *gin.Context) { c.File("./web/admin.html") }) router.GET("/admin.html", func(c *gin.Context) { c.File("./web/admin.html") }) // ✅ Tambahkan ini router.GET("/manager", func(c *gin.Context) { c.File("./web/manager.html") }) router.GET("/manager.html", func(c *gin.Context) { c.File("./web/manager.html") }) // ✅ Tambahkan ini router.GET("/user", func(c *gin.Context) { c.File("./web/user.html") }) router.GET("/user.html", func(c *gin.Context) { c.File("./web/user.html") }) // ✅ Tambahkan ini // Setup API routes routes.SetupRoutes(router, db, logger) logger.Info("✅ API routes configured") // ✅ Start Background Workers logger.Info("🔄 Starting background workers...") expireWorker := workers.NewExpireWorker(db) auditWorker := workers.NewAuditWorker(db) matchingWorker := workers.NewMatchingWorker(db) notificationWorker := workers.NewNotificationWorker(db) // ✅ Background Workers - 4 Goroutines expireWorker.Start() auditWorker.Start() matchingWorker.Start() notificationWorker.Start() logger.Info("✅ All background workers started") // Get server config serverConfig := config.GetServerConfig() port := serverConfig.Port // ✅ DEBUG: Print sebelum create server log.Println("🔧 DEBUG: Creating HTTP server...") log.Printf("🔧 DEBUG: Port = %s\n", port) log.Printf("🔧 DEBUG: Address = :%s\n", port) // ✅ HTTP Server with Timeouts srv := &http.Server{ Addr: ":" + port, Handler: router, ReadTimeout: 15 * time.Second, WriteTimeout: 15 * time.Second, IdleTimeout: 60 * time.Second, } // ✅ DEBUG: Print sebelum start goroutine log.Println("🔧 DEBUG: Starting server goroutine...") // ✅ Start server in goroutine serverErrors := make(chan error, 1) go func() { // ✅ PENTING: Print ini HARUS muncul log.Println("🚀 Server starting...") log.Printf(" 📍 URL: http://localhost:%s\n", port) log.Printf(" 📡 API: http://localhost:%s/api\n", port) log.Printf(" 🌍 ENV: %s\n", serverConfig.Environment) log.Println("✨ Press Ctrl+C to stop") log.Println("🔧 DEBUG: Calling srv.ListenAndServe()...") logger.Info("🚀 Server starting", zap.String("url", "http://localhost:"+port), zap.String("api", "http://localhost:"+port+"/api"), zap.String("environment", serverConfig.Environment), ) // ✅ Ini yang benar-benar mulai server if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Printf("❌ SERVER ERROR: %v\n", err) serverErrors <- err } }() // ✅ DEBUG: Print setelah goroutine log.Println("🔧 DEBUG: Server goroutine launched, waiting for signals...") // ✅ Wait for interrupt signal OR server error quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) select { case sig := <-quit: logger.Info("🛑 Shutdown signal received", zap.String("signal", sig.String())) case err := <-serverErrors: logger.Fatal("❌ Server error", zap.Error(err)) } logger.Info("🔄 Starting graceful shutdown...") // ✅ Step 1: Create shutdown context with timeout shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second) defer shutdownCancel() // Implementasi ACID dan Kontrol Transaksi (15%) // ✅ Step 2: Stop accepting new HTTP requests // Implementasi Graceful Shutdown dengan srv.Shutdown(shutdownCtx) memastikan Durability data yang sedang diproses. logger.Info("🔌 Stopping server from accepting new requests...") if err := srv.Shutdown(shutdownCtx); err != nil { logger.Error("⚠️ Server forced to shutdown", zap.Error(err)) } else { logger.Info("✅ Server stopped accepting new requests") } // ✅ Step 3: Stop background workers logger.Info("🔄 Stopping background workers...") workersDone := make(chan struct{}) go func() { var wg sync.WaitGroup wg.Add(4) go func() { defer wg.Done() expireWorker.Stop() logger.Info("✅ Expire worker stopped") }() go func() { defer wg.Done() auditWorker.Stop() logger.Info("✅ Audit worker stopped") }() go func() { defer wg.Done() matchingWorker.Stop() logger.Info("✅ Matching worker stopped") }() go func() { defer wg.Done() notificationWorker.Stop() logger.Info("✅ Notification worker stopped") }() wg.Wait() close(workersDone) }() // ✅ Wait for workers or timeout select { case <-workersDone: logger.Info("✅ All background workers stopped gracefully") case <-shutdownCtx.Done(): logger.Warn("⚠️ Worker shutdown timeout exceeded, forcing exit") } logger.Info("✅ Graceful shutdown completed") logger.Info("👋 Goodbye!") }