package main import ( "context" "log" "net/http" "os" "os/signal" "time" "git.farahty.com/nimer/go-mongo/app" "github.com/fatih/color" "github.com/joho/godotenv" "github.com/redis/go-redis/v9" ) func main() { // Setup cancelable root context ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Panic recovery defer func() { if r := recover(); r != nil { color.Red("🔴 Panic occurred: %v\n", r) os.Exit(1) } }() color.Yellow("🚀 Starting server ...\n") // Load .env if needed if _, exists := os.LookupEnv("MONGO_URI"); !exists { if err := godotenv.Load(); err != nil { log.Fatal("🔴 Failed to load .env file: ", err) } color.Green("✅ .env file loaded\n") } // Validate environment variables requiredEnvs := []string{"PORT", "MONGO_URI", "REDIS_HOST", "REDIS_PORT"} for _, key := range requiredEnvs { if os.Getenv(key) == "" { log.Fatalf("🔴 Required environment variable %s is missing", key) } } port := os.Getenv("PORT") // Connect to Mongo dbCancel, err := app.Connect() if err != nil { log.Fatalf("🔴 MongoDB connection error: %v", err) } defer func() { color.Red("❌ Closing MongoDB connection...\n") dbCancel() if err := app.Mongo.Disconnect(ctx); err != nil { log.Fatal("🔴 MongoDB disconnection error: ", err) } }() color.Green("✅ Connected to MongoDB successfully\n") // Load authorization policies using root context if err := app.LoadAuthorizer(ctx); err != nil { log.Fatal("🔴 Authorizer error: ", err) } color.Green("✅ Authorization policies loaded successfully\n") // Redis redisClient := redis.NewClient(&redis.Options{ Addr: os.Getenv("REDIS_HOST") + ":" + os.Getenv("REDIS_PORT"), Password: os.Getenv("REDIS_PASSWORD"), DB: 0, }) if _, err := redisClient.Ping(ctx).Result(); err != nil { log.Fatal("🔴 Redis connection error: ", err) } defer func() { color.Red("❌ Closing Redis connection...\n") _ = redisClient.Close() }() color.Green("✅ Connected to Redis cache successfully\n") // Create GraphQL server graphqlServer := createGraphqlServer(redisClient) // Start HTTP server server := &http.Server{ Addr: ":" + port, Handler: createRouter(graphqlServer), ReadTimeout: 30 * time.Second, WriteTimeout: 30 * time.Second, IdleTimeout: 30 * time.Second, } go func() { color.Green("🌐 Server listening at http://localhost:%s\n", port) if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("🔴 Server failed: %v", err) } }() // Graceful shutdown quit := make(chan os.Signal, 1) signal.Notify(quit, os.Interrupt) <-quit color.Yellow("🟡 Shutdown signal received, initiating cleanup...") // Cancel root context and wait for graceful shutdown shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 15*time.Second) defer shutdownCancel() if err := server.Shutdown(shutdownCtx); err != nil { log.Fatalf("🔴 Server forced to shutdown: %v", err) } color.Green("✅ Server shutdown completed gracefully") }