upd: swagger docs, api key auth, bug fixes
- tambah Elysia Swagger di /docs dengan deskripsi lengkap semua endpoint - tambah API key auth (X-API-Key) untuk klien eksternal di POST /api/bugs - tambah normalisasi BugSource: SYSTEM/USER untuk eksternal, QC/SYSTEM/USER untuk dashboard - perbaiki source schema jadi optional string agar tidak reject nilai unknown dari klien lama - hapus field status dari form create bug (selalu OPEN) - perbaiki typo desa_plus → appId di apps.$appId.errors.tsx - tambah toggle hide/show stack trace di bug-reports.tsx dan apps.$appId.errors.tsx - perbaiki grafik desa (width(-1)/height(-1)) dengan minWidth: 0 pada grid item - perbaiki error &[data-active] inline style di DashboardLayout → pindah ke CSS class - update CLAUDE.md dengan arsitektur lengkap Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
157
CLAUDE.md
157
CLAUDE.md
@@ -1,82 +1,121 @@
|
||||
Default to using Bun instead of Node.js.
|
||||
# CLAUDE.md
|
||||
|
||||
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
|
||||
- Use `bun test` instead of `jest` or `vitest`
|
||||
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
|
||||
- Use `bun run <script>` instead of `npm run <script>`
|
||||
- Use `bunx <package> <command>` instead of `npx <package> <command>`
|
||||
- Bun automatically loads .env, so don't use dotenv.
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Server
|
||||
## Runtime
|
||||
|
||||
Elysia.js as the HTTP framework, running on Bun. API routes are in `src/app.ts` (exported as `createApp()`), frontend serving and dev tools are in `src/index.tsx`.
|
||||
Default to Bun instead of Node.js everywhere:
|
||||
|
||||
- `src/app.ts` — Elysia app factory with all API routes (auth, hello, health, Google OAuth). Testable via `app.handle(request)`.
|
||||
- `src/index.tsx` — Server entry. Adds Vite middleware (dev) or static file serving (prod), click-to-source editor integration, and `.listen()`.
|
||||
- `src/serve.ts` — Dev entry (`bun --watch src/serve.ts`). Dynamic import workaround for Bun EADDRINUSE race.
|
||||
- `bun <file>` not `node` / `ts-node`
|
||||
- `bun test` not `jest` / `vitest`
|
||||
- `bun install` not `npm install` / `yarn` / `pnpm`
|
||||
- `bun run <script>` not `npm run`
|
||||
- `bunx <pkg>` not `npx`
|
||||
- Bun auto-loads `.env` — never use dotenv.
|
||||
|
||||
## Database
|
||||
## Common Commands
|
||||
|
||||
PostgreSQL via Prisma v6. Client generated to `./generated/prisma` (gitignored).
|
||||
```bash
|
||||
bun run dev # dev server with hot reload (bun --watch src/serve.ts)
|
||||
bun run build # Vite production build
|
||||
bun run start # production server (NODE_ENV=production)
|
||||
bun run typecheck # tsc --noEmit
|
||||
bun run lint # biome check src/
|
||||
bun run lint:fix # biome check --write src/
|
||||
|
||||
- Schema: `prisma/schema.prisma` — User (id, name, email, password, timestamps) + Session (id, token, userId, expiresAt)
|
||||
- Client singleton: `src/lib/db.ts` — import `{ prisma }` from here
|
||||
- Seed: `prisma/seed.ts` — demo users with `Bun.password.hash` bcrypt
|
||||
- Commands: `bun run db:migrate`, `bun run db:seed`, `bun run db:generate`
|
||||
# Database
|
||||
bun run db:migrate # prisma migrate dev
|
||||
bun run db:seed # seed demo data
|
||||
bun run db:generate # regenerate prisma client
|
||||
bun run db:studio # Prisma Studio GUI
|
||||
bun run db:push # push schema without migration
|
||||
|
||||
## Auth
|
||||
# Tests
|
||||
bun run test # all tests
|
||||
bun run test:unit # tests/unit/
|
||||
bun run test:integration # tests/integration/ — no server needed
|
||||
bun run test:e2e # tests/e2e/ — requires Lightpanda Docker
|
||||
```
|
||||
|
||||
Session-based auth with HttpOnly cookies stored in DB.
|
||||
Run a single test file: `bun test tests/integration/auth.test.ts`
|
||||
|
||||
- Login: `POST /api/auth/login` — finds user by email, verifies password with `Bun.password.verify`, creates Session record
|
||||
- Google OAuth: `GET /api/auth/google` → Google → `GET /api/auth/callback/google` — upserts user, creates session
|
||||
- Session: `GET /api/auth/session` — looks up session by cookie token, returns user or 401, auto-deletes expired
|
||||
- Logout: `POST /api/auth/logout` — deletes session from DB, clears cookie
|
||||
## Architecture
|
||||
|
||||
## Frontend
|
||||
### Server
|
||||
|
||||
React 19 + Vite 8 (middleware mode in dev). File-based routing with TanStack Router.
|
||||
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).
|
||||
|
||||
- Entry: `src/frontend.tsx` — renders App, removes splash screen, DevInspector in dev
|
||||
- App: `src/frontend/App.tsx` — MantineProvider (dark, forced), QueryClientProvider, RouterProvider
|
||||
- Routes: `src/frontend/routes/` — `__root.tsx`, `index.tsx`, `login.tsx`, `dashboard.tsx`
|
||||
- Auth hooks: `src/frontend/hooks/useAuth.ts` — `useSession()`, `useLogin()`, `useLogout()`
|
||||
- UI: Mantine v8 (dark theme `#242424`), react-icons
|
||||
- Splash: `index.html` has inline dark CSS + spinner, removed on React mount
|
||||
### Database
|
||||
|
||||
## Dev Tools
|
||||
PostgreSQL via Prisma v6. Client generated to `./generated/prisma` (gitignored — run `bun run db:generate` after checkout or schema changes).
|
||||
|
||||
- Click-to-source: `Ctrl+Shift+Cmd+C` toggles inspector. Custom Vite plugin (`inspectorPlugin` in `src/vite.ts`) injects `data-inspector-*` attributes. Reads original file from disk for accurate line numbers.
|
||||
- HMR: Vite 8 with `@vitejs/plugin-react` v6. `dedupeRefreshPlugin` fixes double React Refresh injection.
|
||||
- Editor: `REACT_EDITOR` env var. `zed` and `subl` use `file:line:col`, others use `--goto file:line:col`.
|
||||
**Schema models:** `User`, `Session`, `App`, `Log`, `Bug`, `BugImage`, `BugLog`
|
||||
|
||||
## Playwright MCP
|
||||
**Enums:** `Role` (ADMIN, DEVELOPER), `BugStatus` (OPEN, ON_HOLD, IN_PROGRESS, RESOLVED, RELEASED, CLOSED), `BugSource` (QC, SYSTEM, USER), `LogType` (CREATE, UPDATE, DELETE, LOGIN, LOGOUT)
|
||||
|
||||
Playwright MCP server enables AI-assisted browser automation for testing and debugging.
|
||||
Import the singleton: `import { prisma } from './lib/db'`
|
||||
|
||||
- MCP config: `.qwen/settings.json` — Qwen Code auto-loads on session start
|
||||
- Playwright config: `playwright.config.ts` — E2E test configuration
|
||||
- Run manually: `bun run mcp:playwright` — starts headless browser MCP server
|
||||
- Install browsers: `bunx playwright install` — downloads Chromium and other browsers
|
||||
### 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.
|
||||
|
||||
## Testing
|
||||
|
||||
Tests use `bun:test`. Three levels:
|
||||
- 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()`
|
||||
|
||||
```bash
|
||||
bun run test # All tests
|
||||
bun run test:unit # tests/unit/ — env, db connection, bcrypt
|
||||
bun run test:integration # tests/integration/ — API endpoints via app.handle()
|
||||
bun run test:e2e # tests/e2e/ — browser tests via Lightpanda CDP
|
||||
```
|
||||
## Dev Tools
|
||||
|
||||
- `tests/helpers.ts` — `createTestApp()`, `seedTestUser()`, `createTestSession()`, `cleanupTestData()`
|
||||
- Integration tests use `createApp().handle(new Request(...))` — no server needed
|
||||
- E2E tests use Lightpanda browser (Docker, `ws://127.0.0.1:9222`). App URLs use `host.docker.internal` from container. Lightpanda executes JS but POST fetch returns 407 — use integration tests for mutations.
|
||||
|
||||
## APIs
|
||||
|
||||
- `Bun.password.hash()` / `Bun.password.verify()` for bcrypt
|
||||
- `Bun.file()` for static file serving in production
|
||||
- `Bun.which()` / `Bun.spawn()` for editor integration
|
||||
- `crypto.randomUUID()` for session tokens
|
||||
- **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`).
|
||||
|
||||
Reference in New Issue
Block a user