docs: 관리자 사이트 계획(14-admin), 상표명 사용·도메인 game.tkrmagid.kr 확정 반영
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -61,3 +61,8 @@
|
||||
- 화면 설정: 글자 크기(보통/크게/아주 크게), 다크 모드(자동/밝게/어둡게), 소리 켜기/끄기, 진동 켜기/끄기, 색약 모드.
|
||||
- 내 전적: 게임별 판 수/승 수.
|
||||
- 로그아웃, 모든 기기 로그아웃, 계정 삭제(맨 아래, 작게).
|
||||
|
||||
## 8. 권한(역할)
|
||||
- 일반 / 어드민 / 슈퍼어드민. 자세한 내용은 `14-admin.md`.
|
||||
- `/api/me`는 `role`(`user`·`admin`·`superadmin`)을 함께 돌려주고, 웹은 어드민 이상에게만 계정 페이지에 [관리자 페이지] 버튼을 보여 준다.
|
||||
- 이용 제한된 계정은 세션이 모두 지워지고 디스코드로 다시 로그인할 수 없다(`/?login=banned`).
|
||||
|
||||
@@ -130,3 +130,6 @@ CREATE TABLE user_stats (
|
||||
- 저장하는 개인정보: 닉네임, 디스코드 사용자 ID/이름/아바타 해시, 접속 시각, User-Agent(세션 관리용).
|
||||
- IP는 DB에 저장하지 않고 속도 제한용 메모리에만 둔다.
|
||||
- 계정 삭제 시 `users.deleted_at` 설정 + 닉네임을 "탈퇴한 사용자"로, OAuth 연결·세션 삭제. 다른 사람의 전적 표시가 깨지지 않게 행은 유지.
|
||||
|
||||
## 6. 관리자 관련 테이블 (마이그레이션 002)
|
||||
`admins`, `game_settings`, `site_settings`, `admin_audit`, `users.banned_at`/`banned_reason`. 정의는 `14-admin.md` 7절.
|
||||
|
||||
@@ -48,6 +48,7 @@ export interface GameDefinition<S, A, V, O> {
|
||||
```
|
||||
|
||||
### 규칙
|
||||
- **옵션 메타 필수**: `optionsSchema`의 모든 항목에 `.meta({ title: '한국어 제목', labels: { 값: '표시 이름' } })`를 붙인다. 관리자 사이트가 이 정보로 입력 화면을 자동으로 만든다(`14-admin.md` 4절). 규칙 코드 안의 고정 숫자도 가능한 한 옵션으로 꺼낸다.
|
||||
- `apply`는 `validate`가 통과한 행동만 받는다. `apply` 안에서도 불변식이 깨지면 예외를 던진다(GameRunner가 잡음).
|
||||
- 상태는 **불변 객체처럼** 다룬다(새 객체 반환). 구현은 `structuredClone` 후 수정해도 된다(상태가 작음).
|
||||
- 상태는 JSON으로 직렬화 가능해야 한다(Map/Set/클래스 금지). 저장·복구가 그대로 된다.
|
||||
|
||||
@@ -16,6 +16,8 @@ deploy/
|
||||
| `DISCORD_CLIENT_ID` / `DISCORD_CLIENT_SECRET` | 디스코드 앱 |
|
||||
| `SESSION_SECRET` | 짧은 수명 서명 쿠키(OAuth state)용 32바이트 |
|
||||
| `DB_PATH` | 기본 `/data/app.db` |
|
||||
| `SUPERADMIN_DISCORD_IDS` | 슈퍼어드민 디스코드 ID(쉼표 구분). 기본 `293719842274541579` |
|
||||
| `TRUST_PROXY` | `1`이면 Caddy가 붙인 X-Forwarded-For로 IP 판단 |
|
||||
| `PORT` | 기본 3000 |
|
||||
| `LOG_LEVEL` | info |
|
||||
|
||||
@@ -51,5 +53,5 @@ deploy/
|
||||
| 항목 | 선택지 | 기본 제안 |
|
||||
|---|---|---|
|
||||
| 서버 위치 | 이 호스트 / .5 Docker 호스트 / 외부 VPS | 개발 중에는 이 호스트에서 테스트, 공개용은 사용자 결정 |
|
||||
| 도메인 | 예: `game.tkrmagid.kr` | 사용자 결정(DNS 설정 필요) |
|
||||
| 도메인 | `game.tkrmagid.kr` (확정) | 모든 기능 완성 후 DNS 연결 |
|
||||
| 디스코드 앱 | 개발자 포털에서 생성 → client ID/secret, Redirect URI 등록 | 도메인 정해진 뒤 |
|
||||
|
||||
@@ -14,6 +14,11 @@
|
||||
- 웹: 홈, 코드 입력, 대기실(코드/링크 복사, 공유, QR), 오목 화면, 결과, 계정, 글자 크기/다크 모드.
|
||||
- 완료 기준: `10-testing.md` 3절 12개 시나리오 자동 테스트 통과, 오목 규칙 테스트 통과, E2E 1개 통과, 성능 예산 통과.
|
||||
|
||||
## M1.5 관리자 사이트
|
||||
- 슈퍼어드민(디스코드 ID 고정)·어드민(디스코드 ID로 추가) 권한, 관리자 API, 관리자 화면(대시보드·게임·방·사이트·사용자·관리자·기록).
|
||||
- 게임 옵션 스키마로 설정 화면 자동 생성, 옵션 잠금, 사이트 설정(공지·점검·금지어·제한값).
|
||||
- 완료 기준: `14-admin.md` 9절 테스트 통과, 관리자 화면 E2E 통과.
|
||||
|
||||
## M2. 2인 전략 게임 + 안정성 검증
|
||||
- 체스, 바둑, 장기, 오셀로. 개인 시계(초읽기/증가), 무르기 요청, 무승부 제안, 기권.
|
||||
- 디스코드 로그인 실제 연결(앱 정보가 준비된 경우).
|
||||
|
||||
@@ -2,18 +2,23 @@
|
||||
|
||||
아래 항목은 사용자 확인이 필요하다. **답이 없으면 "기본안"으로 진행**하고, 나중에 바꿀 수 있게 옵션으로 만들어 둔다.
|
||||
|
||||
## 0. 확정된 결정 (2026-10-04)
|
||||
- 도메인: **game.tkrmagid.kr** 고정. 모든 기능을 완성한 뒤 연결한다.
|
||||
- 상표명: 친구끼리 쓰는 사이트라 **원래 이름을 그대로 사용**한다.
|
||||
- 관리자 사이트 추가. 슈퍼어드민은 디스코드 ID `293719842274541579`, 어드민은 디스코드 ID로 추가(`14-admin.md`).
|
||||
- 게임 규칙 기본값은 관리자 사이트에서 모두 바꿀 수 있으므로, 3절의 기본안은 "처음 값"이다.
|
||||
|
||||
## 1. 운영 (진행에 꼭 필요)
|
||||
| # | 항목 | 기본안 | 필요한 것 |
|
||||
|---|---|---|---|
|
||||
| 1 | 사이트 주소(도메인)와 서버 위치 | 개발 중에는 봇 호스트에서 테스트 | 도메인(예: `game.tkrmagid.kr`)과 DNS 연결, 공개 서버 위치 |
|
||||
| 2 | 디스코드 로그인 | 게스트만으로 먼저 개발 | 디스코드 개발자 포털에서 앱 생성 → Client ID, Client Secret, Redirect URI(`https://<도메인>/api/auth/discord/callback`) 등록 |
|
||||
| 1 | 서버 위치 | 도메인은 game.tkrmagid.kr 확정. 완성 후 연결 | 공개 서버 위치(이 호스트 / .5 Docker 호스트 등)와 DNS 연결 시점 |
|
||||
| 2 | 디스코드 로그인 | 게스트만으로 먼저 개발. **관리자 사이트는 디스코드 로그인이 있어야 들어갈 수 있다** | 디스코드 개발자 포털에서 앱 생성 → Client ID, Client Secret, Redirect URI(`https://game.tkrmagid.kr/api/auth/discord/callback`) 등록 |
|
||||
|
||||
## 2. 정책
|
||||
| # | 항목 | 기본안 |
|
||||
|---|---|---|
|
||||
| 3 | 상표가 있는 게임 이름 | 화면에는 일반 명칭 + 괄호로 익숙한 이름(예: "숫자 타일(루미큐브 방식)"). 그림·카드는 모두 직접 제작 |
|
||||
| 4 | 포커·섯다·고스톱 같은 웹보드 게임 | 국내에서 공개 서비스하면 보통 청소년이용불가 등급 대상이다. 기본안: 이 게임들을 "어른용" 묶음으로 따로 보여 주고, 처음 들어갈 때 "만 19세 이상" 확인을 받는다. 칩·점수는 방 안에서만 쓰고 돈·충전·환전은 없다. 누구나 들어오는 공개 서비스로 열기 전에는 등급분류 필요 여부를 따로 확인해야 한다 |
|
||||
| 5 | 보석 상인(스플렌더 방식) 카드 수치 | 원작과 같은 수치를 별도 데이터 파일에 둔다(교체 쉬움). 공개 서비스라면 자체 수치로 바꾸기를 권장 |
|
||||
| 4 | 포커·섯다·고스톱 같은 웹보드 게임 | 친구끼리 쓰는 비공개 사이트이므로 별도 연령 확인 없이 일반 게임과 같이 둔다. 칩·점수는 방 안에서만 쓰고 돈·충전·환전은 없다. 관리자 사이트에서 게임별로 끌 수 있다 |
|
||||
| 5 | 스플렌더 카드 수치 | 원작과 같은 수치를 별도 데이터 파일에 둔다(친구끼리 쓰는 사이트라 그대로 사용) |
|
||||
|
||||
## 3. 게임 규칙 기본값 (자세한 내용은 각 게임 문서)
|
||||
| 게임 | 기본안 | 바꿀 수 있는 옵션 |
|
||||
|
||||
105
docs/14-admin.md
Normal file
105
docs/14-admin.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# 14. 관리자 사이트
|
||||
|
||||
## 1. 목표
|
||||
- 게임 규칙과 사이트 설정을 코드 수정 없이 관리자 화면에서 **하나도 빠짐없이** 바꿀 수 있다.
|
||||
- 권한은 디스코드 ID로 관리한다. 슈퍼어드민은 고정, 일반 어드민은 슈퍼어드민이 디스코드 ID로 추가한다.
|
||||
- 관리자가 무엇을 바꿨는지 모두 기록한다.
|
||||
|
||||
## 2. 권한
|
||||
| 등급 | 누가 | 할 수 있는 것 |
|
||||
|---|---|---|
|
||||
| 슈퍼어드민 | 디스코드 ID `293719842274541579` (설정 `SUPERADMIN_DISCORD_IDS`, 기본값이 이 ID) | 모든 것: 아래 어드민 권한 + 사용자 관리 + 어드민 추가/삭제 |
|
||||
| 어드민 | 슈퍼어드민이 디스코드 ID로 등록한 사람 | 관리자 사이트 보기, 게임 설정, 사이트 설정, 방 관리, 기록 보기 |
|
||||
| 일반 | 그 외(게스트 포함) | 관리자 사이트 접근 불가(관리자 주소로 들어오면 "권한이 없어요") |
|
||||
|
||||
- 권한은 **요청마다** 그 사용자에게 연결된 디스코드 ID로 계산한다. 그래서 어드민 등록·해제가 즉시 반영된다.
|
||||
- 게스트는 디스코드 ID가 없으므로 어드민이 될 수 없다. 관리자는 반드시 디스코드로 로그인해야 한다.
|
||||
- 아직 사이트에 한 번도 들어온 적 없는 사람도 디스코드 ID로 미리 어드민 등록할 수 있다(처음 로그인하는 순간부터 어드민).
|
||||
- 설정 파일에 있는 슈퍼어드민은 관리자 화면에서 삭제할 수 없다.
|
||||
|
||||
## 3. 화면 구성 (`/admin`, 관리자 화면 코드는 관리자만 내려받는 별도 묶음)
|
||||
| 탭 | 어드민 | 슈퍼어드민 | 내용 |
|
||||
|---|---|---|---|
|
||||
| 대시보드 | ○ | ○ | 접속 수, 진행 중 방, 오늘/전체 판 수, 사용자 수(게스트/디스코드), 서버 가동 시간·메모리 |
|
||||
| 게임 | ○ | ○ | 게임별 설정(4절) |
|
||||
| 방 | ○ | ○ | 열린 방 목록(코드, 게임, 상태, 자리, 관전 수, 만든 시각), 방 닫기 |
|
||||
| 사이트 | ○ | ○ | 사이트 설정(5절) |
|
||||
| 사용자 | × | ○ | 검색(닉네임/ID/디스코드 ID), 상세(전적·로그인 방식·접속 시각), 닉네임 변경, 이용 제한/해제, 모든 기기 로그아웃, 삭제 |
|
||||
| 관리자 | × | ○ | 어드민 목록, 디스코드 ID로 추가(메모 포함), 삭제 |
|
||||
| 기록 | ○ | ○ | 관리자 작업 기록(누가, 언제, 무엇을, 바꾸기 전/후) |
|
||||
|
||||
계정 페이지(`/me`)에 관리자에게만 [관리자 페이지] 버튼을 보여 준다.
|
||||
|
||||
## 4. 게임 설정 (게임마다)
|
||||
| 항목 | 설명 |
|
||||
|---|---|
|
||||
| 사용 | 끄면 새 방을 만들 수 없고 목록에서 숨김. 진행 중인 방은 그대로 끝까지 |
|
||||
| 표시 이름 / 한 줄 설명 / 안내문 | 홈·게임 화면에 보이는 글. 안내문은 규칙 화면 위에 표시 |
|
||||
| 인원 범위 | 원작 최소~최대 안에서만 좁힐 수 있음 |
|
||||
| 기본 규칙 | 그 게임의 **모든 옵션**(게임 모듈의 `optionsSchema` 전체)을 기본값으로 지정 |
|
||||
| 방장 변경 금지(잠금) | 옵션마다 잠그면 방장이 대기실에서 바꿀 수 없고 관리자 값으로 고정 |
|
||||
| 초기화 | 코드 기본값으로 되돌리기 |
|
||||
|
||||
- 화면의 입력 칸은 게임 모듈의 옵션 스키마(zod → JSON Schema)에서 **자동으로 만든다**. 새 옵션이 생기면 관리자 화면에 자동으로 나타나므로 빠지는 항목이 없다.
|
||||
- 그래서 모든 게임 모듈은 옵션마다 한국어 제목과 선택지 이름을 `meta`로 붙인다(`09-game-engine.md` 2절). 게임 규칙 안에 숫자로 박혀 있던 값(예: 오목 자동 착수 허용 횟수, 무승부 재제안 간격)도 옵션으로 꺼내서 관리자가 바꿀 수 있게 한다.
|
||||
- 저장할 때 스키마로 검사하고, 통과하지 못하면 저장하지 않는다.
|
||||
- 바꾼 설정은 **새로 만드는 방과 새로 시작하는 판**부터 적용된다. 진행 중인 판의 규칙은 중간에 바뀌지 않는다(공정성).
|
||||
|
||||
## 5. 사이트 설정
|
||||
| 항목 | 기본값 | 효과 |
|
||||
|---|---|---|
|
||||
| 사이트 이름 | 같이 놀자 보드게임 | 홈 제목, 브라우저 탭 제목 |
|
||||
| 공지 | 없음 | 모든 화면 위에 띠로 표시 |
|
||||
| 점검 모드 | 꺼짐 | 켜면 새 방 만들기·빠른 시작을 막고 공지 표시. 진행 중인 게임은 계속 |
|
||||
| 금지어 | 없음(내장 목록 외 추가) | 채팅 가림, 닉네임 거부 |
|
||||
| 사용자당 동시 방 수 | 3 | 방 만들기 제한 |
|
||||
| 서버 전체 방 수 | 2000 | 방 만들기 제한 |
|
||||
| 연결 끊김 유예(초) | 60 | 새 방의 기본값 |
|
||||
| 채팅 기본값 | 켜짐 / 필터 켜짐 | 새 방의 기본값 |
|
||||
|
||||
## 6. 사용자 관리 (슈퍼어드민)
|
||||
- **이용 제한**: 사유와 함께 설정. 즉시 그 사용자의 모든 세션 삭제와 접속 종료. 제한된 디스코드 계정은 다시 로그인할 수 없다("이용이 제한된 계정이에요"). 해제하면 다시 로그인 가능.
|
||||
- **닉네임 변경**: 관리자가 바꾸면 하루 변경 횟수 제한을 무시.
|
||||
- **삭제**: `05-accounts-auth.md`의 계정 삭제와 같음(전적 익명화).
|
||||
- 슈퍼어드민 자신은 제한·삭제할 수 없다.
|
||||
|
||||
## 7. 저장 (`08-data-model.md`에 추가)
|
||||
```sql
|
||||
CREATE TABLE admins (discord_id TEXT PRIMARY KEY, note TEXT, added_by TEXT, added_at INTEGER NOT NULL);
|
||||
CREATE TABLE game_settings (game_id TEXT PRIMARY KEY, json TEXT NOT NULL, updated_by TEXT, updated_at INTEGER NOT NULL);
|
||||
CREATE TABLE site_settings (key TEXT PRIMARY KEY, json TEXT NOT NULL, updated_by TEXT, updated_at INTEGER NOT NULL);
|
||||
CREATE TABLE admin_audit (id INTEGER PRIMARY KEY AUTOINCREMENT, actor_id TEXT NOT NULL, action TEXT NOT NULL,
|
||||
target TEXT, before_json TEXT, after_json TEXT, at INTEGER NOT NULL);
|
||||
ALTER TABLE users ADD COLUMN banned_at INTEGER;
|
||||
ALTER TABLE users ADD COLUMN banned_reason TEXT;
|
||||
```
|
||||
- 설정은 서버 메모리에 캐시하고, 저장할 때 DB와 캐시를 같이 바꾼다.
|
||||
|
||||
## 8. API (`/api/admin/*`, 모두 로그인 + 권한 검사, 바꾸는 요청은 Origin 검사 + 기록)
|
||||
| 메서드 | 경로 | 권한 |
|
||||
|---|---|---|
|
||||
| GET | `/api/admin/me` | 어드민+ |
|
||||
| GET | `/api/admin/overview` | 어드민+ |
|
||||
| GET | `/api/admin/games` | 어드민+ (게임별 현재 설정 + 옵션 JSON Schema + 코드 기본값) |
|
||||
| PUT | `/api/admin/games/:id` | 어드민+ |
|
||||
| POST | `/api/admin/games/:id/reset` | 어드민+ |
|
||||
| GET / PUT | `/api/admin/site` | 어드민+ |
|
||||
| GET | `/api/admin/rooms` | 어드민+ |
|
||||
| POST | `/api/admin/rooms/:code/close` | 어드민+ |
|
||||
| GET | `/api/admin/audit` | 어드민+ |
|
||||
| GET | `/api/admin/users?q=&page=` | 슈퍼어드민 |
|
||||
| GET / PATCH / DELETE | `/api/admin/users/:id` | 슈퍼어드민 |
|
||||
| POST | `/api/admin/users/:id/logout` | 슈퍼어드민 |
|
||||
| GET / POST | `/api/admin/admins` | 슈퍼어드민 |
|
||||
| DELETE | `/api/admin/admins/:discordId` | 슈퍼어드민 |
|
||||
|
||||
## 9. 테스트
|
||||
- 게스트·일반 디스코드 사용자는 모든 관리자 API가 403.
|
||||
- 슈퍼어드민 디스코드 ID로 로그인하면 슈퍼어드민. 어드민 추가 후 그 디스코드 사용자가 바로 어드민이 되고, 삭제하면 바로 403.
|
||||
- 어드민은 사용자/관리자 API 403, 게임·사이트 설정은 가능.
|
||||
- 게임 기본 규칙 저장 → 새 방의 옵션에 반영. 잠근 옵션은 방장이 바꿔도 관리자 값 유지. 잘못된 값은 400.
|
||||
- 게임 끄기 → 새 방 만들기 거부, 목록에서 숨김.
|
||||
- 점검 모드 → 방 만들기 거부. 금지어 → 채팅 가림.
|
||||
- 이용 제한 → 세션 삭제, 접속 종료, 디스코드 재로그인 거부.
|
||||
- 모든 변경이 기록에 남음.
|
||||
- 관리자 화면 E2E: 게임 설정 저장 → 새 방 대기실 요약에 반영.
|
||||
@@ -19,7 +19,8 @@
|
||||
| [10-testing.md](10-testing.md) | 테스트 종류, 핵심 시나리오, 부하 기준, 수동 점검표 |
|
||||
| [11-deployment-ops.md](11-deployment-ops.md) | Docker/Caddy 배포, 환경 변수, 백업, 감시, 결정 필요 항목 |
|
||||
| [12-roadmap.md](12-roadmap.md) | 마일스톤 M0~M9와 완료 기준 |
|
||||
| [13-decisions.md](13-decisions.md) | 사용자 결정이 필요한 항목과 기본안 |
|
||||
| [13-decisions.md](13-decisions.md) | 사용자 결정 항목(확정/대기) |
|
||||
| [14-admin.md](14-admin.md) | 관리자 사이트: 권한(슈퍼어드민/어드민), 게임·사이트 설정, 사용자 관리, 기록 |
|
||||
|
||||
## 게임 문서
|
||||
[games/README.md](games/README.md) — 전체 게임 목록, 우선순위, 제외 이유, 상표 표시 방침.
|
||||
|
||||
@@ -52,5 +52,5 @@
|
||||
| 글룸헤이븐, 테라포밍 마스 등 대형 전략 게임 | 플레이 시간이 길고 구성 요소가 많아 "가볍고 쉬운" 목표와 맞지 않음 |
|
||||
| 5초 준다, 갈팡질팡 | 말로 하는 파티 게임(음성 필요) |
|
||||
|
||||
## 5. 상표·이름 표시 (사용자 결정 필요)
|
||||
루미큐브, 우노, 뱅!, 스플렌더, 할리갈리, 다빈치 코드, 모두의 마블/부루마블, 도블, 쿼리도, 달무티 등은 제품 상표다. 친구끼리 쓰는 비공개 사이트라면 익숙한 이름이 가장 쉽지만, 누구나 들어오는 공개 서비스라면 일반 명칭(예: "숫자 타일 게임")을 쓰고 설명에 "OO과 비슷한 규칙"이라고만 적는 편이 안전하다. 기본안: **화면 이름은 일반 명칭 + 괄호로 익숙한 이름**(예: "숫자 타일(루미큐브 방식)"), 그림·카드 디자인은 모두 직접 제작.
|
||||
## 5. 상표·이름 표시 (확정)
|
||||
친구끼리만 쓰는 사이트이므로 **원래 상표명을 그대로 쓴다**(루미큐브, 우노, 뱅!, 스플렌더, 할리갈리, 다빈치 코드, 부루마블 등). 표시 이름은 관리자 사이트에서 언제든 바꿀 수 있다(`14-admin.md` 4절). 카드·화투·타일 그림은 계속 직접 만든 SVG를 쓴다.
|
||||
|
||||
Reference in New Issue
Block a user