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

+82
View File
@@ -0,0 +1,82 @@
(function () {
'use strict';
const form = document.getElementById('form');
const box = document.getElementById('digits');
const inputs = [...box.querySelectorAll('input')];
const errorEl = document.getElementById('error');
const submitBtn = document.getElementById('submit');
let busy = false;
const value = () => inputs.map((i) => i.value).join('');
function fill(digits, from) {
let i = from;
for (const d of digits) {
if (i >= inputs.length) break;
inputs[i++].value = d;
}
inputs[Math.min(i, inputs.length - 1)].focus();
if (value().length === 6) submit();
}
inputs.forEach((input, idx) => {
input.addEventListener('input', () => {
const digits = input.value.replace(/\D/g, '');
input.value = '';
if (digits) fill(digits, idx);
errorEl.textContent = '';
});
input.addEventListener('keydown', (e) => {
if (e.key === 'Backspace' && !input.value && idx > 0) {
inputs[idx - 1].value = '';
inputs[idx - 1].focus();
e.preventDefault();
} else if (e.key === 'ArrowLeft' && idx > 0) {
inputs[idx - 1].focus();
} else if (e.key === 'ArrowRight' && idx < inputs.length - 1) {
inputs[idx + 1].focus();
}
});
input.addEventListener('paste', (e) => {
const digits = (e.clipboardData.getData('text') || '').replace(/\D/g, '');
if (!digits) return;
e.preventDefault();
fill(digits, idx);
});
input.addEventListener('focus', () => input.select());
});
async function submit() {
if (busy) return;
const key = value();
if (key.length !== 6) {
errorEl.textContent = '6桁すべて入力してください。';
return;
}
busy = true;
submitBtn.disabled = true;
try {
await App.api('POST', '/api/auth', { key });
const next = new URLSearchParams(location.search).get('next');
location.replace(App.safeNext(next));
} catch (err) {
errorEl.textContent = err.message;
box.classList.remove('shake');
void box.offsetWidth;
box.classList.add('shake');
inputs.forEach((i) => { i.value = ''; });
inputs[0].focus();
} finally {
busy = false;
submitBtn.disabled = false;
}
}
form.addEventListener('submit', (e) => {
e.preventDefault();
submit();
});
inputs[0].focus();
})();