Files
alla-tms/docs/role-permission-migration-plan.md
2026-07-16 09:53:14 +07:00

16 KiB

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

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

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

Current:

  • มี requireHRD(), requireIT(), requireUserManagementAccess()
  • เช็กสิทธิ์จาก role helper เป็นหลัก

Change:

  • เพิ่ม:
    • requirePermission(permission)
    • requireAnyPermission([...])
    • requireAllPermissions([...])
  • ให้ requireHRD() / requireIT() กลายเป็น transitional wrapper หรือค่อยเลิกใช้
  • ให้ requireOrganizationAccess({ permission }) ใช้ permission matrix ใหม่

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

Change:

  • เพิ่ม organizationRole
  • เปลี่ยน permissions: string[] เป็น type-safe alias ถ้าต้องการ
  • คง businessRole ไว้ช่วงเปลี่ยนผ่าน

Phase 2: Navigation And Access Metadata

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

Change:

  • update nav access types ให้รองรับ permission-based metadata

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

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

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

Change:

  • ทำแบบเดียวกับ announcements
  • เลิกใช้ isHRD() เป็น primary gate

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

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

Current:

  • มี helper canManageTrainingMatrix(role)

Change:

  • เปลี่ยนเป็น permission-based helper

src/features/users/server/user-data.ts

Change:

  • รองรับ role ใหม่ใน CRUD
  • อัปเดตการ map/create/update membership
  • อัปเดต default permissions ตอนสร้าง user

Phase 5: API Route Handlers

Announcements

Files:

Change:

  • เปลี่ยนจาก requireHRD() หรือ isHRD() ไปใช้ permission checks
  • แยก action ตาม permission เช่น create/edit/submit/publish/archive
  • ระยะต่อไปเพิ่ม route action แยกสำหรับ workflow

Online Lessons

Files:

Change:

  • ใช้ permission checks แทน isHRD()
  • แยก read_public / read_all / create / publish / archive

Users

Files:

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

Change:

  • เพิ่ม notification types ที่สะท้อน workflow ใหม่
  • เพิ่ม helper สำหรับ notify ตาม role/permission group ถ้าจำเป็น

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:

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