'use strict'; const fs = require('fs'); const path = require('path'); // プロジェクト直下の .env を読み込む(既存の環境変数は上書きしない)。 // 秘密情報はソースコードに書かず .env(git 管理外)か環境変数で渡す。 function loadDotEnv(file) { let text; try { text = fs.readFileSync(file, 'utf8'); } catch { return; } if (process.platform !== 'win32' && (fs.statSync(file).mode & 0o077) !== 0) { console.warn(`[config] ${file} が他ユーザーから読める状態です。chmod 600 してください。`); } for (const line of text.split(/\r?\n/)) { const m = line.match(/^\s*([A-Z_][A-Z0-9_]*)\s*=\s*(.*?)\s*$/); if (!m || line.trim().startsWith('#')) continue; const value = m[2].replace(/^(['"])(.*)\1$/, '$2'); if (process.env[m[1]] === undefined) process.env[m[1]] = value; } } loadDotEnv(path.join(__dirname, '..', '.env')); const ACCESS_KEY = process.env.ACCESS_KEY; if (!ACCESS_KEY || !/^\d{6}$/.test(ACCESS_KEY)) { console.error('[config] ACCESS_KEY(6桁の数字)が未設定です。.env か環境変数で指定してください。'); process.exit(1); } // 管理者キー(任意)。入力すると管理者として入室でき、ハンドレンジ学習ページが見える const ADMIN_KEY = process.env.ADMIN_KEY || null; if (ADMIN_KEY && (!/^\d{6}$/.test(ADMIN_KEY) || ADMIN_KEY === ACCESS_KEY)) { console.error('[config] ADMIN_KEY は ACCESS_KEY と異なる6桁の数字にしてください。'); process.exit(1); } module.exports = { PORT: Number(process.env.PORT) || 4671, // 既定では自分自身(127.0.0.1)でのみ待ち受け、外部には nginx 等の HTTPS 経由で公開する。 // どうしても直接公開する場合のみ HOST=0.0.0.0 にする(通信が平文になるので非推奨)。 HOST: process.env.HOST || '127.0.0.1', // 公開URL(例: https://poker.kanawo.net)。設定するとこのドメイン以外からのアクセスを拒否する。 // ローカル開発時は未設定のままにする。 PUBLIC_ORIGIN: process.env.PUBLIC_ORIGIN ? new URL(process.env.PUBLIC_ORIGIN).origin : null, // 参加キー(6桁の数字)。.env の ACCESS_KEY ACCESS_KEY, ADMIN_KEY, // 1テーブルの最大着席数 MAX_SEATS: 9, // 同時に存在できるテーブル数の上限 MAX_TABLES: 30, // 誰も接続していないテーブルを削除するまでの時間 EMPTY_TABLE_TTL_MS: 30 * 60 * 1000, // オールインで残りの場札をめくる間隔(カード機能あり) RUNOUT_STEP_MS: 1500, // ハンド終了後、次のハンドまで結果を表示する時間(カードありは手札を見比べられるよう長め) SHOWDOWN_MS: 4000, CARD_SHOWDOWN_MS: 6000, FOLD_WIN_MS: 2500, // CPU の考える時間(ランダム)。人間がハンドに残っていないときは BOT_FAST_MS BOT_THINK_MIN_MS: 700, BOT_THINK_MAX_MS: 1800, BOT_FAST_MS: 400, // CPU が勝率を見積もるときのシミュレーション回数 BOT_ITERATIONS: 800, // レビューで勝率を見積もるときのシミュレーション回数(1回の判断・選択肢ごと) REVIEW_ITERATIONS: 1200, // 参加者が2人揃ってから次のハンドを始めるまで NEXT_HAND_DELAY_MS: 2000, // 鍵入力のブルートフォース対策 // IP(IPv6 は /64 単位)ごと: WINDOW 内で MAX_FAILURES 回まで AUTH_MAX_FAILURES: 5, AUTH_FAILURE_WINDOW_MS: 30 * 60 * 1000, // サーバ全体: 1時間に GLOBAL_MAX_FAILURES 回失敗したら新規の鍵入力を一時停止 // (入室済みの人は Cookie があるので影響なし) AUTH_GLOBAL_MAX_FAILURES: 30, AUTH_GLOBAL_WINDOW_MS: 60 * 60 * 1000, // Socket.IO のイベント数上限(1接続あたり / 秒) SOCKET_EVENTS_PER_SEC: 10, };