package setup import ( "encoding/json" "errors" "net/http" "git.unsupervised.ca/walkies/internal/auth" "git.unsupervised.ca/walkies/internal/respond" ) // TokenIssuer is the subset of auth.Service the setup handler needs. type TokenIssuer interface { IssueToken(volunteerID int64, role string) (string, error) } type Handler struct { store Storer authSvc TokenIssuer } func NewHandler(store *Store, authSvc *auth.Service) *Handler { return &Handler{store: store, authSvc: authSvc} } // NewHandlerFromInterfaces constructs a Handler from interface values, intended for testing. func NewHandlerFromInterfaces(store Storer, authSvc TokenIssuer) *Handler { return &Handler{store: store, authSvc: authSvc} } // Status handles GET /api/v1/setup/status. func (h *Handler) Status(w http.ResponseWriter, r *http.Request) { needs, err := h.store.NeedsSetup(r.Context()) if err != nil { respond.Error(w, http.StatusInternalServerError, "could not check setup status") return } respond.JSON(w, http.StatusOK, map[string]bool{"needs_setup": needs}) } // CreateAdmin handles POST /api/v1/setup/admin. func (h *Handler) CreateAdmin(w http.ResponseWriter, r *http.Request) { var body struct { Name string `json:"name"` Email string `json:"email"` Password string `json:"password"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { respond.Error(w, http.StatusBadRequest, "invalid request body") return } if body.Name == "" || body.Email == "" || body.Password == "" { respond.Error(w, http.StatusBadRequest, "name, email, and password are required") return } if len(body.Password) < 8 { respond.Error(w, http.StatusBadRequest, "password must be at least 8 characters") return } hashed, err := auth.HashPassword(body.Password) if err != nil { respond.Error(w, http.StatusInternalServerError, "could not hash password") return } id, err := h.store.CreateAdmin(r.Context(), body.Name, body.Email, hashed) if errors.Is(err, ErrSetupAlreadyDone) { respond.Error(w, http.StatusForbidden, "setup already completed") return } if err != nil { respond.Error(w, http.StatusInternalServerError, "could not create admin account") return } token, err := h.authSvc.IssueToken(id, "admin") if err != nil { respond.Error(w, http.StatusInternalServerError, "could not issue token") return } respond.JSON(w, http.StatusCreated, map[string]string{"token": token}) }