main.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "log"
  6. "net/http"
  7. "os"
  8. "os/signal"
  9. "syscall"
  10. "time"
  11. "github.com/alecthomas/kong"
  12. "github.com/gin-gonic/gin"
  13. "git.linuxforward.com/byom/byom-onboard/internal/common/jwt"
  14. "git.linuxforward.com/byom/byom-onboard/internal/domain/billing"
  15. "git.linuxforward.com/byom/byom-onboard/internal/domain/provisioning"
  16. "git.linuxforward.com/byom/byom-onboard/internal/domain/register"
  17. "git.linuxforward.com/byom/byom-onboard/internal/platform/config"
  18. "git.linuxforward.com/byom/byom-onboard/internal/platform/database"
  19. "git.linuxforward.com/byom/byom-onboard/internal/platform/mailer"
  20. "git.linuxforward.com/byom/byom-onboard/internal/platform/middleware"
  21. )
  22. var (
  23. // Build information - values will be set during build
  24. version = "dev"
  25. commit = "none"
  26. buildDate = "unknown"
  27. )
  28. type CLI struct {
  29. Config string `help:"Config file path" default:"config.yaml" type:"path"`
  30. Debug bool `help:"Enable debug mode"`
  31. Version VersionCmd `cmd:"" help:"Show version information"`
  32. Serve ServeCmd `cmd:"" help:"Start the HTTP server"`
  33. }
  34. type VersionCmd struct{}
  35. func (v VersionCmd) Run() error {
  36. fmt.Printf("Version:\t%s\n", version)
  37. fmt.Printf("Commit:\t\t%s\n", commit)
  38. fmt.Printf("Built:\t\t%s\n", buildDate)
  39. return nil
  40. }
  41. // ServeCmd handles the serve command
  42. type ServeCmd struct {
  43. Port int `help:"Port to listen on" default:"8080"`
  44. Host string `help:"Host to bind to" default:"0.0.0.0"`
  45. }
  46. func (s *ServeCmd) Run(cli *CLI) error {
  47. // Load configuration
  48. cfg, err := config.Load()
  49. if err != nil {
  50. return fmt.Errorf("failed to load configuration: %w", err)
  51. }
  52. // Initialize Gin
  53. gin.SetMode(gin.ReleaseMode)
  54. r := gin.New()
  55. r.Use(gin.Logger())
  56. r.Use(gin.Recovery())
  57. if cli.Debug {
  58. log.Printf("Debug mode enabled")
  59. gin.SetMode(gin.DebugMode)
  60. log.Printf("Using config file: %s", cli.Config)
  61. }
  62. // Initialize database
  63. db, err := database.NewDatabase(cfg.Database.DSN)
  64. if err != nil {
  65. return fmt.Errorf("failed to initialize database: %w", err)
  66. }
  67. jwtClient := jwt.NewJWTClient([]byte(cfg.JWT.Secret))
  68. // Initialize services
  69. stripeClient := billing.NewStripeClient(cfg.Stripe.SecretKey, cfg.Stripe.WebhookSecret)
  70. mailerClient := mailer.NewMailer(&cfg.Mailer)
  71. ovhClient := provisioning.NewOvhClient(cfg.OVH.Endpoint, cfg.OVH.AppKey, cfg.OVH.AppSecret)
  72. registrationService := register.NewRegistrationService(db, jwtClient)
  73. // Initialize handlers
  74. billingHandler := billing.NewHandler(stripeClient, db)
  75. provisioningHandler := provisioning.NewHandler(ovhClient, db)
  76. registerHandler := register.NewHandler(registrationService, mailerClient, db)
  77. // Setup server
  78. server := setupServer(r, billingHandler, provisioningHandler, registerHandler, cfg)
  79. server.Addr = fmt.Sprintf(":%d", s.Port)
  80. // Start server in a goroutine
  81. go func() {
  82. log.Printf("Starting server on port %d", s.Port)
  83. if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
  84. log.Fatalf("Failed to start server: %v", err)
  85. }
  86. }()
  87. // Setup graceful shutdown
  88. quit := make(chan os.Signal, 1)
  89. signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
  90. <-quit
  91. log.Println("Shutting down server...")
  92. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  93. defer cancel()
  94. if err := server.Shutdown(ctx); err != nil {
  95. return fmt.Errorf("server forced to shutdown: %w", err)
  96. }
  97. log.Println("Server exited properly")
  98. return nil
  99. }
  100. func setupServer(
  101. r *gin.Engine,
  102. billingHandler *billing.Handler,
  103. provisioningHandler *provisioning.Handler,
  104. registerHandler *register.Handler,
  105. config *config.Config,
  106. ) *http.Server {
  107. api := r.Group("/api/v1")
  108. {
  109. // Public routes
  110. api.POST("/register", registerHandler.Register)
  111. api.GET("/validate-email", registerHandler.ValidateEmail)
  112. // Payment routes
  113. api.POST("/payment", billingHandler.CreatePayment)
  114. api.POST("/payment/webhook", billingHandler.HandleWebhook)
  115. // Admin routes
  116. adminMiddleware := middleware.NewAdminMiddleware()
  117. admin := api.Group("/admin", adminMiddleware.Middleware())
  118. {
  119. admin.GET("/vms", provisioningHandler.ListVPS)
  120. admin.POST("/vms", provisioningHandler.AddVPS)
  121. admin.GET("/provisioning/:id", provisioningHandler.GetVPSStatus)
  122. }
  123. }
  124. return &http.Server{
  125. Addr: config.Server.Address,
  126. Handler: r,
  127. }
  128. }
  129. func main() {
  130. cli := CLI{}
  131. ctx := kong.Parse(&cli,
  132. kong.Name("byom"),
  133. kong.Description("BYOM - Onboarding service"),
  134. kong.UsageOnError(),
  135. kong.ConfigureHelp(kong.HelpOptions{
  136. Compact: true,
  137. Summary: true,
  138. }),
  139. )
  140. err := ctx.Run(&cli)
  141. ctx.FatalIfErrorf(err)
  142. }