package main import ( "context" "errors" "log" "net/http" "os" "os/signal" "syscall" "time" "cmroubao/backend-api/internal/config" "cmroubao/backend-api/internal/platform/database" "cmroubao/backend-api/internal/transport/httpapi" ) func main() { if err := run(); err != nil { log.Printf("cmroubao API stopped: %v", err) os.Exit(1) } } func run() error { cfg, err := config.Load(os.LookupEnv) if err != nil { return err } startupContext, cancelStartup := context.WithTimeout( context.Background(), cfg.ReadTimeout, ) defer cancelStartup() db, err := database.Open(startupContext, cfg.DatabasePath) if err != nil { return errors.New("database startup failed") } defer func() { if err := db.Close(); err != nil { log.Print("database close failed") } }() router, err := httpapi.NewRouter(db, func(event string) { log.Print(event) }) if err != nil { return errors.New("HTTP router setup failed") } server := httpapi.NewServer(cfg, router) signalContext, stopSignals := signal.NotifyContext( context.Background(), os.Interrupt, syscall.SIGTERM, ) defer stopSignals() serverErrors := make(chan error, 1) go func() { serverErrors <- server.ListenAndServe() }() select { case err := <-serverErrors: if errors.Is(err, http.ErrServerClosed) { return nil } return errors.New("HTTP server failed") case <-signalContext.Done(): return shutdownServer(server, serverErrors, cfg.ShutdownTimeout) } } func shutdownServer( server *http.Server, serverErrors <-chan error, timeout time.Duration, ) error { shutdownContext, cancelShutdown := context.WithTimeout( context.Background(), timeout, ) defer cancelShutdown() shutdownErr := server.Shutdown(shutdownContext) if shutdownErr != nil { if err := server.Close(); err != nil { return errors.New("HTTP server force close failed") } } waitTimeout := timeout if waitTimeout > time.Second { waitTimeout = time.Second } timer := time.NewTimer(waitTimeout) defer timer.Stop() select { case err := <-serverErrors: if err != nil && !errors.Is(err, http.ErrServerClosed) { return errors.New("HTTP server failed during shutdown") } case <-timer.C: _ = server.Close() return errors.New("HTTP server did not stop") } if shutdownErr != nil { return errors.New("HTTP server graceful shutdown timed out") } return nil }