diff --git a/AGENTS.md b/AGENTS.md index 8bd0e39..35a99f5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,5 +1,73 @@ - # 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. +- **All mutating APIs must verify current `Status = 1` before acting** (idempotency guard, returns 409 if already processed). +- **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. +- 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`. + +## 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 | +| `lib/constants.ts` | Status code constants (`ACTIVATE_STATUS`, `ACCOUNT_TYPE`, `REQUEST_STATUS`) | +| `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 | + +## 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 | +| `update_maestro_email` | Maestro email changed | + +`Remark` column is `char(100)` — keep values under 100 characters. + +## Implemented phases (do not re-implement) + +- Phase 0–8 are complete: login, maestro list/detail, extension requests, upgrade requests, Docker setup, transaction safety, auth guards. + +## Planned phases (not yet implemented) + +- **Phase 9** — Structured logging (`lib/logger.ts`, correlation IDs, Prisma query logging) +- **Phase 10** — Maestro info edit form + student list with pagination (`PATCH /api/maestros/[id]`, `GET /api/maestros/[id]/students`) +- **Phase 11** — Email auto-send via Nodemailer after extension/upgrade approval (`lib/mail.ts`) + +## 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 diff --git a/README.md b/README.md index e215bc4..1b45307 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,133 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +# chocoadmin -## Getting Started +chocomae(mouse-typing) 서비스의 관리자 기능을 독립 Next.js 서비스로 분리한 웹 애플리케이션이다. -First, run the development server: +## 기술 스택 + +| 구분 | 기술 | +|---|---| +| 프레임워크 | Next.js 16 (App Router) | +| 언어 | TypeScript | +| 패키지 매니저 | pnpm | +| UI / CSS | Tailwind CSS v4 + shadcn/ui (Base UI) | +| Table | TanStack Table v8 | +| ORM | Prisma v7 (MariaDB adapter) | +| 인증 | NextAuth.js v5 (admin 테이블 기반) | +| 유효성 검증 | Zod v4 | +| 이메일 | Nodemailer | +| DB | MariaDB 10.11 (기존 chocomae DB 공유) | + +## 구현된 기능 + +- 관리자 로그인 (기존 `admin` 테이블 / MariaDB `PASSWORD()` 호환) +- 마에스트로 전체 목록 — 검색(이름·이메일·ID), 상태/계정유형 필터, 페이지네이션 +- 마에스트로 상세 보기 — 기본 정보, 연장/업그레이드 이력, 처리 로그 +- 연장 신청 목록 — 승인 / 취소 (트랜잭션 + `maestro_log`) +- 업그레이드 신청 목록 — 승인 / 거절 (트랜잭션 + `maestro_log`) +- 비인증 접근 차단 (`proxy.ts`, API route `auth()` 검사) + +## 개발 환경 설정 + +### 1. 의존성 설치 ```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev +pnpm install ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +### 2. 환경변수 설정 -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +```bash +cp .env.example .env.local +``` -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. +`.env.local`을 열어 실제 값으로 교체한다. -## Learn More +```env +DATABASE_URL="mysql://USER:PASSWORD@127.0.0.1:3306/chocomae" +AUTH_SECRET="..." +NEXTAUTH_SECRET="..." +AUTH_URL="http://localhost:3000" +NEXTAUTH_URL="http://localhost:3000" +``` -To learn more about Next.js, take a look at the following resources: +### 3. DB 연결 확인 -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +```bash +pnpm db:check +``` -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! +### 4. 개발 서버 실행 -## Deploy on Vercel +```bash +pnpm dev +``` -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. +[http://localhost:3000](http://localhost:3000) 에서 확인한다. -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. +## 환경변수 전체 목록 + +| 변수 | 필수 | 설명 | +|---|---|---| +| `DATABASE_URL` | ✅ | MariaDB 연결 문자열 (`mysql://...`) | +| `AUTH_SECRET` | ✅ | NextAuth 세션 암호화 키 | +| `NEXTAUTH_SECRET` | ✅ | NextAuth 호환용 (동일 값) | +| `AUTH_URL` | ✅ | 서비스 기본 URL | +| `NEXTAUTH_URL` | ✅ | NextAuth 호환용 (동일 값) | +| `APP_ENV` | — | `local` \| `stage` \| `production` (Phase 9) | +| `LOG_LEVEL` | — | `debug` \| `info` \| `warn` \| `error` (Phase 9) | +| `MAIL_FROM_NAME` | — | 발송자 이름 (Phase 11) | +| `MAIL_FROM_ADDRESS` | — | 발송자 주소 (Phase 11) | +| `MAIL_BCC_ADDRESS` | — | BCC 주소 (Phase 11) | +| `MAIL_SMTP_HOST` | — | SMTP 호스트 (Phase 11) | +| `MAIL_SMTP_PORT` | — | SMTP 포트 (Phase 11) | +| `MAIL_SMTP_SECURE` | — | TLS 여부 (Phase 11) | +| `MAIL_SMTP_USER` | — | SMTP 인증 계정 (Phase 11) | +| `MAIL_SMTP_PASSWORD` | — | SMTP 인증 비밀번호 (Phase 11) | +| `MAIL_SEND_ENABLED` | — | `true` \| `false` 발송 활성화 (Phase 11) | + +## 배포 (Synology NAS Docker) + +```bash +# 이미지 빌드 및 컨테이너 시작 +docker compose up -d --build +``` + +`docker-compose.yml`의 `environment` 섹션에 위 환경변수를 설정한다. 상세 절차는 [`docs/deployment.md`](docs/deployment.md)를 참고한다. + +## 프로젝트 구조 + +``` +chocoadmin/ +├── app/ +│ ├── (auth)/login/ # 관리자 로그인 +│ ├── (admin)/ # 인증 필요 화면 +│ │ ├── maestros/ # 마에스트로 목록·상세 +│ │ ├── extension-requests/ +│ │ └── upgrade-requests/ +│ └── api/ # API routes +├── components/ +│ ├── data-table/ # TanStack Table 기반 테이블 +│ ├── forms/ +│ └── ui/ # shadcn/ui 컴포넌트 +├── lib/ +│ ├── db.ts # Prisma Client 싱글톤 +│ ├── constants.ts # 상태값 상수 +│ ├── maestros.ts # 마에스트로 DB 쿼리 +│ ├── extension-requests.ts +│ └── upgrade-requests.ts +├── prisma/ +│ └── schema.prisma # DB introspection 결과 +├── docs/ +│ ├── business-rules.md # 기존 서비스 비즈니스 규칙 조사 +│ ├── deployment.md +│ └── plan/project-plan.md # 전체 개발 계획 +├── auth.ts # NextAuth 설정 +├── proxy.ts # 미인증 접근 차단 미들웨어 +└── .env.example +``` + +## 문서 + +- [`docs/plan/project-plan.md`](docs/plan/project-plan.md) — 전체 개발 단계 계획 +- [`docs/business-rules.md`](docs/business-rules.md) — 기존 chocomae 비즈니스 규칙 +- [`docs/deployment.md`](docs/deployment.md) — NAS 배포 절차 diff --git a/docs/plan/project-plan.md b/docs/plan/project-plan.md index 15472d3..a270e2e 100644 --- a/docs/plan/project-plan.md +++ b/docs/plan/project-plan.md @@ -37,6 +37,7 @@ | ORM | Prisma (기존 DB introspection 방식으로 활용) | | 유효성 검증 | Zod | | 인증 | NextAuth.js v5 (기존 `admin` 테이블 활용) | +| 이메일 발송 | Nodemailer (기존 `mouse-typing` SMTP 설정을 환경변수로 이관) | | 환경변수 관리 | `.env.local` / `.env.production` | ### Prisma vs Drizzle ORM @@ -170,7 +171,9 @@ chocoadmin/ │ ├── auth/[...nextauth]/ │ ├── maestros/ │ │ ├── route.ts # GET: 목록 -│ │ └── [id]/route.ts # GET: 상세, PATCH: 상태 변경 +│ │ └── [id]/ +│ │ ├── route.ts # GET: 상세, PATCH: 정보 수정 +│ │ └── students/route.ts # GET: 학생 목록 │ ├── extension-requests/ │ │ └── [id]/route.ts # PATCH: 승인/취소 │ └── upgrade-requests/ @@ -184,6 +187,7 @@ chocoadmin/ ├── lib/ │ ├── db.ts # Prisma Client 싱글톤 │ ├── auth.ts # NextAuth 설정 +│ ├── mail.ts # chocomae SMTP 메일 발송 모듈 │ ├── validators/ # Zod 스키마 │ └── constants.ts # 상태값 상수 ├── types/ @@ -222,7 +226,17 @@ chocoadmin/ ### 7-3. 마에스트로 상세 정보 (`/maestros/[maestroId]`) - 기본 계정 정보 (이름, 이메일, 계정 유형, 상태, 만료일, 약관 동의일) -- 플레이어(학생) 목록 요약 +- 기본 계정 정보 수정 및 저장 + - 이름: 저장 시 다른 마에스트로 계정에서 이미 사용 중인지 검사하고, 중복이면 저장 실패 처리 + - 이메일 + - 상태: 콤보박스 (`maestro.ActivateStatus`) + - 계정유형: 콤보박스 (`maestro.AccountType`) + - 사용 가능일 (`maestro.AvailableActivateDateTime`) + - 입장코드 수정: 콤보박스 기반 입력/선택 UI +- 정보 수정 및 저장 성공 시 `maestro_log`에 처리 이력 기록 +- 플레이어(학생) 전체 목록 + - 서버 사이드 페이지네이션 + - 이름 검색 - 연장 신청 이력 - 업그레이드 신청 이력 - 관리자 처리 로그 (`maestro_log`) @@ -240,6 +254,12 @@ chocoadmin/ 4. `maestro.AvailableActivateDateTime` 연장, `maestro.ActivateStatus → 2` (기존 서비스 로직 확인 후 동일 적용) 5. `maestro_log` 기록 6. 트랜잭션 커밋 +7. 커밋 후 연장 완료 이메일 자동 발송 + +**연장 완료 이메일:** +- 기존 호출 위치: `/Users/jisangs/work/jinaju/git/web/mouse-typing/src/web/server/admin/extension_maestro.php:29` +- 제목: `[초코마에] {마에스트로명} - 마에스트로 계정 유효기간 연장 완료` +- 내용: 유효기간 +1년 연장 완료, 새 유효기간 안내 **취소 처리 순서:** 1. `maestro_extension.Status = 1` 인지 확인 @@ -261,6 +281,12 @@ chocoadmin/ 4. `maestro.AccountType → RequestedAccountType` 변경 (PlayerCount 갱신 정책은 기존 서비스 확인 후 동일 적용) 5. `maestro_log` 기록 6. 트랜잭션 커밋 +7. 커밋 후 업그레이드 완료 이메일 자동 발송 + +**업그레이드 완료 이메일:** +- 기존 호출 위치: `/Users/jisangs/work/jinaju/git/web/mouse-typing/src/web/server/admin/upgrade_maestro.php:30` +- 제목: `[초코마에] {마에스트로명} - 마에스트로 계정의 요금제 업그레이드 완료 알림` +- 내용: 기존 요금제에서 상위 요금제로 변경 완료, 유효기간 오늘부터 1년 **거절 처리 순서:** 1. `maestro_upgrade.Status = 1` 인지 확인 @@ -280,6 +306,7 @@ chocoadmin/ 5. 배포 DB 계정은 chocoadmin 전용 계정으로 분리하고 최소 권한(SELECT + 필요한 테이블만 UPDATE)을 적용한다. 6. AWS EC2 보안그룹에서 MariaDB 포트(3306)를 NAS 외부 IP로만 허용한다. 7. 관리자 전용 서비스이므로 외부에 직접 노출하지 않고 VPN 또는 NAS 내부 포트로 접근하는 방식을 권장한다. +8. SMTP/API 인증 정보, 발송자 주소, BCC 주소는 환경변수 또는 배포 시크릿으로 관리하고 코드에 하드코딩하지 않는다. --- @@ -401,6 +428,91 @@ chocoadmin/ - [ ] production 설정에서 결과 전체가 출력되지 않고 summary/slow query 중심으로 출력되는지 확인 - [ ] 로그에 비밀번호, token, cookie, secret, DB password가 노출되지 않는지 확인 +### Phase 10. 마에스트로 정보 수정 및 학생 목록 고도화 + +> `/maestros/[maestroId]` 상세 화면에서 관리자 계정 정보 수정과 학생 목록 조회를 완성한다. + +- [ ] 기존 `mouse-typing` 마에스트로 정보 수정 로직 조사 + - [ ] 이름 변경 시 중복 검사 기준 확인 (`maestro.Name`, 자기 자신 제외) + - [ ] 이메일 변경 처리와 로그 형식 확인 + - [ ] 입장코드 수정 대상 컬럼과 기존 값 생성/검증 규칙 확인 + - [ ] `maestro_log.Type` / `Remark` 형식을 기존 서비스와 맞춤 +- [ ] 마에스트로 정보 수정 API 구현 (`PATCH /api/maestros/[id]`) + - [ ] Zod 요청 스키마 정의: 이름, 이메일, 상태, 계정유형, 사용 가능일, 입장코드 + - [ ] 관리자 인증 및 권한 검증 + - [ ] 이름 중복 검사: 다른 계정이 사용 중이면 저장 실패 응답 + - [ ] 트랜잭션으로 `maestro` 정보 수정 + - [ ] 변경 전/후 값을 포함해 `maestro_log` 기록 + - [ ] 변경 없음, 유효하지 않은 상태값, 존재하지 않는 마에스트로에 대한 응답 정책 정리 +- [ ] 마에스트로 상세 화면 수정 폼 구현 + - [ ] 이름/이메일 입력 + - [ ] 상태 콤보박스 + - [ ] 계정유형 콤보박스 + - [ ] 사용 가능일 입력 + - [ ] 입장코드 수정 콤보박스 + - [ ] 저장 전 확인, 저장 중 비활성화, 성공/실패 토스트 +- [ ] 학생 목록 API 구현 (`GET /api/maestros/[id]/students`) + - [ ] 전체 학생 목록 조회 + - [ ] 서버 사이드 페이지네이션 + - [ ] 이름 검색 + - [ ] 정렬 기준과 기본 page size 정의 +- [ ] 학생 목록 UI 구현 + - [ ] 이름 검색 입력 + - [ ] 페이지네이션 컨트롤 + - [ ] 빈 목록/검색 결과 없음 상태 +- [ ] 검증 + - [ ] 이름 중복 저장 실패 확인 + - [ ] 정보 수정 성공 시 DB 반영과 `maestro_log` 기록 확인 + - [ ] 학생 목록 페이지네이션/검색 동작 확인 + - [ ] 비인증 접근 차단 확인 + +### Phase 11. 이메일 발송 환경 및 자동 발송 + +> 기존 `chocomae` 서비스의 관리자 승인 완료 메일을 `chocoadmin`에서도 동일하게 발송한다. + +- [ ] 기존 이메일 발송 설정 이관 준비 + - [ ] 기존 서비스 파일 확인: `/Users/jisangs/work/jinaju/git/web/mouse-typing/src/web/server/mail/send_jinaju_mail.php` + - [ ] 기존 서비스 파일 확인: `/Users/jisangs/work/jinaju/git/web/mouse-typing/src/web/server/mail/mail_contents.php` + - [ ] 기존 서비스 파일 확인: `/Users/jisangs/work/jinaju/git/web/mouse-typing/src/web/server/mail/mail_setting.php` + - [ ] 발송자 이름/주소, BCC, SMTP host/port/security/auth 설정을 환경변수로 정리 + - [ ] 기존 코드의 하드코딩된 SMTP/API 키를 신규 서비스 코드에 복사하지 않고 `.env` / 배포 시크릿으로 분리 +- [ ] 메일 발송 모듈 구현 (`lib/mail.ts`) + - [ ] 공통 메일 클라이언트 생성 + - [ ] 발송자, 수신자, BCC, 제목, HTML/텍스트 본문 구성 + - [ ] 메일 발송 성공/실패 로그 기록 + - [ ] 운영에서 민감정보가 로그에 남지 않도록 마스킹 +- [ ] 연장 완료 이메일 템플릿 구현 + - [ ] 호출 기준: `/Users/jisangs/work/jinaju/git/web/mouse-typing/src/web/server/admin/extension_maestro.php:29` + - [ ] 제목: `[초코마에] {마에스트로명} - 마에스트로 계정 유효기간 연장 완료` + - [ ] 내용: 유효기간 +1년 연장 완료, 새 유효기간 안내 + - [ ] 관리자가 연장 승인하고 DB 커밋이 정상 완료된 후 자동 발송 +- [ ] 업그레이드 완료 이메일 템플릿 구현 + - [ ] 호출 기준: `/Users/jisangs/work/jinaju/git/web/mouse-typing/src/web/server/admin/upgrade_maestro.php:30` + - [ ] 제목: `[초코마에] {마에스트로명} - 마에스트로 계정의 요금제 업그레이드 완료 알림` + - [ ] 내용: 기존 요금제에서 상위 요금제로 변경 완료, 유효기간 오늘부터 1년 + - [ ] 관리자가 업그레이드 승인하고 DB 커밋이 정상 완료된 후 자동 발송 +- [ ] 승인 API에 이메일 자동 발송 연결 + - [ ] `PATCH /api/extension-requests/[id]` 승인 완료 후 연장 완료 메일 발송 + - [ ] `PATCH /api/upgrade-requests/[id]` 승인 완료 후 업그레이드 완료 메일 발송 + - [ ] 메일 발송 실패 시 승인 결과 롤백 여부를 정책으로 확정하고 구현 + - [ ] 동일 승인 건 재호출 시 이메일 중복 발송 방지 정책 정리 +- [ ] 환경변수 추가 + - [ ] `MAIL_FROM_NAME` + - [ ] `MAIL_FROM_ADDRESS` + - [ ] `MAIL_BCC_ADDRESS` + - [ ] `MAIL_SMTP_HOST` + - [ ] `MAIL_SMTP_PORT` + - [ ] `MAIL_SMTP_SECURE` + - [ ] `MAIL_SMTP_USER` + - [ ] `MAIL_SMTP_PASSWORD` + - [ ] `MAIL_SEND_ENABLED` +- [ ] 문서화 및 검증 + - [ ] `.env.example` 또는 배포 문서에 메일 환경변수 설명 추가 + - [ ] local/stage에서 테스트 수신자 또는 sandbox 설정으로 발송 테스트 + - [ ] 연장 승인 완료 후 이메일 제목/본문/수신자/BCC 확인 + - [ ] 업그레이드 승인 완료 후 이메일 제목/본문/수신자/BCC 확인 + - [ ] 메일 발송 실패 로그와 관리자 화면 응답 확인 + --- ## 10. MVP 범위 @@ -414,8 +526,8 @@ chocoadmin/ - Docker 기반 NAS 배포 **MVP 이후 확장 후보:** -- 마에스트로 상태 직접 변경 - 학생 목록 상세 관리 +- 이메일 발송 이력/재발송 관리 - 데이터 내보내기 (CSV) - 처리 통계 대시보드 - 감사 로그 전용 화면