# 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 หลักอีกต่อไป