Prepare game updates and single-process PM2 deployment

This commit is contained in:
2026-10-14 19:13:36 +09:00
parent 4f24129e29
commit 22792bc2b4
50 changed files with 1546 additions and 197 deletions
+5
View File
@@ -0,0 +1,5 @@
export const FOOTER_LINKS = [
{ id: 'rules', label: 'rule' },
{ id: 'privacy', label: 'privacy' },
{ id: 'contact', label: 'contact' },
] as const;
+4 -2
View File
@@ -1,3 +1,5 @@
import { DEFAULT_GLOBAL_SETTINGS } from './global-settings.js';
export const ACTIONS = {
charge: { name: '으', cost: 0, effect: '생존 시 기 +1', symbol: '✦' },
blast: { name: '파', cost: 1, effect: '상대의 으를 맞힘', symbol: '➜' },
@@ -7,8 +9,8 @@ export const ACTIONS = {
} as const;
export const ACTION_IDS = Object.keys(ACTIONS) as (keyof typeof ACTIONS)[];
export const MATCH_COUNTDOWN_MS = 3000;
export const TURN_DURATION_MS = 4000;
export const TURN_DURATION_MS = DEFAULT_GLOBAL_SETTINGS.turnDurationMs;
export const REVEAL_DURATION_MS = 1200;
export const RULES_VERSION = '1';
export const RULES_VERSION = '2';
export const BOT_VERSION = '1';
export const APP_VERSION = '0.1.0';
+4
View File
@@ -0,0 +1,4 @@
export const GLOBAL_SETTINGS_CACHE_KEY = 'dragon-ball:global-settings:v1';
export const GLOBAL_SETTINGS_REQUEST_TIMEOUT_MS = 5000;
export const DEFAULT_GLOBAL_SETTINGS = { revision: 1, turnDurationMs: 3000, finishCutsceneSpeed: 1, matchingDelayMs: 1000, matchSuccessDelayMs: 1000, matchPreparationDelayMs: 3000 };
export const GLOBAL_SETTINGS_LIMITS = { turnDurationMs: { min: 500, max: 60000 }, finishCutsceneSpeed: { min: 0.25, max: 4 }, matchingDelayMs: { min: 0, max: 10000 }, matchSuccessDelayMs: { min: 0, max: 10000 }, matchPreparationDelayMs: { min: 0, max: 10000 } };
+4
View File
@@ -3,6 +3,10 @@ export const RANKED_QUEUE_TTL_MS = 15000;
export const RANKED_FINISHED_TTL_MS = 15 * 60 * 1000;
export const RANKED_SAVE_RETRY_MS = 1000;
export const RANKED_LEADERBOARD_LIMIT = 100;
export const RANKED_STATS_CACHE_LIMIT = 1000;
export const RANKED_STATS_CACHE_TTL_MS = 5 * 60 * 1000;
export const RANKED_LEADERBOARD_CACHE_LIMIT = 1000;
export const RANKED_LEADERBOARD_CACHE_TTL_MS = 5000;
export const PROFILE_NAME_VERSION = 1;
export const PROFILE_NAME_PREFIXES = ['푸른', '밝은', '고요한', '빛나는', '온화한', '맑은', '은빛', '씩씩한'] as const;
export const PROFILE_NAME_NOUNS = ['구름', '바람', '별빛', '노을', '하늘', '달빛', '물결', '나무'] as const;
+10
View File
@@ -0,0 +1,10 @@
export const HTTP_LIMITS = { windowMs: 60_000, api: 1200, sessions: 30, admin: 60, maxIps: 10_000, cleanupMs: 30_000 } as const;
export const HTTP_MESSAGES = {
rateLimit: '요청이 너무 많습니다. 잠시 후 다시 시도하세요.',
serverError: '요청을 처리하지 못했습니다. 잠시 후 다시 시도하세요.',
badRequest: '요청 형식을 확인하고 다시 시도하세요.',
notReady: '서버가 준비되지 않았습니다. 잠시 후 다시 시도하세요.',
} as const;
export const SERVER_LOG_LEVELS = ['fatal', 'error', 'warn', 'info', 'debug', 'trace', 'silent'] as const;
export const SAFE_ERROR_TYPES = ['Error', 'TypeError', 'RangeError', 'SyntaxError', 'MongoServerError', 'MongoNetworkError', 'MongoServerSelectionError', 'MongoOperationTimeoutError'] as const;
export const SAFE_ERROR_CODES = ['ECONNREFUSED', 'ETIMEDOUT', 'EADDRINUSE', 'EACCES', 'ENOTFOUND', 'INVALID_TRUST_PROXY', 'MISSING_MONGODB_URI'] as const;