package main import ( "context" "errors" "log" "net/http" "os" "os/signal" "syscall" "time" "reanimator/internal/api" "reanimator/internal/config" "reanimator/internal/repository" "reanimator/internal/repository/migrate" ) func main() { cfg, err := config.Load() if err != nil { log.Fatalf("load config: %v", err) } db, err := repository.Open(cfg.DatabaseDSN) if err != nil { log.Fatalf("open database: %v", err) } defer func() { _ = db.Close() }() if err := migrate.EnsureSchema(db, cfg.MigrationsDir); err != nil { log.Fatalf("apply migrations: %v", err) } if err := migrate.ValidateOwnershipSchema(db); err != nil { log.Fatalf("schema validation failed: %v", err) } log.Printf("database schema ready") srv := api.NewServer(cfg.HTTPAddr, cfg.ReadTimeout, cfg.WriteTimeout, db) errCh := make(chan error, 1) go func() { log.Printf("starting API server on %s", cfg.HTTPAddr) errCh <- srv.Start() }() signalCh := make(chan os.Signal, 1) signal.Notify(signalCh, syscall.SIGINT, syscall.SIGTERM) select { case sig := <-signalCh: log.Printf("received signal %s, shutting down", sig) case err := <-errCh: if err == nil || errors.Is(err, http.ErrServerClosed) { return } log.Fatalf("server error: %v", err) } ctx, cancel := context.WithTimeout(context.Background(), cfg.ShutdownGrace) defer cancel() if err := srv.Shutdown(ctx); err != nil { log.Fatalf("shutdown server: %v", err) } // Ensure in-flight logs flush before exit in short-lived environments. time.Sleep(25 * time.Millisecond) }