203 lines
5.7 KiB
Go
203 lines
5.7 KiB
Go
package handlers
|
|
|
|
// persona_groups.go — Persona group (roster template) CRUD (v0.23.2)
|
|
//
|
|
// v0.29.0: Raw SQL replaced with PersonaGroupStore methods.
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"chat-switchboard/models"
|
|
"chat-switchboard/store"
|
|
)
|
|
|
|
type PersonaGroupHandler struct {
|
|
stores store.Stores
|
|
}
|
|
|
|
func NewPersonaGroupHandler(s store.Stores) *PersonaGroupHandler {
|
|
return &PersonaGroupHandler{stores: s}
|
|
}
|
|
|
|
// ── List ────────────────────────────────────────
|
|
|
|
func (h *PersonaGroupHandler) List(c *gin.Context) {
|
|
userID := getUserID(c)
|
|
|
|
groups, err := h.stores.PersonaGroups.List(c.Request.Context(), userID)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list groups"})
|
|
return
|
|
}
|
|
|
|
for i := range groups {
|
|
members, _ := h.stores.PersonaGroups.ListMembers(c.Request.Context(), groups[i].ID)
|
|
groups[i].Members = members
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"data": groups})
|
|
}
|
|
|
|
// ── Get ─────────────────────────────────────────
|
|
|
|
func (h *PersonaGroupHandler) Get(c *gin.Context) {
|
|
userID := getUserID(c)
|
|
id := c.Param("id")
|
|
|
|
g, err := h.stores.PersonaGroups.Get(c.Request.Context(), id, userID)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get group"})
|
|
return
|
|
}
|
|
if g == nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "group not found"})
|
|
return
|
|
}
|
|
|
|
members, _ := h.stores.PersonaGroups.ListMembers(c.Request.Context(), g.ID)
|
|
if members == nil {
|
|
members = []models.PersonaGroupMember{}
|
|
}
|
|
g.Members = members
|
|
|
|
c.JSON(http.StatusOK, g)
|
|
}
|
|
|
|
// ── Create ──────────────────────────────────────
|
|
|
|
func (h *PersonaGroupHandler) Create(c *gin.Context) {
|
|
userID := getUserID(c)
|
|
|
|
var req struct {
|
|
Name string `json:"name" binding:"required,min=1,max=100"`
|
|
Description string `json:"description"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
g := &models.PersonaGroup{
|
|
Name: strings.TrimSpace(req.Name),
|
|
Description: req.Description,
|
|
OwnerID: userID,
|
|
Scope: "personal",
|
|
}
|
|
|
|
if err := h.stores.PersonaGroups.Create(c.Request.Context(), g); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create group"})
|
|
return
|
|
}
|
|
|
|
g.Members = []models.PersonaGroupMember{}
|
|
c.JSON(http.StatusCreated, g)
|
|
}
|
|
|
|
// ── Update ──────────────────────────────────────
|
|
|
|
func (h *PersonaGroupHandler) Update(c *gin.Context) {
|
|
userID := getUserID(c)
|
|
id := c.Param("id")
|
|
|
|
var req struct {
|
|
Name *string `json:"name"`
|
|
Description *string `json:"description"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
// Verify ownership
|
|
ownerID, err := h.stores.PersonaGroups.GetOwnerID(c.Request.Context(), id)
|
|
if err != nil || ownerID == "" {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "group not found"})
|
|
return
|
|
}
|
|
if ownerID != userID {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "not your group"})
|
|
return
|
|
}
|
|
|
|
fields := map[string]interface{}{}
|
|
if req.Name != nil {
|
|
fields["name"] = strings.TrimSpace(*req.Name)
|
|
}
|
|
if req.Description != nil {
|
|
fields["description"] = *req.Description
|
|
}
|
|
if len(fields) > 0 {
|
|
_ = h.stores.PersonaGroups.Update(c.Request.Context(), id, fields)
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"ok": true})
|
|
}
|
|
|
|
// ── Delete ──────────────────────────────────────
|
|
|
|
func (h *PersonaGroupHandler) Delete(c *gin.Context) {
|
|
userID := getUserID(c)
|
|
id := c.Param("id")
|
|
|
|
n, err := h.stores.PersonaGroups.Delete(c.Request.Context(), id, userID)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "delete failed"})
|
|
return
|
|
}
|
|
if n == 0 {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "group not found"})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"ok": true})
|
|
}
|
|
|
|
// ── Add Member ──────────────────────────────────
|
|
|
|
func (h *PersonaGroupHandler) AddMember(c *gin.Context) {
|
|
userID := getUserID(c)
|
|
groupID := c.Param("id")
|
|
|
|
var req struct {
|
|
PersonaID string `json:"persona_id" binding:"required"`
|
|
IsLeader bool `json:"is_leader"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
// Verify ownership
|
|
ownerID, err := h.stores.PersonaGroups.GetOwnerID(c.Request.Context(), groupID)
|
|
if err != nil || ownerID != userID {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "not your group"})
|
|
return
|
|
}
|
|
|
|
if err := h.stores.PersonaGroups.AddMember(c.Request.Context(), groupID, req.PersonaID, req.IsLeader); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to add member"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, gin.H{"ok": true})
|
|
}
|
|
|
|
// ── Remove Member ───────────────────────────────
|
|
|
|
func (h *PersonaGroupHandler) RemoveMember(c *gin.Context) {
|
|
userID := getUserID(c)
|
|
groupID := c.Param("id")
|
|
memberID := c.Param("memberId")
|
|
|
|
ownerID, err := h.stores.PersonaGroups.GetOwnerID(c.Request.Context(), groupID)
|
|
if err != nil || ownerID != userID {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "not your group"})
|
|
return
|
|
}
|
|
|
|
_ = h.stores.PersonaGroups.RemoveMember(c.Request.Context(), memberID, groupID)
|
|
c.JSON(http.StatusOK, gin.H{"ok": true})
|
|
}
|