package router import ( "bytes" "context" "log" "net/http" "strings" "bookhoard/internal/setupstatus" "bookhoard/templates" "github.com/labstack/echo/v5" ) func isSetupComplete(cfg *Config) bool { getter := func(ctx context.Context) (string, error) { row, err := cfg.Queries.GetSystemConfig(ctx, "base_url") if err != nil { return "", err } return row.Value, nil } return setupstatus.IsSetupComplete(context.Background(), cfg.Queries, getter) } // setupAllowedAPIRoutes lists API endpoints that remain accessible before // initial setup is complete so the server can be configured via API. var setupAllowedAPIRoutes = []string{ "/api/auth/register", "/api/auth/login", "/api/system/config", } // isAllowedDuringSetup reports whether a request path should bypass the setup // gate. This includes the setup page itself, static assets, health checks, and // the minimal set of API routes needed to perform initial configuration. func isAllowedDuringSetup(path string) bool { if path == "/setup" || path == "/setup/" { return true } if strings.HasPrefix(path, "/static/") || path == "/health" || path == "/favicon.ico" { return true } for _, route := range setupAllowedAPIRoutes { if path == route || strings.HasPrefix(path, route+"/") { return true } } return false } func setupRedirectMiddleware(cfg *Config) echo.MiddlewareFunc { return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c *echo.Context) error { path := c.Request().URL.Path if isAllowedDuringSetup(path) { return next(c) } if !isSetupComplete(cfg) { if strings.HasPrefix(path, "/api/") { return c.JSON(http.StatusServiceUnavailable, map[string]string{ "error": "Server setup is not complete. Configure an admin account and base_url via the setup wizard or API.", }) } return c.Redirect(http.StatusFound, "/setup") } return next(c) } } } func registerSetupRoutes(cfg *Config) { e := cfg.Echo e.GET("/setup", func(c *echo.Context) error { if isSetupComplete(cfg) { return c.Redirect(http.StatusFound, "/") } var buf bytes.Buffer if err := templates.Setup().Render(c.Request().Context(), &buf); err != nil { log.Printf("Failed to render setup template: %v", err) return c.HTML(http.StatusInternalServerError, "Failed to render setup page") } return c.HTML(http.StatusOK, buf.String()) }) }