package main import ( "bookra/apps/auth-service/internal/config" "bookra/apps/auth-service/internal/db" "bookra/apps/auth-service/internal/email" "bookra/apps/auth-service/internal/handlers" "context" "fmt" "log" "net/http" "os" "os/signal" "syscall" "time" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" _ "github.com/jackc/pgx/v5/stdlib" "github.com/joho/godotenv" "github.com/pressly/goose/v3" ) func main() { _ = godotenv.Load() cfg, err := config.Load() if err != nil { log.Fatalf("Failed to load config: %v", err) } if os.Getenv("APP_ENV") == "production" { gin.SetMode(gin.ReleaseMode) } database, err := db.New(cfg.DatabaseURL) if err != nil { log.Fatalf("Failed to connect to database: %v", err) } defer database.Close() if err := runMigrations(cfg.DatabaseURL); err != nil { log.Printf("Migration warning: %v", err) } emailSvc := email.New(email.Config{ Host: cfg.SMTPHost, Port: cfg.SMTPPort, Username: cfg.SMTPUsername, Password: cfg.SMTPPassword, From: cfg.EmailFrom, }) handler, err := handlers.New(database, emailSvc, cfg) if err != nil { log.Fatalf("Failed to initialize handlers: %v", err) } r := gin.Default() r.Use(cors.New(cors.Config{ AllowOrigins: []string{cfg.FrontendURL, "http://localhost:3000", "http://localhost:5173"}, AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"}, AllowHeaders: []string{"Origin", "Content-Type", "Accept", "Authorization"}, ExposeHeaders: []string{"Content-Length"}, AllowCredentials: true, MaxAge: 12 * time.Hour, })) r.GET("/health", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"status": "healthy", "service": "auth"}) }) handler.RegisterRoutes(r) srv := &http.Server{ Addr: ":" + cfg.Port, Handler: r, } go func() { if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("Failed to start server: %v", err) } }() log.Printf("Auth service running on port %s", cfg.Port) quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit log.Println("Shutting down server...") ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { log.Printf("Server forced to shutdown: %v", err) } log.Println("Server exited") } func runMigrations(databaseURL string) error { db, err := goose.OpenDBWithDriver("pgx", databaseURL) if err != nil { return fmt.Errorf("goose open db: %w", err) } defer db.Close() if err := goose.SetDialect("postgres"); err != nil { return fmt.Errorf("goose set dialect: %w", err) } if err := goose.Up(db, "migrations"); err != nil { return fmt.Errorf("goose up: %w", err) } return nil }