- 권한: 디스코드 ID로 요청마다 계산. 슈퍼어드민 293719842274541579(SUPERADMIN_DISCORD_IDS), 어드민은 슈퍼어드민이 디스코드 ID로 추가(로그인 전 미리 등록 가능), 고정 슈퍼어드민은 삭제 불가 - 게임 설정: 사용 여부, 표시 이름·설명·안내문, 원작 범위 안 인원, 모든 규칙 옵션 기본값, 옵션별 방장 변경 금지(잠금), 초기화. 입력 화면은 옵션 스키마(zod→JSON Schema)로 자동 생성 - 오목 옵션 전체에 한국어 제목·선택지 메타, 엔진 고정값(자동 착수 허용 횟수, 무승부 재제안 간격, 한 수 제한 초)을 옵션으로 꺼냄 - 사이트 설정: 이름, 공지, 점검 모드, 금지어, 방 수 제한, 연결 유예, 채팅 기본값 - 방 관리(목록·닫기), 사용자 관리(검색·닉네임 변경·이용 제한·강제 로그아웃·삭제, 슈퍼어드민 전용), 관리자 작업 기록(전/후 값) - 이용 제한: 세션 삭제 + 접속 종료 + 디스코드 재로그인 거부 - 테스트: 관리자 API 7개(권한·설정 반영·잠금·점검·금지어·방 닫기·이용 제한·기록), 오목 옵션 2개, e2e/admin.e2e.ts(슈퍼어드민 로그인→규칙 변경·잠금→어드민 추가→게스트 새 방 반영) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
39 lines
1.6 KiB
TypeScript
39 lines
1.6 KiB
TypeScript
export interface Config {
|
|
port: number;
|
|
publicOrigin: string;
|
|
/** Extra origins accepted for WS/CSRF checks (e.g. Vite dev server). */
|
|
allowedOrigins: string[];
|
|
dbPath: string;
|
|
discord: { clientId: string; clientSecret: string } | null;
|
|
sessionSecret: string;
|
|
secureCookies: boolean;
|
|
/** Trust X-Forwarded-For (only when running behind our reverse proxy). */
|
|
trustProxy: boolean;
|
|
superadminDiscordIds: string[];
|
|
}
|
|
|
|
/** The site owner's Discord ID (docs/14-admin.md §2). */
|
|
export const DEFAULT_SUPERADMIN = '293719842274541579';
|
|
|
|
export function loadConfig(env: Record<string, string | undefined> = process.env): Config {
|
|
const publicOrigin = (env.PUBLIC_ORIGIN ?? 'http://localhost:5173').replace(/\/$/, '');
|
|
const extra = (env.ALLOWED_ORIGINS ?? '').split(',').map((s) => s.trim()).filter(Boolean);
|
|
const clientId = env.DISCORD_CLIENT_ID;
|
|
const clientSecret = env.DISCORD_CLIENT_SECRET;
|
|
const sessionSecret = env.SESSION_SECRET ?? '';
|
|
if (publicOrigin.startsWith('https://') && sessionSecret.length < 32) {
|
|
throw new Error('SESSION_SECRET must be at least 32 characters in production');
|
|
}
|
|
return {
|
|
port: Number(env.PORT ?? 3000),
|
|
publicOrigin,
|
|
allowedOrigins: [publicOrigin, ...extra],
|
|
dbPath: env.DB_PATH ?? './data/app.db',
|
|
discord: clientId && clientSecret ? { clientId, clientSecret } : null,
|
|
sessionSecret: sessionSecret || 'dev-only-insecure-session-secret-change-me',
|
|
secureCookies: publicOrigin.startsWith('https://'),
|
|
trustProxy: env.TRUST_PROXY === '1',
|
|
superadminDiscordIds: (env.SUPERADMIN_DISCORD_IDS ?? DEFAULT_SUPERADMIN).split(',').map((s) => s.trim()).filter(Boolean),
|
|
};
|
|
}
|