quzuu-api-test / controllers /super_admin_role_controller.go
lifedebugger's picture
Deploy files from GitHub repository
f2b94bf
Raw
History Blame Contribute Delete
9.87 kB
package controllers
import (
"strconv"
"abdanhafidz.com/go-boilerplate/models/dto"
entity "abdanhafidz.com/go-boilerplate/models/entity"
http_error "abdanhafidz.com/go-boilerplate/models/error"
"abdanhafidz.com/go-boilerplate/services"
"github.com/gin-gonic/gin"
)
type SuperAdminRoleController interface {
ListRoles(ctx *gin.Context)
CreateRole(ctx *gin.Context)
UpdateRole(ctx *gin.Context)
DeleteRole(ctx *gin.Context)
ListAuditLogs(ctx *gin.Context)
}
type superAdminRoleController struct {
accountService services.AccountService
}
func NewSuperAdminRoleController(accountService services.AccountService) SuperAdminRoleController {
return &superAdminRoleController{accountService: accountService}
}
// ListRoles godoc
// @Summary List All Roles
// @Description Get all system and named custom roles with their privilege flags
// @Tags Super Admin Roles
// @Accept json
// @Produce json
// @Security BearerAuth
// @Success 200 {object} dto.SuccessResponse[[]dto.RoleResponse]
// @Failure 401 {object} dto.ErrorResponse
// @Failure 403 {object} dto.ErrorResponse
// @Router /api/v1/super-admin/roles [get]
func (c *superAdminRoleController) ListRoles(ctx *gin.Context) {
roles, err := c.accountService.GetAllRoles(ctx.Request.Context())
if err != nil {
ResponseJSON(ctx, gin.H{}, []dto.RoleResponse{}, err)
return
}
var res []dto.RoleResponse
for _, r := range roles {
res = append(res, dto.RoleResponse{
Id: r.Id,
Name: r.Name,
IsSystem: r.IsSystem,
EventManagement: r.EventManagement,
ExamManagement: r.ExamManagement,
AcademyManagement: r.AcademyManagement,
ContentManagement: r.ContentManagement,
})
}
ResponseJSON(ctx, gin.H{}, res, nil)
}
// CreateRole godoc
// @Summary Create Named Role
// @Description Create a new named custom role with privilege flags. Superadmin only.
// @Tags Super Admin Roles
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param request body dto.CreateRoleRequest true "Create Role Request"
// @Success 200 {object} dto.SuccessResponse[dto.RoleResponse]
// @Failure 400 {object} dto.ErrorResponse
// @Failure 401 {object} dto.ErrorResponse
// @Failure 403 {object} dto.ErrorResponse
// @Failure 409 {object} dto.ErrorResponse
// @Router /api/v1/super-admin/roles [post]
func (c *superAdminRoleController) CreateRole(ctx *gin.Context) {
req := RequestJSON[dto.CreateRoleRequest](ctx)
role := entity.Role{
Name: req.Name,
IsSystem: false,
EventManagement: req.EventManagement,
ExamManagement: req.ExamManagement,
AcademyManagement: req.AcademyManagement,
ContentManagement: req.ContentManagement,
}
created, err := c.accountService.CreateRole(ctx.Request.Context(), role)
if err != nil {
ResponseJSON(ctx, req, dto.RoleResponse{}, err)
return
}
callerID := ParseAccountId(ctx)
roleIDStr := created.Id.String()
detailStr := `{"role_name":"` + created.Name + `"}`
_ = c.accountService.WriteAuditLog(ctx.Request.Context(), callerID, "role_created", &roleIDStr, &detailStr)
res := dto.RoleResponse{
Id: created.Id,
Name: created.Name,
IsSystem: created.IsSystem,
EventManagement: created.EventManagement,
ExamManagement: created.ExamManagement,
AcademyManagement: created.AcademyManagement,
ContentManagement: created.ContentManagement,
}
ResponseJSON(ctx, req, res, nil)
}
// UpdateRole godoc
// @Summary Update Named Role
// @Description Update a named custom role's privilege flags. System roles are immutable.
// @Tags Super Admin Roles
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param role_id path string true "Role ID"
// @Param request body dto.UpdateRoleRequest true "Update Role Request"
// @Success 200 {object} dto.SuccessResponse[dto.RoleResponse]
// @Failure 400 {object} dto.ErrorResponse
// @Failure 401 {object} dto.ErrorResponse
// @Failure 403 {object} dto.ErrorResponse
// @Router /api/v1/super-admin/roles/{role_id} [put]
func (c *superAdminRoleController) UpdateRole(ctx *gin.Context) {
roleID := ParseUUID(ctx, "role_id")
req := RequestJSON[dto.UpdateRoleRequest](ctx)
existing, err := c.accountService.GetRoleByID(ctx.Request.Context(), roleID)
if err != nil {
ResponseJSON(ctx, req, dto.RoleResponse{}, err)
return
}
if existing.IsSystem {
ResponseJSON(ctx, req, dto.RoleResponse{}, http_error.ROLE_IMMUTABLE)
return
}
if req.Name != nil {
existing.Name = *req.Name
}
if req.EventManagement != nil {
existing.EventManagement = *req.EventManagement
}
if req.ExamManagement != nil {
existing.ExamManagement = *req.ExamManagement
}
if req.AcademyManagement != nil {
existing.AcademyManagement = *req.AcademyManagement
}
if req.ContentManagement != nil {
existing.ContentManagement = *req.ContentManagement
}
updated, err := c.accountService.UpdateRole(ctx.Request.Context(), existing)
if err != nil {
ResponseJSON(ctx, req, dto.RoleResponse{}, err)
return
}
callerID := ParseAccountId(ctx)
roleIDStr := updated.Id.String()
detailStr := `{"role_name":"` + updated.Name + `"}`
_ = c.accountService.WriteAuditLog(ctx.Request.Context(), callerID, "role_updated", &roleIDStr, &detailStr)
res := dto.RoleResponse{
Id: updated.Id,
Name: updated.Name,
IsSystem: updated.IsSystem,
EventManagement: updated.EventManagement,
ExamManagement: updated.ExamManagement,
AcademyManagement: updated.AcademyManagement,
ContentManagement: updated.ContentManagement,
}
ResponseJSON(ctx, req, res, nil)
}
// DeleteRole godoc
// @Summary Delete Named Role
// @Description Delete a named custom role. System roles are immutable. Cannot delete if assigned to any account.
// @Tags Super Admin Roles
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param role_id path string true "Role ID"
// @Success 200 {object} dto.SuccessResponse[any]
// @Failure 401 {object} dto.ErrorResponse
// @Failure 403 {object} dto.ErrorResponse
// @Failure 409 {object} dto.ErrorResponse
// @Router /api/v1/super-admin/roles/{role_id} [delete]
func (c *superAdminRoleController) DeleteRole(ctx *gin.Context) {
roleID := ParseUUID(ctx, "role_id")
existing, err := c.accountService.GetRoleByID(ctx.Request.Context(), roleID)
if err != nil {
ResponseJSON[any](ctx, gin.H{}, nil, err)
return
}
if existing.IsSystem {
ResponseJSON[any](ctx, gin.H{}, nil, http_error.ROLE_IMMUTABLE)
return
}
inUse, err := c.accountService.CountRoleAssignments(ctx.Request.Context(), roleID)
if err != nil {
ResponseJSON[any](ctx, gin.H{}, nil, err)
return
}
if inUse > 0 {
ResponseJSON[any](ctx, gin.H{}, nil, http_error.ROLE_IN_USE)
return
}
if err := c.accountService.DeleteRole(ctx.Request.Context(), roleID); err != nil {
ResponseJSON[any](ctx, gin.H{}, nil, err)
return
}
callerID := ParseAccountId(ctx)
roleIDStr := roleID.String()
detailStr := `{"role_name":"` + existing.Name + `"}`
_ = c.accountService.WriteAuditLog(ctx.Request.Context(), callerID, "role_deleted", &roleIDStr, &detailStr)
ResponseJSON(ctx, gin.H{}, gin.H{"message": "Role deleted successfully"}, nil)
}
// ListAuditLogs godoc
// @Summary List Audit Logs
// @Description Get paginated audit logs with optional filters. Superadmin only.
// @Tags Super Admin Audit
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param page query int false "Page number (default 1)"
// @Param limit query int false "Items per page (default 10, max 50)"
// @Param action query string false "Filter by action"
// @Param actor_account_id query string false "Filter by actor account ID"
// @Param from query string false "Start time (RFC3339)"
// @Param to query string false "End time (RFC3339)"
// @Success 200 {object} dto.SuccessResponse[[]dto.AuditLogResponse]
// @Failure 401 {object} dto.ErrorResponse
// @Failure 403 {object} dto.ErrorResponse
// @Router /api/v1/super-admin/logs [get]
func (c *superAdminRoleController) ListAuditLogs(ctx *gin.Context) {
limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "10"))
page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1"))
action := ctx.DefaultQuery("action", "")
actorID := ctx.DefaultQuery("actor_account_id", "")
from := ctx.DefaultQuery("from", "")
to := ctx.DefaultQuery("to", "")
if limit < 1 {
limit = 10
} else if limit > 50 {
limit = 50
}
if page < 1 {
page = 1
}
logs, total, err := c.accountService.GetAuditLogs(ctx.Request.Context(), page, limit, action, actorID, from, to)
if err != nil {
ResponseJSON(ctx, gin.H{}, []dto.AuditLogResponse{}, err)
return
}
var res []dto.AuditLogResponse
for _, l := range logs {
res = append(res, dto.AuditLogResponse{
Id: l.Id,
ActorAccountID: l.ActorAccountID,
Action: l.Action,
TargetID: l.TargetID,
Detail: l.Detail,
CreatedAt: l.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
})
}
var totalPages int
if total == 0 {
totalPages = 1
} else {
totalPages = int((total + int64(limit) - 1) / int64(limit))
}
if page > totalPages {
page = totalPages
}
meta := gin.H{
"totalItems": total,
"totalPages": totalPages,
"currentPage": page,
"limit": limit,
}
ResponseJSON(ctx, meta, res, nil)
}