This commit is contained in:
2026-07-16 09:53:14 +07:00
parent 0fc112a2e8
commit 29cec708a3
1236 changed files with 212848 additions and 0 deletions

View File

@@ -0,0 +1,487 @@
# Role Permission Migration Plan
วันที่จัดทำ: 2026-07-07
## Goal
ย้ายระบบสิทธิ์ปัจจุบันจากแนวทางที่พึ่งพา:
- `systemRole`
- `membership.role`
- `businessRole`
- helper แบบ `isHRD()` / `isIT()`
ไปสู่ระบบ `role + permission` ที่ตรวจสอบได้ชัดเจนและรองรับ workflow ใหม่ของโปรเจกต์
เอกสารนี้สรุปเป็นแบบ file-by-file ว่าควรแก้อะไรบ้างในโค้ดปัจจุบัน
## Migration Principles
1. API และ server ต้องใช้ permission เป็นตัวตัดสินสิทธิ์จริง
2. UI และ navigation เป็นเพียง layer สำหรับซ่อน/แสดง
3. `businessRole` ให้คงไว้ช่วง migration เพื่อ compatibility
4. ทุกการเปลี่ยน role model ต้องมี migration ของ `memberships.permissions`
## Phase 0: Schema And Contracts
### [src/db/schema.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/db/schema.ts)
Current:
- `membership_role` = `owner | admin | member`
- `system_role` = `standard | super_admin`
- `permissions` เป็น text array
Change:
- ขยาย `membership_role` หรือสร้าง enum ใหม่ให้รองรับ:
- `owner`
- `org_admin`
- `hrd_manager`
- `hrd_staff`
- `employee`
- คง `permissions` ไว้เป็น array แต่เปลี่ยน default generation ให้สอดคล้อง matrix ใหม่
- พิจารณาเพิ่ม migration สำหรับ backfill permissions ให้ membership เดิม
Deliverable:
- schema update
- drizzle migration
- backfill strategy สำหรับ membership เดิม
## Phase 1: Central Auth Model
### [src/lib/auth/roles.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/lib/auth/roles.ts)
Current:
- มี `BusinessRole = IT | HRD | EMPLOYEE`
- ใช้ `getBusinessRole()`, `isHRD()`, `isIT()`
- `getDefaultPermissionsForRole()` ยังรองรับเพียง admin/user แบบง่าย
Change:
- เพิ่ม type ใหม่:
- `SystemRole`
- `OrganizationRole`
- `Permission`
- แทนที่ default permission แบบเก่าด้วย permission catalog กลาง
- เพิ่ม helper:
- `getDefaultPermissionsForOrganizationRole(role)`
- `hasPermission(permissions, permission)`
- `hasAnyPermission(permissions, permissions[])`
- `hasAllPermissions(permissions, permissions[])`
- คง `getBusinessRole()` ไว้ช่วงเปลี่ยนผ่าน แต่ mark เป็น compatibility helper
- ลดการใช้ `isHRD()` / `isIT()` ลง โดย redirect ให้ไปใช้ permission helper
### New file: `src/lib/auth/permissions.ts`
Add:
- permission constants ทั้งระบบ
- grouped permission sets
- role-to-permission mapping
### [src/lib/auth/session.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/lib/auth/session.ts)
Current:
- มี `requireHRD()`, `requireIT()`, `requireUserManagementAccess()`
- เช็กสิทธิ์จาก role helper เป็นหลัก
Change:
- เพิ่ม:
- `requirePermission(permission)`
- `requireAnyPermission([...])`
- `requireAllPermissions([...])`
- ให้ `requireHRD()` / `requireIT()` กลายเป็น transitional wrapper หรือค่อยเลิกใช้
- ให้ `requireOrganizationAccess({ permission })` ใช้ permission matrix ใหม่
### [src/auth.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/auth.ts)
Current:
- session callback derive `businessRole`
- เติม `permissions` จาก membership หรือ default เดิม
Change:
- เปลี่ยน default permission sourcing ให้ใช้ role mapping ใหม่
- เพิ่ม `organizationRole` ลง session ให้ชัด
- คง `businessRole` ไว้ชั่วคราวเพื่อไม่ให้ UI เดิมพัง
- เตรียมรองรับ role ใหม่ใน session payload
### [src/types/next-auth.d.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/types/next-auth.d.ts)
Change:
- เพิ่ม `organizationRole`
- เปลี่ยน `permissions: string[]` เป็น type-safe alias ถ้าต้องการ
- คง `businessRole` ไว้ช่วงเปลี่ยนผ่าน
## Phase 2: Navigation And Access Metadata
### [src/config/nav-config.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/config/nav-config.ts)
Current:
- ใช้ `businessRole`, `businessRoles`, `systemRole`
Change:
- เปลี่ยน metadata ให้รองรับ:
- `permission`
- `anyPermissions`
- `allPermissions`
- `organizationRole` เฉพาะบางกรณีถ้าจำเป็น
- ลดการใช้ `businessRole` ลง
- sync เมนูที่เปิดให้ employee ใช้จริง เช่น `Reports`, `Announcements`, `Notifications` กับ access rule ใหม่
### [src/types/index.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/types/index.ts)
Change:
- update nav access types ให้รองรับ permission-based metadata
### [src/hooks/use-nav.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/hooks/use-nav.ts)
Current:
- ใช้ `getBusinessRole()`, `hasBusinessRoleAccess()`, `systemRole`
Change:
- เพิ่มการเช็ก:
- `permission`
- `anyPermissions`
- `allPermissions`
- คง support ของ field เก่าไว้ช่วง migration ถ้าจำเป็น
- เปลี่ยน source of truth จาก `businessRole` เป็น `permissions`
## Phase 3: Page Guards
### [src/lib/auth/page-guards.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/lib/auth/page-guards.ts)
Change:
- เพิ่ม:
- `requirePermissionDashboardAccess(permission, fallback?)`
- `requireAnyPermissionDashboardAccess([...], fallback?)`
- ให้หน้าต่าง ๆ ที่เป็น HRD/IT เฉพาะทางย้ายไปใช้ permission guard
### Dashboard pages under `src/app/dashboard/**/page.tsx`
Current:
- หลายหน้าพึ่ง `requireEmployeeDashboardAccess()` หรือ `requireHRDDashboardAccess()`
Change:
- เปลี่ยนทีละหน้าให้ใช้ permission guard ที่ตรง feature มากขึ้น
Priority pages:
- `src/app/dashboard/users/page.tsx`
- `src/app/dashboard/employee-directory/page.tsx`
- `src/app/dashboard/courses/page.tsx`
- `src/app/dashboard/training-policy/page.tsx`
- `src/app/dashboard/pending-review/page.tsx`
- `src/app/dashboard/audit-logs/page.tsx`
- `src/app/dashboard/announcements/page.tsx`
- `src/app/dashboard/online-lessons/page.tsx`
- `src/app/dashboard/reports/page.tsx`
## Phase 4: Feature Server Data Helpers
### [src/features/announcements/server/announcement-data.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/features/announcements/server/announcement-data.ts)
Current:
- ใช้ `isHRD({ businessRole, systemRole })`
Change:
- ส่ง permission context เข้า helper แทน
- แยก:
- public reader
- all-status reader
- content manager
- ใช้ permission เช่น:
- `announcement:read_public`
- `announcement:read_all`
- `announcement:create`
- `announcement:publish`
### [src/features/online-lessons/server/online-lesson-data.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/features/online-lessons/server/online-lesson-data.ts)
Change:
- ทำแบบเดียวกับ announcements
- เลิกใช้ `isHRD()` เป็น primary gate
### [src/features/reports/server/report-data.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/features/reports/server/report-data.ts)
Current:
- ผสม role-based scope แบบ membership role
Change:
- แยก `reports:self_read` กับ `reports:organization_read`
- export ต้องใช้ `reports:export`
### [src/features/training-records/server/training-record-data.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/features/training-records/server/training-record-data.ts)
Change:
- เปลี่ยน review/manage scope ไปใช้ permission:
- `training_record:self_*`
- `training_record:read_all`
- `training_record:review`
- `training_record:manage_attachments`
### [src/features/training-matrix/server/training-matrix-data.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/features/training-matrix/server/training-matrix-data.ts)
Current:
- มี helper `canManageTrainingMatrix(role)`
Change:
- เปลี่ยนเป็น permission-based helper
### [src/features/users/server/user-data.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/features/users/server/user-data.ts)
Change:
- รองรับ role ใหม่ใน CRUD
- อัปเดตการ map/create/update membership
- อัปเดต default permissions ตอนสร้าง user
## Phase 5: API Route Handlers
### Announcements
Files:
- [src/app/api/announcements/route.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/app/api/announcements/route.ts)
- [src/app/api/announcements/[id]/route.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/app/api/announcements/[id]/route.ts)
- [src/app/api/announcements/[id]/attachment/route.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/app/api/announcements/[id]/attachment/route.ts)
Change:
- เปลี่ยนจาก `requireHRD()` หรือ `isHRD()` ไปใช้ permission checks
- แยก action ตาม permission เช่น create/edit/submit/publish/archive
- ระยะต่อไปเพิ่ม route action แยกสำหรับ workflow
### Online Lessons
Files:
- [src/app/api/online-lessons/route.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/app/api/online-lessons/route.ts)
- [src/app/api/online-lessons/[id]/route.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/app/api/online-lessons/[id]/route.ts)
Change:
- ใช้ permission checks แทน `isHRD()`
- แยก read_public / read_all / create / publish / archive
### Users
Files:
- [src/app/api/users/route.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/app/api/users/route.ts)
- [src/app/api/users/[id]/route.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/app/api/users/[id]/route.ts)
Change:
- เปลี่ยนจาก `requireUserManagementAccess()` ไปใช้ `users:*`, `roles:assign`, `permissions:assign`
### Employee Directory / Import / Master Review
Files:
- `src/app/api/employee-directory/**`
- `src/app/api/import-employees/**`
- `src/app/api/master-review/route.ts`
Change:
- ใช้:
- `employee_directory:read`
- `employee_directory:write`
- `employee_import:run`
- `employee_master_review:approve`
### Courses / Training Policy / Training Matrix
Files:
- `src/app/api/courses/**`
- `src/app/api/training-policies/**`
- `src/app/api/training-matrix/**`
Change:
- เปลี่ยนไปใช้ permission ของแต่ละ module โดยตรง
### Training Records
Files:
- `src/app/api/training-records/**`
Change:
- เปลี่ยน review / attachment / update / delete checks ไปใช้ permission model ใหม่
### Audit Logs
Files:
- `src/app/api/audit-logs/**`
Change:
- ใช้ `audit_logs:read`
### Reports
Files:
- `src/app/api/reports/**`
Change:
- ใช้:
- `reports:self_read`
- `reports:organization_read`
- `reports:export`
## Phase 6: Feature UI Components
### Announcements UI
Files:
- `src/features/announcements/components/**`
Change:
- เปลี่ยน visibility ของปุ่มและ action menu ให้ใช้ permission-based props
- หน้า employee กับ HRD ควรใช้ component เดิมได้ แต่แยก action ตาม permission
### Online Lessons UI
Files:
- `src/features/online-lessons/components/**`
Change:
- เปลี่ยน action menu และ create/manage flow ให้ใช้ permission-based props
### Training Records UI
Files:
- `src/features/training-records/components/**`
Change:
- เปลี่ยนปุ่ม review/edit/manage attachments ให้ใช้ permission context แทน business role
### Users UI
Files:
- `src/features/users/components/**`
Change:
- แบบฟอร์มและ action menu ต้องรองรับ role ใหม่
- ต้องมี UI สำหรับเลือก role และอาจรวม permission assignment ถ้าจะเปิดใช้งานรายคน
## Phase 7: Notifications And Audit
### [src/features/notifications/server/notification-service.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/features/notifications/server/notification-service.ts)
Change:
- เพิ่ม notification types ที่สะท้อน workflow ใหม่
- เพิ่ม helper สำหรับ notify ตาม role/permission group ถ้าจำเป็น
### [src/features/audit-logs/server/audit-service.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/features/audit-logs/server/audit-service.ts)
Change:
- เพิ่ม audit action ใหม่สำหรับ workflow/action ที่อ้างกับ permission model
- พิจารณาเพิ่ม `actor_role` หรือ `actor_permissions` ใน payload audit ถ้าต้องการ trace ละเอียดขึ้น
## Phase 8: Documentation And Compatibility Cleanup
### Docs to update
Files:
- [docs/nav-rbac.md](/D:/WY-2569/HRD/training-system-minimal-refactor/docs/nav-rbac.md)
- [docs/PROJECT_ARCHITECTURE.md](/D:/WY-2569/HRD/training-system-minimal-refactor/docs/PROJECT_ARCHITECTURE.md)
- [docs/AI_DEVELOPMENT_GUIDE.md](/D:/WY-2569/HRD/training-system-minimal-refactor/docs/AI_DEVELOPMENT_GUIDE.md)
- [docs/role-permission-review.md](/D:/WY-2569/HRD/training-system-minimal-refactor/docs/role-permission-review.md)
Change:
- update ให้ตรงกับ permission-first model
### Compatibility cleanup
Files to revisit later:
- `src/lib/auth/roles.ts`
- `src/hooks/use-nav.ts`
- feature components ที่ยังส่ง `businessRole` / `systemRole` ลงไปหลายชั้น
Final cleanup:
- ลดการใช้ `businessRole` ให้เหลือเฉพาะ temporary adapter
- ลบ logic `isHRD()` / `isIT()` ที่ไม่จำเป็น
## Suggested Execution Order
1. `schema.ts` + migration
2. `permissions.ts` + `roles.ts`
3. `auth.ts` + `session.ts` + `next-auth.d.ts`
4. `types/index.ts` + `nav-config.ts` + `use-nav.ts`
5. API route handlers
6. feature server helpers
7. feature UI
8. docs cleanup
## High-Risk Areas
1. `src/auth.ts`
เพราะกระทบทุก session
2. `src/lib/auth/session.ts`
เพราะเป็นจุดศูนย์กลางของ server-side authorization
3. `src/config/nav-config.ts` และ `src/hooks/use-nav.ts`
เพราะมีความไม่ตรงกันระหว่างเมนูและสิทธิ์จริงอยู่แล้ว
4. `src/features/users/server/user-data.ts`
เพราะเกี่ยวกับ role assignment และ membership write path
5. `src/app/api/announcements/**` และ `src/app/api/online-lessons/**`
เพราะจะเป็น feature แรกที่ใช้ workflow permission ใหม่แบบจริงจัง
## Done Criteria
ถือว่าย้ายสำเร็จเมื่อ:
- API สำคัญทั้งหมดตรวจ permission โดยตรง
- nav และ page guard ใช้ permission model เดียวกัน
- role ใหม่ถูกสร้าง/แก้ไขได้จาก user management
- default permissions ของแต่ละ role ถูก seed/backfill ครบ
- employee ยังเห็นเฉพาะข้อมูลที่ควรเห็น
- `businessRole` ไม่ใช่ source of truth หลักอีกต่อไป