package main import ( "context" "log" "net" "os" "os/signal" "syscall" bootstrap "stream.api/internal/app" "stream.api/internal/config" "stream.api/internal/database/query" "stream.api/pkg/cache" "stream.api/pkg/database" "stream.api/pkg/logger" "stream.api/pkg/token" ) func main() { // 1. Load Config cfg, err := config.LoadConfig() if err != nil { // Use default if env/file issues, usually LoadConfig returns error only on serious issues // But here if it returns error we might want to panic log.Fatalf("Failed to load config: %v", err) } // 2. Connect DB db, err := database.Connect(cfg.Database.DSN) if err != nil { log.Fatalf("Failed to connect to database: %v", err) } // Initialize generated query query.SetDefault(db) // TODO: Tách database migration ra luồng riêng nếu cần. // 3. Connect Redis (Cache Interface) rdb, err := cache.NewRedisCache(cfg.Redis.Addr, cfg.Redis.Password, cfg.Redis.DB) if err != nil { log.Fatalf("Failed to connect to redis: %v", err) } defer rdb.Close() // Ensure we close cache on exit // 4. Initialize Components tokenProvider := token.NewJWTProvider(cfg.JWT.Secret) appLogger := logger.NewLogger(cfg.Server.Mode) module, err := bootstrap.NewGRPCModule(context.Background(), cfg, db, rdb, tokenProvider, appLogger) if err != nil { log.Fatalf("Failed to setup gRPC runtime module: %v", err) } grpcListener, err := net.Listen("tcp", ":"+cfg.Server.GRPCPort) if err != nil { log.Fatalf("Failed to listen on gRPC port %s: %v", cfg.Server.GRPCPort, err) } go func() { log.Printf("Starting gRPC server on port %s", cfg.Server.GRPCPort) if err := module.ServeGRPC(grpcListener); err != nil { log.Fatalf("Failed to run gRPC server: %v", err) } }() quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit log.Println("Shutting down gRPC server...") module.Shutdown() _ = grpcListener.Close() log.Println("Server exiting") }