docs: add Trellis planning and project specs
This commit is contained in:
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
113
.trellis/tasks/07-01-nextjs-fullstack-crud-rbac-audit/design.md
Normal file
113
.trellis/tasks/07-01-nextjs-fullstack-crud-rbac-audit/design.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# Design
|
||||
|
||||
## Architecture
|
||||
|
||||
This task keeps the app in a single Next.js project and adds a small server-side domain layer inside `modules/`:
|
||||
|
||||
- `modules/core/server/`: server-only persistence, session, permissions, and audit helpers.
|
||||
- `modules/auth/`: UI and client interactions for setup, login, logout, and session state.
|
||||
- `modules/elders/`: elder schemas/types, server actions or API client helpers, and UI components.
|
||||
- `modules/settings/`: role/account/audit display components.
|
||||
- `app/api/.../route.ts`: Route Handlers for auth, session, elders, accounts, and audit APIs.
|
||||
|
||||
The persistence boundary should be centralized behind helper functions that read/write JSON files. UI and route handlers must not know file paths directly.
|
||||
|
||||
## Data Storage
|
||||
|
||||
Use a JSON file store under `.data/teatea.json` by default. The file should contain:
|
||||
|
||||
```ts
|
||||
type AppData = {
|
||||
accounts: Account[];
|
||||
sessions: Session[];
|
||||
elders: Elder[];
|
||||
auditLogs: AuditLog[];
|
||||
};
|
||||
```
|
||||
|
||||
The store module owns initialization, read, write, and update operations. Updates should read the current snapshot, apply a synchronous mutation callback, and write the full file back. This is enough for the MVP and gives a clear future replacement boundary for Drizzle/Postgres.
|
||||
|
||||
## API Contracts
|
||||
|
||||
Route Handlers return a consistent response shape:
|
||||
|
||||
```ts
|
||||
type ApiResult<T> =
|
||||
| ({ success: true; reason: string } & T)
|
||||
| { success: false; reason: string };
|
||||
```
|
||||
|
||||
Planned endpoints:
|
||||
|
||||
- `GET /api/auth/bootstrap`: returns whether setup is required.
|
||||
- `POST /api/auth/setup`: creates the first admin account, creates a session, logs setup.
|
||||
- `POST /api/auth/login`: validates credentials, creates a session, logs login.
|
||||
- `POST /api/auth/logout`: deletes current session cookie/session, logs logout.
|
||||
- `GET /api/auth/session`: returns current account and permissions.
|
||||
- `GET /api/elders`: lists elder profiles.
|
||||
- `POST /api/elders`: creates an elder profile.
|
||||
- `PATCH /api/elders/[id]`: updates an elder profile.
|
||||
- `DELETE /api/elders/[id]`: deletes an elder profile.
|
||||
- `GET /api/settings/accounts`: lists accounts for authorized roles.
|
||||
- `GET /api/settings/roles`: lists built-in role definitions.
|
||||
- `GET /api/audit-logs`: lists recent audit events.
|
||||
|
||||
## Authentication
|
||||
|
||||
Sessions use an HTTP-only cookie. Route Handlers read and write cookies with `await cookies()` from `next/headers`, matching current Next.js behavior.
|
||||
|
||||
Passwords are never stored in plain text. For the MVP, use Node built-in crypto with a per-account salt and `scryptSync` or `scrypt` for password hashing. This avoids adding a dependency while keeping the current local milestone meaningfully better than browser-only auth.
|
||||
|
||||
## Permissions
|
||||
|
||||
Define built-in role permissions in one server/shared constants module:
|
||||
|
||||
- `account:read`
|
||||
- `account:manage`
|
||||
- `audit:read`
|
||||
- `elder:read`
|
||||
- `elder:create`
|
||||
- `elder:update`
|
||||
- `elder:delete`
|
||||
|
||||
Route Handlers call a shared `requirePermission(permission)` helper. This helper returns an authenticated context or a structured forbidden/unauthorized result and writes denied audit entries.
|
||||
|
||||
## Audit Logging
|
||||
|
||||
Audit logging is implemented as a server helper that appends immutable records to the store:
|
||||
|
||||
- timestamp
|
||||
- actor account ID/email if known
|
||||
- action
|
||||
- target type
|
||||
- target ID
|
||||
- result: `success` or `denied` or `failure`
|
||||
- reason
|
||||
|
||||
Audit writes are part of the route handler flow. The MVP accepts best-effort logging for logout when a session has already expired.
|
||||
|
||||
## UI Flow
|
||||
|
||||
Protected app pages should use server session state where practical instead of a client-only `AuthGate`. The app layout can fetch session data and redirect unauthenticated users before rendering protected content.
|
||||
|
||||
The elder page becomes a real data view:
|
||||
|
||||
- Server Component loads initial elders.
|
||||
- Client component handles create/edit/delete forms and refreshes after mutations.
|
||||
- Controls are disabled or hidden based on current permissions.
|
||||
|
||||
The settings page becomes a server-loaded administrative view:
|
||||
|
||||
- Role definitions table.
|
||||
- Accounts table.
|
||||
- Recent audit log table.
|
||||
|
||||
## Compatibility
|
||||
|
||||
Static module pages not in scope remain untouched except for auth/layout integration. Existing visual design should be preserved: dense operational screens, restrained cards/tables, and Tailwind/Radix-compatible UI components.
|
||||
|
||||
## Risks and Rollback
|
||||
|
||||
- File persistence is not safe for high-concurrency production writes. This is acceptable for MVP and documented as a migration boundary.
|
||||
- Replacing `AuthGate` with server-side auth can affect all `/app` routes. Rollback point: keep the old component until server session redirect is working.
|
||||
- Password hashing must use Node runtime APIs, so affected Route Handlers should run in the Node runtime if needed.
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,89 @@
|
||||
# Implementation Plan
|
||||
|
||||
## Checklist
|
||||
|
||||
1. Add core server modules:
|
||||
- JSON store read/write helpers.
|
||||
- Account/session/password helpers.
|
||||
- Role and permission definitions.
|
||||
- Audit logging helper.
|
||||
- Shared API response helpers.
|
||||
|
||||
2. Add auth APIs:
|
||||
- `GET /api/auth/bootstrap`
|
||||
- `POST /api/auth/setup`
|
||||
- `POST /api/auth/login`
|
||||
- `POST /api/auth/logout`
|
||||
- `GET /api/auth/session`
|
||||
|
||||
3. Migrate auth UI:
|
||||
- Update `AuthPanel` to call server APIs.
|
||||
- Update `SignOutButton` to call logout API.
|
||||
- Replace or bypass localStorage-only `AuthGate` with server session protection in the app layout.
|
||||
- Keep unauthenticated redirects and setup redirects working.
|
||||
|
||||
4. Add elder APIs and UI:
|
||||
- Create elder types and input validators.
|
||||
- Implement list/create/update/delete Route Handlers.
|
||||
- Replace `app/(app)/app/elders/page.tsx` static content with server-loaded CRUD UI.
|
||||
|
||||
5. Add settings/audit UI:
|
||||
- Implement settings/account, role, and audit APIs.
|
||||
- Replace `app/(app)/app/settings/page.tsx` static content with server-loaded tables.
|
||||
|
||||
6. Wire audit events:
|
||||
- Account setup/create.
|
||||
- Login/logout.
|
||||
- Elder create/update/delete.
|
||||
- Denied permission checks.
|
||||
|
||||
7. Verification:
|
||||
- Run `pnpm lint`.
|
||||
- Run `pnpm type-check`.
|
||||
- Run `pnpm build`.
|
||||
- Start dev server and manually exercise setup/login/CRUD/settings if build passes.
|
||||
|
||||
## Files Expected to Change
|
||||
|
||||
- `modules/auth/components/AuthPanel.tsx`
|
||||
- `modules/auth/components/AuthGate.tsx`
|
||||
- `modules/auth/components/SignOutButton.tsx`
|
||||
- `app/(app)/app/layout.tsx`
|
||||
- `app/(app)/app/elders/page.tsx`
|
||||
- `app/(app)/app/settings/page.tsx`
|
||||
|
||||
## Files Expected to Be Created
|
||||
|
||||
- `modules/core/server/store.ts`
|
||||
- `modules/core/server/auth.ts`
|
||||
- `modules/core/server/permissions.ts`
|
||||
- `modules/core/server/audit.ts`
|
||||
- `modules/core/server/api.ts`
|
||||
- `modules/elders/types.ts`
|
||||
- `modules/elders/components/EldersClient.tsx`
|
||||
- `modules/settings/components/SettingsOverview.tsx`
|
||||
- `app/api/auth/bootstrap/route.ts`
|
||||
- `app/api/auth/setup/route.ts`
|
||||
- `app/api/auth/login/route.ts`
|
||||
- `app/api/auth/logout/route.ts`
|
||||
- `app/api/auth/session/route.ts`
|
||||
- `app/api/elders/route.ts`
|
||||
- `app/api/elders/[id]/route.ts`
|
||||
- `app/api/settings/accounts/route.ts`
|
||||
- `app/api/settings/roles/route.ts`
|
||||
- `app/api/audit-logs/route.ts`
|
||||
|
||||
## Validation Commands
|
||||
|
||||
```bash
|
||||
pnpm lint
|
||||
pnpm type-check
|
||||
pnpm build
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
## Rollback Points
|
||||
|
||||
- If server auth blocks all app routes, revert only layout/AuthGate changes while keeping APIs.
|
||||
- If elder CRUD UI is unstable, keep APIs and temporarily render a read-only server table.
|
||||
- If file store causes build/runtime issues, move the data directory to `/tmp` behind the same store API for verification, then restore `.data` once filesystem assumptions are fixed.
|
||||
104
.trellis/tasks/07-01-nextjs-fullstack-crud-rbac-audit/prd.md
Normal file
104
.trellis/tasks/07-01-nextjs-fullstack-crud-rbac-audit/prd.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Next.js Full-Stack CRUD, RBAC, and Audit Logs
|
||||
|
||||
## 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.
|
||||
|
||||
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.
|
||||
|
||||
## 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`.
|
||||
|
||||
## 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.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Authentication
|
||||
|
||||
- Setup must create the first administrator account 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.
|
||||
|
||||
### 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.
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
### Audit Logs
|
||||
|
||||
- Audit logs must be persisted server-side and visible in the settings page.
|
||||
- Each audit record must include timestamp, actor account ID/email when available, action, target type, target ID when available, result, and human-readable reason.
|
||||
- Failed permission checks must be logged without exposing sensitive internals to the client.
|
||||
|
||||
### Data and API
|
||||
|
||||
- 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.
|
||||
|
||||
### 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.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] First-run setup creates a server-persisted admin account and 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.
|
||||
- [ ] 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.
|
||||
- [ ] Existing static module pages outside the MVP continue to render.
|
||||
- [ ] `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.
|
||||
- CRUD implementation for every module in the sidebar.
|
||||
- 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.
|
||||
|
||||
## 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.
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "nextjs-fullstack-crud-rbac-audit",
|
||||
"name": "nextjs-fullstack-crud-rbac-audit",
|
||||
"title": "Next.js Full-Stack CRUD, RBAC, and Audit Logs",
|
||||
"description": "",
|
||||
"status": "in_progress",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "TalexDreamSoul",
|
||||
"assignee": "TalexDreamSoul",
|
||||
"createdAt": "2026-07-01",
|
||||
"completedAt": null,
|
||||
"branch": null,
|
||||
"base_branch": "main",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
Reference in New Issue
Block a user