# Local JSON MVP Persistence and API Contracts ## Scenario: Single-Repo MVP Before Database Adoption ### 1. Scope / Trigger - Trigger: implementing full-stack CRUD, auth, RBAC, or audit logging before Drizzle/Postgres/oRPC/better-auth are installed. - Applies to this single-package Next.js app when the feature must be real and durable in local/runtime execution, but production database infrastructure is not yet available. - Store boundary: `modules/core/server/store.ts`. ### 2. Signatures - `readData(): Promise` - `writeData(data: AppData): Promise` - `updateData(mutator: (data: AppData) => T): Promise` - `getCurrentAuthContext(): Promise` - `requirePermission(permission: Permission, auditContext: DeniedAuditContext): Promise` - Route Handlers use standard `GET`, `POST`, `PATCH`, and `DELETE` exports and return `Response`. ### 3. Contracts - Default data file: `.data/teatea.json`. - Override key: `TEATEA_DATA_DIR` points to the directory containing `teatea.json`. - `.data/` must stay ignored; it may contain account hashes and session IDs. - API response shape: ```ts type ApiResult> = | ({ success: true; reason: string } & T) | { success: false; reason: string }; ``` - Session cookie: - Name: `teatea_session` - Flags: `httpOnly`, `sameSite: "lax"`, `path: "/"` - Lifetime: 7 days - Passwords: - Never persist plaintext. - Use Node crypto salt + scrypt hash until a dedicated auth library is introduced. ### 4. Validation & Error Matrix - Missing/expired session -> `401` with `{ success: false, reason: "未登录或会话已过期" }`. - Missing permission -> `403` with `{ success: false, reason: "权限不足" }` and a denied audit log. - Invalid JSON body -> `400` with a Chinese user-facing `reason`. - Missing record by ID -> `404` with `{ success: false, reason: "不存在" }`. - Duplicate account email -> `409` with `{ success: false, reason: "账号已存在" }`. ### 5. Good/Base/Bad Cases - Good: UI submits to Route Handler, Route Handler validates input, calls `requirePermission`, mutates data through `updateData`, writes audit log, returns `ApiResult`. - Base: Server Component reads data directly via `readData` after session/permission checks. - Bad: UI or route handler reads `.data/teatea.json` directly, bypasses `requirePermission`, or stores auth state in localStorage. ### 6. Tests Required - `pnpm lint` - `pnpm type-check` - `pnpm build` - Manual or automated integration assertions: - first setup creates admin and cookie session - protected app route redirects without cookie - CRUD mutation persists across reload/API list - denied permission returns 403 and writes an audit log ### 7. Wrong vs Correct #### Wrong ```ts window.localStorage.setItem("teatea.session", JSON.stringify(session)); ``` #### Correct ```ts const cookieStore = await cookies(); cookieStore.set({ name: "teatea_session", value: sessionId, httpOnly: true, sameSite: "lax", path: "/", }); ```