1
0
forked from wrenn/wrenn

Add team management endpoints

- Three-role model (owner/admin/member) with owner protection invariants
- Team CRUD: create, rename (admin+), soft-delete with VM cleanup (owner only)
- Member management: add by email, remove, role updates (admin+), leave
- Switch-team endpoint re-issues JWT after DB membership verification
- User email prefix search for add-member UI autocomplete
- JWT carries role as a hint; all authorization decisions verified from DB
- Team slug: immutable 12-char hex (e.g. a1b2c3-d1e2f3), reserved on soft-delete
- Migration adds slug + deleted_at to teams; backfills existing rows
This commit is contained in:
2026-03-24 13:29:54 +06:00
parent 4e26d7a292
commit 8e5d426638
21 changed files with 1601 additions and 53 deletions

View File

@ -0,0 +1,47 @@
package api
import (
"net/http"
"strings"
"git.omukk.dev/wrenn/sandbox/internal/auth"
"git.omukk.dev/wrenn/sandbox/internal/service"
)
type usersHandler struct {
svc *service.TeamService
}
func newUsersHandler(svc *service.TeamService) *usersHandler {
return &usersHandler{svc: svc}
}
// Search handles GET /v1/users/search?email=<prefix>
// Returns up to 10 users whose email starts with the given prefix.
// The prefix must contain "@" to scope searches and prevent broad enumeration.
func (h *usersHandler) Search(w http.ResponseWriter, r *http.Request) {
auth.MustFromContext(r.Context()) // ensure authenticated
prefix := strings.TrimSpace(r.URL.Query().Get("email"))
if !strings.Contains(prefix, "@") {
writeError(w, http.StatusBadRequest, "invalid_request", "email prefix must contain '@'")
return
}
results, err := h.svc.SearchUsersByEmailPrefix(r.Context(), prefix)
if err != nil {
status, code, msg := serviceErrToHTTP(err)
writeError(w, status, code, msg)
return
}
type userResult struct {
UserID string `json:"user_id"`
Email string `json:"email"`
}
resp := make([]userResult, len(results))
for i, u := range results {
resp[i] = userResult{UserID: u.ID, Email: u.Email}
}
writeJSON(w, http.StatusOK, resp)
}