Files
chocoadmin/AGENTS.md
T

94 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# This is NOT the Next.js you know
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
---
# Project: chocoadmin
Admin panel for the chocomae service (mouse-typing), extracted into a standalone Next.js 16 app. It shares the existing MariaDB database — **do not modify the DB schema**.
## Critical rules
- **Never alter the DB schema.** All tables are shared with the live chocomae service.
- **Race-condition guard for approve/cancel/reject**: use a conditional `updateMany({ where: { id, Status: REQUEST_STATUSES.REQUESTED }, data: { Status: <target> } })` as the first step inside a transaction. If `count === 0`, check existence to return 404 vs 409. Never use read-then-write (findUnique → check Status → update) — it allows double-processing under concurrent requests.
- **Wrap every mutation in a transaction**: `maestro`, `maestro_extension`/`maestro_upgrade`, and `maestro_log` must be updated atomically.
- **Send email only after a successful commit**, never inside the transaction.
- **Never use `include: { maestro: true }`** when querying `maestro_extension` or `maestro_upgrade`. Always use `select` with only the needed columns to avoid logging the maestro `Password` field.
- Admin password verification uses MariaDB `PASSWORD()` function — the existing `admin` table stores hashed passwords this way. Do not change this without a migration plan.
- Extension approval date calculation happens **server-side**: if current expiry is in the past, use today +1 year; otherwise use current expiry +1 year.
- Upgrade approval sets `AvailableActivateDateTime = NOW() + 1 year`. **Do not change `PlayerCount`** on upgrade.
- When approving an extension or upgrade, close all other `Status = 1` requests for the same `MaestroID` by setting them to `Status = 2`, then set the approved request to `Status = 3`.
- **Docker containers must set `TZ: Asia/Seoul`** so that date calculations (`setHours(0,0,0,0)`, `formatDate`) match the KST-based existing data in the shared MariaDB.
## Authentication
- NextAuth.js v5 (`next-auth@5.0.0-beta.31`) with Credentials Provider.
- Config lives in `auth.ts` (project root), not inside `app/`.
- Unauthenticated requests are caught by `proxy.ts` (middleware) and redirected to `/login`.
- API routes must call `auth()` and return 401 if no session.
## Key files
| File | Purpose |
|---|---|
| `auth.ts` | NextAuth config (Credentials Provider, MariaDB PASSWORD verify) |
| `proxy.ts` | Middleware — redirects unauthenticated users to /login |
| `lib/db.ts` | Prisma Client singleton with SQL pretty-logging and result table logging |
| `lib/logger.ts` | Structured logger — `debug/info/warn/error`, level controlled by `LOG_LEVEL` / `APP_ENV` |
| `lib/errors.ts` | `ApiError` — unified error class with HTTP status code |
| `lib/api-handler.ts` | `withApiHandler` — wraps route handlers with auth, timing, and error logging |
| `lib/constants.ts` | Status code constants (`ACTIVATE_STATUS`, `ACCOUNT_TYPE`, `REQUEST_STATUS`); `PAID_ACCOUNT_TYPE_VALUES` (유료 계정 유형 배열 — 중복 정의 방지용 공용 상수) |
| `lib/maestros.ts` | Maestro DB queries |
| `lib/extension-requests.ts` | Extension request queries and approval logic |
| `lib/upgrade-requests.ts` | Upgrade request queries and approval logic |
| `prisma/schema.prisma` | Generated via `prisma db pull` — do not hand-edit model fields |
| `docs/business-rules.md` | Full reference for existing chocomae business logic |
| `docs/phase/phase9.md` | Log policy: levels, format, masking rules, Docker log operations |
## maestro_log conventions
Always record a log entry after a successful DB change. Use these `Type` values:
| Type | When |
|---|---|
| `extension_maestro` | Extension approved |
| `cancel_extension_maestro` | Extension cancelled (new type, not in original) |
| `upgrade_maestro` | Upgrade approved |
| `reject_upgrade_maestro` | Upgrade rejected (new type, not in original) |
| `update_maestro_name` | Maestro name changed | `{prev} -> {new}` |
| `update_maestro_email` | Maestro email changed | `{prev} -> {new}` |
| `update_maestro_status` | ActivateStatus changed (admin, new) | `{prev} -> {new}` (numeric) |
| `update_maestro_account_type` | AccountType changed (admin, new) | `{prev} -> {new}` (numeric) |
| `update_maestro_available_date` | AvailableActivateDateTime changed (admin, new) | `{YYYY-MM-DD} -> {YYYY-MM-DD}` |
| `update_maestro_allow_enter_code` | AllowEditEnterCode changed (admin, new) | `{prev} -> {new}` (0 or 1) |
`Remark` column is `char(100)` — keep values under 100 characters.
## Implemented phases (do not re-implement)
- Phase 08: login, maestro list/detail, extension requests, upgrade requests, Docker setup, transaction safety, auth guards.
- Phase 9 (substantially complete): structured logging (`lib/logger.ts`), `withApiHandler` wrapper (`lib/api-handler.ts`), `ApiError` class (`lib/errors.ts`), Prisma SQL pretty-logging and result table logging (`lib/db.ts`). Remaining: correlation IDs, stage/production log verification.
- Phase 10: Maestro info edit form (`PATCH /api/maestros/[id]`) + student list with server-side pagination (`GET /api/maestros/[id]/students`).
- Phase 11: Email auto-send via Nodemailer after extension/upgrade approval (`lib/mail.ts`). Upgrade email edge-case verification (trial→paid, paid→paid) still pending.
- Phase 12: Admin-initiated extension/upgrade request registration from maestro detail page.
- `POST /api/maestros/[id]/extension-requests` — inserts into `maestro_extension` using maestro's current `AccountType` read server-side; rejects trial account types.
- `POST /api/maestros/[id]/upgrade-requests` — inserts into `maestro_upgrade` inside a transaction; validates `requestedAccountType > maestro.AccountType`.
- `ExtensionRequestsSection.tsx`, `UpgradeRequestsSection.tsx` — client components with inline action UI; call `router.refresh()` on success.
- `PAID_ACCOUNT_TYPE_VALUES` consolidated in `lib/constants.ts` — shared by `lib/maestros.ts`, `lib/extension-requests.ts`, `lib/upgrade-requests.ts`, and both new API routes.
- Phase 13 (security/correctness fixes):
- P0-1: `getActionTarget` in `lib/extension-requests.ts` replaced with inline `select` (no `include: { maestro: true }`) — prevents `Password` from appearing in debug result logs.
- P0-2: Approve/cancel/reject flows in both `lib/extension-requests.ts` and `lib/upgrade-requests.ts` now use an atomic `updateMany({ Status: REQUESTED → target })` guard; `count === 0` triggers 404/409 — eliminates concurrent double-processing.
- P0-3: `docker-compose.yml` and `docker-compose.stage.yml` now set `TZ: Asia/Seoul`; `lib/maestros.ts` date-change log remark uses `formatDate()` instead of `toISOString().slice(0,10)` for KST consistency.
## Planned phases (not yet implemented)
이후 작업 후보는 `docs/plan/project-plan.md` Section 10 "MVP 이후 확장 후보" 참조.
## Packages to know
- `mariadb` + `@prisma/adapter-mariadb` — Prisma uses the mariadb driver directly (not mysql2)
- `@base-ui/react` — used instead of Radix UI primitives for shadcn components
- `zod` v4 — Zod v4 API differs from v3; check docs before writing schemas
- `next-auth` v5 beta — API surface differs significantly from v4