package handler import ( "baron-sso-backend/internal/domain" "baron-sso-backend/internal/service" "time" "github.com/gofiber/fiber/v2" ) type TenantGroupHandler struct { Service service.TenantGroupService UserService *service.KratosAdminService } func NewTenantGroupHandler(svc service.TenantGroupService, userSvc *service.KratosAdminService) *TenantGroupHandler { return &TenantGroupHandler{Service: svc, UserService: userSvc} } type tenantGroupSummary struct { ID string `json:"id"` Name string `json:"name"` Slug string `json:"slug"` Description string `json:"description"` Tenants []tenantSummary `json:"tenants,omitempty"` Config domain.JSONMap `json:"config,omitempty"` CreatedAt string `json:"createdAt"` UpdatedAt string `json:"updatedAt"` } func (h *TenantGroupHandler) ListGroups(c *fiber.Ctx) error { limit := c.QueryInt("limit", 50) offset := c.QueryInt("offset", 0) groups, total, err := h.Service.ListGroups(c.Context(), limit, offset) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } items := make([]tenantGroupSummary, 0, len(groups)) for _, g := range groups { items = append(items, mapTenantGroupSummary(g)) } return c.JSON(fiber.Map{ "items": items, "total": total, "limit": limit, "offset": offset, }) } func (h *TenantGroupHandler) GetGroup(c *fiber.Ctx) error { id := c.Params("id") group, err := h.Service.GetGroup(c.Context(), id) if err != nil { return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "group not found"}) } return c.JSON(mapTenantGroupSummary(*group)) } func (h *TenantGroupHandler) CreateGroup(c *fiber.Ctx) error { var req struct { Name string `json:"name"` Slug string `json:"slug"` Description string `json:"description"` } if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid request body"}) } group, err := h.Service.CreateGroup(c.Context(), req.Name, req.Slug, req.Description) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.Status(fiber.StatusCreated).JSON(mapTenantGroupSummary(*group)) } func (h *TenantGroupHandler) UpdateGroup(c *fiber.Ctx) error { id := c.Params("id") var req struct { Name string `json:"name"` Description string `json:"description"` } if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid request body"}) } group, err := h.Service.UpdateGroup(c.Context(), id, req.Name, req.Description) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(mapTenantGroupSummary(*group)) } func (h *TenantGroupHandler) DeleteGroup(c *fiber.Ctx) error { id := c.Params("id") if err := h.Service.DeleteGroup(c.Context(), id); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.SendStatus(fiber.StatusNoContent) } func (h *TenantGroupHandler) AddTenantToGroup(c *fiber.Ctx) error { groupID := c.Params("id") tenantID := c.Params("tenantId") if err := h.Service.AddTenantToGroup(c.Context(), groupID, tenantID); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"message": "tenant added to group"}) } func (h *TenantGroupHandler) RemoveTenantFromGroup(c *fiber.Ctx) error { groupID := c.Params("id") tenantID := c.Params("tenantId") if err := h.Service.RemoveTenantFromGroup(c.Context(), groupID, tenantID); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"message": "tenant removed from group"}) } func (h *TenantGroupHandler) ListAdmins(c *fiber.Ctx) error { groupID := c.Params("id") userIDs, err := h.Service.ListGroupAdmins(c.Context(), groupID) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } type adminInfo struct { ID string `json:"id"` Name string `json:"name"` Email string `json:"email"` } admins := make([]adminInfo, 0, len(userIDs)) for _, uid := range userIDs { identity, err := h.UserService.GetIdentity(c.Context(), uid) if err == nil && identity != nil { name, _ := identity.Traits["name"].(string) email, _ := identity.Traits["email"].(string) admins = append(admins, adminInfo{ ID: uid, Name: name, Email: email, }) } else { // Fallback if identity not found in Kratos admins = append(admins, adminInfo{ID: uid}) } } return c.JSON(admins) } func (h *TenantGroupHandler) AddAdmin(c *fiber.Ctx) error { groupID := c.Params("id") userID := c.Params("userId") if err := h.Service.AddGroupAdmin(c.Context(), groupID, userID); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"message": "admin added to group"}) } func (h *TenantGroupHandler) RemoveAdmin(c *fiber.Ctx) error { groupID := c.Params("id") userID := c.Params("userId") if err := h.Service.RemoveGroupAdmin(c.Context(), groupID, userID); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"message": "admin removed from group"}) } func mapTenantGroupSummary(g domain.TenantGroup) tenantGroupSummary { tenants := make([]tenantSummary, 0, len(g.Tenants)) for _, t := range g.Tenants { tenants = append(tenants, mapTenantSummary(t)) } return tenantGroupSummary{ ID: g.ID, Name: g.Name, Slug: g.Slug, Description: g.Description, Tenants: tenants, Config: g.Config, CreatedAt: g.CreatedAt.Format(time.RFC3339), UpdatedAt: g.UpdatedAt.Format(time.RFC3339), } }