Resolve bulk role path segment from RBAC roles.id.
Admin bulk deactivate/reactivate accepts decimal path segments matching GET /rbac/roles IDs, resolving RoleRecord.name to the platform key. Document 404 when id is unknown. Add Cursor rule: on push, commit dirty tree first without secrets. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
2f73b60122
commit
4a681265d7
14
.cursor/rules/git-push-commit-if-dirty.mdc
Normal file
14
.cursor/rules/git-push-commit-if-dirty.mdc
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
description: Commit before push whenever the tree is dirty
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# Git push
|
||||
|
||||
When the user asks to push (including phrases like “push”, “push to remote”, or “git push”):
|
||||
|
||||
1. Run `git status` (and if needed `git diff`) to check for unstaged/uncommitted changes.
|
||||
2. If there are changes worth shipping, stage and **commit first**—never omit secrets such as `.env`, credentials files, or private keys. Follow the repo’s commit message conventions.
|
||||
3. Then run `git push` to the tracked upstream.
|
||||
|
||||
If nothing is staged and the working tree is clean, pushing without a commit is fine.
|
||||
20
docs/docs.go
20
docs/docs.go
|
|
@ -512,7 +512,7 @@ const docTemplate = `{
|
|||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Sets all platform users with the given users.role to DEACTIVATED (except the caller) and all team_members with the given team_role to inactive. The path role must match a valid learner role or team role string (e.g. STUDENT, INSTRUCTOR, ADMIN, CONTENT_MANAGER). SUPER_ADMIN cannot be bulk-deactivated. ADMIN platform users must use SUPER_ADMIN to bulk change other platform ADMIN users (team_members with team_role ADMIN under path ADMIN remain allowed). Empty body allowed; optionally pass exclude_team_member_id to skip one team_members row (e.g. yourself).",
|
||||
"description": "Sets all platform users with the given users.role to DEACTIVATED (except the caller) and all team_members with the given team_role to inactive. Path :role may be a role key (e.g. INSTRUCTOR, ADMIN) or a decimal RBAC roles.id from GET /api/v1/rbac/roles (resolved to RoleRecord.name uppercased). SUPER_ADMIN cannot be bulk-deactivated. ADMIN platform users must use SUPER_ADMIN to bulk change other platform ADMIN users (team_members with team_role ADMIN under path ADMIN remain allowed). Empty body allowed; optionally pass exclude_team_member_id to skip one team_members row (e.g. yourself).",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
|
|
@ -526,7 +526,7 @@ const docTemplate = `{
|
|||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Role key (matches users.role and/or team_members.team_role)",
|
||||
"description": "Role key (INSTRUCTOR etc.) or RBAC roles.id (integer string)",
|
||||
"name": "role",
|
||||
"in": "path",
|
||||
"required": true
|
||||
|
|
@ -559,6 +559,12 @@ const docTemplate = `{
|
|||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
|
|
@ -575,7 +581,7 @@ const docTemplate = `{
|
|||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Sets all platform users with the given role from DEACTIVATED to ACTIVE (except the caller) and all team_members with the given team_role from inactive to active. Path role must be a valid platform or team role. SUPER_ADMIN cannot be bulk changed. ADMIN callers cannot bulk change other platform ADMIN users (team_members ADMIN under path ADMIN is allowed). Matches only users currently DEACTIVATED and team rows currently inactive.",
|
||||
"description": "Sets all platform users with the given role from DEACTIVATED to ACTIVE (except the caller) and all team_members with the given team_role from inactive to active. Path :role may be a role key or decimal RBAC roles.id (see bulk-deactivate). Path role must correspond to valid platform users.role or team_members.team_role (after resolving id → name). SUPER_ADMIN cannot be bulk changed. ADMIN callers cannot bulk change other platform ADMIN users (team_members ADMIN under path ADMIN is allowed). Matches only users currently DEACTIVATED and team rows currently inactive.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
|
|
@ -589,7 +595,7 @@ const docTemplate = `{
|
|||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Role key (matches users.role and/or team_members.team_role)",
|
||||
"description": "Role key (INSTRUCTOR etc.) or RBAC roles.id (integer string)",
|
||||
"name": "role",
|
||||
"in": "path",
|
||||
"required": true
|
||||
|
|
@ -622,6 +628,12 @@ const docTemplate = `{
|
|||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
|
|
|
|||
|
|
@ -504,7 +504,7 @@
|
|||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Sets all platform users with the given users.role to DEACTIVATED (except the caller) and all team_members with the given team_role to inactive. The path role must match a valid learner role or team role string (e.g. STUDENT, INSTRUCTOR, ADMIN, CONTENT_MANAGER). SUPER_ADMIN cannot be bulk-deactivated. ADMIN platform users must use SUPER_ADMIN to bulk change other platform ADMIN users (team_members with team_role ADMIN under path ADMIN remain allowed). Empty body allowed; optionally pass exclude_team_member_id to skip one team_members row (e.g. yourself).",
|
||||
"description": "Sets all platform users with the given users.role to DEACTIVATED (except the caller) and all team_members with the given team_role to inactive. Path :role may be a role key (e.g. INSTRUCTOR, ADMIN) or a decimal RBAC roles.id from GET /api/v1/rbac/roles (resolved to RoleRecord.name uppercased). SUPER_ADMIN cannot be bulk-deactivated. ADMIN platform users must use SUPER_ADMIN to bulk change other platform ADMIN users (team_members with team_role ADMIN under path ADMIN remain allowed). Empty body allowed; optionally pass exclude_team_member_id to skip one team_members row (e.g. yourself).",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
|
|
@ -518,7 +518,7 @@
|
|||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Role key (matches users.role and/or team_members.team_role)",
|
||||
"description": "Role key (INSTRUCTOR etc.) or RBAC roles.id (integer string)",
|
||||
"name": "role",
|
||||
"in": "path",
|
||||
"required": true
|
||||
|
|
@ -551,6 +551,12 @@
|
|||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
|
|
@ -567,7 +573,7 @@
|
|||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Sets all platform users with the given role from DEACTIVATED to ACTIVE (except the caller) and all team_members with the given team_role from inactive to active. Path role must be a valid platform or team role. SUPER_ADMIN cannot be bulk changed. ADMIN callers cannot bulk change other platform ADMIN users (team_members ADMIN under path ADMIN is allowed). Matches only users currently DEACTIVATED and team rows currently inactive.",
|
||||
"description": "Sets all platform users with the given role from DEACTIVATED to ACTIVE (except the caller) and all team_members with the given team_role from inactive to active. Path :role may be a role key or decimal RBAC roles.id (see bulk-deactivate). Path role must correspond to valid platform users.role or team_members.team_role (after resolving id → name). SUPER_ADMIN cannot be bulk changed. ADMIN callers cannot bulk change other platform ADMIN users (team_members ADMIN under path ADMIN is allowed). Matches only users currently DEACTIVATED and team rows currently inactive.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
|
|
@ -581,7 +587,7 @@
|
|||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Role key (matches users.role and/or team_members.team_role)",
|
||||
"description": "Role key (INSTRUCTOR etc.) or RBAC roles.id (integer string)",
|
||||
"name": "role",
|
||||
"in": "path",
|
||||
"required": true
|
||||
|
|
@ -614,6 +620,12 @@
|
|||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/domain.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
|
|
|
|||
|
|
@ -2879,14 +2879,14 @@ paths:
|
|||
- application/json
|
||||
description: Sets all platform users with the given users.role to DEACTIVATED
|
||||
(except the caller) and all team_members with the given team_role to inactive.
|
||||
The path role must match a valid learner role or team role string (e.g. STUDENT,
|
||||
INSTRUCTOR, ADMIN, CONTENT_MANAGER). SUPER_ADMIN cannot be bulk-deactivated.
|
||||
ADMIN platform users must use SUPER_ADMIN to bulk change other platform ADMIN
|
||||
users (team_members with team_role ADMIN under path ADMIN remain allowed).
|
||||
Empty body allowed; optionally pass exclude_team_member_id to skip one team_members
|
||||
row (e.g. yourself).
|
||||
Path :role may be a role key (e.g. INSTRUCTOR, ADMIN) or a decimal RBAC roles.id
|
||||
from GET /api/v1/rbac/roles (resolved to RoleRecord.name uppercased). SUPER_ADMIN
|
||||
cannot be bulk-deactivated. ADMIN platform users must use SUPER_ADMIN to bulk
|
||||
change other platform ADMIN users (team_members with team_role ADMIN under
|
||||
path ADMIN remain allowed). Empty body allowed; optionally pass exclude_team_member_id
|
||||
to skip one team_members row (e.g. yourself).
|
||||
parameters:
|
||||
- description: Role key (matches users.role and/or team_members.team_role)
|
||||
- description: Role key (INSTRUCTOR etc.) or RBAC roles.id (integer string)
|
||||
in: path
|
||||
name: role
|
||||
required: true
|
||||
|
|
@ -2911,6 +2911,10 @@ paths:
|
|||
description: Forbidden
|
||||
schema:
|
||||
$ref: '#/definitions/domain.ErrorResponse'
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
$ref: '#/definitions/domain.ErrorResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
|
|
@ -2927,12 +2931,14 @@ paths:
|
|||
- application/json
|
||||
description: Sets all platform users with the given role from DEACTIVATED to
|
||||
ACTIVE (except the caller) and all team_members with the given team_role from
|
||||
inactive to active. Path role must be a valid platform or team role. SUPER_ADMIN
|
||||
cannot be bulk changed. ADMIN callers cannot bulk change other platform ADMIN
|
||||
users (team_members ADMIN under path ADMIN is allowed). Matches only users
|
||||
currently DEACTIVATED and team rows currently inactive.
|
||||
inactive to active. Path :role may be a role key or decimal RBAC roles.id
|
||||
(see bulk-deactivate). Path role must correspond to valid platform users.role
|
||||
or team_members.team_role (after resolving id → name). SUPER_ADMIN cannot
|
||||
be bulk changed. ADMIN callers cannot bulk change other platform ADMIN users
|
||||
(team_members ADMIN under path ADMIN is allowed). Matches only users currently
|
||||
DEACTIVATED and team rows currently inactive.
|
||||
parameters:
|
||||
- description: Role key (matches users.role and/or team_members.team_role)
|
||||
- description: Role key (INSTRUCTOR etc.) or RBAC roles.id (integer string)
|
||||
in: path
|
||||
name: role
|
||||
required: true
|
||||
|
|
@ -2957,6 +2963,10 @@ paths:
|
|||
description: Forbidden
|
||||
schema:
|
||||
$ref: '#/definitions/domain.ErrorResponse'
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
$ref: '#/definitions/domain.ErrorResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package domain
|
|||
|
||||
// BulkAccountsByRoleRequest is optional JSON for POST /admin/roles/:role/bulk-{deactivate,reactivate}.
|
||||
// Optional exclusions apply to team_members bulk updates only.
|
||||
// Path :role is a platform/team role key or a numeric RBAC roles.id string (decimal digits resolve via GET /api/v1/rbac/roles ids).
|
||||
type BulkAccountsByRoleRequest struct {
|
||||
ExcludeTeamMemberID *int64 `json:"exclude_team_member_id,omitempty"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@ import (
|
|||
"Yimaru-Backend/internal/web_server/response"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
|
@ -381,18 +383,57 @@ func (h *Handler) UpdateAdmin(c *fiber.Ctx) error {
|
|||
return response.WriteJSON(c, fiber.StatusOK, "Admin updated successfully", nil, nil)
|
||||
}
|
||||
|
||||
// bulkAccountsRoleFromPath resolves admin bulk :role: decimal digits → rbac roles.id lookup (same ids as GET /api/v1/rbac/roles); otherwise uppercase role key.
|
||||
func (h *Handler) bulkAccountsRoleFromPath(c *fiber.Ctx) (roleKey string, ok bool) {
|
||||
raw := strings.TrimSpace(c.Params("role"))
|
||||
if raw == "" {
|
||||
_ = c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||
Message: "Invalid role",
|
||||
Error: "role path parameter is required",
|
||||
})
|
||||
return "", false
|
||||
}
|
||||
if rbacID, parseErr := strconv.ParseInt(raw, 10, 64); parseErr == nil {
|
||||
if rbacID <= 0 {
|
||||
_ = c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||
Message: "Invalid role",
|
||||
Error: "numeric role path must be a positive RBAC roles.id (see GET /api/v1/rbac/roles)",
|
||||
})
|
||||
return "", false
|
||||
}
|
||||
rec, err := h.rbacSvc.GetRoleByID(c.Context(), rbacID)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
_ = c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
|
||||
Message: "Invalid role",
|
||||
Error: "RBAC role id not found",
|
||||
})
|
||||
return "", false
|
||||
}
|
||||
_ = c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
||||
Message: "RBAC lookup failed",
|
||||
Error: err.Error(),
|
||||
})
|
||||
return "", false
|
||||
}
|
||||
return strings.ToUpper(strings.TrimSpace(rec.Name)), true
|
||||
}
|
||||
return strings.ToUpper(raw), true
|
||||
}
|
||||
|
||||
// BulkDeactivateAccountsByRole godoc
|
||||
// @Summary Bulk deactivate accounts by role (SUPER_ADMIN or ADMIN platform users only)
|
||||
// @Description Sets all platform users with the given users.role to DEACTIVATED (except the caller) and all team_members with the given team_role to inactive. The path role must match a valid learner role or team role string (e.g. STUDENT, INSTRUCTOR, ADMIN, CONTENT_MANAGER). SUPER_ADMIN cannot be bulk-deactivated. ADMIN platform users must use SUPER_ADMIN to bulk change other platform ADMIN users (team_members with team_role ADMIN under path ADMIN remain allowed). Empty body allowed; optionally pass exclude_team_member_id to skip one team_members row (e.g. yourself).
|
||||
// @Description Sets all platform users with the given users.role to DEACTIVATED (except the caller) and all team_members with the given team_role to inactive. Path :role may be a role key (e.g. INSTRUCTOR, ADMIN) or a decimal RBAC roles.id from GET /api/v1/rbac/roles (resolved to RoleRecord.name uppercased). SUPER_ADMIN cannot be bulk-deactivated. ADMIN platform users must use SUPER_ADMIN to bulk change other platform ADMIN users (team_members with team_role ADMIN under path ADMIN remain allowed). Empty body allowed; optionally pass exclude_team_member_id to skip one team_members row (e.g. yourself).
|
||||
// @Tags admin
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param role path string true "Role key (matches users.role and/or team_members.team_role)"
|
||||
// @Param role path string true "Role key (INSTRUCTOR etc.) or RBAC roles.id (integer string)"
|
||||
// @Param body body domain.BulkAccountsByRoleRequest false "Optional exclusions"
|
||||
// @Success 200 {object} domain.Response
|
||||
// @Failure 400 {object} domain.ErrorResponse
|
||||
// @Failure 403 {object} domain.ErrorResponse
|
||||
// @Failure 404 {object} domain.ErrorResponse
|
||||
// @Failure 500 {object} domain.ErrorResponse
|
||||
// @Router /api/v1/admin/roles/{role}/bulk-deactivate [post]
|
||||
func (h *Handler) BulkDeactivateAccountsByRole(c *fiber.Ctx) error {
|
||||
|
|
@ -418,12 +459,9 @@ func (h *Handler) BulkDeactivateAccountsByRole(c *fiber.Ctx) error {
|
|||
})
|
||||
}
|
||||
|
||||
roleKey := strings.ToUpper(strings.TrimSpace(c.Params("role")))
|
||||
if roleKey == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||
Message: "Invalid role",
|
||||
Error: "role path parameter is required",
|
||||
})
|
||||
roleKey, rpOK := h.bulkAccountsRoleFromPath(c)
|
||||
if !rpOK {
|
||||
return nil
|
||||
}
|
||||
if roleKey == string(domain.RoleSuperAdmin) || roleKey == string(domain.TeamRoleSuperAdmin) {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||
|
|
@ -509,16 +547,17 @@ func (h *Handler) BulkDeactivateAccountsByRole(c *fiber.Ctx) error {
|
|||
|
||||
// BulkReactivateAccountsByRole godoc
|
||||
// @Summary Bulk reactivate accounts by role (SUPER_ADMIN or ADMIN platform users only)
|
||||
// @Description Sets all platform users with the given role from DEACTIVATED to ACTIVE (except the caller) and all team_members with the given team_role from inactive to active. Path role must be a valid platform or team role. SUPER_ADMIN cannot be bulk changed. ADMIN callers cannot bulk change other platform ADMIN users (team_members ADMIN under path ADMIN is allowed). Matches only users currently DEACTIVATED and team rows currently inactive.
|
||||
// @Description Sets all platform users with the given role from DEACTIVATED to ACTIVE (except the caller) and all team_members with the given team_role from inactive to active. Path :role may be a role key or decimal RBAC roles.id (see bulk-deactivate). Path role must correspond to valid platform users.role or team_members.team_role (after resolving id → name). SUPER_ADMIN cannot be bulk changed. ADMIN callers cannot bulk change other platform ADMIN users (team_members ADMIN under path ADMIN is allowed). Matches only users currently DEACTIVATED and team rows currently inactive.
|
||||
// @Tags admin
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param role path string true "Role key (matches users.role and/or team_members.team_role)"
|
||||
// @Param role path string true "Role key (INSTRUCTOR etc.) or RBAC roles.id (integer string)"
|
||||
// @Param body body domain.BulkAccountsByRoleRequest false "Optional exclusions"
|
||||
// @Success 200 {object} domain.Response
|
||||
// @Failure 400 {object} domain.ErrorResponse
|
||||
// @Failure 403 {object} domain.ErrorResponse
|
||||
// @Failure 404 {object} domain.ErrorResponse
|
||||
// @Failure 500 {object} domain.ErrorResponse
|
||||
// @Router /api/v1/admin/roles/{role}/bulk-reactivate [post]
|
||||
func (h *Handler) BulkReactivateAccountsByRole(c *fiber.Ctx) error {
|
||||
|
|
@ -544,12 +583,9 @@ func (h *Handler) BulkReactivateAccountsByRole(c *fiber.Ctx) error {
|
|||
})
|
||||
}
|
||||
|
||||
roleKey := strings.ToUpper(strings.TrimSpace(c.Params("role")))
|
||||
if roleKey == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||
Message: "Invalid role",
|
||||
Error: "role path parameter is required",
|
||||
})
|
||||
roleKey, rpOK := h.bulkAccountsRoleFromPath(c)
|
||||
if !rpOK {
|
||||
return nil
|
||||
}
|
||||
if roleKey == string(domain.RoleSuperAdmin) || roleKey == string(domain.TeamRoleSuperAdmin) {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user