docs: sync drizzle task contracts

This commit is contained in:
2026-07-02 18:01:21 -07:00
parent 381233c675
commit ac06490bb9
5 changed files with 379 additions and 149 deletions

View File

@@ -1,57 +1,94 @@
# Next.js Full-Stack CRUD, RBAC, and Audit Logs
# Next.js Drizzle Full-Stack CRUD, RBAC, Audit Logs, and Admission Ops
## Goal
Turn the current static/local-storage Next.js app into a minimal real full-stack application for the first operational slice: account setup/login, built-in RBAC, elder profile CRUD, and auditable administrative actions.
Stabilize the current Next.js 15 application around the Drizzle/PostgreSQL persistence layer that has already been introduced, then finish the first operational slice and prepare a controlled Phase 2 path for bed/admission operations, tabs-based workspace navigation, and real chart components.
The first release should remove mock/local-only behavior from the core flow and persist data through server-side APIs so the app can be exercised end-to-end without browser localStorage state.
The near-term release should keep moving away from static/mock-only surfaces, but it must treat the existing Drizzle schema and migrations as the source of truth. JSON-file persistence is no longer the target architecture for this task.
## Confirmed Facts
- The repository is already a Next.js 15 App Router project with routes under `app/`.
- Protected app screens are currently guarded by `modules/auth/components/AuthGate.tsx`, which reads localStorage through `modules/auth/lib/local-auth.ts`.
- Login/register/setup UI exists in `modules/auth/components/AuthPanel.tsx`, but password input is not validated server-side and accounts are browser-local.
- The "老人档案" route at `app/(app)/app/elders/page.tsx` is currently a static `ModulePage`.
- The "权限设置" route at `app/(app)/app/settings/page.tsx` is currently a static `ModulePage`.
- The project has no installed database/auth dependencies such as Drizzle, PostgreSQL client, oRPC, Zod, or better-auth.
- Current `package.json` already supports `pnpm lint`, `pnpm type-check`, and `pnpm build`.
- Drizzle and PostgreSQL dependencies are installed: `drizzle-orm`, `drizzle-kit`, and `postgres`.
- Drizzle schema exists at `modules/core/server/schema.ts`, with migrations under `drizzle/`.
- The schema already includes accounts, sessions, organizations, memberships, roles, permissions, elders, rooms, beds, admissions, audit logs, system incidents, invitations, and system settings.
- `modules/core/server/db.ts` owns the Drizzle/PostgreSQL connection and requires `DATABASE_URL`.
- `modules/core/server/store.ts` is now a Drizzle-backed compatibility read model. `readData()` aggregates data from PostgreSQL, while `writeData()` and `updateData()` intentionally throw after the PostgreSQL migration.
- Server-side authentication, password hashing, HTTP-only session cookies, role seeding, permission checks, and audit logging are implemented around Drizzle.
- Elder CRUD APIs at `app/api/elders/*` write through Drizzle and can create an active admission when a bed is selected.
- Facility and admission routes exist for rooms, beds, and admissions, but the bed/admission UI is still mostly read-only and does not yet provide a complete operator workflow.
- The dashboard still contains hard-coded operational counters and hand-built chart-like progress bars.
- The app shell has a top-bar "入住" button, but it is not wired to a usable admission workflow.
- The screenshot feedback requires moving workspace-level navigation into tabs, removing oversized module intro blocks, and avoiding redundant header actions such as the extra "入住" affordance in the top bar.
- Current `package.json` already supports `pnpm lint`, `pnpm type-check`, `pnpm build`, `pnpm db:generate`, `pnpm db:migrate`, and `pnpm db:studio`.
## MVP Scope
- Implement a real server-side persistence layer using JSON files under a server-owned data directory for this milestone. This is not mock data: API mutations must write durable data on disk during local/runtime execution.
- Use Next.js Route Handlers for the first API surface instead of adding oRPC/Drizzle/better-auth in this milestone.
- Replace localStorage authentication with server-side account/session APIs and HTTP-only cookie sessions.
- Provide built-in roles and permission groups without user-defined custom role creation in this milestone.
- Implement full CRUD for elder profiles as the first business entity.
- Implement a permissions/settings page that shows accounts, roles, permission coverage, and audit logs from server data.
- Record audit log entries for login, logout, account creation, elder create/update/delete, and permission-sensitive denied actions.
- Use the existing Drizzle/PostgreSQL layer for all new and changed persistent business data.
- Keep Next.js Route Handlers as the current API surface for this milestone. Do not introduce oRPC or better-auth in this task unless a separate migration task is created.
- Preserve server-side account/session APIs, HTTP-only cookie sessions, built-in platform and organization roles, membership permissions, and audit logging.
- Complete full CRUD for elder profiles as the first business entity, backed by Drizzle.
- Complete basic bed and admission operations: list rooms/beds, show current occupancy, create occupancy/admission, support bed transfer, and keep bed/admission/elder status consistent in a transaction.
- Implement an operator-friendly bed/admission UI under the app workspace instead of exposing "create through API" placeholders.
- Add workspace tabs where the current UI needs secondary navigation, especially for operational pages that combine overview, records, and management views.
- Replace hard-coded counters on implemented areas with Drizzle-backed data.
- Use a standard React chart library for selected operational charts instead of hand-rolled visual bars where the visualization is meaningful and data-backed.
- Keep settings pages showing accounts, roles, permission coverage, organizations, status, and audit logs from server data.
- Record audit log entries for login, logout, account creation, elder create/update/delete, admission create/transfer/discharge, facility mutations, and permission-sensitive denied actions.
## Requirements
### Authentication
- Setup must create the first administrator account on the server.
- Setup must create the first platform administrator account, initial organization, initial organization membership, and cookie session on the server.
- Login must validate account credentials on the server and set an HTTP-only session cookie.
- Logout must clear the session cookie and record an audit event when possible.
- App routes must no longer depend on localStorage for authentication.
- App routes must not depend on localStorage for authentication.
### Roles and Permissions
- The system must include built-in roles:
- `admin`: full access to accounts, permissions, audit logs, and elder CRUD.
- `manager`: elder CRUD and audit log viewing, but no account/role administration.
- `caregiver`: elder read/update access for care-facing fields, no delete or account administration.
- `viewer`: read-only elder access.
- `platform_admin`: full platform access.
- `platform_operator`: platform organization/account operations without full security ownership.
- `platform_auditor`: platform read/audit access.
- `platform_ops`: platform operations and incident management.
- `org_admin`: full organization-level access.
- `manager`: elder, facility, admission, and audit operations inside an organization.
- `caregiver`: elder read/update plus read-only facility/admission access.
- `viewer`: read-only elder, facility, and admission access.
- Permission checks must run on the server for all protected APIs.
- UI navigation or controls may hide unavailable actions, but hidden UI must not be the only enforcement.
### Elder CRUD
- Users with permission can list, create, update, and delete elder profiles.
- Elder records must include at minimum: name, gender, birth date or age, care level, room/bed, status, primary contact, phone, medical notes, created/updated timestamps.
- Elder records must include at minimum: name, gender, age, care level, current room/bed when admitted, status, primary contact, phone, medical notes, created/updated timestamps.
- The elder page must show real server data and support create/edit/delete interactions.
- Invalid input must return structured API errors and show usable feedback in the UI.
### Bed and Admission Operations
- Operators with `facility:read` can view rooms, beds, occupancy status, current elder assignment, and admission history.
- Operators with `facility:manage` can create or update room and bed metadata where the UI exposes those controls.
- Operators with `admission:manage` can admit an elder to an available bed, transfer an active elder to another available bed, and discharge an elder from a bed.
- Admission mutations must be transactional: active admission records, bed statuses, and elder status must stay consistent.
- Bed assignment conflicts must be rejected with structured API errors.
- The UI must make common workflows discoverable without relying on raw API calls.
- The top app header should not contain redundant or dead "入住" controls. Admission actions should live inside the relevant bed/admission workspace.
### Workspace UI and Tabs
- The app shell should keep dense operational navigation and avoid large hero-style module introductions.
- Pages that combine multiple operator modes should use tabs or equivalent segmented navigation, not stacked explanatory sections.
- The screenshot feedback for notices/general workspace applies broadly: remove oversized first-screen intro blocks when they do not help an operator act, place secondary navigation tabs near the workspace header, and keep action controls local to the page.
- UI text should remain operational and data-oriented, not marketing copy.
### Charts
- Use a standard chart library for selected data-backed charts such as bed occupancy composition, admission activity, or status distribution.
- Avoid hand-coded fake chart bars for business metrics that should reflect persisted data.
- Keep charts lightweight, readable, and useful for operations; tables remain the primary surface for detailed records.
### Audit Logs
- Audit logs must be persisted server-side and visible in the settings page.
@@ -62,43 +99,57 @@ The first release should remove mock/local-only behavior from the core flow and
- API responses must use a consistent `{ success, reason, ... }` shape.
- Server-side data helpers must avoid browser APIs.
- All file persistence operations must be centralized so future Drizzle/Postgres migration has a single boundary to replace.
- Do not use hard-coded UI counters for the implemented CRUD/settings/audit areas once server data exists.
- New persistent mutations must use Drizzle queries or transactions through the server database boundary, not JSON file writes.
- `readData()` may remain temporarily as a compatibility read model, but new mutation code must not depend on `writeData()` or `updateData()`.
- Prefer domain-specific Drizzle query helpers over growing the compatibility read model for every new use case.
- Do not use hard-coded UI counters for implemented CRUD, settings, audit, bed, admission, or dashboard areas once server data exists.
- Routes or server components that depend on cookies/session or live database state must opt out of static caching where required.
### Quality
- Keep TypeScript strict without `any`, non-null assertions, or `@ts-ignore`/`@ts-expect-error`.
- Prefer Server Components for initial data loading and small Client Components only for forms/mutations.
- `pnpm lint`, `pnpm type-check`, and `pnpm build` should pass before marking implementation complete.
- `pnpm lint`, `pnpm type-check`, `pnpm build`, and relevant Drizzle migration validation should pass before marking implementation complete.
## Acceptance Criteria
- [ ] First-run setup creates a server-persisted admin account and redirects into the app.
- [ ] First-run setup creates a Drizzle-persisted platform admin account, initial organization, organization roles, membership, and cookie session, then redirects into the app.
- [ ] Login/logout uses server APIs and an HTTP-only cookie session, not localStorage.
- [ ] Visiting `/app/elders` while authenticated loads elder records from the server persistence layer.
- [ ] Visiting `/app/elders` while authenticated loads elder records from PostgreSQL through the server layer.
- [ ] An authorized user can create, edit, and delete elder records from the UI, and changes survive page reloads.
- [ ] Unauthorized role attempts against protected elder/account/audit APIs return a forbidden response and create audit log entries.
- [ ] `/app/settings` displays built-in roles, account list, and recent audit log entries from server data.
- [ ] Audit logs are written for account creation, login, logout, elder create/update/delete, and denied permission checks.
- [ ] Settings pages display built-in roles, account list, organizations, system status, and recent audit log entries from server data.
- [ ] `/app/beds` or the equivalent admission workspace shows Drizzle-backed room, bed, occupancy, and admission data without raw API placeholders.
- [ ] An authorized user can admit an elder to an available bed from the UI.
- [ ] An authorized user can transfer or discharge an active admission from the UI, with bed status and elder status updated transactionally.
- [ ] Admission conflict attempts return structured errors and do not corrupt bed occupancy state.
- [ ] The screenshot feedback is reflected in the workspace layout: tabs are introduced where needed, oversized intro blocks are removed, and redundant top-bar admission action is removed or replaced with a useful page-local action.
- [ ] At least one implemented operational chart uses a standard chart library and real server data.
- [ ] Audit logs are written for account creation, login, logout, elder create/update/delete, admission create/transfer/discharge, facility mutations implemented in this task, and denied permission checks.
- [ ] Existing static module pages outside the MVP continue to render.
- [ ] Existing Drizzle migrations remain coherent with `modules/core/server/schema.ts`.
- [ ] `pnpm lint` passes.
- [ ] `pnpm type-check` passes.
- [ ] `pnpm build` passes.
## Out of Scope
- PostgreSQL/Drizzle migration.
- oRPC adoption.
- better-auth adoption.
- Password reset, email verification, OAuth, multi-factor authentication, or account invitations.
- Custom role builder UI.
- Reverting to JSON-file persistence.
- Full historical data migration from any old `.data/teatea.json` files unless a separate migration task is created.
- Replacing the current Route Handlers with oRPC.
- Replacing the current custom session implementation with better-auth.
- Password reset, email verification, OAuth, multi-factor authentication, or new invitation flows beyond what already exists.
- Full custom role builder beyond the existing role/permission management surface.
- CRUD implementation for every module in the sidebar.
- Advanced care plan, billing, family app, device telemetry, and emergency workflow automation.
- Production-grade password hashing beyond Node built-in cryptographic hashing suitable for this local MVP.
## Open Questions
- None blocking. The MVP assumes "basic CRUD" means the first core business entity, elder profiles, plus real account/session/audit support.
- Which chart library should be standardized for the project? Recharts is a practical default for React dashboards, but the decision should be captured before adding the dependency.
- Should room/bed creation be part of this immediate slice, or should the UI focus first on admission, transfer, and discharge against existing room/bed records?
## Notes
- Next.js documentation confirms App Router Route Handlers support `GET`, `POST`, `PATCH`, and `DELETE`, `Response.json`, `request.json()`, and async `cookies()` from `next/headers` for cookie reads/writes in current versions.
- The current codebase already demonstrates the intended Drizzle direction. Future task text should not describe JSON files as the target persistence layer unless explicitly working on a compatibility migration.