diff --git a/CLAUDE.md b/CLAUDE.md index 14d73f5..1375132 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -41,81 +41,12 @@ Run a single test file: `bun test tests/integration/auth.test.ts` ## Architecture -### Server - -Elysia.js on Bun. All API routes are in `src/app.ts` as `createApp()` — testable via `app.handle(request)` without starting a server. `src/index.tsx` adds Vite middleware (dev) or static serving (prod) and calls `.listen()`. `src/serve.ts` is the dev entry point (dynamic import workaround for Bun EADDRINUSE race). - -### Database - -PostgreSQL via Prisma v6. Client generated to `./generated/prisma` (gitignored — run `bun run db:generate` after checkout or schema changes). - -**Schema models:** `User`, `Session`, `App`, `Log`, `Bug`, `BugImage`, `BugLog` - -**Enums:** `Role` (ADMIN, DEVELOPER), `BugStatus` (OPEN, ON_HOLD, IN_PROGRESS, RESOLVED, RELEASED, CLOSED), `BugSource` (QC, SYSTEM, USER), `LogType` (CREATE, UPDATE, DELETE, LOGIN, LOGOUT) - -Import the singleton: `import { prisma } from './lib/db'` - -### Auth & Roles - -Session-based auth with HttpOnly cookies stored in the DB (24h expiry). Two roles: `DEVELOPER` (super admin) and `ADMIN`. Users listed in `SUPER_ADMIN_EMAIL` env var are auto-promoted to DEVELOPER on login. - -Endpoints: `POST /api/auth/login`, `POST /api/auth/logout`, `GET /api/auth/session` - -Auth state on the frontend is managed via `useSession()` / `useLogin()` / `useLogout()` in `src/frontend/hooks/useAuth.ts` (TanStack Query). - -### Frontend - -React 19 + Vite 8 (middleware mode in dev). TanStack Router with file-based routing in `src/frontend/routes/`. All routes are wrapped in `DashboardLayout` from `src/frontend/components/DashboardLayout.tsx`. - -**Route structure:** -- `/` → redirect -- `/login` → login page -- `/dashboard` → stats overview -- `/apps` → app list -- `/apps/$appId` → per-app layout with nested routes: `index`, `errors`, `logs`, `users`, `villages`, `orders`, `products`, `payments` -- `/users` → operator management -- `/logs` → system activity log -- `/bug-reports` → cross-app bug reports -- `/profile` → user profile - -**App configs** are defined in `src/frontend/config/appMenus.ts` — each app has an ID and a menu list. Currently active: `desa-plus`. Add new app entries here to register them. - -**routeTree.gen.ts** is auto-generated by the TanStack Router Vite plugin — never edit it manually. - -**UI:** Mantine v8, dark theme forced (`#242424`). Charts use `@mantine/charts` (recharts under the hood). Icons from `react-icons/tb`. - -### API Structure - -All API routes live in `src/app.ts`. Key groups: -- `/api/auth/*` — authentication -- `/api/dashboard/*` — stats and recent errors -- `/api/apps`, `/api/apps/:appId` — app listing and detail -- `/api/bugs`, `/api/bugs/:id/status`, `/api/bugs/:id/feedback` — bug report CRUD -- `/api/operators`, `/api/operators/:id` — user management -- `/api/logs` — system activity log -- `/api/system/status` — health check with DB connectivity - -### Logging - -`createSystemLog(userId, type, message)` from `src/lib/logger.ts` writes to the `Log` model. Call it for any significant user action (login/logout/CRUD). Logging errors are swallowed so they never break the main flow. - -### Environment Variables - -Required: `DATABASE_URL`, `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` -Optional: `PORT` (default 3000), `NODE_ENV`, `REACT_EDITOR`, `SUPER_ADMIN_EMAIL` (comma-separated) - -Validated at startup in `src/lib/env.ts` — missing required vars throw immediately. +See @docs/ARCHITECTURE.md ## Testing -- Unit tests: env, DB connection, bcrypt — in `tests/unit/` -- Integration tests: `createApp().handle(new Request(...))` — no running server needed, use these for mutations -- E2E tests: Lightpanda browser via CDP (`ws://127.0.0.1:9222`). App URLs use `host.docker.internal` from inside Docker. Lightpanda executes JS but POST fetch returns 407 — use integration tests for anything that writes data. -- Helpers: `tests/helpers.ts` — `createTestApp()`, `seedTestUser()`, `createTestSession()`, `cleanupTestData()` +See @docs/TESTING.md ## Dev Tools -- **Click-to-source:** `Ctrl+Shift+Cmd+C` toggles inspector. Custom Vite plugin in `src/vite.ts` injects `data-inspector-*` attributes; reads original source from disk for accurate line numbers. -- **HMR:** Vite 8 + `@vitejs/plugin-react` v6. `dedupeRefreshPlugin` in `src/vite.ts` prevents double React Refresh injection. -- **Editor:** Set `REACT_EDITOR` env var. `zed`/`subl` use `file:line:col`; others get `--goto file:line:col`. -- **Playwright MCP:** `bun run mcp:playwright` starts headless browser MCP server (config in `.qwen/settings.json`). +See @docs/DEV_TOOLS.md diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..eaddfda --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,66 @@ +# Architecture + +## Server + +Elysia.js on Bun. All API routes are in `src/app.ts` as `createApp()` — testable via `app.handle(request)` without starting a server. `src/index.tsx` adds Vite middleware (dev) or static serving (prod) and calls `.listen()`. `src/serve.ts` is the dev entry point (dynamic import workaround for Bun EADDRINUSE race). + +## Database + +PostgreSQL via Prisma v6. Client generated to `./generated/prisma` (gitignored — run `bun run db:generate` after checkout or schema changes). + +**Schema models:** `User`, `Session`, `App`, `Log`, `Bug`, `BugImage`, `BugLog` + +**Enums:** `Role` (ADMIN, DEVELOPER), `BugStatus` (OPEN, ON_HOLD, IN_PROGRESS, RESOLVED, RELEASED, CLOSED), `BugSource` (QC, SYSTEM, USER), `LogType` (CREATE, UPDATE, DELETE, LOGIN, LOGOUT) + +Import the singleton: `import { prisma } from './lib/db'` + +## Auth & Roles + +Session-based auth with HttpOnly cookies stored in the DB (24h expiry). Two roles: `DEVELOPER` (super admin) and `ADMIN`. Users listed in `SUPER_ADMIN_EMAIL` env var are auto-promoted to DEVELOPER on login. + +Endpoints: `POST /api/auth/login`, `POST /api/auth/logout`, `GET /api/auth/session` + +Auth state on the frontend is managed via `useSession()` / `useLogin()` / `useLogout()` in `src/frontend/hooks/useAuth.ts` (TanStack Query). + +## Frontend + +React 19 + Vite 8 (middleware mode in dev). TanStack Router with file-based routing in `src/frontend/routes/`. All routes are wrapped in `DashboardLayout` from `src/frontend/components/DashboardLayout.tsx`. + +**Route structure:** +- `/` → redirect +- `/login` → login page +- `/dashboard` → stats overview +- `/apps` → app list +- `/apps/$appId` → per-app layout with nested routes: `index`, `errors`, `logs`, `users`, `villages`, `orders`, `products`, `payments` +- `/users` → operator management +- `/logs` → system activity log +- `/bug-reports` → cross-app bug reports +- `/profile` → user profile + +**App configs** are defined in `src/frontend/config/appMenus.ts` — each app has an ID and a menu list. Currently active: `desa-plus`. Add new app entries here to register them. + +**routeTree.gen.ts** is auto-generated by the TanStack Router Vite plugin — never edit it manually. + +**UI:** Mantine v8, dark theme forced (`#242424`). Charts use `@mantine/charts` (recharts under the hood). Icons from `react-icons/tb`. + +## API Structure + +All API routes live in `src/app.ts`. Key groups: +- `/api/auth/*` — authentication +- `/api/dashboard/*` — stats and recent errors +- `/api/apps`, `/api/apps/:appId` — app listing and detail +- `/api/bugs`, `/api/bugs/:id/status`, `/api/bugs/:id/feedback` — bug report CRUD +- `/api/operators`, `/api/operators/:id` — user management +- `/api/logs` — system activity log +- `/api/system/status` — health check with DB connectivity + +## Logging + +`createSystemLog(userId, type, message)` from `src/lib/logger.ts` writes to the `Log` model. Call it for any significant user action (login/logout/CRUD). Logging errors are swallowed so they never break the main flow. + +## Environment Variables + +Required: `DATABASE_URL`, `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` +Optional: `PORT` (default 3000), `NODE_ENV`, `REACT_EDITOR`, `SUPER_ADMIN_EMAIL` (comma-separated) + +Validated at startup in `src/lib/env.ts` — missing required vars throw immediately. diff --git a/docs/DEV_TOOLS.md b/docs/DEV_TOOLS.md new file mode 100644 index 0000000..19cb26a --- /dev/null +++ b/docs/DEV_TOOLS.md @@ -0,0 +1,6 @@ +# Dev Tools + +- **Click-to-source:** `Ctrl+Shift+Cmd+C` toggles inspector. Custom Vite plugin in `src/vite.ts` injects `data-inspector-*` attributes; reads original source from disk for accurate line numbers. +- **HMR:** Vite 8 + `@vitejs/plugin-react` v6. `dedupeRefreshPlugin` in `src/vite.ts` prevents double React Refresh injection. +- **Editor:** Set `REACT_EDITOR` env var. `zed`/`subl` use `file:line:col`; others get `--goto file:line:col`. +- **Playwright MCP:** `bun run mcp:playwright` starts headless browser MCP server (config in `.qwen/settings.json`). diff --git a/docs/TESTING.md b/docs/TESTING.md new file mode 100644 index 0000000..1692d49 --- /dev/null +++ b/docs/TESTING.md @@ -0,0 +1,6 @@ +# Testing + +- **Unit:** env, DB connection, bcrypt — in `tests/unit/` +- **Integration:** `createApp().handle(new Request(...))` — no running server needed, use these for mutations +- **E2E:** Lightpanda browser via CDP (`ws://127.0.0.1:9222`). App URLs use `host.docker.internal` from inside Docker. Lightpanda executes JS but POST fetch returns 407 — use integration tests for anything that writes data. +- **Helpers:** `tests/helpers.ts` — `createTestApp()`, `seedTestUser()`, `createTestSession()`, `cleanupTestData()`