Initial commit: private Texas Hold'em ring game site (lobby + table stub)

- 6-digit access key gate with long-lived HttpOnly cookie, brute-force limits
- Lobby with saved player name and live table list
- Table creation (name, starting stack, blinds, blind timer)
- Table play screen stub (seats, button/SB/BB, phase, blinds, timer)
- Security hardening: strict CSP, origin checks, host pinning, input sanitizing

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
agomizi1cmandClaude Opus 5.5 committed 2026-10-15 16:51:15 +09:00
commit b2c91062f1
22 files changed
+3450

No files matched your search

+67
View File
@@ -0,0 +1,67 @@
'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);
}
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,
// 1テーブルの最大着席数
MAX_SEATS: 9,
// 同時に存在できるテーブル数の上限
MAX_TABLES: 30,
// 誰も接続していないテーブルを削除するまでの時間
EMPTY_TABLE_TTL_MS: 30 * 60 * 1000,
// 鍵入力のブルートフォース対策
// 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,
};