| package main |
|
|
| |
|
|
| import ( |
| "context" |
| _ "embed" |
| "errors" |
| "flag" |
| "log" |
| "net/http" |
| "os" |
| "os/signal" |
| "strings" |
| "syscall" |
| "time" |
|
|
| _ "github.com/Wei-Shaw/sub2api/ent/runtime" |
| "github.com/Wei-Shaw/sub2api/internal/config" |
| "github.com/Wei-Shaw/sub2api/internal/handler" |
| "github.com/Wei-Shaw/sub2api/internal/pkg/logger" |
| "github.com/Wei-Shaw/sub2api/internal/server/middleware" |
| "github.com/Wei-Shaw/sub2api/internal/setup" |
| "github.com/Wei-Shaw/sub2api/internal/web" |
|
|
| "github.com/gin-gonic/gin" |
| "golang.org/x/net/http2" |
| "golang.org/x/net/http2/h2c" |
| ) |
|
|
| |
| var embeddedVersion string |
|
|
| |
| var ( |
| Version = "" |
| Commit = "unknown" |
| Date = "unknown" |
| BuildType = "source" |
| ) |
|
|
| func init() { |
| |
| if strings.TrimSpace(Version) != "" { |
| return |
| } |
|
|
| |
| Version = strings.TrimSpace(embeddedVersion) |
| if Version == "" { |
| Version = "0.0.0-dev" |
| } |
| } |
|
|
| |
| |
| func main() { |
| logger.InitBootstrap() |
| defer logger.Sync() |
|
|
| |
| setupMode := flag.Bool("setup", false, "Run setup wizard in CLI mode") |
| showVersion := flag.Bool("version", false, "Show version information") |
| flag.Parse() |
|
|
| if *showVersion { |
| log.Printf("Sub2API %s (commit: %s, built: %s)\n", Version, Commit, Date) |
| return |
| } |
|
|
| |
| if *setupMode { |
| if err := setup.RunCLI(); err != nil { |
| log.Fatalf("Setup failed: %v", err) |
| } |
| return |
| } |
|
|
| |
| if setup.NeedsSetup() { |
| |
| if setup.AutoSetupEnabled() { |
| log.Println("Auto setup mode enabled...") |
| if err := setup.AutoSetupFromEnv(); err != nil { |
| log.Fatalf("Auto setup failed: %v", err) |
| } |
| |
| } else { |
| log.Println("First run detected, starting setup wizard...") |
| runSetupServer() |
| return |
| } |
| } |
|
|
| |
| runMainServer() |
| } |
|
|
| func runSetupServer() { |
| r := gin.New() |
| r.Use(middleware.Recovery()) |
| r.Use(middleware.CORS(config.CORSConfig{})) |
| r.Use(middleware.SecurityHeaders(config.CSPConfig{Enabled: true, Policy: config.DefaultCSPPolicy}, nil)) |
|
|
| |
| setup.RegisterRoutes(r) |
|
|
| |
| if web.HasEmbeddedFrontend() { |
| r.Use(web.ServeEmbeddedFrontend()) |
| } |
|
|
| |
| |
| addr := config.GetServerAddress() |
| log.Printf("Setup wizard available at http://%s", addr) |
| log.Println("Complete the setup wizard to configure Sub2API") |
|
|
| server := &http.Server{ |
| Addr: addr, |
| Handler: h2c.NewHandler(r, &http2.Server{}), |
| ReadHeaderTimeout: 30 * time.Second, |
| IdleTimeout: 120 * time.Second, |
| } |
|
|
| if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { |
| log.Fatalf("Failed to start setup server: %v", err) |
| } |
| } |
|
|
| func runMainServer() { |
| cfg, err := config.LoadForBootstrap() |
| if err != nil { |
| log.Fatalf("Failed to load config: %v", err) |
| } |
| if err := logger.Init(logger.OptionsFromConfig(cfg.Log)); err != nil { |
| log.Fatalf("Failed to initialize logger: %v", err) |
| } |
| if cfg.RunMode == config.RunModeSimple { |
| log.Println("⚠️ WARNING: Running in SIMPLE mode - billing and quota checks are DISABLED") |
| } |
|
|
| buildInfo := handler.BuildInfo{ |
| Version: Version, |
| BuildType: BuildType, |
| } |
|
|
| app, err := initializeApplication(buildInfo) |
| if err != nil { |
| log.Fatalf("Failed to initialize application: %v", err) |
| } |
| defer app.Cleanup() |
|
|
| |
| go func() { |
| if err := app.Server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { |
| log.Fatalf("Failed to start server: %v", err) |
| } |
| }() |
|
|
| log.Printf("Server started on %s", app.Server.Addr) |
|
|
| |
| 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 := app.Server.Shutdown(ctx); err != nil { |
| log.Fatalf("Server forced to shutdown: %v", err) |
| } |
|
|
| log.Println("Server exited") |
| } |
|
|