Architecture

Permission Model

Deep Understanding of NextJS Base RBAC Permission System

Model Design ยท Permission Check ยท Best Practices


๐ŸŽฏ Overview

NextJS Base adopts the RBAC (Role-Based Access Control) permission model, managing user access permissions through roles.

Core Concepts

ConceptDescriptionExample
UserSystem UserAdmin, Editor, Viewer
RoleRole, Collection of Permissionsadmin, editor, viewer
PermissionPermission, Executable OperationCreate User, Edit Article
MenuMenu, Page Access EntryUser Management, Article Management

Relationship Diagram

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                           RBAC Model                                 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚    User     โ”‚
                    โ”‚   User      โ”‚
                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                           โ”‚
                           โ”‚ roles: String[]
                           โ”‚ (Role ID Array)
                           โ–ผ
                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚    Role     โ”‚
                    โ”‚   Role      โ”‚
                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                           โ”‚
           โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
           โ”‚               โ”‚               โ”‚
           โ”‚ permission    โ”‚ menu          โ”‚ inheritMenuPermissions
           โ”‚ String[]      โ”‚ String[]      โ”‚ Boolean
           โ–ผ               โ–ผ               โ”‚
    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”       โ”‚
    โ”‚ Permission  โ”‚ โ”‚    Menu     โ”‚โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
    โ”‚ Permission  โ”‚ โ”‚   Menu      โ”‚
    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                           โ”‚
                           โ”‚ permission: String[]
                           โ”‚ (Menu Associated Permissions)
                           โ–ผ
                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚ Permission  โ”‚
                    โ”‚ Permission  โ”‚
                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐Ÿ“Š Model Design

User Model

model User {
  id                String    @id @default(cuid())
  email             String    @unique
  name              String?
  
  // Role Association
  roles             String[]  @default([])    // Role ID Array
  
  // Admin Access Permission
  hasBackendAccess  Boolean   @default(false)
  
  // Other Fields...
}

Role Model

model Role {
  id                      String    @id @default(cuid())
  name                    String    @unique        // Role Name
  remark                  String?                  // Remarks
  enable                  Boolean   @default(true) // Enabled
  
  // Permission Association
  permission              String[]  @default([])   // Permission ID Array
  
  // Menu Association
  menu                    String[]  @default([])   // Menu ID Array
  inheritMenuPermissions  Boolean   @default(true) // Inherit Menu Permissions
  
  // Timestamps
  createdAt               DateTime  @default(now())
  updatedAt               DateTime  @updatedAt
}

Permission Model

model Permission {
  id           String    @id @default(cuid())
  name         String                          // Permission Name
  parentId     String?                         // Parent ID (Supports Tree)
  
  // Category and Level
  crudCategory String?                         // CRUD Category
  level        Int       @default(0)           // Level
  
  // Associated Actions
  actions      String[]  @default([])          // Server Action Name Array
  
  // Associated APIs
  apis         String[]  @default([])          // API Path Array
  
  // Sort and Status
  sort         Int       @default(0)
  enable       Boolean   @default(true)
  remark       String?
  
  // Soft Delete
  deletedAt    DateTime?
  
  // Timestamps
  createdAt    DateTime  @default(now())
  updatedAt    DateTime  @updatedAt
}
model Menu {
  id         String    @id @default(cuid())
  name       String                          // Menu Name
  parentId   String?                         // Parent ID (Supports Tree)
  
  // Route Configuration
  url        String?                         // Page Path
  icon       String?                         // Icon
  
  // Associated Permissions
  permission String[]  @default([])          // Associated Permission ID Array
  
  // Display Control
  sort       Int       @default(0)
  enable     Boolean   @default(true)
  hidden     Boolean   @default(false)       // Hidden
  
  remark     String?
  
  // Timestamps
  createdAt  DateTime  @default(now())
  updatedAt  DateTime  @updatedAt
}

๐Ÿ” Permission Check

Action Permission Check Flow

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Call Server Action                                โ”‚
โ”‚                    sysGetRoleListAction()                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                                โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Step 1: Parse Action Name                          โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚ actionName: 'sysGetRoleList'                                 โ”‚   โ”‚
โ”‚  โ”‚                                                              โ”‚   โ”‚
โ”‚  โ”‚ Parse Result:                                                โ”‚   โ”‚
โ”‚  โ”‚ - prefix: 'sys' โ†’ SYSTEM Level                              โ”‚   โ”‚
โ”‚  โ”‚ - action: 'Get'                                              โ”‚   โ”‚
โ”‚  โ”‚ - resource: 'RoleList'                                       โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                                โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Step 2: Authentication Check                      โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚ const session = await auth()                                 โ”‚   โ”‚
โ”‚  โ”‚                                                              โ”‚   โ”‚
โ”‚  โ”‚ if (!session?.user) {                                       โ”‚   โ”‚
โ”‚  โ”‚   throw new Error('Please login first')                     โ”‚   โ”‚
โ”‚  โ”‚ }                                                            โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                                โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Step 3: Admin Access Check                        โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚ // Check if user has admin access permission                โ”‚   โ”‚
โ”‚  โ”‚ if (!user.hasBackendAccess) {                               โ”‚   โ”‚
โ”‚  โ”‚   throw new Error('No admin access permission')              โ”‚   โ”‚
โ”‚  โ”‚ }                                                            โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                                โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Step 4: RBAC Permission Check                      โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚ // 1. Get all user roles                                     โ”‚   โ”‚
โ”‚  โ”‚ const roles = await getRolesByIds(user.roles)               โ”‚   โ”‚
โ”‚  โ”‚                                                              โ”‚   โ”‚
โ”‚  โ”‚ // 2. Aggregate all permission IDs                          โ”‚   โ”‚
โ”‚  โ”‚ let permissionIds = roles.flatMap(r => r.permission)        โ”‚   โ”‚
โ”‚  โ”‚                                                              โ”‚   โ”‚
โ”‚  โ”‚ // 3. If role has menu permission inheritance enabled        โ”‚   โ”‚
โ”‚  โ”‚ for (const role of roles) {                                 โ”‚   โ”‚
โ”‚  โ”‚   if (role.inheritMenuPermissions) {                        โ”‚   โ”‚
โ”‚  โ”‚     const menus = await getMenusByIds(role.menu)            โ”‚   โ”‚
โ”‚  โ”‚     permissionIds.push(...menus.flatMap(m => m.permission)) โ”‚   โ”‚
โ”‚  โ”‚   }                                                          โ”‚   โ”‚
โ”‚  โ”‚ }                                                            โ”‚   โ”‚
โ”‚  โ”‚                                                              โ”‚   โ”‚
โ”‚  โ”‚ // 4. Get permission details                                โ”‚   โ”‚
โ”‚  โ”‚ const permissions = await getPermissionsByIds(permissionIds)โ”‚   โ”‚
โ”‚  โ”‚                                                              โ”‚   โ”‚
โ”‚  โ”‚ // 5. Aggregate all allowed Actions                         โ”‚   โ”‚
โ”‚  โ”‚ const allowedActions = permissions.flatMap(p => p.actions)  โ”‚   โ”‚
โ”‚  โ”‚                                                              โ”‚   โ”‚
โ”‚  โ”‚ // 6. Check if current Action is in allowed list            โ”‚   โ”‚
โ”‚  โ”‚ if (!allowedActions.includes('sysGetRoleList')) {           โ”‚   โ”‚
โ”‚  โ”‚   throw new Error('No permission to execute this operation')โ”‚   โ”‚
โ”‚  โ”‚ }                                                            โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                                โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Step 5: Execute Business Logic                    โ”‚
โ”‚                    โœ… Permission Check Passed                         โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Menu permissions are used to control sidebar display in the admin panel:

// Get user accessible menus
async function getUserMenus(userId) {
  // 1. Get user
  const user = await getUser(userId)
  
  // 2. Get user roles
  const roles = await getRolesByIds(user.roles)
  
  // 3. Aggregate all menu IDs
  const menuIds = [...new Set(roles.flatMap(r => r.menu))]
  
  // 4. Get menu details
  const menus = await getMenusByIds(menuIds)
  
  // 5. Filter enabled and non-hidden menus
  const visibleMenus = menus.filter(m => m.enable && !m.hidden)
  
  // 6. Build menu tree
  return buildMenuTree(visibleMenus)
}

๐ŸŽจ Permission Configuration Example

Example: Article Management Permissions

1. Create Permissions

// Article Management - Parent Permission
{
  name: 'Article Management',
  parentId: null,
  crudCategory: 'post',
  level: 0,
  actions: [],
  apis: [],
}

// Article Management - View
{
  name: 'View Article',
  parentId: 'Article Management ID',
  crudCategory: 'post',
  level: 1,
  actions: ['sysGetPostList', 'sysGetPostDetail'],
  apis: [],
}

// Article Management - Create
{
  name: 'Create Article',
  parentId: 'Article Management ID',
  crudCategory: 'post',
  level: 1,
  actions: ['sysCreatePost'],
  apis: [],
}

// Article Management - Edit
{
  name: 'Edit Article',
  parentId: 'Article Management ID',
  crudCategory: 'post',
  level: 1,
  actions: ['sysUpdatePost'],
  apis: [],
}

// Article Management - Delete
{
  name: 'Delete Article',
  parentId: 'Article Management ID',
  crudCategory: 'post',
  level: 1,
  actions: ['sysDeletePost', 'sysBatchDeletePost'],
  apis: [],
}

2. Create Menu

{
  name: 'Article Management',
  parentId: 'Content Management ID',
  url: '/admin/content/posts',
  icon: 'FileTextOutlined',
  permission: ['View Article ID', 'Create Article ID', 'Edit Article ID', 'Delete Article ID'],
  sort: 10,
  enable: true,
}

3. Assign to Role

// Editor Role - Only View and Edit Permissions
{
  name: 'editor',
  permission: ['View Article ID', 'Edit Article ID'],
  menu: ['Article Management Menu ID'],
  inheritMenuPermissions: false,  // Don't inherit menu permissions, use explicit assignment
}

// Admin Role - All Permissions
{
  name: 'admin',
  permission: [],  // Can be empty
  menu: ['Article Management Menu ID'],
  inheritMenuPermissions: true,  // Inherit all permissions associated with menu
}

๐Ÿ“‹ Permission Naming Conventions

Action Naming

PrefixLevelDescriptionExample
pubPUBLICPublic AccesspubGetConfig
authAUTHLogin RequiredauthGetUserInfo
sysSYSTEMAdmin Permission RequiredsysGetRoleList

Complete Naming Format

{prefix}{Action}{Resource}Action

Examples:
- sysGetRoleListAction      // Get Role List
- sysCreateRoleAction       // Create Role
- sysUpdateRoleAction       // Update Role
- sysDeleteRoleAction       // Delete Role
- sysBatchDeleteRoleAction  // Batch Delete Role

Permission Category (crudCategory)

CategoryDescriptionAssociated Actions
roleRole ManagementsysGetRoleList, sysCreateRole, ...
permissionPermission ManagementsysGetPermissionList, ...
menuMenu ManagementsysGetMenuList, ...
userUser ManagementsysGetUserList, ...
postArticle ManagementsysGetPostList, ...

โœ… Best Practices

1. Permission Granularity

โœ… Recommended: Fine-grained Permissions
- View Role
- Create Role
- Edit Role
- Delete Role

โŒ Not Recommended: Coarse-grained Permissions
- Role Management (Includes All Operations)

2. Use crudCategory

// โœ… Recommended: Use crudCategory for Grouping
{
  name: 'View Role',
  crudCategory: 'role',
  actions: ['sysGetRoleList', 'sysGetRoleDetail'],
}

// โŒ Not Recommended: Don't Use Category
{
  name: 'View Role',
  actions: ['sysGetRoleList', 'sysGetRoleDetail'],
}

3. Menu Permission Inheritance

// โœ… Recommended: For admin roles, use menu permission inheritance
{
  name: 'admin',
  menu: ['All Menu IDs'],
  inheritMenuPermissions: true,  // Automatically get all permissions associated with menu
}

// โœ… Recommended: For regular roles, explicitly assign permissions
{
  name: 'editor',
  permission: ['Specific Permission IDs'],
  menu: ['Edit Related Menu IDs'],
  inheritMenuPermissions: false,  // Don't inherit, use explicit assignment
}

4. Permission Check Location

// โœ… Recommended: Automatic check in wrapAction
export const sysGetRoleListAction = wrapAction(
  'sysGetRoleList',
  async (params) => {
    // Permission already checked automatically
    return await dao.getList(params)
  }
)

// โŒ Not Recommended: Manual check in business logic
export const getRoleListAction = async (params) => {
  // Manually check permission
  if (!await checkPermission('sysGetRoleList')) {
    throw new Error('No Permission')
  }
  return await dao.getList(params)
}