167 lines
6.6 KiB
Markdown
167 lines
6.6 KiB
Markdown
# Project Instructions — Fiddy (External DB)
|
||
|
||
## 1) Core expectation
|
||
This project connects to an **external Postgres instance (on-prem server)**. Dev and Prod must share the **same schema** through **migrations**.
|
||
|
||
## 2) Authority & doc order
|
||
1) **PROJECT_INSTRUCTIONS.md** (this file) is the source of truth.
|
||
2) **DEBUGGING_INSTRUCTIONS.md** (repo root) is required for bugfix work.
|
||
3) Other instruction files (e.g. `.github/copilot-instructions.md`) must not conflict with this doc.
|
||
|
||
If anything conflicts, follow **this** doc.
|
||
|
||
---
|
||
|
||
## 3) Non-negotiables (hard rules)
|
||
|
||
### External DB + migrations
|
||
- `DATABASE_URL` points to **on-prem Postgres** (**NOT** a container).
|
||
- Dev/Prod share schema via migrations in: `packages/db/migrations`.
|
||
|
||
### No background jobs
|
||
- **No cron/worker jobs**. Any fix must work without background tasks.
|
||
|
||
### Security / logging
|
||
- **Never log secrets** (passwords, tokens, session cookies).
|
||
- **Never log receipt bytes**.
|
||
- **Never log full invite codes** — logs/audit store **last4 only**.
|
||
|
||
### Server-side authorization only
|
||
- **Server-side RBAC only.** Client checks are UX only and must not be trusted.
|
||
|
||
---
|
||
|
||
## 4) Non-regression contracts (do not break)
|
||
|
||
### Auth
|
||
- Custom email/password auth.
|
||
- Sessions are **DB-backed** and stored in table `sessions`.
|
||
- Session cookies are **HttpOnly**.
|
||
|
||
### Receipts
|
||
- Receipt images are stored in Postgres `bytea` table `receipts`.
|
||
- **Entries list endpoints must never return receipt image bytes.**
|
||
- Receipt bytes are fetched only via a **separate endpoint** when inspecting a single item.
|
||
|
||
### Request IDs + audit
|
||
- API must generate a **`request_id`** and return it in responses.
|
||
- Audit logs must include `request_id`.
|
||
- Audit logs must never store full invite codes (store **last4 only**).
|
||
|
||
---
|
||
|
||
## 5) Architecture contract (Backend ↔ Client ↔ Hooks ↔ UI)
|
||
|
||
### No-assumptions rule (required)
|
||
Before making structural changes, first scan the repo and identify:
|
||
- where `app/`, `components/`, `features/`, `hooks/`, `lib/` live
|
||
- existing API routes and helpers
|
||
- patterns already in use
|
||
Do not invent files/endpoints/conventions. If something is missing, add it **minimally** and **consistently**.
|
||
|
||
### Single mechanism rule (required)
|
||
For any cross-component state propagation concern, keep **one** canonical mechanism only:
|
||
- Context **OR** custom events **OR** cache invalidation
|
||
Do not keep old and new mechanisms in parallel. Remove superseded utilities/imports/files in the same PR.
|
||
|
||
### Layering (hard boundaries)
|
||
For every domain (auth, groups, entries, receipts, etc.) follow this flow:
|
||
|
||
1) **API Route Handlers** — `app/api/.../route.ts`
|
||
- Thin: parse/validate input, call a server service, return JSON.
|
||
- No direct DB queries in route files unless there is no existing server service.
|
||
|
||
2) **Server Services (DB + authorization)** — `lib/server/*`
|
||
- Own all DB access and authorization helpers.
|
||
- Server-only modules must include: `import "server-only";`
|
||
- Prefer small domain modules: `lib/server/auth.ts`, `lib/server/groups.ts`, `lib/server/entries.ts`, `lib/server/receipts.ts`, `lib/server/session.ts`.
|
||
|
||
3) **Client API Wrappers** — `lib/client/*`
|
||
- Typed fetch helpers only (no React state).
|
||
- Centralize fetch + error normalization.
|
||
- Always send credentials (cookies) and never trust client-side RBAC.
|
||
|
||
4) **Hooks (UI-facing API layer)** — `hooks/use-*.ts`
|
||
- Hooks are the primary interface for components/pages to call APIs.
|
||
- Components should not call `fetch()` directly unless there is a strong reason.
|
||
|
||
### API conventions
|
||
- Prefer consistent JSON error shape:
|
||
- `{ error: { code: string, message: string }, request_id?: string }`
|
||
- Validate inputs at the route boundary (shape/type), authorize in server services.
|
||
- Mirror existing REST style used in the project.
|
||
|
||
### Next.js route params checklist (required)
|
||
For `app/api/**/[param]/route.ts`:
|
||
- Treat `context.params` as **async** and `await` it before reading properties.
|
||
- Example: `const { id } = await context.params;`
|
||
|
||
### Frontend structure preference
|
||
- Prefer domain-first structure: `features/<domain>/...` + `shared/...`.
|
||
- Use `components/*` only for compatibility shims during migrations (remove them after imports are migrated).
|
||
|
||
### Maintainability thresholds (refactor triggers)
|
||
- Component files > **400 lines** should be split into container/presentational parts.
|
||
- Hook files > **150 lines** should extract helper functions/services.
|
||
- Functions with more than **3 nested branches** should be extracted.
|
||
|
||
---
|
||
|
||
## 6) Decisions / constraints (Group Settings)
|
||
- Add `GROUP_OWNER` role to group roles; migrate existing groups so the first admin becomes owner.
|
||
- Join policy default is `NOT_ACCEPTING`. Policies: `NOT_ACCEPTING`, `AUTO_ACCEPT`, `APPROVAL_REQUIRED`.
|
||
- Both owner and admins can approve join requests and manage invite links.
|
||
- Invite links:
|
||
- TTL limited to 1–7 days.
|
||
- Settings are immutable after creation (policy, single-use, etc.).
|
||
- Single-use does not override approval-required.
|
||
- Expired links are retained and can be revived.
|
||
- Single-use links are deleted after successful use.
|
||
- Revive resets `used_at` and `revoked_at`, refreshes `expires_at`, and creates a new audit event.
|
||
- No cron/worker jobs for now (auto ownership transfer and invite rotation are paused).
|
||
- Group role icons must be consistent: 👑 owner, 🛡️ admin, 👤 member.
|
||
|
||
---
|
||
|
||
## 7) Do first (vertical slice)
|
||
1) DB migrate command + schema
|
||
2) Register/Login/Logout (custom sessions)
|
||
3) Protected dashboard page
|
||
4) Group create/join + group switcher (approval-based joins + optional join disable)
|
||
5) Entries CRUD (no receipt bytes in list)
|
||
6) Receipt upload/download endpoints
|
||
7) Settings + Reports
|
||
|
||
---
|
||
|
||
## 8) Definition of done
|
||
- Works via `docker-compose.dev.yml` with external DB
|
||
- Migrations applied via `npm run db:migrate`
|
||
- Tests + lint pass
|
||
- RBAC enforced server-side
|
||
- No large files
|
||
- No TypeScript warnings or lint errors in touched files
|
||
- No new cron/worker dependencies unless explicitly approved
|
||
- No orphaned utilities/hooks/contexts after refactors
|
||
- No duplicate mechanisms for the same state flow
|
||
- Text encoding remains clean in user-facing strings/docs
|
||
|
||
---
|
||
|
||
## 9) Desktop + mobile UX checklist (required)
|
||
- Touch: long-press affordance for item-level actions when no visible button.
|
||
- Mouse: hover affordance on interactive rows/cards.
|
||
- Tap targets remain >= 40px on mobile.
|
||
- Modal overlays must close on outside click/tap.
|
||
- Use bubble notifications for main actions (create/update/delete/join).
|
||
- Add Playwright UI tests for new UI features and critical flows.
|
||
|
||
---
|
||
|
||
## 10) Tests (required)
|
||
- Add/update tests for API behavior changes (auth, groups, entries, receipts).
|
||
- Include negative cases where applicable:
|
||
- unauthorized
|
||
- not-a-member
|
||
- invalid input
|