package main import ( "context" "fmt" "log" "net/http" "os" "os/signal" "syscall" "time" "osdb/internal/auth" "osdb/internal/config" "osdb/internal/database" "osdb/internal/migrate" ) func main() { cfg := config.Load() args := os.Args[1:] switch { case len(args) >= 1 && args[0] == "migrate": if len(args) >= 2 && args[1] == "down" { if err := migrate.Down(cfg.DatabaseURL); err != nil { log.Fatalf("migrate down: %v", err) } log.Println("migrate down: done") } else { if err := migrate.Up(cfg.DatabaseURL); err != nil { log.Fatalf("migrate up: %v", err) } log.Println("migrate up: applied") } return default: serve(cfg) } } func serve(cfg *config.Config) { // Use a timeout context for the initial DB connection so a slow/unreachable // Postgres doesn't block server startup indefinitely. connectCtx, connectCancel := context.WithTimeout(context.Background(), 30*time.Second) defer connectCancel() pool, err := database.Connect(connectCtx, cfg.DatabaseURL) if err != nil { log.Fatalf("database connect: %v", err) } defer pool.Close() log.Println("database: connected") users, err := auth.LoadUsers(cfg.UsersFile) if err != nil { log.Fatalf("auth: load users from %s: %v", cfg.UsersFile, err) } log.Printf("auth: loaded %d user(s)", len(users)) e := New(pool, users, cfg.JWTSecret) // Start server; signal failures via a channel so the main goroutine can // handle them without calling os.Exit from a goroutine (which would skip // all deferred cleanup, including pool.Close). startErr := make(chan error, 1) go func() { addr := fmt.Sprintf(":%s", cfg.Port) log.Printf("server: listening on %s", addr) if err := e.Start(addr); err != nil && err != http.ErrServerClosed { startErr <- err } }() // Graceful shutdown on SIGINT / SIGTERM or fatal startup error. quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) select { case err := <-startErr: log.Fatalf("server: %v", err) case <-quit: } log.Println("server: shutting down") shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() if err := e.Shutdown(shutdownCtx); err != nil { log.Printf("server: shutdown error: %v", err) } }