package main import ( "context" "fmt" "log" "net/http" "os" "os/signal" "strings" "syscall" "time" "github.com/alecthomas/kong" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" "git.linuxforward.com/byom/byom-onboard/internal/common/jwt" "git.linuxforward.com/byom/byom-onboard/internal/domain/register" "git.linuxforward.com/byom/byom-onboard/internal/platform/config" "git.linuxforward.com/byom/byom-onboard/internal/platform/database" "git.linuxforward.com/byom/byom-onboard/internal/platform/mailer" "git.linuxforward.com/byom/byom-onboard/internal/platform/payment" "git.linuxforward.com/byom/byom-onboard/internal/platform/stripe" ) var ( // Build information - values will be set during build version = "dev" commit = "none" buildDate = "unknown" ) type CLI struct { Config string `help:"Config file path" default:"config.yaml" type:"path"` Debug bool `help:"Enable debug mode"` Version VersionCmd `cmd:"" help:"Show version information"` Serve ServeCmd `cmd:"" help:"Start the HTTP server"` } type VersionCmd struct{} func (v VersionCmd) Run() error { fmt.Printf("Version:\t%s\n", version) fmt.Printf("Commit:\t\t%s\n", commit) fmt.Printf("Built:\t\t%s\n", buildDate) return nil } // ServeCmd handles the serve command type ServeCmd struct { Port int `help:"Port to listen on" default:"8080"` Host string `help:"Host to bind to" default:"0.0.0.0"` } func (s *ServeCmd) Run(cli *CLI) error { // Load configuration cfg, err := config.Load() if err != nil { return fmt.Errorf("failed to load configuration: %w", err) } // Initialize Stripe if err := stripe.Initialize(); err != nil { log.Fatalf("Failed to initialize Stripe: %v", err) } // Initialize Gin gin.SetMode(gin.ReleaseMode) r := gin.New() r.Use(gin.Logger()) r.Use(gin.Recovery()) //read cors from env corsUrl := os.Getenv("CORS_ALLOWED_ORIGIN") // Split the CORS URL by comma to handle multiple origins corsOrigins := strings.Split(corsUrl, ",") corsConfig := cors.DefaultConfig() corsConfig.AllowOrigins = corsOrigins corsConfig.AllowCredentials = true corsConfig.AllowHeaders = []string{"Content-Type", "Authorization"} corsConfig.AllowMethods = []string{"*"} r.Use(cors.New(corsConfig)) if cli.Debug { log.Printf("Debug mode enabled") gin.SetMode(gin.DebugMode) log.Printf("Using config file: %s", cli.Config) } // Initialize database db, err := database.NewDatabase(cfg.Database.DSN) if err != nil { return fmt.Errorf("failed to initialize database: %w", err) } jwtClient := jwt.NewJWTClient([]byte(cfg.JWT.Secret)) //stripeClient := billing.NewStripeClient(cfg.Stripe.APIKey, cfg.Stripe.EndpointSecret) mailerClient := mailer.NewMailer(&cfg.Mailer) // Initialize Stripe handler stripeHandler, err := payment.NewStripeHandler( "https://byom.moooffle.com", mailerClient, ) if err != nil { return fmt.Errorf("failed to initialize Stripe: %w", err) } // Initialize services //ovhClient := provisioning.NewOvhClient(cfg.OVH.Endpoint, cfg.OVH.AppKey, cfg.OVH.AppSecret) //provisioningHandler := provisioning.NewHandler(ovhClient, db) registerHandler := register.NewHandler(mailerClient, db, jwtClient) // Setup server server := setupServer(r, registerHandler, cfg, stripeHandler) server.Addr = fmt.Sprintf(":%d", s.Port) // Start server in a goroutine go func() { log.Printf("Starting server on port %d", s.Port) if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("Failed to start server: %v", err) } }() // Setup graceful shutdown 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 := server.Shutdown(ctx); err != nil { return fmt.Errorf("server forced to shutdown: %w", err) } log.Println("Server exited properly") return nil } func setupServer( r *gin.Engine, registerHandler *register.Handler, config *config.Config, stripeHandler *payment.StripeHandler, ) *http.Server { api := r.Group("/api/v1/onboard") { // Public routes api.POST("/check-email", registerHandler.CheckEmail) api.POST("/register", registerHandler.Register) api.GET("/validate-email", registerHandler.ValidateEmail) // Stripe routes api.POST("/create-checkout-session", stripeHandler.CreateCheckoutSession) api.POST("/create-portal-session", stripeHandler.CreatePortalSession) api.POST("/webhook", stripeHandler.HandleWebhook) // Add the new products endpoint api.GET("/products", stripeHandler.GetProducts) } return &http.Server{ Addr: config.Server.Address, Handler: r, } } func main() { cli := CLI{} ctx := kong.Parse(&cli, kong.Name("byom"), kong.Description("BYOM - Onboarding service"), kong.UsageOnError(), kong.ConfigureHelp(kong.HelpOptions{ Compact: true, Summary: true, }), ) err := ctx.Run(&cli) ctx.FatalIfErrorf(err) }