feat: add bootstrap availability check to login page with conditional UI

Add useEffect hook to fetch bootstrap status on component mount. Add bootstrapAvailable and bootstrapStatusLoaded state variables to track bootstrap endpoint availability. Hide mode toggle button when bootstrap is unavailable or status hasn't loaded yet. Add auth-brand and auth-brand-copy CSS classes to improve login page layout and branding. Add BootstrapStatus handler and BootstrapAvailable service method to expose bootstrap availability
This commit is contained in:
2026-03-17 19:59:15 +01:00
parent b288f0d155
commit 09dd3a5ea6
5 changed files with 91 additions and 9 deletions

View File

@@ -82,6 +82,18 @@ func (h *Handler) Bootstrap(w http.ResponseWriter, r *http.Request) {
apiutil.JSON(w, http.StatusCreated, user)
}
func (h *Handler) BootstrapStatus(w http.ResponseWriter, r *http.Request) {
available, err := h.service.BootstrapAvailable(r.Context())
if err != nil {
apiutil.Error(w, http.StatusInternalServerError, "bootstrap_status_failed", "unable to determine bootstrap status")
return
}
apiutil.JSON(w, http.StatusOK, map[string]any{
"bootstrap_available": available,
})
}
func (h *Handler) Refresh(w http.ResponseWriter, r *http.Request) {
var input RefreshRequest
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {

View File

@@ -125,6 +125,15 @@ func (s *Service) Logout(ctx context.Context, refreshToken string) error {
return s.repo.RevokeRefreshToken(ctx, hashToken(refreshToken))
}
func (s *Service) BootstrapAvailable(ctx context.Context) (bool, error) {
hasUsers, err := s.repo.HasUsers(ctx)
if err != nil {
return false, err
}
return !hasUsers, nil
}
func (s *Service) BootstrapAdmin(ctx context.Context, username, displayName, password string) (UserView, error) {
hasUsers, err := s.repo.HasUsers(ctx)
if err != nil {

View File

@@ -32,6 +32,7 @@ func NewRouter(jwtSecret string, handlers Handlers) http.Handler {
})
r.Route("/api/v1", func(r chi.Router) {
r.Get("/auth/bootstrap/status", handlers.Auth.BootstrapStatus)
r.Post("/auth/bootstrap", handlers.Auth.Bootstrap)
r.Post("/auth/login", handlers.Auth.Login)
r.Post("/auth/refresh", handlers.Auth.Refresh)