feat: disciple link

This commit is contained in:
Krispire Admin
2026-08-07 22:46:08 +05:30
parent 4553725d4e
commit afcdd7ef19
21 changed files with 863 additions and 23 deletions
+63
View File
@@ -8,6 +8,7 @@ import (
"strings"
"github.com/go-chi/chi/v5"
"github.com/prabhupadasays/spsays-bff/internal/repository"
"github.com/prabhupadasays/spsays-bff/internal/service"
"github.com/prabhupadasays/spsays-bff/pkg/response"
)
@@ -331,6 +332,68 @@ func (h *AdminHandler) setUserBanned(w http.ResponseWriter, r *http.Request, ban
// { "bookmark_limit": 10|null, "public_collection_limit": …, "private_collection_limit": … }.
// Each field is optional: a null (or omitted) value resets that limit to the
// plan default; a number caps that resource for this user regardless of plan.
// ListDisciples — GET /admin/disciples?q=&limit= — options for the
// direct-disciple picker. Read-only; an empty q returns the first page
// alphabetically so the picker opens with content.
func (h *AdminHandler) ListDisciples(w http.ResponseWriter, r *http.Request) {
limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
items, err := h.svc.SearchDisciples(r.Context(), r.URL.Query().Get("q"), limit)
if err != nil {
if errors.Is(err, service.ErrDisciplesUnavailable) {
response.Error(w, http.StatusServiceUnavailable, "disciple directory unavailable")
return
}
response.Error(w, http.StatusInternalServerError, "could not search disciples")
return
}
response.JSON(w, http.StatusOK, items)
}
// LinkDisciple — POST /admin/users/{id}/disciple {"disciple_id":"…"} — marks the
// account as one of Śrīla Prabhupāda's direct disciples. Re-posting with a
// different id moves the link.
func (h *AdminHandler) LinkDisciple(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
var in struct {
DiscipleID string `json:"disciple_id"`
}
if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
response.Error(w, http.StatusBadRequest, "invalid request body")
return
}
if strings.TrimSpace(in.DiscipleID) == "" {
response.Error(w, http.StatusBadRequest, "disciple_id is required")
return
}
switch err := h.svc.LinkDisciple(r.Context(), id, in.DiscipleID); {
case err == nil:
response.JSON(w, http.StatusOK, map[string]any{"linked": true})
case errors.Is(err, service.ErrAdminUserNotFound):
response.Error(w, http.StatusNotFound, "user not found")
case errors.Is(err, service.ErrDiscipleNotFound):
response.Error(w, http.StatusNotFound, "disciple not found")
case errors.Is(err, repository.ErrDiscipleClaimed):
response.Error(w, http.StatusConflict, "that disciple is already linked to another user")
case errors.Is(err, service.ErrDisciplesUnavailable):
response.Error(w, http.StatusServiceUnavailable, "disciple directory unavailable")
default:
response.Error(w, http.StatusInternalServerError, "could not link disciple")
}
}
// UnlinkDisciple — DELETE /admin/users/{id}/disciple — removes the link.
func (h *AdminHandler) UnlinkDisciple(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
switch err := h.svc.UnlinkDisciple(r.Context(), id); {
case err == nil:
response.JSON(w, http.StatusOK, map[string]any{"linked": false})
case errors.Is(err, service.ErrAdminUserNotFound):
response.Error(w, http.StatusNotFound, "user not found")
default:
response.Error(w, http.StatusInternalServerError, "could not unlink disciple")
}
}
func (h *AdminHandler) SetUserLimits(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
var in struct {