commit
This commit is contained in:
319
docs/reviews/full-system-audit-summary.md
Normal file
319
docs/reviews/full-system-audit-summary.md
Normal file
@@ -0,0 +1,319 @@
|
||||
# Full System Audit Summary
|
||||
|
||||
## Executive Summary
|
||||
|
||||
Across Phases 1-7, the system shows strong implementation momentum but is not yet ready for production release. The codebase builds successfully, core CRUD flows are largely functional, and server-side authorization is present in many sensitive paths. However, the combined review found material gaps in security hardening, permission consistency, legacy architecture migration, operational readiness, and feature completeness.
|
||||
|
||||
The most serious blockers are concentrated in a few areas: public exposure of online-lesson assets, unsafe spreadsheet import posture, missing automated release gates, incomplete backup and rollback readiness, and an authorization model that is not yet consistently enforced from one source of truth. In parallel, the architecture still carries unresolved drift between the intended `users`-centric model and legacy `employees` usage, while several major workflow files are too large and too coupled for low-risk iteration.
|
||||
|
||||
Overall recommendation: **No-Go** until Wave 0 and Wave 1 remediation items are complete.
|
||||
|
||||
## Overall System Health
|
||||
|
||||
| Dimension | Status | Notes |
|
||||
|---|---|---|
|
||||
| Build Health | Partially Healthy | `lint`, `tsc`, and `build` pass, but no `test` script or CI gate exists. |
|
||||
| Functional Coverage | Partially Healthy | Core CRUD works, but reports, review workflow, and training-policy lifecycle are incomplete. |
|
||||
| Authorization Model | At Risk | Guards exist, but enforcement is split between permission templates and legacy role helpers. |
|
||||
| Security Posture | At Risk | Public lesson assets, unbounded spreadsheet imports, and unresolved dependency advisories remain. |
|
||||
| UI/UX Consistency | At Risk | Encoding corruption and cross-feature inconsistency materially reduce production quality. |
|
||||
| Maintainability | At Risk | Oversized route handlers, large client forms, and duplicated workflow logic raise regression risk. |
|
||||
| Production Operations | Not Ready | No CI/CD workflow, no health endpoint, no verified restore/rollback process, stale docs. |
|
||||
|
||||
## Findings Summary by Severity
|
||||
|
||||
### High
|
||||
|
||||
- `F-001` Runtime architecture still depends heavily on legacy `employees` despite the documented `users`-first target model.
|
||||
- `F-002` Training-record review workflow lacks a true rejected path.
|
||||
- `F-003` Reports module is incomplete for expected report coverage and export formats.
|
||||
- `F-004` Training-policy lifecycle is incomplete: delete/soft delete, effective dating, and versioning are missing.
|
||||
- `F-005` Permission templates are not the true single source of truth; sensitive paths still rely on compatibility role helpers.
|
||||
- `F-006` Thai text encoding corruption is widespread in production UI.
|
||||
- `F-007` Several route handlers, server data modules, and client workflow forms are oversized and over-coupled.
|
||||
- `F-008` Online-lesson uploaded assets are publicly accessible by direct URL.
|
||||
- `F-009` Spreadsheet import endpoints accept unbounded `.xlsx` uploads and rely on a dependency with active high-severity advisories.
|
||||
- `F-010` Production release discipline is incomplete: no automated tests, no CI pipeline, and no health/readiness endpoint.
|
||||
|
||||
### Medium
|
||||
|
||||
- `F-011` Production dashboard still exposes demo/template/legacy routes.
|
||||
- `F-012` Page guards, nav guards, and API guards are inconsistent across modules.
|
||||
- `F-013` Announcements and online lessons lack scheduling/versioning capabilities expected for publishing workflows.
|
||||
- `F-014` Notifications are limited and incomplete as a delivery system.
|
||||
- `F-015` Reports bypass shared table patterns and remain visually dense.
|
||||
- `F-016` API/client/error handling and some data integrity rules are still generic or app-level only.
|
||||
- `F-017` Rate limiting, CSRF/origin hardening, and explicit security headers are not implemented centrally.
|
||||
- `F-018` Deployment and environment docs are stale and partly inherited from the starter template.
|
||||
|
||||
### Low
|
||||
|
||||
- `F-019` Proxy perimeter coverage is incomplete but partially compensated by route-handler guards.
|
||||
- `F-020` Encoding corruption also appears in permission metadata and supporting UI text, reducing trust and maintainability.
|
||||
|
||||
## Findings Summary by Module
|
||||
|
||||
| Module | Key Findings |
|
||||
|---|---|
|
||||
| Auth / Session / Proxy | Permission model split, inconsistent perimeter, fallback secret posture, missing abuse protection. |
|
||||
| Users / Employees | Architecture drift between intended `users` runtime model and legacy `employees` dependencies. |
|
||||
| Training Records | Review workflow incomplete, large form boundary, import hardening needed. |
|
||||
| Reports | Functional gaps, role-scoped authorization drift, duplicated scope resolution, coupled export/data logic, dense UX. |
|
||||
| Training Policy | Missing lifecycle completeness and version/effective-date controls. |
|
||||
| Training Matrix | Missing route target, inconsistent page/API guard placement. |
|
||||
| Announcements | Large workflow handler, publishing lifecycle gaps, attachment UX differs from other modules. |
|
||||
| Online Lessons | Public asset exposure, large client form, attachment workflow inconsistency, publishing capability gaps. |
|
||||
| Permission Management | Template model not yet authoritative, metadata encoding issues, DB integrity TODO remains. |
|
||||
| Overview / Dashboard | Large server aggregation modules, legacy/demo routes still present in production surface. |
|
||||
| Platform / Ops | No CI/CD, no tests, no health endpoint, stale docs, incomplete runbooks. |
|
||||
|
||||
## Production Blockers
|
||||
|
||||
| Blocker | Problem | Impact | Module | Recommended Owner | Dependency | Acceptance Criteria |
|
||||
|---|---|---|---|---|---|---|
|
||||
| `PB-01` | Online-lesson files are served as public URLs from `public/uploads/...`. | Unauthorized data access and uncontrolled file distribution. | Online Lessons / Storage | Backend | Storage refactor | Lesson assets require authenticated, organization-aware download access. |
|
||||
| `PB-02` | Spreadsheet import accepts unbounded `.xlsx` uploads and uses vulnerable parsing dependency. | DoS risk and unsafe untrusted file parsing. | Training Records / Employees Import | Backend | Dependency upgrade | File-size limits, MIME validation, rate limits, and upgraded parser are in place. |
|
||||
| `PB-03` | Authorization decisions are not consistently permission-first across pages, nav, and APIs. | Privilege drift and inconsistent access behavior. | Auth / Reports / Matrix / Employees / Audit Logs | Backend | Permission model cleanup | Sensitive routes and pages resolve access from the same permission source of truth. |
|
||||
| `PB-04` | No CI pipeline, no automated test suite, no release gate. | Regressions can reach production without enforcement. | Platform / Repo | DevOps / Full-stack | Test baseline | PR workflow runs lint, typecheck, build, and targeted tests before merge. |
|
||||
| `PB-05` | No health/readiness endpoint and no documented rollback or restore procedure. | Slow incident detection and unsafe deployment recovery. | Platform / Deployment / DB Ops | DevOps | Operational docs | Health endpoint exists and restore/rollback runbooks are documented and verified. |
|
||||
| `PB-06` | Docs and deployment artifacts still contain stale starter-template and Clerk-oriented assumptions. | Incorrect deployment setup and operator confusion. | Docs / Docker / README | Full-stack | Architecture alignment | README, env docs, Docker notes, and deployment instructions match current Auth.js TMS behavior. |
|
||||
| `PB-07` | No automated tests or verified smoke coverage for critical business workflows. | Production behavior cannot be trusted after fixes or releases. | Whole System | Full-stack / QA | Test harness | Critical workflows have automated coverage and manual smoke checklist. |
|
||||
|
||||
## Cross-Phase Root Causes
|
||||
|
||||
- Incomplete migration from the original starter/template application into a focused TMS product.
|
||||
- Incomplete migration from legacy `employees` ownership to the documented `users`-centric runtime model.
|
||||
- Incomplete migration from compatibility role checks to permission-first authorization.
|
||||
- Workflow-heavy features have grown inside large route handlers and large client components instead of being decomposed into smaller services.
|
||||
- Shared UI primitives exist, but design-system governance is inconsistent across older and newer modules.
|
||||
- Operational maturity lagged behind feature delivery: testing, CI/CD, monitoring depth, and runbooks were not finished alongside implementation.
|
||||
|
||||
## Risk Matrix
|
||||
|
||||
| Risk ID | Risk | Likelihood | Impact | Overall |
|
||||
|---|---|---|---|---|
|
||||
| `R-01` | Unauthorized access to online-lesson assets | High | High | Critical |
|
||||
| `R-02` | Spreadsheet import abuse or parser exploitation | High | High | Critical |
|
||||
| `R-03` | Permission drift exposes data incorrectly across reports and admin modules | Medium | High | High |
|
||||
| `R-04` | Release regression reaches production without test or CI gate | High | High | Critical |
|
||||
| `R-05` | Incident recovery fails due to absent backup/rollback process | Medium | High | High |
|
||||
| `R-06` | Large coupled modules cause high-risk regressions during remediation | High | Medium | High |
|
||||
| `R-07` | Users lose trust due to corrupted Thai content and inconsistent workflows | High | Medium | High |
|
||||
| `R-08` | Architecture drift around `employees` vs `users` blocks future feature correctness | Medium | High | High |
|
||||
|
||||
## Traceability Matrix
|
||||
|
||||
| Finding ID | Phase | Severity | Module | Production Blocker | Recommended Wave | Related Finding |
|
||||
|---|---|---|---|---|---|---|
|
||||
| `F-001` | 1 | High | Users / Employees / Reports | No | Wave 2 | `F-003`, `F-012` |
|
||||
| `F-002` | 2 | High | Training Records | No | Wave 1 | `F-007` |
|
||||
| `F-003` | 2 | High | Reports | No | Wave 2 | `F-001`, `F-005`, `F-015` |
|
||||
| `F-004` | 2 | High | Training Policy | No | Wave 2 | `F-007` |
|
||||
| `F-005` | 3 | High | Auth / Permissions / Reports | Yes (`PB-03`) | Wave 1 | `F-012`, `F-019` |
|
||||
| `F-006` | 4 | High | Shared UI / Content | No | Wave 1 | `F-020` |
|
||||
| `F-007` | 5 | High | Announcements / Reports / Overview / Training Records | No | Wave 2 | `F-016` |
|
||||
| `F-008` | 6 | High | Online Lessons / Storage | Yes (`PB-01`) | Wave 0 | `F-013`, `F-017` |
|
||||
| `F-009` | 6 | High | Import Endpoints / Dependencies | Yes (`PB-02`) | Wave 0 | `F-017` |
|
||||
| `F-010` | 7 | High | Repo / Platform / Deployment | Yes (`PB-04`, `PB-05`, `PB-07`) | Wave 1 | `F-018` |
|
||||
| `F-011` | 1 | Medium | Dashboard Surface | No | Wave 1 | `F-018` |
|
||||
| `F-012` | 1,3 | Medium | Page Guards / APIs / Nav | No | Wave 1 | `F-005`, `F-001` |
|
||||
| `F-013` | 2 | Medium | Announcements / Online Lessons | No | Wave 3 | `F-008` |
|
||||
| `F-014` | 2 | Medium | Notifications | No | Wave 3 | `F-003` |
|
||||
| `F-015` | 4 | Medium | Reports UI | No | Wave 2 | `F-003` |
|
||||
| `F-016` | 5 | Medium | API Client / Permission Data / Shared Utils | No | Wave 2 | `F-007` |
|
||||
| `F-017` | 6 | Medium | Security Platform | No | Wave 1 | `F-008`, `F-009` |
|
||||
| `F-018` | 7 | Medium | Docs / Env / Docker | Yes (`PB-06`) | Wave 1 | `F-011` |
|
||||
| `F-019` | 1,6 | Low | Proxy / Perimeter | No | Wave 3 | `F-005` |
|
||||
| `F-020` | 3,4 | Low | Permission Metadata / Shared UI Copy | No | Wave 1 | `F-006` |
|
||||
|
||||
## Remediation Roadmap
|
||||
|
||||
### Wave 0 - Emergency Fix
|
||||
|
||||
- Close public access to online-lesson assets and replace direct file URLs with guarded delivery.
|
||||
- Harden spreadsheet import surfaces with file-size limits, MIME validation, rate limiting, dependency upgrades, and safer parsing controls.
|
||||
- Re-run dependency audit and clear high-severity advisories affecting active runtime paths.
|
||||
|
||||
### Wave 1 - Pre-Production Fix
|
||||
|
||||
- Unify authorization around permission-first server checks for reports, matrix, employees, audit logs, and org-management paths.
|
||||
- Remove, quarantine, or explicitly disable demo/template production routes that should not ship.
|
||||
- Fix Thai text encoding corruption across navigation, forms, labels, pagination, and permission metadata.
|
||||
- Add CI release gates: lint, typecheck, build, and critical test workflow.
|
||||
- Add health/readiness endpoints plus rollback, restore, and deployment runbooks.
|
||||
- Update README, env documentation, Docker instructions, and release notes to match current Auth.js TMS architecture.
|
||||
|
||||
### Wave 2 - Stabilization
|
||||
|
||||
- Refactor oversized route handlers, report data modules, overview aggregators, and workflow-heavy forms into smaller service boundaries.
|
||||
- Complete missing business lifecycle behavior in training-policy and training-record review flows.
|
||||
- Normalize reporting architecture: authorization scope resolution, export generation, and shared UI patterns.
|
||||
- Add missing data integrity constraints, typed error models, and reusable validation utilities.
|
||||
- Continue the `employees` to `users` migration in runtime-critical flows.
|
||||
|
||||
### Wave 3 - Continuous Improvement
|
||||
|
||||
- Add scheduling/versioning features to content publishing modules where required by the product.
|
||||
- Mature notification delivery beyond current in-app limitations.
|
||||
- Expand structured logging, correlation IDs, security headers, CSP, and abuse monitoring.
|
||||
- Tighten perimeter protection and continue cleanup of legacy/demo/dead code.
|
||||
|
||||
## Coding Phase Backlog
|
||||
|
||||
### `AUDIT-001` Secure Online Lesson Asset Delivery
|
||||
|
||||
- Problem: lesson files are directly accessible without authenticated authorization checks.
|
||||
- Scope: move online-lesson file access to guarded download/stream endpoints and stop serializing raw public URLs.
|
||||
- Out of Scope: full cloud object-storage migration.
|
||||
- Files / Modules: `src/lib/online-lesson-storage.ts`, `src/features/online-lessons/server/online-lesson-data.ts`, `src/features/online-lessons/components/online-lesson-detail-page.tsx`, related route handlers.
|
||||
- Acceptance Criteria: unauthenticated direct URL access fails; authorized users can still view/download assets through guarded endpoints.
|
||||
- Test Required: route authorization tests, manual cross-role smoke test, file access regression test.
|
||||
- Dependency: `AUDIT-006`.
|
||||
- Severity: High.
|
||||
- Priority: P0.
|
||||
|
||||
### `AUDIT-002` Harden Spreadsheet Import Surface
|
||||
|
||||
- Problem: imports rely on extension-only validation, unbounded memory reads, and vulnerable parsing dependency.
|
||||
- Scope: add file-size caps, MIME checks, rate limits, parser upgrade/replacement, and explicit failure telemetry.
|
||||
- Out of Scope: redesign of import mapping UX.
|
||||
- Files / Modules: `src/app/api/training-records/import/route.ts`, `src/app/api/import-employees/route.ts`, `package.json`, lockfile.
|
||||
- Acceptance Criteria: oversized or invalid files are rejected early; dependency audit shows no high advisory on active import path.
|
||||
- Test Required: upload validation tests, negative import smoke tests, dependency audit verification.
|
||||
- Dependency: none.
|
||||
- Severity: High.
|
||||
- Priority: P0.
|
||||
|
||||
### `AUDIT-003` Unify Permission Enforcement
|
||||
|
||||
- Problem: permission templates and role compatibility helpers produce inconsistent access rules.
|
||||
- Scope: define authoritative permission checks for pages, APIs, and nav visibility in sensitive modules.
|
||||
- Out of Scope: complete product-wide RBAC redesign beyond current permission catalog.
|
||||
- Files / Modules: `src/lib/auth/session.ts`, `src/lib/auth/roles.ts`, reports, training-matrix, employees, audit logs, organizer access paths.
|
||||
- Acceptance Criteria: the same permission decision applies across nav, page load, and API action for each reviewed module.
|
||||
- Test Required: authorization matrix tests by role/permission, manual direct-URL verification.
|
||||
- Dependency: `AUDIT-008`.
|
||||
- Severity: High.
|
||||
- Priority: P0.
|
||||
|
||||
### `AUDIT-004` Establish Release Gate and Smoke Coverage
|
||||
|
||||
- Problem: repository has no CI workflow, no automated tests, and no production release gate.
|
||||
- Scope: add CI workflow for lint, typecheck, build, and critical smoke/test jobs; add `npm test` or equivalent script.
|
||||
- Out of Scope: full end-to-end suite for every feature.
|
||||
- Files / Modules: `.github/workflows/*`, `package.json`, test harness folders.
|
||||
- Acceptance Criteria: pull requests fail on broken lint/type/build/tests; critical user flows have executable automated checks.
|
||||
- Test Required: CI self-verification on a test branch.
|
||||
- Dependency: none.
|
||||
- Severity: High.
|
||||
- Priority: P0.
|
||||
|
||||
### `AUDIT-005` Add Health, Rollback, and Restore Readiness
|
||||
|
||||
- Problem: operational recovery and readiness signals are missing.
|
||||
- Scope: add health/readiness endpoints and document rollback, backup, and restore procedures.
|
||||
- Out of Scope: full SRE platform implementation.
|
||||
- Files / Modules: `src/app/api/*`, `README.md`, ops docs, deployment docs.
|
||||
- Acceptance Criteria: operators can verify service health and follow documented restore/rollback steps.
|
||||
- Test Required: endpoint smoke tests, tabletop recovery walkthrough.
|
||||
- Dependency: `AUDIT-004`.
|
||||
- Severity: High.
|
||||
- Priority: P0.
|
||||
|
||||
### `AUDIT-006` Remove Public File URL Assumptions
|
||||
|
||||
- Problem: file-serving patterns are inconsistent across modules, and online lessons diverge from guarded download patterns already used elsewhere.
|
||||
- Scope: standardize protected file access conventions across announcements, certificates, and lesson assets.
|
||||
- Out of Scope: media CDN optimization.
|
||||
- Files / Modules: file storage helpers, download routes, serialization contracts.
|
||||
- Acceptance Criteria: protected uploads are never exposed as unconditional public URLs.
|
||||
- Test Required: file access regression tests.
|
||||
- Dependency: none.
|
||||
- Severity: Medium.
|
||||
- Priority: P1.
|
||||
|
||||
### `AUDIT-007` Repair Thai Encoding and Shared UX Copy
|
||||
|
||||
- Problem: corrupted Thai text degrades usability and trust across the product.
|
||||
- Scope: fix encoding/copy corruption in nav, forms, pagination, empty states, and permission metadata.
|
||||
- Out of Scope: net-new content strategy.
|
||||
- Files / Modules: shared UI copy, feature labels, permission metadata catalogs.
|
||||
- Acceptance Criteria: reviewed screens display valid Thai copy consistently.
|
||||
- Test Required: manual UI verification across major screens.
|
||||
- Dependency: none.
|
||||
- Severity: High.
|
||||
- Priority: P1.
|
||||
|
||||
### `AUDIT-008` Refactor Oversized Workflow Modules
|
||||
|
||||
- Problem: several large route handlers and components hold too many responsibilities.
|
||||
- Scope: split report data logic, announcement workflows, overview aggregation, and training-record form orchestration into smaller units.
|
||||
- Out of Scope: unrelated feature redesign.
|
||||
- Files / Modules: `src/app/api/announcements/[id]/route.ts`, `src/features/reports/server/report-data.ts`, `src/features/overview/server/overview-data.ts`, `src/features/training-records/components/training-record-form.tsx`.
|
||||
- Acceptance Criteria: each target module has clearer single-responsibility boundaries and lower regression surface.
|
||||
- Test Required: focused regression tests around refactored workflows.
|
||||
- Dependency: `AUDIT-004`.
|
||||
- Severity: High.
|
||||
- Priority: P1.
|
||||
|
||||
### `AUDIT-009` Complete Missing Business Workflow Paths
|
||||
|
||||
- Problem: training-record rejection, training-policy lifecycle, and report coverage remain incomplete.
|
||||
- Scope: implement rejected review path, policy lifecycle operations, and prioritized missing report outputs.
|
||||
- Out of Scope: non-priority analytics expansion.
|
||||
- Files / Modules: training-records, training-policy, reports.
|
||||
- Acceptance Criteria: required workflow states and agreed report set are supported end-to-end.
|
||||
- Test Required: workflow tests, export smoke tests, role-scoped manual verification.
|
||||
- Dependency: `AUDIT-003`.
|
||||
- Severity: High.
|
||||
- Priority: P1.
|
||||
|
||||
### `AUDIT-010` Align Runtime Data Model Around `users`
|
||||
|
||||
- Problem: architecture documentation and runtime implementation still diverge on person ownership.
|
||||
- Scope: identify remaining runtime-critical `employees` dependencies and migrate them toward `users`-based ownership where required.
|
||||
- Out of Scope: historical legacy-data archival strategy.
|
||||
- Files / Modules: auth/session joins, reports, overview, training-policy, training-record participant flows.
|
||||
- Acceptance Criteria: runtime-critical training flows no longer require legacy `employees` as the effective source of truth.
|
||||
- Test Required: data-scope regression tests and migration verification.
|
||||
- Dependency: `AUDIT-003`, `AUDIT-009`.
|
||||
- Severity: High.
|
||||
- Priority: P2.
|
||||
|
||||
## Testing Strategy After Fix
|
||||
|
||||
- Run mandatory gates on every change set: `npm run lint`, `npx tsc --noEmit`, `npm run build`, and the new automated test command.
|
||||
- Add authorization matrix tests covering nav/page/API behavior for `super_admin`, `admin`, and `user`.
|
||||
- Add storage access tests for protected download and streaming routes.
|
||||
- Add import validation tests for size, file type, malformed spreadsheet, and permission failure cases.
|
||||
- Add regression tests for training-record review transitions and prioritized report exports.
|
||||
- Run a manual production-like smoke checklist covering login, org switching, CRUD basics, imports, exports, file access, and audit logging.
|
||||
|
||||
## Recommended Release Gate
|
||||
|
||||
The release gate should require all of the following:
|
||||
|
||||
- Wave 0 completed and verified.
|
||||
- Wave 1 completed for permission consistency, encoding repair, CI gate, health/readiness, and documentation alignment.
|
||||
- No unresolved high-severity security advisory on active runtime dependencies.
|
||||
- Critical-path automated coverage exists for auth, authorization, imports, exports, and protected file delivery.
|
||||
- Backup, restore, and rollback procedures are documented and exercised at least once.
|
||||
- Final UAT is re-run after remediation on a production-like environment.
|
||||
|
||||
## Final Go-live Recommendation
|
||||
|
||||
**No-Go**.
|
||||
|
||||
The system is close enough to justify continued investment, but not close enough to justify production release. The combination of security blockers, missing release discipline, authorization inconsistency, and operational readiness gaps creates too much avoidable risk for go-live.
|
||||
|
||||
No material contradiction was found across Phases 1-7. The findings reinforce one another: structural drift identified in the architecture and permission reviews directly explains later quality, security, and production-readiness issues.
|
||||
|
||||
## Appendix: Source Reports
|
||||
|
||||
- [Phase 1 - Architecture Audit](/D:/WY-2569/HRD/training-system-minimal-refactor/docs/reviews/phase-1-architecture-audit.md)
|
||||
- [Phase 2 - Functional Review](/D:/WY-2569/HRD/training-system-minimal-refactor/docs/reviews/phase-2-functional-review.md)
|
||||
- [Phase 3 - Permission & Authorization Audit](/D:/WY-2569/HRD/training-system-minimal-refactor/docs/reviews/phase-3-permission-authorization-audit.md)
|
||||
- [Phase 4 - UI/UX & Design System Audit](/D:/WY-2569/HRD/training-system-minimal-refactor/docs/reviews/phase-4-ui-ux-design-system-audit.md)
|
||||
- [Phase 5 - Code Quality & Performance Audit](/D:/WY-2569/HRD/training-system-minimal-refactor/docs/reviews/phase-5-code-quality-performance-audit.md)
|
||||
- [Phase 6 - Security Audit](/D:/WY-2569/HRD/training-system-minimal-refactor/docs/reviews/phase-6-security-audit.md)
|
||||
- [Phase 7 - Production Readiness Review](/D:/WY-2569/HRD/training-system-minimal-refactor/docs/reviews/phase-7-production-readiness-review.md)
|
||||
300
docs/reviews/phase-1-architecture-audit.md
Normal file
300
docs/reviews/phase-1-architecture-audit.md
Normal file
@@ -0,0 +1,300 @@
|
||||
# Phase 1 - Architecture Audit
|
||||
|
||||
## Executive Summary
|
||||
|
||||
ภาพรวมสถาปัตยกรรมของระบบปัจจุบันมีแกนหลักที่ชัดเจนในระดับ app-owned stack คือ `Next.js App Router + Auth.js + Drizzle + PostgreSQL + TanStack Query` และหลาย feature หลักเดินตาม pattern `page -> feature api/service -> route handler -> server data -> db` ได้ดี โดยเฉพาะ `training-records`, `courses`, `announcements`, `reports`, และ `permission-management`
|
||||
|
||||
อย่างไรก็ตาม implementation ปัจจุบันยังมี architectural drift อยู่หลายจุดเมื่อเทียบกับ target architecture ในเอกสารโครงการ โดยประเด็นสำคัญที่สุดคือ runtime ยังพึ่งพาโมเดล `employees` อย่างกว้างขวาง แม้เอกสารสถาปัตยกรรมจะระบุว่า `users` ควรเป็น runtime person entity หลักและ `employees` เป็น legacy migration data เท่านั้น นอกจากนี้ยังพบ production dashboard surface ที่ยังรวม route กลุ่ม demo/template/legacy และมีความไม่สม่ำเสมอของ guard placement และ route contract บางจุด
|
||||
|
||||
## System Shape Observed
|
||||
|
||||
- Framework: Next.js 16 App Router
|
||||
- Auth boundary: `src/auth.ts`, `src/lib/auth/session.ts`, `src/lib/auth/page-guards.ts`, `src/proxy.ts`
|
||||
- Data boundary: `src/app/api/*` route handlers + Drizzle schema ใน `src/db/schema.ts`
|
||||
- Feature modularization: ส่วนใหญ่ใช้ `src/features/<feature>` ตามแนวทางที่เอกสารกำหนด
|
||||
- Canonical app-owned domains ที่เห็นชัด: `users`, `organizations`, `memberships`, `courses`, `training_records`, `training_matrices`, `training_policies`, `announcements`, `online_lessons`, `reports`, `permissions`
|
||||
|
||||
## Strengths
|
||||
|
||||
- มีการแยก auth/session, page guard, API handler, และ feature server helpers ค่อนข้างชัด
|
||||
- route handlers สำคัญส่วนมากไม่เข้าฐานข้อมูลจาก UI โดยตรง แต่ผ่าน service/server data helpers
|
||||
- RBAC model แบบ `systemRole + membership.role + effective permissions` ถูกวางโครงไว้ครบทั้ง session และ permission-resolution
|
||||
- feature หลักจำนวนมากเดินตาม shared stack เดียวกัน เช่น `PageContainer`, TanStack Query, shared table/form primitives
|
||||
|
||||
## Findings
|
||||
|
||||
### [High] Runtime architecture ยังผูกกับ legacy `employees` อย่างลึก แม้ target architecture ระบุให้ `users` เป็น runtime person entity หลัก
|
||||
|
||||
**Module:** Auth, Schema, Training Records, Reports, Overview, Employee Directory, Training Policy
|
||||
|
||||
**ตำแหน่งที่พบ:**
|
||||
|
||||
- `src/db/schema.ts` ประมาณบรรทัด 81, 281, 438, 541
|
||||
- `src/auth.ts` ประมาณบรรทัด 14, 126, 155, 285
|
||||
- `src/features/employees/server/employee-identity-linking.ts`
|
||||
- `src/features/training-records/server/training-record-data.ts`
|
||||
- `src/features/reports/server/report-data.ts`
|
||||
- `src/features/overview/server/overview-data.ts`
|
||||
- `src/features/training-policy/server/employee-training-target-data.ts`
|
||||
|
||||
**รายละเอียด:**
|
||||
|
||||
เอกสาร `docs/PROJECT_ARCHITECTURE.md` และ `AGENTS.md` ระบุว่า runtime person data ควรอยู่บน `users` และ `employees` เป็น legacy migration data เท่านั้น แต่ implementation ปัจจุบันยังใช้ `employees` เป็น dependency หลักในหลาย workflow สำคัญ เช่น:
|
||||
|
||||
- `users.employeeId` ยังอ้างอิง `employees.id`
|
||||
- มีตาราง `user_employee_maps`
|
||||
- `training_records.employeeId` ยังอ้างอิง `employees.id`
|
||||
- `employee_training_targets.employeeId` ยังถูกใช้ใน target resolution
|
||||
- session enrichment ใน `src/auth.ts` เรียก `syncUserEmployeeLink(...)` ตอน login
|
||||
- reports, overview, training matrix compliance, และ training records query หลักยัง join ผ่าน `employees`
|
||||
|
||||
**ผลกระทบ:**
|
||||
|
||||
- target architecture กับ implementation ปัจจุบันไม่สอดคล้องกัน
|
||||
- การพัฒนาฟีเจอร์ใหม่มีความเสี่ยงสร้าง dual-source-of-truth ระหว่าง `users` และ `employees`
|
||||
- การวาง roadmap ย้ายข้อมูลหรือ permission scoping ต่อจากนี้จะซับซ้อนขึ้น
|
||||
|
||||
**หลักฐานจากโค้ด:**
|
||||
|
||||
- `src/auth.ts` เรียก `syncUserEmployeeLink(...)` ระหว่าง authorize และเก็บ `employeeId` ลง session
|
||||
- `src/db/schema.ts` ยังเก็บ foreign key หลายจุดไปที่ `employees`
|
||||
- `src/features/reports/server/report-data.ts` และ `src/features/overview/server/overview-data.ts` ใช้ `employees` เป็นฐานการคำนวณ scope และ dataset
|
||||
|
||||
**สถานการณ์ตัวอย่าง:**
|
||||
|
||||
หากทีมพัฒนาฟีเจอร์ใหม่โดยอิงเอกสารอย่างเดียว จะเข้าใจว่า `users` คือ runtime source of truth แต่เมื่อเชื่อมกับ reports หรือ training records จริง ระบบยังต้องพึ่ง `employees` อยู่ ทำให้เกิดการ implement ผิดชั้นข้อมูลได้ง่าย
|
||||
|
||||
**คำแนะนำ:**
|
||||
|
||||
ระบุสถานะสถาปัตยกรรมปัจจุบันให้ชัดว่าอยู่ในช่วง transition และจัดทำ migration roadmap ระดับสถาปัตยกรรมก่อน coding phase แยกให้ชัดว่า domain ใดใช้ `users` จริงแล้ว และ domain ใดยังผูกกับ `employees`
|
||||
|
||||
**ต้องแก้ก่อน Production หรือไม่:** ใช่
|
||||
|
||||
### [Medium] Dashboard production surface ยังรวม route กลุ่ม demo/template/legacy ไว้ใน `app/dashboard`
|
||||
|
||||
**Module:** Dashboard routing, legacy/template features
|
||||
|
||||
**ตำแหน่งที่พบ:**
|
||||
|
||||
- `src/app/dashboard/billing/page.tsx`
|
||||
- `src/app/dashboard/chat/page.tsx`
|
||||
- `src/app/dashboard/forms/page.tsx`
|
||||
- `src/app/dashboard/forms/advanced/page.tsx`
|
||||
- `src/app/dashboard/forms/basic/page.tsx`
|
||||
- `src/app/dashboard/forms/multi-step/page.tsx`
|
||||
- `src/app/dashboard/forms/sheet-form/page.tsx`
|
||||
- `src/app/dashboard/react-query/page.tsx`
|
||||
- `src/app/dashboard/kanban/page.tsx`
|
||||
- `src/app/dashboard/product/page.tsx`
|
||||
- `src/app/dashboard/workspaces/page.tsx`
|
||||
- `src/app/dashboard/elements/icons/page.tsx`
|
||||
- `src/app/dashboard/exclusive/page.tsx`
|
||||
|
||||
**รายละเอียด:**
|
||||
|
||||
เอกสารสถาปัตยกรรมระบุให้บางพื้นที่ถูกมองเป็น legacy/template เท่านั้น แต่ route เหล่านี้ยังอยู่ใต้ dashboard จริง ทำให้ production route tree ปัจจุบันปะปนระหว่าง business modules กับ sample/demo/template modules
|
||||
|
||||
**ผลกระทบ:**
|
||||
|
||||
- เพิ่มความคลุมเครือว่าหน้าใดเป็น production-supported feature
|
||||
- ทำให้การ audit permission, navigation, QA scope, และ release readiness ซับซ้อนขึ้น
|
||||
- เพิ่ม maintenance overhead เพราะ route เหล่านี้ต้องถูกคำนึงถึงใน middleware, session, layout, และ regression surface
|
||||
|
||||
**หลักฐานจากโค้ด:**
|
||||
|
||||
- `src/app/dashboard/chat/page.tsx` render `ChatViewPage` ตรง
|
||||
- `src/app/dashboard/react-query/page.tsx` ใช้ `features/react-query-demo`
|
||||
- `src/app/dashboard/billing/page.tsx` ระบุชัดว่าเป็น placeholder หลังถอด Clerk billing ออกแล้ว
|
||||
|
||||
**สถานการณ์ตัวอย่าง:**
|
||||
|
||||
เมื่อทีมทำ full-system regression หรือ permission audit จะต้องตัดสินใจเองว่าหน้าเหล่านี้อยู่ในขอบเขต production หรือไม่ เพราะ route ยังอยู่ใน tree เดียวกับโมดูลธุรกิจหลัก
|
||||
|
||||
**คำแนะนำ:**
|
||||
|
||||
กำหนด policy ระดับสถาปัตยกรรมให้ชัดว่าหน้า demo/template ต้องย้ายออก, ปิด route, หรือ mark เป็น non-production module อย่างเป็นระบบ
|
||||
|
||||
**ต้องแก้ก่อน Production หรือไม่:** ควรพิจารณา
|
||||
|
||||
### [Medium] การวาง authorization guard ไม่สม่ำเสมอระหว่าง page routes
|
||||
|
||||
**Module:** Dashboard route access control
|
||||
|
||||
**ตำแหน่งที่พบ:**
|
||||
|
||||
- `src/app/dashboard/training-matrix/page.tsx`
|
||||
- `src/app/dashboard/organizers/page.tsx`
|
||||
- `src/app/dashboard/overview/page.tsx`
|
||||
- `src/app/dashboard/layout.tsx`
|
||||
- `src/lib/auth/page-guards.ts`
|
||||
- `src/proxy.ts`
|
||||
|
||||
**รายละเอียด:**
|
||||
|
||||
บางหน้าใช้ shared page guards ตามมาตรฐาน (`requireEmployeeDashboardAccess`, `requireHRDDashboardAccess`, `requirePermissionDashboardAccess`) แต่บางหน้าใช้ custom auth/redirect เอง และบางหน้าพึ่ง proxy หรือ parent layout แทน โดยไม่มี local page guard ตรงตัว
|
||||
|
||||
ตัวอย่าง:
|
||||
|
||||
- `src/app/dashboard/training-matrix/page.tsx` ไม่เรียก page guard เลย
|
||||
- `src/app/dashboard/organizers/page.tsx` ใช้ `auth()` + `isHRD(...)` + `redirect(...)` เอง แทน shared page guard
|
||||
- `src/app/dashboard/overview/page.tsx` ไม่มี guard ตรงตัว โดย guard อยู่ที่ `src/app/dashboard/overview/layout.tsx`
|
||||
- `src/app/dashboard/layout.tsx` ไม่มี auth check ในตัวเอง และอาศัย `src/proxy.ts` เป็น outer boundary
|
||||
|
||||
**ผลกระทบ:**
|
||||
|
||||
- access policy อ่านยากและตรวจสอบยาก
|
||||
- เพิ่มโอกาสให้ route ใหม่ถูกสร้างโดยลืมวาง guard ในตำแหน่งที่เหมาะสม
|
||||
- ลดความสม่ำเสมอของ architecture ที่เอกสารพยายามกำหนดไว้
|
||||
|
||||
**หลักฐานจากโค้ด:**
|
||||
|
||||
- `src/app/dashboard/training-matrix/page.tsx` parse search params และ render listing ทันที
|
||||
- `src/app/dashboard/organizers/page.tsx` ไม่ใช้ helper จาก `src/lib/auth/page-guards.ts`
|
||||
|
||||
**สถานการณ์ตัวอย่าง:**
|
||||
|
||||
หากมีการ refactor proxy หรือย้าย layout/sub-layout ในอนาคต หน้าเหล่านี้อาจเปลี่ยนพฤติกรรมการป้องกันโดยไม่ได้ตั้งใจ เพราะ guard placement ไม่ได้อยู่รูปแบบเดียวกัน
|
||||
|
||||
**คำแนะนำ:**
|
||||
|
||||
กำหนด architecture rule เดียวสำหรับ dashboard pages ว่าต้องใช้ shared page guard ใน route entrypoint ทุกครั้ง หรือบันทึกข้อยกเว้นเป็น explicit convention
|
||||
|
||||
**ต้องแก้ก่อน Production หรือไม่:** ควรพิจารณา
|
||||
|
||||
### [Medium] Route contract ของ Training Matrix ไม่สมบูรณ์: มี CTA ไปยัง route ที่ไม่มีอยู่จริง
|
||||
|
||||
**Module:** Training Matrix dashboard route
|
||||
|
||||
**ตำแหน่งที่พบ:**
|
||||
|
||||
- `src/app/dashboard/training-matrix/page.tsx` ประมาณบรรทัด 28
|
||||
- ไม่พบ `src/app/dashboard/training-matrix/new/page.tsx`
|
||||
|
||||
**รายละเอียด:**
|
||||
|
||||
หน้า `training-matrix` สร้างปุ่ม `Add Rule` ที่ลิงก์ไป `/dashboard/training-matrix/new` แต่จากโครงสร้างไฟล์ปัจจุบันไม่พบ route ปลายทางดังกล่าว
|
||||
|
||||
**ผลกระทบ:**
|
||||
|
||||
- route map ของ feature ไม่ครบตามที่ UI สื่อสาร
|
||||
- เพิ่มสัญญาณว่าระบบยังมี implementation gap ระหว่าง page shell กับ feature workflow จริง
|
||||
|
||||
**หลักฐานจากโค้ด:**
|
||||
|
||||
- `src/app/dashboard/training-matrix/page.tsx` มี `href='/dashboard/training-matrix/new'`
|
||||
- ตรวจไม่พบไฟล์ `src/app/dashboard/training-matrix/new/page.tsx`
|
||||
|
||||
**สถานการณ์ตัวอย่าง:**
|
||||
|
||||
ผู้ใช้ HRD/แอดมินกด `Add Rule` แล้วไปสู่ 404 หรือเส้นทางที่ไม่รองรับ ทำให้ workflow หลักของ matrix management ไม่ต่อเนื่อง
|
||||
|
||||
**คำแนะนำ:**
|
||||
|
||||
ระบุให้ชัดว่า feature นี้ตั้งใจใช้ modal/sheet แทน route create หรือมี route create จริงแต่ยังขาด implementation จากนั้นปรับ route contract ให้สอดคล้อง
|
||||
|
||||
**ต้องแก้ก่อน Production หรือไม่:** ใช่
|
||||
|
||||
### [Low] API perimeter protection ใน `proxy.ts` ใช้ allowlist แบบบางส่วน ทำให้ boundary strategy ไม่สม่ำเสมอ
|
||||
|
||||
**Module:** Middleware / API protection strategy
|
||||
|
||||
**ตำแหน่งที่พบ:**
|
||||
|
||||
- `src/proxy.ts`
|
||||
- เปรียบเทียบกับ API modules ใต้ `src/app/api`
|
||||
|
||||
**รายละเอียด:**
|
||||
|
||||
`src/proxy.ts` ป้องกัน dashboard ทั้งหมด และ API บาง prefix เท่านั้น เช่น `/api/products`, `/api/training-records`, `/api/reports`, `/api/announcements` แต่ API กลุ่มใหม่บางส่วน เช่น `permission-templates`, `permission-audit-logs`, `user-permissions`, `permissions/catalog` ไม่ได้อยู่ใน `protectedApiPrefixes` และพึ่ง handler-level auth เป็นหลัก
|
||||
|
||||
ประเด็นนี้ไม่ใช่ auth bypass โดยตรง เพราะ handler หลายตัวมี `requireSuperAdminOrganizationAccess()` อยู่แล้ว แต่เป็นความไม่สม่ำเสมอของ perimeter architecture
|
||||
|
||||
**ผลกระทบ:**
|
||||
|
||||
- อ่าน boundary policy ได้ยาก
|
||||
- เพิ่มความเสี่ยงที่ API ใหม่ในอนาคตจะถูกลืมเพิ่มเข้า proxy allowlist หรือทำ auth ซ้ำซ้อนแบบไม่เป็นระบบ
|
||||
|
||||
**หลักฐานจากโค้ด:**
|
||||
|
||||
- `src/proxy.ts` ระบุ `protectedApiPrefixes` แบบ hard-coded
|
||||
- API permission management อยู่ใต้ `src/app/api/permission-templates`, `src/app/api/user-permissions`, `src/app/api/permission-audit-logs`, `src/app/api/permissions`
|
||||
|
||||
**สถานการณ์ตัวอย่าง:**
|
||||
|
||||
ทีมเพิ่ม API ใหม่ตาม feature module แล้วคิดว่า proxy คุ้มครองทุก API อยู่แล้ว ทั้งที่จริง route บางกลุ่มต้องพึ่ง handler guard เพียงอย่างเดียว
|
||||
|
||||
**คำแนะนำ:**
|
||||
|
||||
กำหนด perimeter strategy ให้เป็นแบบเดียว เช่น ปกป้อง API app-owned ทั้งหมดด้วย proxy matcher หรือประกาศชัดเจนว่า proxy มีไว้กัน unauthenticated only และ authorization ทั้งหมดอยู่ที่ handler
|
||||
|
||||
**ต้องแก้ก่อน Production หรือไม่:** ไม่ใช่
|
||||
|
||||
## Commands Executed and Results
|
||||
|
||||
1. `Get-Content -Raw docs/AI_DEVELOPMENT_GUIDE.md`
|
||||
Result: ใช้เป็นเกณฑ์อ้างอิง pattern หลักของโปรเจ็กต์
|
||||
2. `Get-Content -Raw docs/PROJECT_ARCHITECTURE.md`
|
||||
Result: ใช้ยืนยัน target architecture และ legacy areas
|
||||
3. `Get-Content -Raw plans/tms-system-full-audit-prompts.md`
|
||||
Result: ใช้กำหนดรูปแบบรายงานและข้อห้ามระหว่าง audit
|
||||
4. `Get-ChildItem src/features | Select-Object -ExpandProperty Name`
|
||||
Result: ใช้ inventory feature modules
|
||||
5. `rg -n "require(Employee|HRD|IT|Organization|SuperAdmin).*" src/app/dashboard src/app/api`
|
||||
Result: ใช้สำรวจ route/page guard และ API guard ที่ใช้อยู่จริง
|
||||
6. `Get-ChildItem -Recurse -Depth 2 src/app/dashboard | Select-Object FullName`
|
||||
Result: ใช้ตรวจ route surface ทั้งหมดใน dashboard
|
||||
7. `Get-Content -Raw src/auth.ts`
|
||||
Result: พบ session enrichment และ employee-link sync
|
||||
8. `Get-Content -Raw src/lib/auth/session.ts`
|
||||
Result: ใช้ยืนยัน org access และ permission enforcement helper
|
||||
9. `Get-Content -Raw src/lib/auth/page-guards.ts`
|
||||
Result: ใช้เทียบกับ guard placement ในแต่ละ dashboard page
|
||||
10. `Get-Content -Raw src/proxy.ts`
|
||||
Result: ใช้ตรวจ perimeter protection ของ dashboard และ API
|
||||
11. `rg -n "/dashboard/training-matrix/new" src`
|
||||
Result: พบ CTA route reference เพียงจุดเดียวใน `training-matrix/page.tsx`
|
||||
12. `Test-Path src/app/dashboard/training-matrix/new/page.tsx`
|
||||
Result: `False`
|
||||
|
||||
## Files Reviewed
|
||||
|
||||
- `docs/AI_DEVELOPMENT_GUIDE.md`
|
||||
- `docs/PROJECT_ARCHITECTURE.md`
|
||||
- `plans/tms-system-full-audit-prompts.md`
|
||||
- `src/auth.ts`
|
||||
- `src/proxy.ts`
|
||||
- `src/lib/auth/session.ts`
|
||||
- `src/lib/auth/page-guards.ts`
|
||||
- `src/lib/auth/roles.ts`
|
||||
- `src/db/schema.ts`
|
||||
- `src/app/dashboard/layout.tsx`
|
||||
- `src/app/dashboard/page.tsx`
|
||||
- `src/app/dashboard/overview/page.tsx`
|
||||
- `src/app/dashboard/organizers/page.tsx`
|
||||
- `src/app/dashboard/training-matrix/page.tsx`
|
||||
- `src/app/dashboard/training-policy/page.tsx`
|
||||
- `src/app/dashboard/users/page.tsx`
|
||||
- `src/app/dashboard/billing/page.tsx`
|
||||
- `src/app/dashboard/chat/page.tsx`
|
||||
- `src/app/dashboard/react-query/page.tsx`
|
||||
- `src/app/dashboard/product/page.tsx`
|
||||
- `src/app/api/training-matrix/route.ts`
|
||||
- `src/app/api/reports/export/route.ts`
|
||||
- `src/app/api/auth/register/route.ts`
|
||||
- `src/features/reports/server/report-data.ts`
|
||||
- `src/features/products/components/product-listing.tsx`
|
||||
- `src/features/organizers/components/organizer-listing.tsx`
|
||||
- `src/features/chat/components/chat-view-page.tsx`
|
||||
- `src/features/react-query-demo/components/pokemon-info.tsx`
|
||||
- `src/features/employees/server/employee-identity-linking.ts`
|
||||
|
||||
## Areas Not Verified
|
||||
|
||||
- ไม่ได้รันแอปจริงใน browser เพื่อยืนยันพฤติกรรม redirect/404/runtime navigation
|
||||
- ไม่ได้เชื่อมต่อฐานข้อมูลจริงเพื่อตรวจสอบ data migration state ระหว่าง `users` และ `employees`
|
||||
- ไม่ได้ตรวจ coverage ของทุก route handler แบบครบทั้งระบบในเฟสนี้
|
||||
- ยังไม่ได้ประเมินคุณภาพของ UI, performance, security hardening, หรือ production ops detail เพราะอยู่นอกขอบเขตของ Phase 1
|
||||
|
||||
## Recommended Next Step
|
||||
|
||||
Phase ถัดไปควรเป็น `Phase 2 - Functional Review` โดยเริ่มจาก feature inventory ของโมดูลธุรกิจหลักและแยกขอบเขต production module ออกจาก demo/template module ให้ชัด เพื่อให้ findings เชิง functional และ permission ใน phase ถัดไปไม่ปะปนกัน
|
||||
499
docs/reviews/phase-2-functional-review.md
Normal file
499
docs/reviews/phase-2-functional-review.md
Normal file
@@ -0,0 +1,499 @@
|
||||
# Phase 2 - Functional Review
|
||||
|
||||
## Executive Summary
|
||||
|
||||
จากการตรวจ functional flow ของระบบ TMS ในรอบนี้ พบว่าฟีเจอร์หลักหลายส่วนสามารถใช้งานได้จริงในระดับ CRUD และ workflow พื้นฐาน เช่น login/session, employee directory, training records creation/import, announcements, online lessons, notifications แบบ in-app, และ training policy activation/history
|
||||
|
||||
อย่างไรก็ตาม เมื่อเทียบกับขอบเขตที่ระบุใน prompt แล้ว ระบบยังมี functional gap สำคัญหลายจุด โดยเฉพาะ:
|
||||
|
||||
- workflow รีวิว `training-records` ยังไม่รองรับ `Reject` แบบแยกจาก `Return for Edit`
|
||||
- reports ที่รองรับจริงยังน้อยกว่าที่ต้องการ และไม่มี CSV export
|
||||
- `training-policy` ยังไม่รองรับ delete/soft delete/effective date/versioning
|
||||
- `announcements` และ `online-lessons` ยังไม่มี scheduled publish / versioning และบาง requirement ยังไม่ถูก model ไว้
|
||||
- notifications ยังเป็น in-app only และ filter capability ไม่ครบกับ type ที่ระบบประกาศไว้
|
||||
|
||||
## Feature Inventory
|
||||
|
||||
| Module | Status | Notes |
|
||||
|---|---|---|
|
||||
| Authentication | Partial | Login/session unauthorized flow มี, self-service sign-up ถูกปิด, ไม่พบ first-time access flow หรือ LDAP/AD integration |
|
||||
| Dashboard Overview | Partial | มี dashboard แยก admin/employee พร้อม year/company/department filters บาง role |
|
||||
| Employee Management | Supported | มี list/search/filter/sort/pagination/detail/create/edit/import/targets |
|
||||
| Training Records | Partial | Create/edit draft/submit/delete/import/certificate/download มี, review flow ยังไม่ครบ reject path |
|
||||
| Online Lessons | Partial | Draft/submit/edit/review/publish/archive มี, schedule publish/versioning/completion ไม่พบ |
|
||||
| Announcements | Partial | Draft/submit/edit/review/publish/archive/pin/unpin/expiration มี, audience/schedule publish/versioning ไม่พบ |
|
||||
| Policy Management | Partial | List/create/edit/clone/activate/deactivate/history มี, delete/soft delete/effective date/versioning ไม่พบ |
|
||||
| Approval Workflow | Partial | Content approval มีหลาย transition, training-record review ยังไม่ครบ state ตาม requirement |
|
||||
| Permission Template Management | Partial | template CRUD-ish/clone/assign/overrides มี, soft delete และ default template behavior ต้องตรวจต่อใน Phase 3 |
|
||||
| Reports | Partial | มี employee transcript/training matrix/department summary/annual summary + Excel/PDF export |
|
||||
| Notifications | Partial | In-app/read-unread/link มี, email notification ไม่พบ |
|
||||
| Audit Log | Supported | login/security event และ mutation modules หลักมี audit integration จำนวนมาก |
|
||||
|
||||
## Findings
|
||||
|
||||
### [High] Training Record review workflow ยังไม่รองรับ `Reject` แบบแยกจาก `Return for Edit`
|
||||
|
||||
**Module:** Training Records, Approval Workflow
|
||||
|
||||
**ตำแหน่งที่พบ:**
|
||||
|
||||
- `src/app/api/training-records/[id]/review/route.ts`
|
||||
- `src/features/training-records/components/training-record-review-form.tsx`
|
||||
- `src/features/training-records/api/types.ts`
|
||||
- `src/features/training-records/schemas/training-record.ts`
|
||||
|
||||
**รายละเอียด:**
|
||||
|
||||
prompt ระบุให้ตรวจ flow `HRD Approve`, `HRD Reject`, `HRD Return for Edit` และ status path:
|
||||
|
||||
`Draft -> Pending Review -> Approved`
|
||||
|
||||
`Draft -> Pending Review -> Rejected`
|
||||
|
||||
`Draft -> Pending Review -> Returned for Edit -> Pending Review`
|
||||
|
||||
แต่ implementation ปัจจุบันของ training-record review รองรับเพียงสองผลลัพธ์:
|
||||
|
||||
- `approved`
|
||||
- `needs_revision`
|
||||
|
||||
โดยไม่มี payload/action สำหรับ `rejected` แยกต่างหาก
|
||||
|
||||
**ผลกระทบ:**
|
||||
|
||||
- business workflow ของการรีวิวประวัติอบรมไม่ครบตาม requirement
|
||||
- สถานะ `rejected` มีใน type/list/filter ของ training records แต่ไม่มีช่องทาง review flow ที่ทำให้ state นี้เกิดขึ้นในเส้นทางปกติ
|
||||
- ผู้ใช้และผู้ตรวจอาจแยกไม่ออกระหว่าง “ส่งกลับให้แก้ไข” กับ “ปฏิเสธถาวร”
|
||||
|
||||
**หลักฐานจากโค้ด:**
|
||||
|
||||
- `src/features/training-records/api/types.ts` กำหนด `TrainingRecordReviewPayload.status` เป็น `'approved' | 'needs_revision'`
|
||||
- `src/features/training-records/components/training-record-review-form.tsx` มี Tabs แค่ `approved` และ `needs_revision`
|
||||
- `src/app/api/training-records/[id]/review/route.ts` map non-approved ทุกกรณีไป `approvalStatus: 'needs_revision'`
|
||||
|
||||
**สถานการณ์ตัวอย่าง:**
|
||||
|
||||
หาก HRD ต้อง “ปฏิเสธรายการอบรม” แบบจบ workflow โดยไม่เปิดให้แก้ไข ผู้ใช้จะทำไม่ได้ เพราะระบบรองรับแค่การอนุมัติหรือส่งกลับให้แก้ไข
|
||||
|
||||
**คำแนะนำ:**
|
||||
|
||||
แยก review decision ออกเป็น `approved` / `needs_revision` / `rejected` ให้ตรงกับ business rule และให้ type, schema, form, notification, audit log และ table filters สอดคล้องกัน
|
||||
|
||||
**ต้องแก้ก่อน Production หรือไม่:** ใช่
|
||||
|
||||
### [High] Reports module รองรับรายงานจริงไม่ครบตาม scope และไม่มี CSV export
|
||||
|
||||
**Module:** Reports
|
||||
|
||||
**ตำแหน่งที่พบ:**
|
||||
|
||||
- `src/features/reports/components/reports-page-content.tsx`
|
||||
- `src/features/reports/api/types.ts`
|
||||
- `src/app/api/reports/export/route.ts`
|
||||
- `src/features/reports/server/report-data.ts`
|
||||
|
||||
**รายละเอียด:**
|
||||
|
||||
prompt ระบุให้ตรวจ:
|
||||
|
||||
- Employee Training Report
|
||||
- Department Report
|
||||
- Company Report
|
||||
- K/S/A Report
|
||||
- Required Hours Report
|
||||
- Missing Training Report
|
||||
- Approval Report
|
||||
- Export CSV
|
||||
- Export Excel
|
||||
- Export PDF
|
||||
- Filter
|
||||
- Sort
|
||||
- Pagination
|
||||
|
||||
แต่ implementation ปัจจุบันรองรับเพียง:
|
||||
|
||||
- Employee Transcript
|
||||
- Training Matrix
|
||||
- Department Summary
|
||||
- Annual Training Summary
|
||||
- Export Excel
|
||||
- Export PDF
|
||||
|
||||
และไม่พบ:
|
||||
|
||||
- Company Report
|
||||
- K/S/A report
|
||||
- Required Hours report
|
||||
- Missing Training report
|
||||
- Approval report
|
||||
- CSV export
|
||||
- sorting/pagination ของ report output ในหน้า reports
|
||||
|
||||
**ผลกระทบ:**
|
||||
|
||||
- report module ยังไม่ครอบคลุม use case ที่ผู้ใช้งานสาย HRD/manager คาดหวัง
|
||||
- งาน operational/reporting ต้องพึ่งการสรุปเองจากข้อมูลดิบหรือ export ไปทำต่อภายนอก
|
||||
|
||||
**หลักฐานจากโค้ด:**
|
||||
|
||||
- `src/app/api/reports/export/route.ts` รองรับ report แค่ `employeeTranscript`, `trainingMatrix`, `departmentSummary`, `annualTrainingSummary`
|
||||
- `src/features/reports/components/reports-page-content.tsx` สร้าง export links เฉพาะ `xlsx` และ `pdf`
|
||||
- ไม่พบโค้ดเกี่ยวกับ CSV export ใน `src/features/reports` และ `src/app/api/reports`
|
||||
|
||||
**สถานการณ์ตัวอย่าง:**
|
||||
|
||||
หาก HRD ต้องการรายงาน “พนักงานที่ยังขาดชั่วโมงบังคับ” หรือ “สรุปตามบริษัท” จะยังไม่สามารถดึงจาก report module ปัจจุบันได้โดยตรง
|
||||
|
||||
**คำแนะนำ:**
|
||||
|
||||
แยก report backlog ตาม report family และกำหนด output contract ที่ชัดเจน รวมถึงเพิ่ม CSV export และพิจารณา UX สำหรับ sort/pagination หรือ downloadable large-result patterns
|
||||
|
||||
**ต้องแก้ก่อน Production หรือไม่:** ใช่
|
||||
|
||||
### [High] Policy Management ยังไม่ครอบคลุม delete/soft delete/effective date/versioning ตาม requirement
|
||||
|
||||
**Module:** Policy Management
|
||||
|
||||
**ตำแหน่งที่พบ:**
|
||||
|
||||
- `src/app/api/training-policies/route.ts`
|
||||
- `src/app/api/training-policies/[id]/route.ts`
|
||||
- `src/app/api/training-policies/[id]/activate/route.ts`
|
||||
- `src/app/api/training-policies/[id]/deactivate/route.ts`
|
||||
- `src/app/api/training-policies/[id]/clone/route.ts`
|
||||
- `src/app/api/training-policies/[id]/history/route.ts`
|
||||
- `src/features/training-policy/api/types.ts`
|
||||
|
||||
**รายละเอียด:**
|
||||
|
||||
training policy ปัจจุบันรองรับ:
|
||||
|
||||
- list
|
||||
- create
|
||||
- edit
|
||||
- activate/deactivate
|
||||
- clone
|
||||
- audit history
|
||||
|
||||
แต่ไม่พบ capability ที่ prompt ระบุไว้ ได้แก่:
|
||||
|
||||
- delete
|
||||
- soft delete
|
||||
- effective date
|
||||
- versioning
|
||||
|
||||
data contract ของ policy ยังมีเพียง `year`, `total_hours`, `k_hours`, `s_hours`, `a_hours`, `is_active`
|
||||
|
||||
**ผลกระทบ:**
|
||||
|
||||
- การจัดการนโยบายย้อนหลังหรือเตรียม policy ล่วงหน้าแบบมีวันเริ่มใช้จริงทำไม่ได้
|
||||
- การแยก “ร่าง version ถัดไป” กับ “policy ที่ใช้งานอยู่” ยังไม่ชัดเจนในเชิงโดเมน
|
||||
|
||||
**หลักฐานจากโค้ด:**
|
||||
|
||||
- `src/app/api/training-policies/route.ts` มีแค่ `GET` และ `POST`
|
||||
- `src/app/api/training-policies/[id]/route.ts` มีแค่ `GET` และ `PATCH`
|
||||
- `src/features/training-policy/api/types.ts` ไม่มี field กลุ่ม `effectiveDate`, `deletedAt`, `version`, `publishedAt`
|
||||
- ไม่พบ `DELETE` route ใน `src/app/api/training-policies`
|
||||
|
||||
**สถานการณ์ตัวอย่าง:**
|
||||
|
||||
หากองค์กรต้องเตรียม policy ปีหน้าไว้ล่วงหน้าแต่ยังไม่ activate ทันทีตามวันมีผลบังคับใช้ ระบบยังไม่มี model/function ตรงนี้แบบ explicit
|
||||
|
||||
**คำแนะนำ:**
|
||||
|
||||
กำหนด business model ของ policy lifecycle ให้ชัดว่าใช้ปีอย่างเดียวพอหรือจำเป็นต้องมี effective date/version chain/retire state แล้วค่อยแตกเป็น API และ UX ที่ตรง requirement
|
||||
|
||||
**ต้องแก้ก่อน Production หรือไม่:** ใช่
|
||||
|
||||
### [Medium] Announcements และ Online Lessons ยังไม่มี scheduled publish / versioning และ requirement เสริมบางข้อยังไม่ถูก model
|
||||
|
||||
**Module:** Announcements, Online Lessons
|
||||
|
||||
**ตำแหน่งที่พบ:**
|
||||
|
||||
- `src/features/announcements/api/types.ts`
|
||||
- `src/features/online-lessons/api/types.ts`
|
||||
- `src/features/content-approval/lib/workflow.ts`
|
||||
- `src/app/api/announcements/route.ts`
|
||||
- `src/app/api/announcements/[id]/route.ts`
|
||||
- `src/app/api/online-lessons/route.ts`
|
||||
- `src/app/api/online-lessons/[id]/route.ts`
|
||||
|
||||
**รายละเอียด:**
|
||||
|
||||
ระบบ content workflow ปัจจุบันรองรับ state/action แบบทันที:
|
||||
|
||||
- draft
|
||||
- pending_review
|
||||
- approved
|
||||
- published
|
||||
- returned
|
||||
- rejected
|
||||
- archived
|
||||
|
||||
โดย action มีแค่ `submit/approve/return/reject/publish/archive`
|
||||
|
||||
สิ่งที่ prompt ต้องการแต่ยังไม่พบใน contract/model ปัจจุบัน:
|
||||
|
||||
- scheduled publish
|
||||
- versioning
|
||||
- audience targeting สำหรับ announcements
|
||||
- lesson completion สำหรับ online lessons
|
||||
|
||||
announcement มี `startDate/endDate` ซึ่งช่วยเรื่องช่วงเวลาแสดงผลและ expiration ได้ แต่ไม่ใช่ scheduled publish แบบ explicit lifecycle
|
||||
|
||||
**ผลกระทบ:**
|
||||
|
||||
- content team ยังตั้งเวลาเผยแพร่ล่วงหน้าแบบ workflow จริงไม่ได้
|
||||
- ไม่มี version chain สำหรับติดตามฉบับก่อน/หลัง
|
||||
- announcement ยังไม่แยกกลุ่มผู้รับแบบ audience-specific
|
||||
- online lesson ยังไม่ track completion state ของพนักงาน
|
||||
|
||||
**หลักฐานจากโค้ด:**
|
||||
|
||||
- `src/features/content-approval/lib/workflow.ts` ไม่มี action สำหรับ schedule/unpublish/version
|
||||
- `src/features/announcements/api/types.ts` ไม่มี field audience/version/publishAt
|
||||
- `src/features/online-lessons/api/types.ts` ไม่มี field completion/version/publishAt
|
||||
|
||||
**สถานการณ์ตัวอย่าง:**
|
||||
|
||||
หาก HRD ต้องเตรียมประกาศให้เผยแพร่วันจันทร์ 09:00 โดยอนุมัติล่วงหน้าวันศุกร์ หรืออยากเก็บ revision history ของบทเรียนออนไลน์ ระบบยังไม่รองรับ workflow นี้โดยตรง
|
||||
|
||||
**คำแนะนำ:**
|
||||
|
||||
แยก requirement ของ content lifecycle ออกเป็น:
|
||||
|
||||
- schedule model
|
||||
- publication window
|
||||
- audience scope
|
||||
- version history
|
||||
- completion tracking
|
||||
|
||||
แล้วทำให้ schema/API/UI ใช้ศัพท์เดียวกัน
|
||||
|
||||
**ต้องแก้ก่อน Production หรือไม่:** ควรพิจารณา
|
||||
|
||||
### [Medium] Notifications เป็น in-app only และ filter capability ไม่ครบกับ notification types ที่ระบบประกาศไว้
|
||||
|
||||
**Module:** Notifications
|
||||
|
||||
**ตำแหน่งที่พบ:**
|
||||
|
||||
- `src/features/notifications/api/types.ts`
|
||||
- `src/app/api/notifications/route.ts`
|
||||
- `src/features/notifications/server/notification-data.ts`
|
||||
- `src/features/notifications/server/notification-service.ts`
|
||||
|
||||
**รายละเอียด:**
|
||||
|
||||
prompt ระบุให้ตรวจทั้ง `In-app Notification` และ `Email Notification`
|
||||
|
||||
implementation ปัจจุบันรองรับ:
|
||||
|
||||
- in-app notifications
|
||||
- read/unread
|
||||
- mark all read
|
||||
- link navigation
|
||||
|
||||
แต่ไม่พบ email delivery implementation เช่น SMTP/provider/send function
|
||||
|
||||
อีกทั้ง type layer ประกาศ notification types ไว้กว้างกว่า filter API ที่รองรับจริง เช่น:
|
||||
|
||||
- `CONTENT_SUBMITTED`
|
||||
- `CONTENT_APPROVED`
|
||||
- `CONTENT_RETURNED`
|
||||
- `CONTENT_REJECTED`
|
||||
- `CONTENT_PUBLISHED`
|
||||
|
||||
มีใน `src/features/notifications/api/types.ts` และถูกสร้างได้จาก `notification-service.ts` แต่ `src/app/api/notifications/route.ts` เปิดให้ filter ได้เพียง subset หนึ่งเท่านั้น
|
||||
|
||||
**ผลกระทบ:**
|
||||
|
||||
- ผู้ใช้ที่คาดหวังการแจ้งเตือนทางอีเมลจะยังไม่ได้รับ
|
||||
- filtering experience ของ notifications ไม่สอดคล้องกับ event types ที่ระบบเองประกาศไว้
|
||||
|
||||
**หลักฐานจากโค้ด:**
|
||||
|
||||
- `src/app/api/notifications/route.ts` ไม่มี logic ส่งอีเมล และ enum filter รองรับ subset จำกัด
|
||||
- `src/features/notifications/server/notification-service.ts` มีแต่ `createNotification(...)` ลงตาราง notifications
|
||||
- ไม่พบ library/provider สำหรับ email sending ใน codebase ที่ตรวจ
|
||||
|
||||
**สถานการณ์ตัวอย่าง:**
|
||||
|
||||
ผู้จัดการที่ไม่เข้า dashboard เป็นประจำจะไม่ถูกแจ้งผ่าน email เมื่อมี content รออนุมัติหรือประกาศใหม่ แม้ระบบจะมี event notification ระดับ in-app อยู่แล้ว
|
||||
|
||||
**คำแนะนำ:**
|
||||
|
||||
แยก notification channel model ให้ชัดว่าอะไรคือ in-app only และอะไรต้องมี email จากนั้นทำให้ notification type/filter API ครอบคลุม types ที่ระบบสร้างจริง
|
||||
|
||||
**ต้องแก้ก่อน Production หรือไม่:** ควรพิจารณา
|
||||
|
||||
### [Medium] Training Records ยังไม่พบ export feature ระดับโมดูล แม้ import/template/download certificate จะรองรับแล้ว
|
||||
|
||||
**Module:** Training Records
|
||||
|
||||
**ตำแหน่งที่พบ:**
|
||||
|
||||
- `src/app/dashboard/training-records/page.tsx`
|
||||
- `src/app/api/training-records/route.ts`
|
||||
- `src/app/api/training-records/import/route.ts`
|
||||
- `src/app/api/training-records/import/template/route.ts`
|
||||
- `src/features/training-records/components/training-record-import-page.tsx`
|
||||
|
||||
**รายละเอียด:**
|
||||
|
||||
training records ปัจจุบันรองรับ:
|
||||
|
||||
- create/edit draft
|
||||
- submit
|
||||
- delete editable record
|
||||
- import
|
||||
- import template download
|
||||
- certificate upload/download/delete
|
||||
|
||||
แต่ไม่พบ export flow ของ training records module โดยตรง แม้ prompt ระบุให้ตรวจ `Export`
|
||||
|
||||
**ผลกระทบ:**
|
||||
|
||||
- ผู้ใช้ต้องพึ่ง reports module หรือดึงข้อมูลจากตารางแทน หากต้องการ export รายการประวัติอบรมโดยตรงจากหน้า feature
|
||||
|
||||
**หลักฐานจากโค้ด:**
|
||||
|
||||
- ไม่พบ route กลุ่ม `/api/training-records/export`
|
||||
- `src/app/dashboard/training-records/page.tsx` มี CTA สำหรับ import/template/review/create แต่ไม่มี export action
|
||||
|
||||
**สถานการณ์ตัวอย่าง:**
|
||||
|
||||
HRD ต้องการ export เฉพาะรายการจากหน้า training records หลัง filter แล้ว แต่ workflow ปัจจุบันไม่รองรับตรงตัว
|
||||
|
||||
**คำแนะนำ:**
|
||||
|
||||
ตัดสินใจให้ชัดว่า export ถือเป็น responsibility ของ report module เท่านั้น หรือควรมี export จาก training-record listing โดยตรง
|
||||
|
||||
**ต้องแก้ก่อน Production หรือไม่:** ควรพิจารณา
|
||||
|
||||
### [Low] Notification filter API กับ UI ไม่สอดคล้องกับ types ที่ระบบสร้างได้จริง
|
||||
|
||||
**Module:** Notifications
|
||||
|
||||
**ตำแหน่งที่พบ:**
|
||||
|
||||
- `src/features/notifications/api/types.ts`
|
||||
- `src/app/api/notifications/route.ts`
|
||||
- `src/features/notifications/constants/notification-options.ts`
|
||||
- `src/features/notifications/server/notification-service.ts`
|
||||
|
||||
**รายละเอียด:**
|
||||
|
||||
ระบบสร้าง content workflow notifications ได้หลายประเภท แต่ GET `/api/notifications` filter schema รองรับไม่ครบ และหน้า notifications อิงจาก option set ที่แคบกว่า type universe จริง
|
||||
|
||||
**ผลกระทบ:**
|
||||
|
||||
- ผู้ใช้ filter หา event บางชนิดไม่ได้ ทั้งที่ระบบบันทึก notification ชนิดนั้นอยู่
|
||||
|
||||
**หลักฐานจากโค้ด:**
|
||||
|
||||
- `notification-service.ts` รองรับ `CONTENT_SUBMITTED`, `CONTENT_APPROVED`, `CONTENT_RETURNED`, `CONTENT_REJECTED`, `CONTENT_PUBLISHED`
|
||||
- `src/app/api/notifications/route.ts` enum filter ไม่รวม type เหล่านี้ทั้งหมด
|
||||
|
||||
**สถานการณ์ตัวอย่าง:**
|
||||
|
||||
ทีมต้องการเปิดเฉพาะ “content returned” notifications เพื่อติดตามงานแก้ไข แต่หน้า UI/API ปัจจุบันยังไม่มี filter ตรงประเภทนี้
|
||||
|
||||
**คำแนะนำ:**
|
||||
|
||||
ทำ notification type registry กลาง และให้ API/UI/share same enum
|
||||
|
||||
**ต้องแก้ก่อน Production หรือไม่:** ไม่ใช่
|
||||
|
||||
## Positive Coverage Observed
|
||||
|
||||
- `employee-directory` รองรับ search/filter/sort/pagination/detail/create/import ได้ค่อนข้างครบ
|
||||
- `announcements` รองรับ draft/submit/edit/review/publish/pin-unpin/expiration/employee view ได้ดีในระดับพื้นฐาน
|
||||
- `online-lessons` รองรับทั้ง video URL และ video upload พร้อม thumbnail/attachment และ approval/publish flow พื้นฐาน
|
||||
- `training-records` รองรับ draft/edit/delete/import/template/certificate handling ได้ค่อนข้างครบ
|
||||
- overview และ reports มี role-aware scoping สำหรับ employee vs admin
|
||||
|
||||
## Commands Executed and Results
|
||||
|
||||
1. `rg -n "Phase 2|Functional Review|phase-2-functional-review" plans/tms-system-full-audit-prompts.md`
|
||||
Result: ใช้ยืนยันหัวข้อและ output file ของ Phase 2
|
||||
2. `Get-ChildItem -Recurse -File src/app/dashboard | Select-Object FullName`
|
||||
Result: ใช้ inventory route/pages ของ dashboard
|
||||
3. `Get-ChildItem -Recurse -File src/app/api | Select-Object FullName`
|
||||
Result: ใช้ inventory route handlers ที่รองรับแต่ละ feature
|
||||
4. `rg --files -g "*test*" -g "*spec*" src`
|
||||
Result: ไม่พบ test coverage ที่มีนัยสำคัญสำหรับ functional verification
|
||||
5. `rg -n "/dashboard/.*/new" src`
|
||||
Result: ใช้เทียบ CTA create routes กับ implementation จริง
|
||||
6. `rg -n "csv|CSV|text/csv|createCsv|export csv" src/features/reports src/app/api/reports`
|
||||
Result: ไม่พบ CSV export ใน reports module
|
||||
7. `rg -n "sendMail|nodemailer|resend|smtp|email" src/features src/app/api src/lib`
|
||||
Result: ไม่พบ email notification implementation ที่ชัดเจน
|
||||
8. `rg -n "delete|DELETE" src/app/api/training-policies src/features/training-policy`
|
||||
Result: ไม่พบ delete route ของ training policies
|
||||
9. `rg -n "effective|version|audience|schedule|publishAt|publishedAt|expires|expiration" ...`
|
||||
Result: พบเพียง publish timestamp/expiration บางส่วน แต่ไม่พบ scheduled publish/versioning/audience model
|
||||
|
||||
## Files Reviewed
|
||||
|
||||
- `plans/tms-system-full-audit-prompts.md`
|
||||
- `src/app/dashboard/training-records/page.tsx`
|
||||
- `src/app/dashboard/training-records/[trainingRecordId]/page.tsx`
|
||||
- `src/app/dashboard/announcements/page.tsx`
|
||||
- `src/app/dashboard/announcements/[id]/page.tsx`
|
||||
- `src/app/dashboard/online-lessons/page.tsx`
|
||||
- `src/app/dashboard/online-lessons/manage/[id]/page.tsx`
|
||||
- `src/app/dashboard/reports/page.tsx`
|
||||
- `src/app/dashboard/notifications/page.tsx`
|
||||
- `src/app/dashboard/employee-directory/page.tsx`
|
||||
- `src/app/dashboard/overview/@area_stats/page.tsx`
|
||||
- `src/features/training-records/components/training-record-form.tsx`
|
||||
- `src/features/training-records/components/training-record-review-form.tsx`
|
||||
- `src/features/training-records/components/training-record-view-page.tsx`
|
||||
- `src/features/training-records/components/training-record-import-page.tsx`
|
||||
- `src/features/announcements/components/announcement-form.tsx`
|
||||
- `src/features/announcements/components/announcement-view-page.tsx`
|
||||
- `src/features/announcements/components/announcements-page.tsx`
|
||||
- `src/features/online-lessons/components/online-lesson-form.tsx`
|
||||
- `src/features/online-lessons/components/online-lesson-detail-page.tsx`
|
||||
- `src/features/online-lessons/components/online-lessons-manage-page.tsx`
|
||||
- `src/features/training-policy/components/training-policy-page.tsx`
|
||||
- `src/features/reports/components/reports-page-content.tsx`
|
||||
- `src/features/notifications/components/notifications-page.tsx`
|
||||
- `src/features/notifications/server/notification-data.ts`
|
||||
- `src/features/notifications/server/notification-service.ts`
|
||||
- `src/features/content-approval/lib/workflow.ts`
|
||||
- `src/features/employee-directory/components/employee-directory-columns.tsx`
|
||||
- `src/features/employee-directory/components/employee-directory-header.tsx`
|
||||
- `src/app/api/training-records/route.ts`
|
||||
- `src/app/api/training-records/[id]/route.ts`
|
||||
- `src/app/api/training-records/[id]/review/route.ts`
|
||||
- `src/app/api/training-records/import/route.ts`
|
||||
- `src/app/api/training-records/import/template/route.ts`
|
||||
- `src/app/api/announcements/route.ts`
|
||||
- `src/app/api/announcements/[id]/route.ts`
|
||||
- `src/app/api/online-lessons/route.ts`
|
||||
- `src/app/api/online-lessons/[id]/route.ts`
|
||||
- `src/app/api/training-policies/route.ts`
|
||||
- `src/app/api/training-policies/[id]/route.ts`
|
||||
- `src/app/api/reports/export/route.ts`
|
||||
- `src/app/api/notifications/route.ts`
|
||||
|
||||
## Areas Not Verified
|
||||
|
||||
- ไม่ได้รัน UI จริงใน browser เพื่อยืนยัน end-to-end interaction, toast, redirect, และ empty/error state runtime behavior
|
||||
- ไม่ได้ส่ง request จริงไปทุก API route พร้อมฐานข้อมูลจริง
|
||||
- ไม่ได้ทดสอบไฟล์ upload/download จริง
|
||||
- ไม่ได้ยืนยัน behavior ของ permission templates เชิงลึก เพราะ Phase 3 จะลงรายละเอียดกว่า
|
||||
- ไม่ได้ยืนยัน notification delivery ภายนอกระบบ เพราะไม่พบ implementation email channel ในรอบนี้
|
||||
|
||||
## Recommended Next Step
|
||||
|
||||
Phase ถัดไปควรเป็น `Phase 3 - Permission & Authorization Audit` โดยเน้น:
|
||||
|
||||
- data scoping ของ employee vs HRD vs IT vs super admin
|
||||
- route/API consistency
|
||||
- effective permissions vs nav visibility
|
||||
- ownership rules ใน training records / announcements / online lessons
|
||||
446
docs/reviews/phase-3-permission-authorization-audit.md
Normal file
446
docs/reviews/phase-3-permission-authorization-audit.md
Normal file
@@ -0,0 +1,446 @@
|
||||
# Phase 3 - Permission & Authorization Audit
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The authorization model is partially migrated to a permission-first design, but the live implementation is still mixed across `effectivePermissions`, `membership.role`, `businessRole`, and `systemRole`. That split creates real consistency risk: several sensitive pages and API routes still authorize by role-derived compatibility helpers even though the project now has permission templates, per-user overrides, and `effectivePermissions`.
|
||||
|
||||
The most important gap is that permission templates are not yet the single source of truth for access decisions. In practice, revoking a capability from an HRD/admin-style user through templates or overrides does not guarantee the capability is removed everywhere, because multiple paths still trust role-based helpers such as `requireHRD()`, `requireIT()`, `canManageTrainingMatrix(membership.role)`, and `canViewAllReports(role)`.
|
||||
|
||||
## Authorization Architecture Overview
|
||||
|
||||
Current authorization layers:
|
||||
|
||||
1. Authentication boundary
|
||||
- `src/proxy.ts`
|
||||
- Protects `/dashboard/**` and a subset of `/api/**` routes from unauthenticated access.
|
||||
2. Session and organization access
|
||||
- `src/lib/auth/session.ts`
|
||||
- `requireSession()` -> `requireOrganizationAccess()` -> specialized guards.
|
||||
3. Compatibility role helpers
|
||||
- `src/lib/auth/roles.ts`
|
||||
- Maps `super_admin`, membership roles, and legacy business roles into `IT`, `HRD`, and `EMPLOYEE`.
|
||||
4. Permission helpers
|
||||
- `src/lib/auth/authorization.ts`
|
||||
- `hasPermission`, `hasAnyPermission`, `hasAllPermissions`.
|
||||
5. Effective permission resolution
|
||||
- `src/lib/auth/authorization.server.ts`
|
||||
- Merges template assignments and user overrides into `effectivePermissions`.
|
||||
6. Navigation filtering
|
||||
- `src/config/nav-config.ts`
|
||||
- `src/hooks/use-nav.ts`
|
||||
- Mostly UX-only visibility filtering.
|
||||
|
||||
Observed architecture state:
|
||||
|
||||
- Permission-first exists and is used correctly in some modules such as `training-policy`.
|
||||
- Compatibility-mode role checks still control many sensitive routes and data scopes.
|
||||
- Navigation, page guards, API guards, and data-scope rules are not fully aligned yet.
|
||||
|
||||
## Permission Inventory
|
||||
|
||||
Defined permission families from `src/lib/auth/permissions.ts`:
|
||||
|
||||
- Organization: `organization:manage`, `organization:switch`
|
||||
- Permission templates: `permission_template:*`
|
||||
- User permission overrides: `user_permission:*`
|
||||
- Users: `users:*`, `users:manage`
|
||||
- Employee directory: `employee_directory:*`, `employee_import:run`, `employee_master_review:approve`
|
||||
- Courses and matrix: `courses:*`, `training_policy:*`, `training_matrix:*`
|
||||
- Training records: self/all read-write plus review workflow permissions
|
||||
- Announcements: full publish workflow permissions
|
||||
- Online lessons: full publish workflow permissions
|
||||
- Reports: `reports:self_read`, `reports:organization_read`, `reports:export`, `reports:export_excel`, `reports:export_pdf`
|
||||
- Audit logs: `audit_logs:read`, `audit_logs:export`
|
||||
- Notifications: `notifications:read`, `notifications:manage`
|
||||
|
||||
Inventory assessment:
|
||||
|
||||
- Coverage is broad enough for a permission-first model.
|
||||
- The problem is not missing permission keys; the problem is inconsistent enforcement of those keys.
|
||||
|
||||
## Route Permission Matrix
|
||||
|
||||
Representative page-level checks:
|
||||
|
||||
| Route | Nav Rule | Page Guard | Effective Result |
|
||||
|---|---|---|---|
|
||||
| `/dashboard/training-policy` | permission-based in nav | `requirePermissionDashboardAccess('training_policy:read')` | Consistent permission-first |
|
||||
| `/dashboard/users` | `businessRoles: ['IT']` | `requireUserManagementDashboardAccess()` | Role-based compatibility check |
|
||||
| `/dashboard/employee-directory` | `businessRoles: ['HRD', 'IT']` | `requireHRDDashboardAccess()` | Page is narrower than nav and ignores permission keys |
|
||||
| `/dashboard/training-matrix` | `businessRoles: ['HRD', 'IT']` | no page guard | Direct URL can render page shell without authorization gate |
|
||||
| `/dashboard/reports` | `businessRole: 'HRD'` | `requireEmployeeDashboardAccess()` | Page allows broader access than nav; server content later scopes by role |
|
||||
| `/dashboard/audit-logs` | `systemRole: 'super_admin'` | `requireITDashboardAccess()` | Nav and page criteria differ |
|
||||
| `/dashboard/organizers` | `systemRole: 'super_admin'` | inline `isHRD(...)` check | Page allows HRD-compatible access even though nav is super-admin only |
|
||||
| `/dashboard/permissions/*` | `systemRole: 'super_admin'` | `requireSuperAdminDashboardAccess()` | Consistent, super-admin only |
|
||||
|
||||
## API Permission Matrix
|
||||
|
||||
Representative API-level checks:
|
||||
|
||||
| API Route | Guard Style | Assessment |
|
||||
|---|---|---|
|
||||
| `/api/training-policies` | `requirePermission('training_policy:read/write')` | Good permission-first enforcement |
|
||||
| `/api/permission-templates` | `requireSuperAdminOrganizationAccess()` | Strong but role-based, not template-based |
|
||||
| `/api/users` | `requireUserManagementAccess()` | Compatibility-mode `IT` check, not `users:*` permissions |
|
||||
| `/api/training-matrix` | `requireOrganizationAccess()` + `canManageTrainingMatrix(membership.role)` | Role-based, bypasses `training_matrix:*` permissions |
|
||||
| `/api/reports/export` | `getReportsAccess()` + `canViewAllReports(role)` | Scope based on membership role, not report permissions |
|
||||
| `/api/training-records/[id]/review` | `requireOrganizationAccess()` + `canReviewTrainingRecords(membership.role)` | Role-based workflow approval |
|
||||
| `/api/master-review` | `requireHRD()` | Role-based, bypasses employee master-review permission keys |
|
||||
| `/api/employees` | `requireOrganizationAccess()` only | Any org member can list/create employees by direct API call |
|
||||
| `/api/products` | `requireOrganizationAccess()` only | Any org member can list/create products by direct API call |
|
||||
| `/api/notifications` | `requireOrganizationAccess()` + self-scope | Acceptable because data is always self-scoped |
|
||||
|
||||
## Action-Level Permission Matrix
|
||||
|
||||
Representative action enforcement:
|
||||
|
||||
| Action | UI Check | API Check | Assessment |
|
||||
|---|---|---|---|
|
||||
| View training policy | permission-based | permission-based | Consistent |
|
||||
| Create/update training policy | permission-based | permission-based | Consistent |
|
||||
| Review training record | mixed role/permission UI | role-based API | Template revokes may not be honored everywhere |
|
||||
| Manage training matrix | nav role-based | role-based API | `training_matrix:write` is not the real gate |
|
||||
| Export reports | page accessible broadly | export scope constrained by membership role | `reports:export*` keys do not drive the actual outcome |
|
||||
| Manage employee directory | page role-based | employees API open to all org members | UI/API mismatch |
|
||||
| Manage users | page/API `IT` compatibility | no `users:*` permission-first gate | Fine-grained permission model not fully active |
|
||||
| Read audit logs | nav super-admin, page/API IT | inconsistent source of truth |
|
||||
|
||||
## Role / Template Mapping
|
||||
|
||||
Observed effective role mapping from `src/lib/auth/roles.ts`:
|
||||
|
||||
- `super_admin` -> treated as `IT`
|
||||
- membership `owner`/`admin` -> treated as `HRD`
|
||||
- membership `member` -> treated as `EMPLOYEE`
|
||||
|
||||
Template behavior from `src/lib/auth/authorization.server.ts`:
|
||||
|
||||
- If user has assigned permission templates, template permissions replace fallback role defaults.
|
||||
- User overrides then allow or deny individual permissions.
|
||||
|
||||
Audit conclusion:
|
||||
|
||||
- The permission engine is capable of precise access control.
|
||||
- The application layer still frequently ignores that engine and falls back to role compatibility rules.
|
||||
- Role names and template names therefore do not reliably predict real access without tracing the exact route.
|
||||
|
||||
## Data Scope Assessment
|
||||
|
||||
Positive observations:
|
||||
|
||||
- Notifications are self-scoped by `organizationId + userId`.
|
||||
- Training records use organization scoping and record-level lookup helpers.
|
||||
- Reports distinguish self vs organization views in data-loading logic.
|
||||
|
||||
Scope weaknesses:
|
||||
|
||||
- Reports determine organization-wide visibility with `canViewAllReports(role)` in `src/features/reports/server/report-data.ts`, not with `reports:organization_read`.
|
||||
- Department summary, annual summary, and training matrix reports are therefore governed by membership role instead of permission keys.
|
||||
- Training matrix management also trusts `membership.role`, not `training_matrix:read/write`.
|
||||
- Employees and products APIs are scoped to organization, but not to action-level authority inside that organization.
|
||||
|
||||
## Direct URL Assessment
|
||||
|
||||
Direct URL behavior is mixed:
|
||||
|
||||
- `/dashboard/**` is authentication-protected by `src/proxy.ts`, but authorization still depends on page-level guards.
|
||||
- `/dashboard/training-matrix` has no page guard, so an authenticated user can load the page shell directly even though its data API later blocks non-admin membership roles.
|
||||
- Several APIs are missing proxy-level authentication coverage entirely, including:
|
||||
- `/api/online-lessons/**`
|
||||
- `/api/permission-templates/**`
|
||||
- `/api/user-permissions/**`
|
||||
- `/api/permissions/catalog`
|
||||
- `/api/permission-audit-logs`
|
||||
- Those routes appear to rely on route-handler guards instead of proxy enforcement. That is acceptable for security if every handler is guarded, but it is inconsistent with the rest of the API surface and increases review burden.
|
||||
|
||||
## UI vs API Authorization Consistency
|
||||
|
||||
Main inconsistencies:
|
||||
|
||||
- Employee directory UI is HRD-only at page level, but `/api/employees` only requires organization membership.
|
||||
- Training matrix nav suggests HRD/IT management access, but the page has no server guard and the API uses membership-role checks only.
|
||||
- Reports nav is HRD-only, but the page allows all employee-area users and delegates scope reduction to server data logic.
|
||||
- Audit logs nav is super-admin only, while page and API enforce `IT` access.
|
||||
- Organizers nav is super-admin only, while the page uses `isHRD(...)`.
|
||||
|
||||
Conclusion:
|
||||
|
||||
- Navigation is not a reliable indicator of backend authorization.
|
||||
- Several pages are stricter or looser than their APIs.
|
||||
- Permission template rollout is incomplete because UI and API do not consistently consume the same access model.
|
||||
|
||||
## Compatibility Mode Findings
|
||||
|
||||
Compatibility-mode authorization is still widespread:
|
||||
|
||||
- `requireHRD()`
|
||||
- `requireIT()`
|
||||
- `requireUserManagementAccess()`
|
||||
- `canManageTrainingMatrix(membership.role)`
|
||||
- `canViewAllReports(role)`
|
||||
- `canReviewTrainingRecords(membership.role)`
|
||||
- inline `isHRD(...)` page checks
|
||||
|
||||
Risk:
|
||||
|
||||
- Any path that authorizes from compatibility roles can bypass explicit permission denies from templates or user overrides.
|
||||
- This is especially important for review, reporting, user management, training matrix, and master-review flows.
|
||||
|
||||
## Missing Permission Descriptions
|
||||
|
||||
Permission metadata review in `src/features/permission-management/server/permission-management-catalog.ts`:
|
||||
|
||||
- Most permission keys have catalog metadata and Thai descriptions.
|
||||
- `descriptionEn` is not populated for most entries.
|
||||
- The file content shows text encoding corruption in the stored description strings, which will degrade readability in the permission-management UI and audit output.
|
||||
|
||||
## Findings by Severity
|
||||
|
||||
### [High] Permission templates and user overrides are not the single source of truth for authorization
|
||||
|
||||
**Module:** Cross-cutting authorization
|
||||
|
||||
**Location Found:**
|
||||
|
||||
- `src/app/api/training-records/[id]/review/route.ts`
|
||||
- `src/app/api/training-matrix/route.ts`
|
||||
- `src/app/api/master-review/route.ts`
|
||||
- `src/features/reports/server/report-data.ts`
|
||||
- `src/app/dashboard/users/page.tsx`
|
||||
- `src/app/dashboard/employee-directory/page.tsx`
|
||||
|
||||
**Details:**
|
||||
|
||||
The codebase already resolves `effectivePermissions`, but many sensitive routes still authorize from membership or business-role compatibility logic. That means a permission template deny may not remove access from all paths for the same user.
|
||||
|
||||
**Impact:**
|
||||
|
||||
- Fine-grained RBAC is unreliable.
|
||||
- Permission template rollout can produce false confidence.
|
||||
- Privileged workflows may remain available after explicit deny overrides.
|
||||
|
||||
**Evidence from Code:**
|
||||
|
||||
- Training matrix uses `canManageTrainingMatrix(membership.role)`.
|
||||
- Reports use `canViewAllReports(role)`.
|
||||
- Master review uses `requireHRD()`.
|
||||
- User management uses `requireUserManagementAccess()` which maps to `IT`.
|
||||
|
||||
**Example Scenario:**
|
||||
|
||||
An admin-style member receives a template that removes report-wide access or training-matrix management. The user can still pass role-based guards because the route trusts membership role instead of `effectivePermissions`.
|
||||
|
||||
**Recommendation:**
|
||||
|
||||
Standardize all sensitive server-side authorization on `requirePermission`, `requireAnyPermission`, or `requireAllPermissions`, and reserve compatibility helpers only for temporary transitional wrappers.
|
||||
|
||||
**Must Fix Before Production:** Yes
|
||||
|
||||
### [High] Employee and product management APIs allow any organization member to perform management actions by direct API access
|
||||
|
||||
**Module:** Employee Management, Products
|
||||
|
||||
**Location Found:**
|
||||
|
||||
- `src/app/api/employees/route.ts`
|
||||
- `src/app/api/products/route.ts`
|
||||
|
||||
**Details:**
|
||||
|
||||
Both APIs only require `requireOrganizationAccess()`. There is no action-level permission check for list or create operations.
|
||||
|
||||
**Impact:**
|
||||
|
||||
- Any authenticated member in the same organization can query or create records through direct API calls even if the dashboard UI hides those actions.
|
||||
- This is a true UI-vs-API authorization gap.
|
||||
|
||||
**Evidence from Code:**
|
||||
|
||||
- `GET /api/employees` and `POST /api/employees` have no `employee_directory:*` or import/review permission gate.
|
||||
- `GET /api/products` and `POST /api/products` have no product-specific privilege gate.
|
||||
|
||||
**Example Scenario:**
|
||||
|
||||
A standard employee cannot reach the employee directory through nav, but can still call `/api/employees` directly once authenticated in the same organization.
|
||||
|
||||
**Recommendation:**
|
||||
|
||||
Add server-side action gates for every management API and align them with the intended permission model.
|
||||
|
||||
**Must Fix Before Production:** Yes
|
||||
|
||||
### [High] Reports authorization and scope are driven by membership role instead of report permissions
|
||||
|
||||
**Module:** Reports
|
||||
|
||||
**Location Found:**
|
||||
|
||||
- `src/features/reports/server/report-data.ts`
|
||||
- `src/app/api/reports/export/route.ts`
|
||||
- `src/app/dashboard/reports/page.tsx`
|
||||
|
||||
**Details:**
|
||||
|
||||
`canViewAllReports(role)` grants organization-wide reporting only to membership `owner` or `admin`. The actual permission keys `reports:self_read`, `reports:organization_read`, and `reports:export*` do not control the real scope boundary.
|
||||
|
||||
**Impact:**
|
||||
|
||||
- Report permissions are partially decorative.
|
||||
- Templates cannot precisely grant or revoke report scope independent of membership role.
|
||||
- Export rules depend on role-derived access rather than permission-derived access.
|
||||
|
||||
**Evidence from Code:**
|
||||
|
||||
- `getReportsAccess()` derives `canViewAll` from `canViewAllReports(scope.role)`.
|
||||
- Export route blocks non-organization-wide users from all reports except `employeeTranscript` using `canViewAll`, not `reports:organization_read`.
|
||||
|
||||
**Example Scenario:**
|
||||
|
||||
A user granted `reports:organization_read` through a template but holding membership `member` still cannot access org-wide reports, while an `admin` membership can.
|
||||
|
||||
**Recommendation:**
|
||||
|
||||
Replace report scope decisions with explicit report permissions and keep role defaults only as template seed values.
|
||||
|
||||
**Must Fix Before Production:** Yes
|
||||
|
||||
### [Medium] Navigation, page guards, and API guards are inconsistent across key modules
|
||||
|
||||
**Module:** Navigation and dashboard access
|
||||
|
||||
**Location Found:**
|
||||
|
||||
- `src/config/nav-config.ts`
|
||||
- `src/hooks/use-nav.ts`
|
||||
- `src/app/dashboard/audit-logs/page.tsx`
|
||||
- `src/app/dashboard/organizers/page.tsx`
|
||||
- `src/app/dashboard/reports/page.tsx`
|
||||
- `src/app/dashboard/training-matrix/page.tsx`
|
||||
|
||||
**Details:**
|
||||
|
||||
The same feature is often gated differently in nav, page render, and API access.
|
||||
|
||||
**Impact:**
|
||||
|
||||
- Users get confusing UX.
|
||||
- Direct URL behavior is harder to reason about.
|
||||
- Authorization reviews become more error-prone.
|
||||
|
||||
**Recommendation:**
|
||||
|
||||
Define one canonical access contract per module and apply it consistently to nav visibility, page guard, and API handlers.
|
||||
|
||||
**Must Fix Before Production:** Consider
|
||||
|
||||
### [Medium] Super-admin organization access mutates membership data as a side effect of authorization
|
||||
|
||||
**Module:** Session and organization access
|
||||
|
||||
**Location Found:**
|
||||
|
||||
- `src/lib/auth/session.ts`
|
||||
|
||||
**Details:**
|
||||
|
||||
`requireOrganizationAccess()` automatically inserts an `admin` membership when a `super_admin` selects an organization without an existing membership.
|
||||
|
||||
**Impact:**
|
||||
|
||||
- Authorization has write-side effects.
|
||||
- Auditability and least-privilege reasoning become harder.
|
||||
- Accidental access expansion is possible through normal navigation.
|
||||
|
||||
**Recommendation:**
|
||||
|
||||
Separate "temporary super-admin impersonation/access" from persistent membership creation, or make membership creation an explicit administrative action.
|
||||
|
||||
**Must Fix Before Production:** Consider
|
||||
|
||||
### [Low] Permission catalog descriptions have metadata quality issues
|
||||
|
||||
**Module:** Permission Management
|
||||
|
||||
**Location Found:**
|
||||
|
||||
- `src/features/permission-management/server/permission-management-catalog.ts`
|
||||
|
||||
**Details:**
|
||||
|
||||
Descriptions exist, but `descriptionEn` is mostly absent and Thai strings appear encoding-corrupted in the file.
|
||||
|
||||
**Impact:**
|
||||
|
||||
- Permission management UX and audit readability are reduced.
|
||||
- Not a direct security failure.
|
||||
|
||||
**Recommendation:**
|
||||
|
||||
Normalize encoding and complete optional description metadata where needed.
|
||||
|
||||
**Must Fix Before Production:** No
|
||||
|
||||
## Privilege Escalation Risks
|
||||
|
||||
Primary privilege-escalation paths observed:
|
||||
|
||||
1. Direct API access to `/api/employees` and `/api/products` by ordinary org members.
|
||||
2. Role-based fallback that ignores explicit permission denies in templates/overrides.
|
||||
3. Super-admin membership auto-creation inside `requireOrganizationAccess()`.
|
||||
|
||||
## Recommended Remediation Order
|
||||
|
||||
1. Lock down management APIs that currently require only organization membership.
|
||||
2. Convert sensitive server routes from compatibility-role checks to permission-first checks.
|
||||
3. Redefine reports scope and export authorization around `reports:*` permissions.
|
||||
4. Add missing page guards, especially for `training-matrix`, and align page/API/nav contracts.
|
||||
5. Remove authorization side effects from super-admin organization access.
|
||||
6. Clean up permission catalog metadata and encoding.
|
||||
|
||||
## Files Reviewed
|
||||
|
||||
- `plans/tms-system-full-audit-prompts.md`
|
||||
- `src/config/nav-config.ts`
|
||||
- `src/hooks/use-nav.ts`
|
||||
- `src/components/layout/app-sidebar.tsx`
|
||||
- `src/proxy.ts`
|
||||
- `src/lib/auth/session.ts`
|
||||
- `src/lib/auth/page-guards.ts`
|
||||
- `src/lib/auth/roles.ts`
|
||||
- `src/lib/auth/authorization.ts`
|
||||
- `src/lib/auth/authorization.server.ts`
|
||||
- `src/lib/auth/permissions.ts`
|
||||
- `src/app/dashboard/users/page.tsx`
|
||||
- `src/app/dashboard/employee-directory/page.tsx`
|
||||
- `src/app/dashboard/training-matrix/page.tsx`
|
||||
- `src/app/dashboard/reports/page.tsx`
|
||||
- `src/app/dashboard/audit-logs/page.tsx`
|
||||
- `src/app/dashboard/organizers/page.tsx`
|
||||
- `src/app/dashboard/training-policy/page.tsx`
|
||||
- `src/app/dashboard/permissions/templates/page.tsx`
|
||||
- `src/app/dashboard/permissions/users/page.tsx`
|
||||
- `src/app/dashboard/permissions/audit-logs/page.tsx`
|
||||
- `src/app/api/users/route.ts`
|
||||
- `src/app/api/employees/route.ts`
|
||||
- `src/app/api/products/route.ts`
|
||||
- `src/app/api/training-matrix/route.ts`
|
||||
- `src/app/api/training-records/[id]/review/route.ts`
|
||||
- `src/app/api/reports/export/route.ts`
|
||||
- `src/app/api/audit-logs/route.ts`
|
||||
- `src/app/api/audit-logs/export/route.ts`
|
||||
- `src/app/api/training-policies/route.ts`
|
||||
- `src/app/api/master-review/route.ts`
|
||||
- `src/app/api/notifications/route.ts`
|
||||
- `src/app/api/permission-templates/route.ts`
|
||||
- `src/app/api/permissions/catalog/route.ts`
|
||||
- `src/features/reports/server/report-data.ts`
|
||||
- `src/features/training-matrix/server/training-matrix-data.ts`
|
||||
- `src/features/permission-management/server/permission-management-catalog.ts`
|
||||
|
||||
## Areas Not Verified
|
||||
|
||||
- Runtime browser behavior for redirects, hidden actions, and toast/error handling
|
||||
- Full API execution against seeded users for every role/template combination
|
||||
- Exact UI rendering impact of permission catalog encoding issues
|
||||
- Online lessons, announcements, and user-permission routes at full action-by-action depth
|
||||
- Whether all non-proxy-covered API routes always apply equivalent route-handler guards in every nested handler
|
||||
424
docs/reviews/phase-4-ui-ux-design-system-audit.md
Normal file
424
docs/reviews/phase-4-ui-ux-design-system-audit.md
Normal file
@@ -0,0 +1,424 @@
|
||||
# Phase 4 - UI/UX & Design System Audit
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The dashboard already has a usable shared UI foundation through `PageContainer`, shadcn primitives, shared table utilities, and consistent card-based page composition. Responsive handling is generally better than average for an admin system because many key layouts already use `min-w-0`, wrapping button groups, and scoped table overflow.
|
||||
|
||||
The largest UI/UX problem is not layout instability but system inconsistency. Several feature areas still diverge from the intended design system, especially reports tables, file-upload flows, heading structure, and copy quality. The most visible issue across the entire UI is widespread Thai text encoding corruption, which materially harms readability, trust, and learnability.
|
||||
|
||||
## Screen Inventory
|
||||
|
||||
Primary screens reviewed:
|
||||
|
||||
- Dashboard shell and sidebar
|
||||
- Organization switcher and user menu
|
||||
- Employee directory list
|
||||
- Training records form and review-oriented detail states
|
||||
- Announcements form and detail view
|
||||
- Online lessons form
|
||||
- Reports page
|
||||
- Notifications page
|
||||
- Audit logs table
|
||||
- Shared page header, heading, data table, toolbar, pagination, notification card
|
||||
|
||||
## Layout Assessment
|
||||
|
||||
Strengths:
|
||||
|
||||
- `src/components/layout/page-container.tsx` gives pages a stable shell with responsive spacing and full-width containment.
|
||||
- Shared tables correctly scope horizontal overflow inside the table shell instead of the full page.
|
||||
- Header actions commonly wrap well on small screens.
|
||||
|
||||
Weaknesses:
|
||||
|
||||
- Header patterns are inconsistent. Some pages use `PageContainer` header only, while others inject another local hero/header block inside the page body, creating double-header behavior.
|
||||
- Reports stack multiple large cards and wide tables vertically, which is workable but visually heavy and cognitively dense.
|
||||
- Access fallback inside `PageContainer` is centered plain text rather than a full empty/error-state pattern.
|
||||
|
||||
## Typography Assessment
|
||||
|
||||
Strengths:
|
||||
|
||||
- Core sizes are mostly sensible: large page titles, smaller descriptions, readable card body text.
|
||||
- Tables generally keep compact admin-dashboard typography.
|
||||
|
||||
Weaknesses:
|
||||
|
||||
- Typography hierarchy is semantically inconsistent because `Heading` renders `h2` while feature pages often behave like top-level page titles.
|
||||
- Some screens add their own `h1` under `PageContainer`, producing mixed heading structures across modules.
|
||||
- Thai text corruption appears in navigation, headers, descriptions, pagination labels, and empty states across many files, which is the most serious typography/content issue in the system.
|
||||
- Copy style mixes Thai and English unevenly, for example `PageContainer` access fallback remains English while most feature UI is Thai.
|
||||
|
||||
## Color & Status Assessment
|
||||
|
||||
Strengths:
|
||||
|
||||
- Shared `Badge` usage is established for workflow and report states.
|
||||
- Content approval states appear to reuse shared variants through `content-approval` helpers.
|
||||
|
||||
Weaknesses:
|
||||
|
||||
- Status language is not fully standardized across features. Similar states use different wording such as archive/hide/remove-publish semantics depending on module.
|
||||
- Some feedback and button copy rely on variant differences only, without additional structural separation for risky actions.
|
||||
|
||||
## Button Standard Assessment
|
||||
|
||||
Strengths:
|
||||
|
||||
- Most primary/secondary actions use shared `Button` variants.
|
||||
- Mobile width handling is good in many forms and headers through `w-full sm:w-auto`.
|
||||
- Action ordering is usually reasonable: cancel/back separated from primary submit action.
|
||||
|
||||
Weaknesses:
|
||||
|
||||
- Workflow buttons are not standardized across content modules. Announcements and online lessons use similar but not fully shared action group patterns.
|
||||
- Some significant actions such as publish/archive are triggered directly from detail views without explicit confirmation, which weakens safety and workflow clarity.
|
||||
- Loading labels are inconsistent and often use `...` instead of a consistent loading copy pattern.
|
||||
|
||||
## Table Standard Assessment
|
||||
|
||||
Strengths:
|
||||
|
||||
- Shared `DataTable`, toolbar, and pagination are good system-level primitives.
|
||||
- Canonical table modules like employee directory and audit logs follow the shared pattern well.
|
||||
- Action columns are generally handled in a stable way with pinning and reusable menus.
|
||||
|
||||
Weaknesses:
|
||||
|
||||
- Reports bypass the shared DataTable stack entirely and use manual `Table` cards, so sorting, pagination, column management, and interaction patterns differ from the rest of the app.
|
||||
- Manual report tables rely on horizontal scrolling only and do not offer the stronger affordances already available in the shared table system.
|
||||
- Table empty/loading states are reasonably present, but wording and visual treatment vary by module.
|
||||
|
||||
## Form Standard Assessment
|
||||
|
||||
Strengths:
|
||||
|
||||
- Major forms use TanStack Form wrappers and shared field primitives.
|
||||
- Validation errors are shown inline and `scrollToFirstError()` is used in important flows.
|
||||
- Training records form handles employee vs HRD mode clearly.
|
||||
|
||||
Weaknesses:
|
||||
|
||||
- Required mark style is inconsistent. Some screens use literal `*`, some use styled destructive spans, and some rely on validation only.
|
||||
- Placeholder and helper-text patterns vary widely between modules.
|
||||
- Inputs often omit `name` and `autocomplete` attributes, which is a lower-level accessibility and usability gap against modern form guidelines.
|
||||
- Feature forms mix shared field wrappers with raw HTML elements such as custom `textarea`, creating slightly different spacing and behavior.
|
||||
|
||||
## File Attachment UX Assessment
|
||||
|
||||
Strengths:
|
||||
|
||||
- Training records use a shared `FileUploader` and attachment list, which gives a clearer system pattern.
|
||||
- Content forms usually show current file, replace/remove flows, and post-save expectations.
|
||||
|
||||
Weaknesses:
|
||||
|
||||
- Attachment UX is fragmented across modules:
|
||||
- Training records use shared uploader + attachment list
|
||||
- Announcements use a raw file input
|
||||
- Online lessons use custom `AssetPicker` sections
|
||||
- This creates different expectations for drag-and-drop, file preview, replacement, and removal across similar content workflows.
|
||||
- View-mode attachment presentation is not standardized across modules.
|
||||
|
||||
## Feedback State Assessment
|
||||
|
||||
Strengths:
|
||||
|
||||
- Most reviewed modules include loading, error, and empty states.
|
||||
- Toasts are used consistently for mutation feedback.
|
||||
- Audit logs and notifications have decent empty-state handling.
|
||||
|
||||
Weaknesses:
|
||||
|
||||
- Loading states are inconsistent between skeleton-style, spinner-in-card, and plain text messaging.
|
||||
- `PageContainer` access denial uses a plain centered sentence rather than a richer restricted-state pattern.
|
||||
- Success and error messaging quality varies and is harder to read because of copy encoding issues.
|
||||
|
||||
## Dialog / Sheet / Drawer Assessment
|
||||
|
||||
Observed strengths:
|
||||
|
||||
- Shared dialog/sheet primitives are clearly part of the system.
|
||||
- Content review uses a shared action dialog component rather than ad hoc per-page modal markup.
|
||||
|
||||
Observed gaps:
|
||||
|
||||
- This audit did not fully validate focus trap, escape handling, and mobile height behavior for every dialog/sheet implementation.
|
||||
- Action-safety patterns are not uniform; some important transitions use dialogs while other significant actions run immediately.
|
||||
|
||||
## Workflow UX Assessment
|
||||
|
||||
Strengths:
|
||||
|
||||
- Training records and content modules expose state-aware actions instead of showing every action at once.
|
||||
- Review notes and current status are surfaced in key detail/form views.
|
||||
|
||||
Weaknesses:
|
||||
|
||||
- Reports page is functionally useful but visually dense; it reads like stacked exports rather than a guided reporting workspace.
|
||||
- Content modules blur the line between draft editing, review transition, publish, and archive actions because button patterns are similar but not always grouped by risk or workflow stage.
|
||||
- Employee directory uses an extra local page hero inside `PageContainer`, which makes it feel visually different from other management screens.
|
||||
|
||||
## Responsive Assessment
|
||||
|
||||
Strengths:
|
||||
|
||||
- Shared layouts use `min-w-0`, wrapped actions, and scoped overflow correctly.
|
||||
- Employee directory and shared tables are intentionally built for horizontal table scroll on smaller screens.
|
||||
- Forms generally collapse to a single column well.
|
||||
|
||||
Weaknesses:
|
||||
|
||||
- Reports is the most likely responsive pain point because of multiple wide manual tables on one page.
|
||||
- Some custom card headers and action groups depend on wrapping rather than a stronger mobile-specific layout hierarchy.
|
||||
- Sidebar behavior was not visually runtime-tested in browser during this audit.
|
||||
|
||||
## Accessibility Assessment
|
||||
|
||||
Positive observations:
|
||||
|
||||
- Many interactive controls use semantic elements such as `button`, `Link`, and form labels.
|
||||
- Some icon-only actions already have `aria-label`, for example notification mark-as-read and table pagination buttons.
|
||||
- Inline validation patterns exist in major forms.
|
||||
|
||||
Key gaps:
|
||||
|
||||
- No verified skip-link pattern was found for the dashboard shell.
|
||||
- Decorative icons are widely rendered without explicit `aria-hidden`, which can add noise for assistive tech depending on the icon implementation.
|
||||
- Heading hierarchy is inconsistent at the page level because of mixed `h1`/`h2` usage.
|
||||
- Native form enhancements such as `autocomplete`, input naming consistency, and copy punctuation are not standardized.
|
||||
- Notifications pagination uses `href='#'` plus click interception, which is weaker semantically than true navigation controls.
|
||||
|
||||
## Design System Inconsistencies
|
||||
|
||||
Most important system drifts:
|
||||
|
||||
1. Copy quality and encoding
|
||||
- Thai labels are corrupted across multiple feature surfaces.
|
||||
2. Page header structure
|
||||
- Shared page shell exists, but some modules duplicate internal hero headers.
|
||||
3. Table architecture
|
||||
- Canonical modules use shared DataTable; reports does not.
|
||||
4. File upload pattern
|
||||
- Different modules use different uploader UX for similar tasks.
|
||||
5. Form semantics
|
||||
- Required markers, placeholders, descriptions, and input metadata are inconsistent.
|
||||
6. Workflow action treatment
|
||||
- Some risky actions confirm through dialogs; others execute directly.
|
||||
|
||||
## Findings by Severity
|
||||
|
||||
### [High] Thai UI copy is encoding-corrupted across major navigation, form, table, and feedback surfaces
|
||||
|
||||
**Module:**
|
||||
|
||||
Cross-cutting UI copy
|
||||
|
||||
**Location Found:**
|
||||
|
||||
- `src/config/nav-config.ts`
|
||||
- `src/components/layout/page-container.tsx`
|
||||
- `src/components/ui/table/data-table.tsx`
|
||||
- `src/components/ui/table/data-table-pagination.tsx`
|
||||
- `src/features/employee-directory/components/employee-directory-header.tsx`
|
||||
- `src/features/reports/components/reports-page-content.tsx`
|
||||
- `src/features/notifications/components/notifications-page.tsx`
|
||||
- `src/features/announcements/components/announcement-form.tsx`
|
||||
- `src/features/announcements/components/announcement-view-page.tsx`
|
||||
- `src/features/training-records/components/training-record-form.tsx`
|
||||
- `src/features/online-lessons/components/online-lesson-form.tsx`
|
||||
|
||||
**Details:**
|
||||
|
||||
Large portions of user-facing Thai text render as mojibake-like corrupted strings instead of readable Thai copy.
|
||||
|
||||
**Impact:**
|
||||
|
||||
- Severely damages readability and trust.
|
||||
- Makes workflows harder to learn.
|
||||
- Reduces accessibility because labels and feedback become harder to interpret.
|
||||
|
||||
**Evidence from Code:**
|
||||
|
||||
Corrupted Thai strings appear in page titles, descriptions, nav items, buttons, empty states, and pagination labels.
|
||||
|
||||
**Example Scenario:**
|
||||
|
||||
A user opening navigation, forms, or reports sees broken Thai labels in core actions and status descriptions, making the system feel unreliable even when the workflow itself works.
|
||||
|
||||
**Recommendation:**
|
||||
|
||||
Normalize file encoding and re-validate every user-facing Thai string before expanding UI work.
|
||||
|
||||
**Must Fix Before Production:** Yes
|
||||
|
||||
### [Medium] Heading and page-header patterns are semantically and visually inconsistent across modules
|
||||
|
||||
**Module:**
|
||||
|
||||
Shared layout and page composition
|
||||
|
||||
**Location Found:**
|
||||
|
||||
- `src/components/ui/heading.tsx`
|
||||
- `src/components/layout/page-container.tsx`
|
||||
- `src/features/employee-directory/components/employee-directory-header.tsx`
|
||||
|
||||
**Details:**
|
||||
|
||||
`Heading` renders `h2`, while some screens also introduce their own `h1` inside the page body. Other screens rely entirely on the shared heading component.
|
||||
|
||||
**Impact:**
|
||||
|
||||
- Inconsistent visual rhythm across pages.
|
||||
- Weaker accessibility and document structure.
|
||||
- Makes it harder to standardize page templates.
|
||||
|
||||
**Recommendation:**
|
||||
|
||||
Define one canonical page-title pattern for dashboard screens and apply it uniformly.
|
||||
|
||||
**Must Fix Before Production:** Consider
|
||||
|
||||
### [Medium] Reports UI diverges from the shared table system and creates a noticeably different interaction model
|
||||
|
||||
**Module:**
|
||||
|
||||
Reports
|
||||
|
||||
**Location Found:**
|
||||
|
||||
- `src/features/reports/components/reports-page-content.tsx`
|
||||
- `src/features/reports/components/report-table-card.tsx`
|
||||
|
||||
**Details:**
|
||||
|
||||
Reports use manual tables instead of the shared DataTable stack used elsewhere in the dashboard.
|
||||
|
||||
**Impact:**
|
||||
|
||||
- Table behavior differs from the rest of the product.
|
||||
- Large report cards become dense and harder to scan.
|
||||
- Users lose familiar controls such as unified table toolbars and standardized interaction patterns.
|
||||
|
||||
**Recommendation:**
|
||||
|
||||
Either formally designate reports as a separate reporting presentation pattern, or migrate the module toward shared table conventions where practical.
|
||||
|
||||
**Must Fix Before Production:** Consider
|
||||
|
||||
### [Medium] File attachment UX is fragmented across modules instead of following one reusable pattern
|
||||
|
||||
**Module:**
|
||||
|
||||
Training Records, Announcements, Online Lessons
|
||||
|
||||
**Location Found:**
|
||||
|
||||
- `src/features/training-records/components/training-record-form.tsx`
|
||||
- `src/features/announcements/components/announcement-form.tsx`
|
||||
- `src/features/online-lessons/components/online-lesson-form.tsx`
|
||||
|
||||
**Details:**
|
||||
|
||||
Comparable upload tasks use different components and different interaction rules.
|
||||
|
||||
**Impact:**
|
||||
|
||||
- Increases cognitive load when users move between modules.
|
||||
- Makes future improvements and accessibility hardening harder to apply consistently.
|
||||
|
||||
**Recommendation:**
|
||||
|
||||
Standardize a single attachment/upload UX contract for content and training modules, even if internal storage logic remains feature-specific.
|
||||
|
||||
**Must Fix Before Production:** Consider
|
||||
|
||||
### [Low] Form and pagination semantics miss several modern UX/a11y conventions
|
||||
|
||||
**Module:**
|
||||
|
||||
Shared forms and notifications
|
||||
|
||||
**Location Found:**
|
||||
|
||||
- `src/features/training-records/components/training-record-form.tsx`
|
||||
- `src/features/announcements/components/announcement-form.tsx`
|
||||
- `src/features/online-lessons/components/online-lesson-form.tsx`
|
||||
- `src/features/notifications/components/notifications-page.tsx`
|
||||
|
||||
**Details:**
|
||||
|
||||
Common gaps include missing `autocomplete` conventions, inconsistent input metadata, and pagination links implemented with `href='#'` plus click interception.
|
||||
|
||||
**Impact:**
|
||||
|
||||
- Mostly incremental usability and accessibility debt rather than immediate blockers.
|
||||
|
||||
**Recommendation:**
|
||||
|
||||
Add a lightweight shared checklist for form semantics, link semantics, and icon accessibility before future UI expansion.
|
||||
|
||||
**Must Fix Before Production:** No
|
||||
|
||||
## Recommended UI Standard
|
||||
|
||||
Suggested system rules for this repo:
|
||||
|
||||
1. One dashboard page header pattern
|
||||
- Top-level page title, description, optional info button, optional right-side action area.
|
||||
2. One table family
|
||||
- Use shared DataTable unless a page is intentionally a reporting surface with documented exceptions.
|
||||
3. One attachment pattern
|
||||
- Shared file state, current file preview, replace/remove actions, and empty state wording.
|
||||
4. One required-field style
|
||||
- Same marker, same placement, same helper/error rhythm.
|
||||
5. One workflow action grouping rule
|
||||
- Primary action, secondary action, destructive action, and confirmation behavior defined centrally.
|
||||
6. One language-quality rule
|
||||
- All user-facing Thai strings must be UTF-safe, readable, and consistently localized.
|
||||
|
||||
## Recommended Remediation Order
|
||||
|
||||
1. Fix Thai text encoding across all user-facing surfaces.
|
||||
2. Standardize heading semantics and page-header composition.
|
||||
3. Decide whether reports is an intentional exception or should move closer to the shared table pattern.
|
||||
4. Unify attachment UX across training records, announcements, and online lessons.
|
||||
5. Add a small accessibility/design checklist for forms, icon buttons, and navigation semantics.
|
||||
|
||||
## Files Reviewed
|
||||
|
||||
- `plans/tms-system-full-audit-prompts.md`
|
||||
- `docs/AI_DEVELOPMENT_GUIDE.md`
|
||||
- `docs/PROJECT_ARCHITECTURE.md`
|
||||
- `src/components/layout/page-container.tsx`
|
||||
- `src/components/layout/app-sidebar.tsx`
|
||||
- `src/components/layout/user-nav.tsx`
|
||||
- `src/components/org-switcher.tsx`
|
||||
- `src/components/ui/heading.tsx`
|
||||
- `src/components/ui/notification-card.tsx`
|
||||
- `src/components/ui/table/data-table.tsx`
|
||||
- `src/components/ui/table/data-table-toolbar.tsx`
|
||||
- `src/components/ui/table/data-table-pagination.tsx`
|
||||
- `src/config/nav-config.ts`
|
||||
- `src/features/employee-directory/components/employee-directory-header.tsx`
|
||||
- `src/features/employee-directory/components/employee-directory-table.tsx`
|
||||
- `src/features/training-records/components/training-record-form.tsx`
|
||||
- `src/features/announcements/components/announcement-form.tsx`
|
||||
- `src/features/announcements/components/announcement-view-page.tsx`
|
||||
- `src/features/announcements/components/announcement-status-badge.tsx`
|
||||
- `src/features/online-lessons/components/online-lesson-form.tsx`
|
||||
- `src/features/reports/components/reports-page-content.tsx`
|
||||
- `src/features/reports/components/report-table-card.tsx`
|
||||
- `src/features/notifications/components/notifications-page.tsx`
|
||||
- `src/features/audit-logs/components/audit-log-listing.tsx`
|
||||
- `src/features/audit-logs/components/audit-log-table.tsx`
|
||||
- `src/features/content-approval/components/content-approval-status-badge.tsx`
|
||||
|
||||
## Areas Not Verified
|
||||
|
||||
- Runtime browser validation of focus order, keyboard traversal, and screen-reader output
|
||||
- Visual contrast measurement against rendered theme tokens
|
||||
- All dialogs/sheets/drawers at runtime, including focus trap and mobile height behavior
|
||||
- Full mobile behavior of the sidebar and complex tables in a live viewport
|
||||
- Every feature module in the repo; this review focused on canonical and high-traffic dashboard surfaces
|
||||
352
docs/reviews/phase-5-code-quality-performance-audit.md
Normal file
352
docs/reviews/phase-5-code-quality-performance-audit.md
Normal file
@@ -0,0 +1,352 @@
|
||||
# Phase 5 - Code Quality & Performance Audit
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The system currently passes `typecheck`, `lint`, and production `build`, so the codebase is operational and deployable in its current state. The main risk in this phase is not immediate build failure, but accumulated maintainability and performance debt concentrated in several oversized route handlers, server data modules, and workflow-heavy client forms.
|
||||
|
||||
The most important quality issue is structural complexity. A small set of files currently own too many responsibilities at once: authorization-aware data loading, filtering, export generation, workflow transitions, storage handling, notifications, and UI orchestration. This increases regression risk, makes optimization harder, and reduces testability.
|
||||
|
||||
The most important performance issue is repeated work inside large reporting and workflow paths rather than obvious catastrophic hotspots. In particular, report generation repeats scope-resolution queries, export logic is tightly coupled to dataset construction, and several large client components keep broad interactive state on the client without further decomposition.
|
||||
|
||||
## Build/Config Assessment
|
||||
|
||||
### Observed configuration
|
||||
|
||||
- `package.json:10-15` defines `dev`, `build`, `lint`, `lint:fix`, and `lint:strict`.
|
||||
- There is no dedicated `typecheck` script in `package.json`.
|
||||
- `tsconfig.json:5-19` uses `strict: true`, `isolatedModules: true`, and `incremental: true`.
|
||||
- `tsconfig.json:5-6` also keeps `allowJs: true` and `skipLibCheck: true`.
|
||||
- Linting is done by `oxlint`, not a stricter combined lint-plus-type gate.
|
||||
|
||||
### Assessment
|
||||
|
||||
Strengths:
|
||||
|
||||
- The project already compiles cleanly under production build.
|
||||
- TypeScript strict mode is enabled.
|
||||
- The repo has a stricter lint mode available through `lint:strict`.
|
||||
|
||||
Weaknesses:
|
||||
|
||||
- Missing `typecheck` script makes CI and contributor workflows less explicit.
|
||||
- `allowJs: true` weakens long-term type-safety expectations in a TypeScript-first codebase.
|
||||
- `skipLibCheck: true` is a practical default, but it can hide dependency typing drift.
|
||||
- The default lint command allows warnings to pass, so code-quality drift can accumulate silently.
|
||||
|
||||
## TypeScript Assessment
|
||||
|
||||
### Strengths
|
||||
|
||||
- `npx tsc --noEmit` completed successfully.
|
||||
- Most feature modules use explicit contracts and typed query/mutation boundaries.
|
||||
- Shared data access patterns appear to prefer typed service layers over untyped ad hoc fetches.
|
||||
|
||||
### Findings
|
||||
|
||||
- `src/instrumentation-client.ts` uses `(Sentry as any)`, which weakens observability-related type safety.
|
||||
- `src/components/forms/demo-form.tsx` and `src/features/products/schemas/product.ts` use permissive `any`-style schema patterns, suggesting leftover demo/legacy looseness.
|
||||
- `src/features/forms/components/sheet-product-form.tsx` uses `productSchema as any`, which bypasses compiler guarantees.
|
||||
- `src/lib/api-client.ts:57` throws generic `Error` instances instead of a typed API/domain error model.
|
||||
|
||||
### Assessment
|
||||
|
||||
TypeScript quality is acceptable at the baseline level, but not yet fully mature. The compiler settings are strong enough to prevent many failures, while a small set of escape hatches and generic error shapes still reduce confidence in cross-layer correctness.
|
||||
|
||||
## React Assessment
|
||||
|
||||
### Strengths
|
||||
|
||||
- The app reuses shared primitives and a shared query client.
|
||||
- Several flows use TanStack Query instead of bespoke client fetching logic.
|
||||
- Shared table state is centralized in `src/hooks/use-data-table.ts`, which is a good reuse decision even though the hook is now quite complex.
|
||||
|
||||
### Findings
|
||||
|
||||
- `src/features/training-records/components/training-record-form.tsx` is 765 lines and combines form orchestration, uploads, mutations, attachment state, and workflow logic in one client component.
|
||||
- `src/features/online-lessons/components/online-lesson-form.tsx` is 554 lines and keeps many local file/removal states plus multiple workflow mutations in one component.
|
||||
- `src/features/announcements/components/announcement-form.tsx` uses the same multi-mutation workflow pattern and is trending toward the same shape.
|
||||
- `src/features/reports/components/reports-page-content.tsx:278,317` triggers `react(no-unstable-nested-components)` warnings.
|
||||
- `src/components/ui/calendar.tsx:58` and `src/components/kbar/render-result.tsx:10` also define nested components during render.
|
||||
|
||||
### Assessment
|
||||
|
||||
The main React risk is not hook misuse at scale, but oversized client components with broad responsibility and broad rerender surfaces. These components are harder to reason about, harder to profile, and harder to unit test than smaller view-model plus presentational splits.
|
||||
|
||||
## Next.js Assessment
|
||||
|
||||
### Strengths
|
||||
|
||||
- Production build succeeded under Next.js 16.2.6.
|
||||
- The app makes meaningful use of server-side data loading.
|
||||
- `src/features/overview/server/overview-data.ts:171,185,249,519` uses `cache()` well in several high-read paths.
|
||||
|
||||
### Findings
|
||||
|
||||
- `src/app/api/announcements/[id]/route.ts` is 683 lines and combines multipart parsing, draft/save/submit transitions, storage operations, audit logging, and delete handling in a single route module.
|
||||
- `src/lib/api-client.ts:29-41` resolves server request headers and reconstructs origin dynamically on every server request path, which adds per-call overhead and keeps the route-handler pattern tightly coupled to internal HTTP.
|
||||
- The build output still includes many legacy/demo screens and routes such as `/dashboard/forms`, `/dashboard/kanban`, `/dashboard/chat`, `/dashboard/product`, and `/dashboard/employees`, which increases bundle and maintenance surface.
|
||||
|
||||
### Assessment
|
||||
|
||||
The app is using Next.js capably, but it is not yet taking full advantage of cleaner server boundaries. The biggest framework-level opportunity is reducing workflow complexity inside route handlers and separating internal server logic from HTTP transport concerns.
|
||||
|
||||
## Validation Assessment
|
||||
|
||||
### Strengths
|
||||
|
||||
- The broader codebase follows a Zod-plus-form-wrapper approach.
|
||||
- Workflow modules show evidence of server-side validation before persistence.
|
||||
|
||||
### Findings
|
||||
|
||||
- Validation quality varies by feature. Demo and product-related areas still contain permissive schema patterns such as `.any()`.
|
||||
- File-heavy workflow routes appear to validate through branching procedural logic inside route handlers rather than through smaller reusable validation units.
|
||||
- Status/date/file validation logic appears repeated across announcement and online-lesson workflow code paths instead of being fully centralized.
|
||||
|
||||
### Assessment
|
||||
|
||||
Validation is good enough for core runtime flows, but consistency is not uniform across the repo. The main weakness is schema fragmentation and procedural validation logic inside large handlers.
|
||||
|
||||
## Error Handling Assessment
|
||||
|
||||
### Strengths
|
||||
|
||||
- Auth/session helpers throw explicit `AuthError` instances for unauthorized and forbidden cases.
|
||||
- Route handlers generally return structured JSON responses rather than raw exceptions.
|
||||
|
||||
### Findings
|
||||
|
||||
- `src/lib/api-client.ts:57` throws plain `Error`, collapsing HTTP status, domain code, and user-display concerns into one generic error shape.
|
||||
- `src/lib/auth/session.ts` contains many repeated `throw new AuthError(...)` branches, which is clear but repetitive and hints at opportunities for more composable authorization guards.
|
||||
- Large route handlers and data modules mix business logic with logging and side effects, which makes it harder to guarantee consistent error mapping.
|
||||
|
||||
### Assessment
|
||||
|
||||
Error handling is functional but not yet strongly normalized. The biggest gap is the lack of a consistent typed application error model spanning route handlers, services, and client consumers.
|
||||
|
||||
## Duplicate Code Assessment
|
||||
|
||||
### Findings
|
||||
|
||||
- Date formatting is scattered across many files through direct `new Date(...).toISOString()` and `toLocaleDateString(...)` usage, including `src/app/api/announcements/route.ts`, `src/app/api/announcements/[id]/route.ts`, `src/app/api/online-lessons/[id]/route.ts`, `src/app/api/audit-logs/export/route.ts`, and `src/features/reports/components/reports-page-content.tsx:78-83`.
|
||||
- Workflow patterns for draft/save/submit/approve/archive actions are conceptually similar between announcements and online lessons, but remain implemented separately.
|
||||
- Report rendering repeats table-like configuration and inline mapping logic inside `src/features/reports/components/reports-page-content.tsx`.
|
||||
- Authorization guard usage is centralized, but authorization-related branching still appears repeatedly inside high-level server modules.
|
||||
|
||||
### Assessment
|
||||
|
||||
The repo has good reuse in core scaffolding, but duplication is still visible in domain workflows, date formatting, report presentation, and status transition logic. This is manageable now, but it will get more expensive as more content modules are added.
|
||||
|
||||
## Naming & Convention Assessment
|
||||
|
||||
### Strengths
|
||||
|
||||
- Feature and file naming is generally readable and follows the project structure.
|
||||
- Route names align well with feature intent.
|
||||
|
||||
### Findings
|
||||
|
||||
- `src/components/ui/slider.tsx:16` uses `_values`, which triggers a lint naming warning.
|
||||
- Mixed legacy/demo/runtime naming remains across the repo, especially in `products`, `forms`, `chat`, `kanban`, and old `employees` areas.
|
||||
- Some modules use domain-accurate names but still house too many concerns for the file name to communicate actual scope, for example `report-data.ts` and `overview-data.ts`.
|
||||
|
||||
### Assessment
|
||||
|
||||
Naming is mostly not a blocking issue. The larger convention problem is scope drift: files whose names imply a focused responsibility but whose implementations have become mini-subsystems.
|
||||
|
||||
## Database Query Performance Assessment
|
||||
|
||||
### Strengths
|
||||
|
||||
- Several aggregation-heavy paths use SQL composition and `Promise.all` rather than purely sequential querying.
|
||||
- Overview data makes a meaningful attempt to cache expensive scope/filter work.
|
||||
|
||||
### Findings
|
||||
|
||||
- `src/features/reports/server/report-data.ts:126,247,308,386` repeatedly calls `getScopedEmployeeIds(...)` in multiple report builders, which risks repeated scope-resolution queries within one report/export request.
|
||||
- `src/features/reports/server/report-data.ts:314,567,621` mixes broad dataset fetching with export preparation and follow-up joins, making it harder to deduplicate query work.
|
||||
- `src/features/permission-management/server/user-permission-data.ts:375` relies on transactional app logic to preserve one-active-template behavior, while the file itself contains a TODO noting the absence of a database-level unique constraint.
|
||||
- Large export paths in reports include PDF and Excel generation in the same module as query logic, increasing the chance of over-fetching and memory-heavy processing.
|
||||
|
||||
### Assessment
|
||||
|
||||
The main database-performance risk is repeated query work in analytics/reporting flows rather than obvious N+1 loops in everyday CRUD pages. Reporting will likely be the first area to degrade under scale.
|
||||
|
||||
## Frontend Performance Assessment
|
||||
|
||||
### Strengths
|
||||
|
||||
- Shared data-table state management includes debounce support and URL-state integration.
|
||||
- Many pages are server-rendered first and hydrate into targeted client interactivity rather than fully client-side shells.
|
||||
|
||||
### Findings
|
||||
|
||||
- `src/features/training-records/components/training-record-form.tsx`, `src/features/online-lessons/components/online-lesson-form.tsx`, and `src/features/announcements/components/announcement-form.tsx` keep large interactive workflows entirely in single client components.
|
||||
- `src/features/reports/components/reports-page-content.tsx` contains nested render-time component definitions flagged by lint, which can cause unnecessary remounting and rerender churn.
|
||||
- The build output still contains many legacy/demo dashboard surfaces, which increases long-term client bundle pressure even if each page is route-split.
|
||||
- Heavy UI packages and legacy dashboard features remain installed even where they do not appear central to the TMS runtime.
|
||||
|
||||
### Assessment
|
||||
|
||||
Frontend performance risk is moderate. The biggest concerns are rerender breadth and bundle sprawl, not obviously broken runtime performance today.
|
||||
|
||||
## API Performance Assessment
|
||||
|
||||
### Strengths
|
||||
|
||||
- The production build completed successfully, which suggests current route composition is at least operational.
|
||||
- Many server modules already use batching through `Promise.all`.
|
||||
|
||||
### Findings
|
||||
|
||||
- `src/app/api/announcements/[id]/route.ts:88,162-623` performs multipart parsing, state transition branching, storage actions, and many audit log writes in one path.
|
||||
- The same route logs multiple events in sequence at `445,458,475,522,549,573,609,623`, and only one section uses `Promise.all` at `492`.
|
||||
- `src/app/api/online-lessons/[id]/route.ts` follows a similar content-workflow route shape, increasing the chance of repeated sequential I/O.
|
||||
- `src/lib/api-client.ts:41` routes internal feature services through HTTP fetch calls, which is acceptable for consistency but adds overhead versus direct server-side composition when both sides live in the same app.
|
||||
|
||||
### Assessment
|
||||
|
||||
API performance is acceptable for current scale, but workflow-heavy content endpoints are likely to become latency hotspots because they combine validation, persistence, storage, notification, and audit concerns in one request lifecycle.
|
||||
|
||||
## Dead Code Assessment
|
||||
|
||||
### Findings
|
||||
|
||||
- `npm run lint` reports unused imports and variables in active code:
|
||||
- `src/components/ui/duration-picker.tsx:13`
|
||||
- `src/features/online-lessons/components/online-lesson-form.tsx:121`
|
||||
- `src/features/organizers/components/organizers-page.tsx:31`
|
||||
- `src/features/reports/components/reports-page-content.tsx:120`
|
||||
- `src/app/api/online-lessons/[id]/route.ts:240`
|
||||
- `src/features/reports/server/report-data.ts:8,11,12`
|
||||
- The build output confirms many legacy/demo pages and features are still present in the shipped app tree.
|
||||
- `src/constants/mock-api.ts` and `src/constants/mock-api-users.ts` still exist and receive lint warnings, indicating legacy/demo residue remains in the repository.
|
||||
|
||||
### Assessment
|
||||
|
||||
Dead code is not overwhelming, but there is enough residue to create noise, hide real issues, and expand the maintenance surface beyond the actual TMS runtime.
|
||||
|
||||
## Testability Assessment
|
||||
|
||||
### Strengths
|
||||
|
||||
- Some data access is already separated into feature-level server modules.
|
||||
- Shared hooks and shared auth helpers create a starting point for isolated testing.
|
||||
|
||||
### Findings
|
||||
|
||||
- `src/features/reports/server/report-data.ts` couples filtering, authorization scoping, querying, table shaping, and export generation in one file.
|
||||
- `src/features/overview/server/overview-data.ts` couples multiple dashboard datasets and policy/filter logic in one file despite good cache usage.
|
||||
- `src/app/api/announcements/[id]/route.ts` mixes multipart parsing, workflow policy, storage, database updates, notifications, and auditing, which is difficult to unit test without broad integration scaffolding.
|
||||
- `src/hooks/use-data-table.ts` is valuable shared infrastructure, but its breadth makes it a likely source of subtle regressions and harder isolated testing.
|
||||
|
||||
### Assessment
|
||||
|
||||
Testability is the clearest structural weakness after maintainability. The codebase is not short on logic separation everywhere, but its heaviest business flows are still assembled in units that are larger than ideal for focused tests.
|
||||
|
||||
## Findings by Severity
|
||||
|
||||
### High
|
||||
|
||||
1. Oversized workflow handlers and data modules concentrate too many responsibilities.
|
||||
- Evidence: `src/app/api/announcements/[id]/route.ts` (683 lines), `src/features/reports/server/report-data.ts` (759 lines), `src/features/overview/server/overview-data.ts` (773 lines), `src/features/training-records/components/training-record-form.tsx` (765 lines).
|
||||
|
||||
2. Reporting paths repeat scope-resolution work and combine querying with export generation.
|
||||
- Evidence: `src/features/reports/server/report-data.ts:126,247,308,386,766-822`.
|
||||
|
||||
3. Core workflow-heavy client forms are broad client boundaries with large rerender surfaces and low testability.
|
||||
- Evidence: `training-record-form.tsx`, `online-lesson-form.tsx`, `announcement-form.tsx`.
|
||||
|
||||
### Medium
|
||||
|
||||
1. Error handling is not yet consistently typed across service and client boundaries.
|
||||
- Evidence: `src/lib/api-client.ts:57`.
|
||||
|
||||
2. Database integrity for active permission-template assignment relies on app logic instead of a DB constraint.
|
||||
- Evidence: TODO in `src/features/permission-management/server/user-permission-data.ts`, transaction at `375`.
|
||||
|
||||
3. Duplicate date formatting and workflow status handling remain spread across many routes and components.
|
||||
|
||||
4. Default lint workflow allows warnings to pass, and no dedicated `typecheck` script exists.
|
||||
|
||||
5. Legacy/demo modules continue to expand bundle and maintenance surface.
|
||||
|
||||
### Low
|
||||
|
||||
1. Small unused variables/imports remain in active modules.
|
||||
2. Naming/convention drift exists in a few UI utility files and legacy/demo areas.
|
||||
|
||||
## Quick Wins
|
||||
|
||||
1. Add a dedicated `typecheck` script and run it in CI with `lint:strict`.
|
||||
2. Remove current unused imports/variables reported by `oxlint`.
|
||||
3. Centralize date formatting helpers for API payloads and Thai display formatting.
|
||||
4. Extract nested render-time component definitions flagged by lint.
|
||||
5. Split report export formatting from report data retrieval.
|
||||
|
||||
## Structural Improvements
|
||||
|
||||
1. Break `report-data.ts` into scoped-query, dataset-builder, and export-renderer modules.
|
||||
2. Break `overview-data.ts` into dashboard-section loaders plus a smaller composition layer.
|
||||
3. Move announcement and online-lesson workflow transition logic into dedicated domain services invoked by thin route handlers.
|
||||
4. Split large client forms into smaller sections with isolated mutation/upload adapters.
|
||||
5. Introduce a typed application error model shared by route handlers, service callers, and client UI.
|
||||
6. Audit legacy/demo routes and dependencies for removal or quarantine outside the production dashboard surface.
|
||||
|
||||
## Recommended Refactor Order
|
||||
|
||||
1. Reports server path
|
||||
- Highest combined maintainability, query-efficiency, and export-performance payoff.
|
||||
|
||||
2. Announcement and online-lesson workflow handlers
|
||||
- Highest API complexity and side-effect density.
|
||||
|
||||
3. Large client workflow forms
|
||||
- Best follow-up once workflow services are thinner and easier to consume.
|
||||
|
||||
4. Error model and date-format normalization
|
||||
- Broad cross-cutting cleanup with good leverage.
|
||||
|
||||
5. Legacy/demo surface reduction
|
||||
- Useful after core runtime paths are stabilized.
|
||||
|
||||
## Commands Executed and Results
|
||||
|
||||
- `npx tsc --noEmit`
|
||||
- Passed.
|
||||
- `npm run lint`
|
||||
- Passed with warnings. Key warnings included unused imports/variables and `react(no-unstable-nested-components)`.
|
||||
- `npm run build`
|
||||
- Passed on Next.js `16.2.6` with production build and static page generation completed successfully.
|
||||
- Multiple `rg` and `Select-String` read-only scans
|
||||
- Used to inspect large files, duplicate patterns, lint targets, and route complexity.
|
||||
|
||||
## Files Reviewed
|
||||
|
||||
- `package.json`
|
||||
- `tsconfig.json`
|
||||
- `src/lib/api-client.ts`
|
||||
- `src/lib/auth/session.ts`
|
||||
- `src/lib/query-client.ts`
|
||||
- `src/hooks/use-data-table.ts`
|
||||
- `src/features/reports/server/report-data.ts`
|
||||
- `src/features/overview/server/overview-data.ts`
|
||||
- `src/features/permission-management/server/user-permission-data.ts`
|
||||
- `src/features/reports/components/reports-page-content.tsx`
|
||||
- `src/features/training-records/components/training-record-form.tsx`
|
||||
- `src/features/online-lessons/components/online-lesson-form.tsx`
|
||||
- `src/features/announcements/components/announcement-form.tsx`
|
||||
- `src/app/api/announcements/[id]/route.ts`
|
||||
- `src/app/api/online-lessons/[id]/route.ts`
|
||||
- `src/components/ui/calendar.tsx`
|
||||
- `src/components/ui/duration-picker.tsx`
|
||||
- `src/components/ui/slider.tsx`
|
||||
- `src/constants/mock-api.ts`
|
||||
- `src/constants/mock-api-users.ts`
|
||||
|
||||
## Areas Not Verified
|
||||
|
||||
- No runtime profiling was performed in a browser or with production tracing, so rerender cost and interaction latency were assessed from structure and lint/tool evidence rather than flamegraphs.
|
||||
- No database execution plans were captured, so index/selectivity findings are risk-based rather than EXPLAIN-verified.
|
||||
- No bundle analyzer output was generated, so bundle-size findings are based on route inventory and dependency surface, not measured chunk sizes.
|
||||
- No automated test suite review was performed because this phase focused on code quality, static analysis, and build/runtime structure.
|
||||
370
docs/reviews/phase-6-security-audit.md
Normal file
370
docs/reviews/phase-6-security-audit.md
Normal file
@@ -0,0 +1,370 @@
|
||||
# Phase 6 - Security Audit
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The application has a solid baseline in two important areas: server-side authorization is used broadly across route handlers, and many sensitive workflow actions are audited. The main security risks are not widespread missing auth checks, but a smaller set of high-impact gaps in file delivery, request hardening, dependency posture, and abuse protection.
|
||||
|
||||
The most serious application-layer issue is direct public exposure of uploaded online-lesson assets. Uploaded video, attachment, and thumbnail files are stored under `public/uploads/...`, serialized back to clients as direct public URLs, and rendered directly in the UI. This bypasses the guarded download pattern that already exists in other parts of the system.
|
||||
|
||||
The second major risk is the import surface. Both spreadsheet import endpoints accept files based only on extension presence, read the entire file into memory, and rely on `xlsx`, which `npm audit` currently reports with high-severity advisories. Combined with the absence of rate limiting and explicit file-size controls on import endpoints, this increases the risk of denial-of-service and unsafe spreadsheet parsing.
|
||||
|
||||
## Threat Surface Inventory
|
||||
|
||||
Primary security-sensitive surfaces reviewed:
|
||||
|
||||
- Auth.js credential login flow
|
||||
- Session and active-organization switching
|
||||
- Server-side authorization helpers
|
||||
- Permission-management APIs
|
||||
- Announcement, online-lesson, and certificate file handling
|
||||
- Training-record and employee spreadsheet imports
|
||||
- Report export endpoints
|
||||
- Notification and workflow action endpoints
|
||||
- Next.js runtime/configuration and proxy perimeter
|
||||
|
||||
## Authentication Assessment
|
||||
|
||||
### Strengths
|
||||
|
||||
- `src/auth.ts:25-33` validates login input and uses bcrypt password verification.
|
||||
- `src/auth.ts:57,81,103,137` records login success and failure events in audit logs.
|
||||
- Self-service registration is disabled in `src/app/api/auth/register/route.ts`.
|
||||
- Session enforcement is centralized through `requireSession()`.
|
||||
|
||||
### Findings
|
||||
|
||||
- `src/auth.ts:33` falls back to `dev-only-auth-secret-change-me` outside production. This is acceptable for local development but risky if a non-production environment is internet-exposed with weak secret hygiene.
|
||||
- `src/auth.ts:36` sets `trustHost: true`. This is common in proxied deployments, but it raises deployment sensitivity if host/header forwarding is not tightly controlled upstream.
|
||||
- No brute-force controls, lockout logic, or request throttling were found around credential login.
|
||||
- Password policy is minimal: sign-in and auth forms enforce only `min(8)`, and `src/features/users/schemas/user.ts` does not enforce password strength itself.
|
||||
|
||||
### Assessment
|
||||
|
||||
Authentication is functionally correct, but it lacks abuse resistance and stronger operational guardrails.
|
||||
|
||||
## Authorization Assessment
|
||||
|
||||
### Strengths
|
||||
|
||||
- Broad route coverage uses server-side guards such as `requireOrganizationAccess`, `requirePermission`, `requireHRD`, `requireAllPermissions`, and `requireSuperAdminOrganizationAccess`.
|
||||
- Permission-management endpoints are protected at the route-handler level even though they are not included in the proxy perimeter list.
|
||||
- Download routes for announcement attachments and training-record certificates enforce organization-aware access checks before reading files from disk.
|
||||
|
||||
### Findings
|
||||
|
||||
- `src/proxy.ts:4-18,41-56` only protects a subset of API prefixes at the perimeter. Notably, online-lesson and permission-management APIs are not listed there, even though their handlers do perform server-side checks. This is a defense-in-depth gap rather than a direct bypass.
|
||||
- `src/lib/auth/session.ts` and `src/app/api/organizations/active/route.ts` can auto-create admin memberships for super-admin/HRD organization access. This may be intended, but it is a privileged side effect inside an access path and should be treated carefully.
|
||||
- Permission-management routes use `requireSuperAdminOrganizationAccess()`, which currently depends on the same organization-access helper that can create fallback memberships.
|
||||
|
||||
### Assessment
|
||||
|
||||
Authorization is stronger than average for an admin dashboard. The main risk is not broken route protection by default, but privileged side effects embedded inside access-resolution flows.
|
||||
|
||||
## Input Validation Assessment
|
||||
|
||||
### Strengths
|
||||
|
||||
- Core workflow routes use Zod or equivalent server-side validation.
|
||||
- Online-lesson URLs are constrained to `http/https` via schema validation.
|
||||
- Route params are commonly parsed and rejected when numeric IDs are invalid.
|
||||
|
||||
### Findings
|
||||
|
||||
- `src/app/api/training-records/import/route.ts:228-236` and `src/app/api/import-employees/route.ts:460-474` only verify that the upload is a `File` and that the name ends with `.xlsx`; no explicit file-size or MIME validation is enforced before parsing.
|
||||
- Import endpoints load the full spreadsheet with `await file.arrayBuffer()` before further validation.
|
||||
- Several workflow routes perform large amounts of procedural validation inline inside route handlers, which makes consistency harder to maintain.
|
||||
|
||||
### Assessment
|
||||
|
||||
Input validation is decent for routine CRUD, but weaker on large-file and batch-import surfaces.
|
||||
|
||||
## Injection Assessment
|
||||
|
||||
### Strengths
|
||||
|
||||
- Most database access uses Drizzle query builders and parameterized expressions.
|
||||
- No command-execution patterns or child-process usage were found in application runtime paths.
|
||||
- Storage helpers sanitize path segments and verify resolved paths stay under upload roots.
|
||||
|
||||
### Findings
|
||||
|
||||
- `npm audit` reports a high-severity advisory for `drizzle-orm` regarding SQL injection via improperly escaped SQL identifiers in versions `<0.45.2`.
|
||||
- `src/features/reports/server/report-data.ts:446-555` uses raw SQL composition for reporting. The visible usage appears parameterized, but this area should be considered high-scrutiny because it is the most SQL-heavy path in the app.
|
||||
- `npm audit` reports high-severity advisories for `xlsx`, which is used to parse untrusted spreadsheet uploads.
|
||||
- CSV export exists in `src/app/api/audit-logs/export/route.ts`; this phase did not fully verify whether cells beginning with spreadsheet formula prefixes are neutralized.
|
||||
|
||||
### Assessment
|
||||
|
||||
There is no clear app-authored SQL injection bug in the reviewed code, but dependency-level injection risk is real and currently unresolved.
|
||||
|
||||
## XSS Assessment
|
||||
|
||||
### Strengths
|
||||
|
||||
- Announcement content, review comments, and online-lesson descriptions are rendered as plain text with `whitespace-pre-wrap`, not injected HTML.
|
||||
- No user-content Markdown or rich-text renderer was found in the reviewed content workflows.
|
||||
- The single `dangerouslySetInnerHTML` usage in `src/components/ui/chart.tsx` is used to emit generated CSS variables, not user-provided rich text.
|
||||
|
||||
### Findings
|
||||
|
||||
- Online-lesson external video URLs are rendered into outbound links and embed decisions. The code restricts embeddable iframes to recognized YouTube hosts, which is good, but general outbound URLs are still allowed for non-embeddable links.
|
||||
- No repo-level Content Security Policy was found in `next.config.ts` or related runtime config.
|
||||
|
||||
### Assessment
|
||||
|
||||
Direct stored-XSS exposure appears low in the reviewed flows. The larger XSS concern is missing browser-side hardening such as CSP.
|
||||
|
||||
## CSRF & Request Protection Assessment
|
||||
|
||||
### Strengths
|
||||
|
||||
- State-changing operations generally use non-GET methods.
|
||||
- Auth is session-based and routed through Auth.js rather than custom ad hoc cookies.
|
||||
|
||||
### Findings
|
||||
|
||||
- No explicit CSRF token validation, origin checks, or referer checks were found in state-changing route handlers.
|
||||
- No centralized request-hardening middleware was found for mutation endpoints.
|
||||
- Because cookie behavior is library-managed, SameSite/secure cookie posture was not explicitly verified from local code.
|
||||
|
||||
### Assessment
|
||||
|
||||
This is primarily a hardening gap, not a confirmed exploit in the reviewed code. Still, the app currently relies heavily on framework defaults and browser cookie behavior rather than explicit anti-CSRF controls.
|
||||
|
||||
## File Upload Assessment
|
||||
|
||||
### High-risk finding
|
||||
|
||||
- `src/lib/online-lesson-storage.ts:36-86` writes uploaded lesson assets to `public/uploads/online-lessons/...` and returns direct public URLs.
|
||||
- `src/features/online-lessons/server/online-lesson-data.ts:51,53,55` serializes those direct file URLs to clients unchanged.
|
||||
- `src/features/online-lessons/components/online-lesson-detail-page.tsx:198-200,284-290,328-336` renders those URLs directly in `<img>`, `<video>`, and download/open links.
|
||||
|
||||
Impact:
|
||||
|
||||
- Any user or external party who obtains one of these URLs can fetch the file directly from the public assets path without passing through authorization checks.
|
||||
- This creates a real broken-access-control and data-exposure risk for uploaded lesson media and attachments.
|
||||
|
||||
### Additional findings
|
||||
|
||||
- `src/lib/announcement-storage.ts` and `src/lib/certificate-storage.ts` also store files under `public/uploads/...` and create direct public paths, but the primary application serializers now prefer guarded API download URLs instead of returning the raw storage path to clients.
|
||||
- No virus scanning, content scanning, or magic-byte verification was found for uploaded files.
|
||||
- Online-lesson attachments permit several document/archive formats, including ZIP, but are still served from public paths.
|
||||
|
||||
### Assessment
|
||||
|
||||
File upload handling is the most important application-layer security weakness in the current codebase, especially for online-lesson assets.
|
||||
|
||||
## Sensitive Data Assessment
|
||||
|
||||
### Strengths
|
||||
|
||||
- Many protected download responses use `Cache-Control: private, no-store`.
|
||||
- Permission and organization access is checked server-side before reading many sensitive records.
|
||||
|
||||
### Findings
|
||||
|
||||
- Uploaded lesson assets are publicly reachable by URL, which can expose internal training content and attachments.
|
||||
- Login failure audit logs in `src/auth.ts` persist identifiers and, in one case, user email inside audit payloads. This may be acceptable operationally, but it increases PII footprint inside logs.
|
||||
- User and employee tables render email, employee code, organization, and department information broadly across admin UIs; this is expected for the app, but it increases the need for stronger export and audit controls.
|
||||
|
||||
### Assessment
|
||||
|
||||
The biggest sensitive-data problem is file exposure, not routine JSON overexposure in the reviewed API routes.
|
||||
|
||||
## Security Headers Assessment
|
||||
|
||||
### Findings
|
||||
|
||||
- No `headers()` configuration was found in `next.config.ts`.
|
||||
- No explicit Content Security Policy, `X-Frame-Options`, `X-Content-Type-Options`, `Referrer-Policy`, `Permissions-Policy`, or HSTS configuration was found in reviewed app config.
|
||||
- Download routes set `Cache-Control`, but broader site-level browser hardening appears absent.
|
||||
|
||||
### Assessment
|
||||
|
||||
Browser-side security headers are currently under-configured.
|
||||
|
||||
## Rate Limiting Assessment
|
||||
|
||||
### Findings
|
||||
|
||||
- No rate-limiting or throttling implementation was found for login, imports, exports, uploads, or workflow actions.
|
||||
- This directly affects:
|
||||
- Credential login
|
||||
- Spreadsheet import endpoints
|
||||
- Report export endpoint
|
||||
- File upload endpoints
|
||||
- Notification mutation endpoints
|
||||
|
||||
### Assessment
|
||||
|
||||
Rate limiting is a notable gap and materially increases brute-force and denial-of-service risk.
|
||||
|
||||
## Audit Log Assessment
|
||||
|
||||
### Strengths
|
||||
|
||||
- Authentication success/failure is logged.
|
||||
- Permission changes have dedicated audit infrastructure.
|
||||
- Import operations log audit entries.
|
||||
- Announcement and workflow transitions show extensive audit logging.
|
||||
|
||||
### Findings
|
||||
|
||||
- `src/app/api/reports/export/route.ts` does not log report exports, even though exports can contain organization-wide training and employee data.
|
||||
- This creates an audit gap for sensitive data extraction.
|
||||
- This phase did not verify logout logging or sensitive-record read auditing beyond the reviewed routes.
|
||||
|
||||
### Assessment
|
||||
|
||||
Audit coverage is good for mutations, but weaker for data-export visibility.
|
||||
|
||||
## Dependency & Configuration Assessment
|
||||
|
||||
### `npm audit` results
|
||||
|
||||
- Total vulnerabilities: `11`
|
||||
- High: `3`
|
||||
- Moderate: `8`
|
||||
|
||||
Key findings:
|
||||
|
||||
- `drizzle-orm`
|
||||
- High severity advisory reported by `npm audit`.
|
||||
- `xlsx`
|
||||
- High severity advisories reported by `npm audit`.
|
||||
- Especially relevant because the app parses untrusted spreadsheet uploads in two endpoints.
|
||||
- `sort-by` / `object-path`
|
||||
- High/moderate advisory chain reported by `npm audit`.
|
||||
- `@opentelemetry/core` and related packages
|
||||
- Moderate memory-allocation advisory reported by `npm audit`.
|
||||
|
||||
### Configuration findings
|
||||
|
||||
- `src/auth.ts:33` uses a development fallback secret.
|
||||
- `src/auth.ts:36` sets `trustHost: true`.
|
||||
- `next.config.ts` removes console logs in production, which is good, but does not add security headers.
|
||||
|
||||
### Assessment
|
||||
|
||||
Dependency security posture needs attention now, especially because the vulnerable packages are in active code paths rather than unused tooling only.
|
||||
|
||||
## Business Logic Security Assessment
|
||||
|
||||
### Strengths
|
||||
|
||||
- Review/publish/archive flows check content status and permissions before transition.
|
||||
- Training-record certificate download checks ownership/scope through the parent training record.
|
||||
|
||||
### Findings
|
||||
|
||||
- Privileged organization switching can materialize new admin memberships as a side effect.
|
||||
- Report exports are permission-checked, but not audited.
|
||||
- This phase did not find a clear "publish without approve" bypass in the reviewed content workflows, but the workflow handlers are large enough that they deserve dedicated regression tests.
|
||||
|
||||
### Assessment
|
||||
|
||||
Business-logic security is generally thoughtful, but several privileged flows remain too implicit and too large for easy verification.
|
||||
|
||||
## Findings by Severity
|
||||
|
||||
### High
|
||||
|
||||
1. Online-lesson uploaded assets are publicly accessible by direct URL.
|
||||
- Evidence: [online-lesson-storage.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/lib/online-lesson-storage.ts:36), [online-lesson-data.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/features/online-lessons/server/online-lesson-data.ts:51), [online-lesson-detail-page.tsx](/D:/WY-2569/HRD/training-system-minimal-refactor/src/features/online-lessons/components/online-lesson-detail-page.tsx:284)
|
||||
|
||||
2. Spreadsheet import endpoints accept unbounded `.xlsx` uploads and parse them with a dependency that currently has high-severity advisories.
|
||||
- Evidence: [training-record import route](/D:/WY-2569/HRD/training-system-minimal-refactor/src/app/api/training-records/import/route.ts:228), [employee import route](/D:/WY-2569/HRD/training-system-minimal-refactor/src/app/api/import-employees/route.ts:460), `npm audit` finding for `xlsx`
|
||||
|
||||
3. Active dependency vulnerabilities include high-severity advisories in `drizzle-orm` and `xlsx`.
|
||||
- Evidence: `npm audit --json`
|
||||
|
||||
### Medium
|
||||
|
||||
1. No brute-force or rate-limiting protections were found for login, upload, import, or export endpoints.
|
||||
2. No repo-level security headers configuration was found.
|
||||
3. No explicit CSRF/origin validation layer was found for mutation endpoints.
|
||||
4. Report exports are not audit-logged.
|
||||
5. Privileged organization access can create admin memberships as a side effect.
|
||||
6. Login audit logs increase sensitive identifier/email footprint inside logs.
|
||||
|
||||
### Low
|
||||
|
||||
1. Proxy perimeter coverage is incomplete, though server-side route guards still protect the affected APIs.
|
||||
2. Development fallback secret is acceptable locally, but risky if misused in a shared environment.
|
||||
|
||||
## Quick Wins
|
||||
|
||||
1. Stop returning direct public URLs for online-lesson files; serve them through guarded download/stream routes.
|
||||
2. Add strict file-size limits and MIME checks to both spreadsheet import endpoints before `arrayBuffer()` parsing.
|
||||
3. Add rate limiting for `/api/auth/[...nextauth]`, import, export, and upload routes.
|
||||
4. Add export audit logging for reports.
|
||||
5. Add baseline browser security headers in Next.js config or edge middleware.
|
||||
|
||||
## Structural Improvements
|
||||
|
||||
1. Move all sensitive file delivery to authenticated route handlers and eliminate `public/uploads` for protected assets.
|
||||
2. Introduce a centralized request-hardening layer for origin checking, abuse controls, and common mutation protections.
|
||||
3. Separate privileged organization-access resolution from membership-creation side effects.
|
||||
4. Add dedicated security tests for workflow transitions, file access, and export scope enforcement.
|
||||
5. Review and remediate vulnerable dependencies in a controlled upgrade track, especially `xlsx` and `drizzle-orm`.
|
||||
|
||||
## Recommended Remediation Order
|
||||
|
||||
1. Online-lesson file exposure
|
||||
- Highest real-world access-control risk.
|
||||
|
||||
2. Import surface hardening
|
||||
- Add file-size checks and reduce exposure from vulnerable spreadsheet parsing.
|
||||
|
||||
3. Dependency remediation
|
||||
- Prioritize packages in active parsing/data-access paths.
|
||||
|
||||
4. Rate limiting and request-hardening
|
||||
- Best leverage across login, import, export, and uploads.
|
||||
|
||||
5. Security headers and audit-gap cleanup
|
||||
- Important defense-in-depth after the higher-risk access issues are contained.
|
||||
|
||||
## Commands Executed and Results
|
||||
|
||||
- `rg` and `Select-String` across auth, proxy, storage, API routes, and config
|
||||
- Used to inspect auth guards, upload paths, security headers, and audit coverage.
|
||||
- `npm audit --json`
|
||||
- Reported `11` vulnerabilities total: `3` high and `8` moderate.
|
||||
|
||||
## Files Reviewed
|
||||
|
||||
- `src/auth.ts`
|
||||
- `src/proxy.ts`
|
||||
- `src/lib/auth/session.ts`
|
||||
- `src/lib/auth/authorization.server.ts`
|
||||
- `next.config.ts`
|
||||
- `src/lib/announcement-storage.ts`
|
||||
- `src/lib/online-lesson-storage.ts`
|
||||
- `src/lib/certificate-storage.ts`
|
||||
- `src/app/api/auth/register/route.ts`
|
||||
- `src/app/api/organizations/active/route.ts`
|
||||
- `src/app/api/reports/export/route.ts`
|
||||
- `src/app/api/training-records/import/route.ts`
|
||||
- `src/app/api/import-employees/route.ts`
|
||||
- `src/app/api/announcements/[id]/attachment/route.ts`
|
||||
- `src/app/api/training-records/[id]/certificates/route.ts`
|
||||
- `src/app/api/training-records/[id]/certificates/[certificateId]/download/route.ts`
|
||||
- `src/app/api/user-permissions/route.ts`
|
||||
- `src/app/api/permission-templates/route.ts`
|
||||
- `src/app/api/permissions/catalog/route.ts`
|
||||
- `src/features/announcements/server/announcement-data.ts`
|
||||
- `src/features/online-lessons/server/online-lesson-data.ts`
|
||||
- `src/features/training-records/server/training-record-data.ts`
|
||||
- `src/features/online-lessons/schemas/online-lesson.ts`
|
||||
- `src/features/announcements/components/announcement-view-page.tsx`
|
||||
- `src/features/online-lessons/components/online-lesson-detail-page.tsx`
|
||||
- `src/components/ui/chart.tsx`
|
||||
- `src/features/users/schemas/user.ts`
|
||||
|
||||
## Areas Not Verified
|
||||
|
||||
- This phase did not perform live penetration testing, browser interception, or runtime CSRF validation against a running deployment.
|
||||
- Cookie flags were not verified from actual HTTP responses.
|
||||
- This phase did not exhaustively review every route handler in the repository; it focused on the highest-risk auth, permission, upload, import, and export surfaces.
|
||||
- Dependency advisories were taken from local `npm audit` output and were not independently triaged against actual reachable exploit paths in this specific deployment model.
|
||||
412
docs/reviews/phase-7-production-readiness-review.md
Normal file
412
docs/reviews/phase-7-production-readiness-review.md
Normal file
@@ -0,0 +1,412 @@
|
||||
# Phase 7 - Production Readiness Review
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The codebase is buildable and can likely be deployed in a controlled environment, but it is not yet production-ready as an operational system. The strongest signals in favor of readiness are that typecheck, lint, and production build all succeed, Drizzle migrations exist in source control, Docker artifacts exist, and Sentry instrumentation is wired in.
|
||||
|
||||
The strongest signals against readiness are operational rather than purely code-level: there is no real CI pipeline in the repository, no automated test suite, no health-check endpoint, no documented backup/restore process, no documented rollback runbook, and major documentation drift remains from the original starter template. On top of that, the unresolved high-risk security issue from Phase 6 around public online-lesson assets should be treated as a production blocker.
|
||||
|
||||
Production decision: **No-Go** in the current state.
|
||||
|
||||
## Build Readiness
|
||||
|
||||
### Verified commands
|
||||
|
||||
- `npm run lint`
|
||||
- Passed with warnings.
|
||||
- `npx tsc --noEmit`
|
||||
- Passed.
|
||||
- `npm run build`
|
||||
- Passed on Next.js `16.2.6`.
|
||||
- `npm test`
|
||||
- Failed because no `test` script exists.
|
||||
|
||||
### Strengths
|
||||
|
||||
- `package.json` includes `build`, `start`, `lint`, migration, and seed scripts.
|
||||
- `.nvmrc` pins Node major version `22`.
|
||||
- Production build completed successfully and emitted the expected standalone-compatible output tree.
|
||||
|
||||
### Findings
|
||||
|
||||
- There is no `test` script in `package.json`.
|
||||
- There is no dedicated `typecheck` script in `package.json`, even though `tsc` itself passes.
|
||||
- `npm run lint` passes only with warnings; the default path does not gate warning-free readiness.
|
||||
- Production `start` script exists, but this review did not perform a live runtime smoke test of `next start`.
|
||||
|
||||
### Assessment
|
||||
|
||||
**Partially Ready**. Buildability is good, but release gating is incomplete.
|
||||
|
||||
## Environment Readiness
|
||||
|
||||
### Strengths
|
||||
|
||||
- `env.example.txt` documents a minimal environment contract:
|
||||
- `AUTH_SECRET`
|
||||
- `DATABASE_URL`
|
||||
- optional Sentry variables
|
||||
- `BUILD_STANDALONE`
|
||||
- `drizzle.config.ts` explicitly fails when `DATABASE_URL` is missing.
|
||||
|
||||
### Findings
|
||||
|
||||
- No central runtime environment validation schema was found.
|
||||
- Missing-variable behavior is inconsistent across the app; some values fail fast while others silently fall back.
|
||||
- `src/auth.ts` still contains a non-production fallback secret value (`dev-only-auth-secret-change-me`).
|
||||
- README and deployment docs still reference Clerk-oriented variables and setup, while the app now uses Auth.js.
|
||||
- Only one generic example env file is present; no explicit production/UAT/test environment matrix or variable ownership guide was found.
|
||||
|
||||
### Assessment
|
||||
|
||||
**Partially Ready**. Basic env documentation exists, but production-grade validation and documentation are incomplete.
|
||||
|
||||
## Database Readiness
|
||||
|
||||
### Strengths
|
||||
|
||||
- The repository contains `20` migration files plus Drizzle metadata journal history.
|
||||
- The schema defines many useful unique indexes and constraints.
|
||||
- Soft-delete and active/inactive patterns are present for key domains such as organizations and permission templates.
|
||||
- Transaction usage exists in several write-sensitive paths.
|
||||
|
||||
### Findings
|
||||
|
||||
- No verified migration-status check against a real target database was performed in this review.
|
||||
- No documented production migration runbook or rollback runbook was found.
|
||||
- No documented backup frequency, restore procedure, RPO, or RTO was found.
|
||||
- Phase 5 already identified at least one important integrity gap still enforced at app level instead of DB level: active user permission template assignment uniqueness.
|
||||
- `src/lib/db.ts` does not fail fast on an empty `DATABASE_URL`; it passes `""` into the postgres client constructor and relies on downstream behavior.
|
||||
|
||||
### Assessment
|
||||
|
||||
**Partially Ready** for schema-managed deployment, **Not Ready** for production database operations discipline.
|
||||
|
||||
## Deployment Readiness
|
||||
|
||||
### Strengths
|
||||
|
||||
- Both `Dockerfile` and `Dockerfile.bun` exist.
|
||||
- Both Dockerfiles use multi-stage builds and non-root runtime users.
|
||||
- Standalone Next.js output mode is wired through `BUILD_STANDALONE=true`.
|
||||
- `docker-compose.yml` provides a local PostgreSQL service with a volume.
|
||||
|
||||
### Findings
|
||||
|
||||
- Dockerfiles and README deployment instructions still reference Clerk build args and sign-in/sign-up URLs, which are stale for the current Auth.js architecture.
|
||||
- `docker-compose.yml` health check uses `pg_isready -d mydatabase` while the configured database name is `training_system`, so the compose health check is incorrect.
|
||||
- No application health endpoint or readiness endpoint was found in `src/app/api`.
|
||||
- No reverse proxy, TLS, ingress, or persistent app-file-storage deployment guidance was found.
|
||||
- The app still writes protected uploads to local disk under `public/uploads/...`, which is not a robust production storage strategy and conflicts with multi-instance/container scaling.
|
||||
|
||||
### Assessment
|
||||
|
||||
**Partially Ready** for local/containerized development, **Not Ready** for production deployment standardization.
|
||||
|
||||
## CI/CD Readiness
|
||||
|
||||
### Findings
|
||||
|
||||
- `.github` contains only `FUNDING.yml`.
|
||||
- No GitHub Actions workflows, PR checks, deployment approvals, security scans, or rollback automation were found in the repository.
|
||||
- No evidence of branch protection requirements, environment protection rules, or release promotion workflow was found locally.
|
||||
|
||||
### Assessment
|
||||
|
||||
**Not Ready**. CI/CD readiness is one of the clearest production gaps.
|
||||
|
||||
## Logging Readiness
|
||||
|
||||
### Strengths
|
||||
|
||||
- Audit logging is implemented broadly for important business actions.
|
||||
- Sentry is integrated for global error capture and request-error capture.
|
||||
|
||||
### Findings
|
||||
|
||||
- Application logging is mostly `console.error` / `console.warn`, not structured logging.
|
||||
- No request ID or correlation ID propagation was found.
|
||||
- No documented log retention, log sinks, or redaction policy was found.
|
||||
- Sentry is configured with `sendDefaultPii: true` and `tracesSampleRate: 1`, which may be acceptable temporarily but needs explicit production policy decisions.
|
||||
|
||||
### Assessment
|
||||
|
||||
**Partially Ready** for basic debugging, **Not Ready** for mature production observability.
|
||||
|
||||
## Monitoring & Alerting Readiness
|
||||
|
||||
### Strengths
|
||||
|
||||
- Sentry server and client instrumentation are present.
|
||||
- `app/global-error.tsx` and overview error boundaries report exceptions to Sentry.
|
||||
|
||||
### Findings
|
||||
|
||||
- No health endpoint was found for uptime probes.
|
||||
- No metrics stack, alert channel configuration, uptime monitor definition, or error-budget/SLO documentation was found.
|
||||
- No documented monitoring for database connection health, disk usage, upload storage growth, CPU, or memory was found.
|
||||
|
||||
### Assessment
|
||||
|
||||
**Partially Ready** for exception capture, **Not Ready** for operational monitoring.
|
||||
|
||||
## Backup & Recovery Readiness
|
||||
|
||||
### Findings
|
||||
|
||||
- No backup documentation was found for database or uploaded files.
|
||||
- No restore procedure, restore test evidence, retention policy, encryption policy, RPO, or RTO was found.
|
||||
- No disaster-recovery or incident recovery runbook was found.
|
||||
|
||||
### Assessment
|
||||
|
||||
**Not Ready**.
|
||||
|
||||
## Operational Workflow Readiness
|
||||
|
||||
### Strengths
|
||||
|
||||
- Seed scripts exist for master data, super admin, and UAT data.
|
||||
- There is meaningful domain documentation around UAT flows and import scenarios.
|
||||
|
||||
### Findings
|
||||
|
||||
- No documented runbook was found for:
|
||||
- failed imports
|
||||
- notification failures
|
||||
- file storage exhaustion
|
||||
- user provisioning operations
|
||||
- permission correction under production incidents
|
||||
- emergency rollback
|
||||
- Several privileged behaviors, such as organization-switch side effects, remain implementation-defined rather than operationally documented.
|
||||
|
||||
### Assessment
|
||||
|
||||
**Partially Ready** for controlled internal support by developers, **Not Ready** for formal production operations.
|
||||
|
||||
## Testing Readiness
|
||||
|
||||
### Findings
|
||||
|
||||
- No unit/integration/API/end-to-end test suite files were found in the repository.
|
||||
- `npm test` fails because no `test` script exists.
|
||||
- Existing readiness evidence is largely manual and audit-driven:
|
||||
- UAT docs
|
||||
- seed-data docs
|
||||
- pre-UAT review
|
||||
- hardening review
|
||||
- Browser smoke testing is referenced in docs, but not fully automated.
|
||||
|
||||
### Assessment
|
||||
|
||||
**Not Ready** for production confidence at scale.
|
||||
|
||||
## Documentation Readiness
|
||||
|
||||
### Strengths
|
||||
|
||||
- The project has substantial internal documentation for architecture, UAT, reviews, and implementation history.
|
||||
- `docs/PROJECT_ARCHITECTURE.md` and `docs/AI_DEVELOPMENT_GUIDE.md` are useful engineering references.
|
||||
|
||||
### Findings
|
||||
|
||||
- Root `README.md` is heavily outdated and still describes the starter template, Clerk auth, billing/workspaces demo features, and generic SaaS positioning rather than the current TMS product.
|
||||
- Docker/README deployment instructions still reference Clerk-specific env vars and flows that no longer match the app.
|
||||
- No production deployment guide, backup guide, restore guide, or troubleshooting runbook was found.
|
||||
- No formal admin operations manual or release checklist was found.
|
||||
|
||||
### Assessment
|
||||
|
||||
**Partially Ready** for engineering context, **Not Ready** for production handoff documentation.
|
||||
|
||||
## Production Checklist
|
||||
|
||||
### Ready
|
||||
|
||||
- Production build compiles successfully.
|
||||
- TypeScript compile passes.
|
||||
- Basic env example file exists.
|
||||
- Drizzle migrations exist in source control.
|
||||
- Docker artifacts exist.
|
||||
- Sentry instrumentation exists.
|
||||
|
||||
### Partially Ready
|
||||
|
||||
- Linting
|
||||
- Passes with warnings, not clean.
|
||||
- Environment configuration
|
||||
- Documented minimally, but not centrally validated.
|
||||
- Database schema management
|
||||
- Migrations exist, but production migration/rollback procedure is undocumented.
|
||||
- Deployment packaging
|
||||
- Dockerfiles exist, but docs/config still have starter drift and no health endpoint.
|
||||
- Logging/monitoring
|
||||
- Sentry and audit logs exist, but no structured logging or alerting model is documented.
|
||||
- Operational workflow support
|
||||
- Some UAT/support docs exist, but no production runbooks.
|
||||
|
||||
### Not Ready
|
||||
|
||||
- Automated tests
|
||||
- CI/CD pipeline
|
||||
- Health/readiness checks
|
||||
- Backup and recovery process
|
||||
- Rollback runbook
|
||||
- Production-aligned root documentation
|
||||
- Resolution of known high-risk security issue from Phase 6
|
||||
|
||||
### Not Verified
|
||||
|
||||
- Real production start under load
|
||||
- Real migration execution against target environments
|
||||
- Real backup/restore drills
|
||||
- Actual hosting/network/TLS topology
|
||||
- Alert routing and on-call ownership
|
||||
|
||||
## Go-live Risk Assessment
|
||||
|
||||
### Production Blockers
|
||||
|
||||
1. No CI/CD workflow or enforced pre-merge release gate exists.
|
||||
2. No automated test suite exists, and `npm test` is not implemented.
|
||||
3. No health-check endpoint exists for uptime/readiness orchestration.
|
||||
4. No backup/restore/runbook documentation exists.
|
||||
5. Root deployment and setup documentation is stale and mismatched to the current Auth.js-based system.
|
||||
6. Phase 6 identified unresolved high-risk public file exposure for online-lesson assets.
|
||||
|
||||
### Must Fix Before Go-live
|
||||
|
||||
1. Resolve the public online-lesson asset exposure.
|
||||
2. Establish at least a minimal CI gate:
|
||||
- install
|
||||
- lint
|
||||
- typecheck
|
||||
- build
|
||||
3. Add at least a smoke-level automated test path or scripted release verification.
|
||||
4. Add a real health/readiness endpoint and document probe expectations.
|
||||
5. Produce backup, restore, and rollback procedures.
|
||||
6. Rewrite root README/deployment docs to match the actual TMS architecture and auth model.
|
||||
|
||||
### Can Fix After Go-live
|
||||
|
||||
1. Eliminate remaining lint warnings.
|
||||
2. Improve structured logging and correlation IDs.
|
||||
3. Expand monitoring from Sentry-only to broader service metrics and alerting.
|
||||
4. Clean up demo/template production surface after operational blockers are cleared.
|
||||
|
||||
### Operational Risk
|
||||
|
||||
- High. The system currently relies too heavily on manual knowledge and ad hoc developer intervention.
|
||||
|
||||
### Data Risk
|
||||
|
||||
- High. There is no documented backup/restore posture, and file storage remains local-disk oriented.
|
||||
|
||||
### Security Risk
|
||||
|
||||
- High. Phase 6 uncovered unresolved production-impacting file-access risk.
|
||||
|
||||
## Findings by Severity
|
||||
|
||||
### High
|
||||
|
||||
1. No CI/CD pipeline or PR gate exists in the repository.
|
||||
2. No automated test suite exists, and `npm test` is missing.
|
||||
3. No backup/restore/runbook evidence exists.
|
||||
4. Online-lesson public asset exposure from Phase 6 remains a production blocker.
|
||||
5. Deployment and root docs remain materially out of sync with the implemented Auth.js architecture.
|
||||
|
||||
### Medium
|
||||
|
||||
1. No health/readiness endpoint exists.
|
||||
2. Docker compose health check is misconfigured.
|
||||
3. Dockerfiles and README still reference Clerk-oriented build args and setup concepts.
|
||||
4. Logging is largely unstructured and lacks request correlation.
|
||||
5. Monitoring exists mainly through Sentry and not through a documented alerting model.
|
||||
6. Local-disk protected upload strategy is weak for multi-instance/container production.
|
||||
|
||||
### Low
|
||||
|
||||
1. Lint warnings remain.
|
||||
2. Environment validation is uneven rather than centrally enforced.
|
||||
|
||||
## Production Blockers
|
||||
|
||||
1. Fix the online-lesson file exposure identified in Phase 6.
|
||||
2. Add a real CI workflow with at least `lint`, `typecheck`, and `build`.
|
||||
3. Add a test strategy with executable scripts, even if initial coverage is smoke-level.
|
||||
4. Add health/readiness endpoints.
|
||||
5. Document backup, restore, and rollback procedures.
|
||||
6. Replace stale starter-template deployment/auth documentation.
|
||||
|
||||
## Recommended Remediation Order
|
||||
|
||||
1. Security blocker cleanup
|
||||
- Resolve public asset exposure before any production deployment discussion.
|
||||
|
||||
2. Release gate foundation
|
||||
- Add CI workflow and executable test command(s).
|
||||
|
||||
3. Operability baseline
|
||||
- Add health endpoint, startup smoke verification, and deployment docs.
|
||||
|
||||
4. Data safety baseline
|
||||
- Define backup, restore, retention, and rollback procedure.
|
||||
|
||||
5. Observability hardening
|
||||
- Improve logging, alerting, and request correlation.
|
||||
|
||||
6. Deployment/documentation cleanup
|
||||
- Remove remaining Clerk/starter assumptions from README and Docker guidance.
|
||||
|
||||
## Go / No-Go Recommendation
|
||||
|
||||
Recommendation: **No-Go**
|
||||
|
||||
Reason:
|
||||
|
||||
- The app is technically deployable, but not yet operationally production-ready.
|
||||
- Build readiness alone is not enough to offset the absence of CI, tests, health checks, recovery procedures, and production-accurate deployment documentation.
|
||||
- The unresolved high-risk security issue from Phase 6 should independently block production rollout.
|
||||
|
||||
## Commands Executed and Results
|
||||
|
||||
- `npm run lint`
|
||||
- Passed with warnings.
|
||||
- `npx tsc --noEmit`
|
||||
- Passed.
|
||||
- `npm run build`
|
||||
- Passed successfully on Next.js `16.2.6`.
|
||||
- `npm test`
|
||||
- Failed because no `test` script exists.
|
||||
- `rg`, `Get-Content`, `git ls-files`
|
||||
- Used to inspect deployment/config/docs/test/ops artifacts.
|
||||
|
||||
## Files Reviewed
|
||||
|
||||
- `README.md`
|
||||
- `env.example.txt`
|
||||
- `package.json`
|
||||
- `.nvmrc`
|
||||
- `.gitignore`
|
||||
- `Dockerfile`
|
||||
- `Dockerfile.bun`
|
||||
- `docker-compose.yml`
|
||||
- `drizzle.config.ts`
|
||||
- `drizzle/meta/_journal.json`
|
||||
- `src/lib/db.ts`
|
||||
- `src/instrumentation.ts`
|
||||
- `src/instrumentation-client.ts`
|
||||
- `next.config.ts`
|
||||
- `src/proxy.ts`
|
||||
- `docs/AI_DEVELOPMENT_GUIDE.md`
|
||||
- `docs/PROJECT_ARCHITECTURE.md`
|
||||
- `docs/pre-uat-readiness-review.md`
|
||||
- `.github/FUNDING.yml`
|
||||
|
||||
## Areas Not Verified
|
||||
|
||||
- This review did not execute a real container startup, reverse-proxy integration, or hosted deployment.
|
||||
- This review did not inspect actual production secret values or external secret-management systems.
|
||||
- This review did not verify real backup jobs, restore drills, or infrastructure monitoring dashboards.
|
||||
- This review did not perform runtime load, failover, or chaos testing.
|
||||
Reference in New Issue
Block a user