컴퓨터 방 자동 정리 + 게스트 계정 삭제(로그아웃·하루 미접속) + 테스트 10분→1분

- 방 화면을 벗어나면(홈·규칙) detach: 자리·접속 상태는 유지, 방 접속 수에서 빠짐
- 컴퓨터가 있는 방: 사람이 모두 [방 나가기]하면 바로 닫고, 아무도 안 보면 대기실·끝난 방 1분, 게임 중 30분 뒤 정리
- 게스트 로그아웃 = 계정 삭제(자리·전적·세션·소켓), 사이트를 닫고 24시간 지난 게스트도 1분 주기로 삭제
- users.last_seen_at을 요청·소켓 종료 때 갱신(이전엔 갱신 안 됨), 003 마이그레이션에서 기존 게스트 시계 초기화
- 테스트: 무작위 대국 시드를 playoutSeeds()로 평소 1/10, test:full은 전부. bun test --parallel=4, 테스트 서버 종료 대기 1s→50ms

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
EJClaw
2026-10-07 17:10:52 +09:00
parent 373231b553
commit 39223bacdd
49 changed files with 629 additions and 155 deletions

View File

@@ -104,7 +104,7 @@ function AccountMenu() {
{confirm && (
<ConfirmSheet
title="로그아웃할까요?"
body={me.kind === 'guest' ? '게스트는 로그아웃하면 이 이름과 기록으로 다시 들어올 수 없어요.' : undefined}
body={me.kind === 'guest' ? '게스트 계정은 로그아웃하면 이름과 기록이 모두 지워져요.' : undefined}
confirm="로그아웃"
danger={me.kind === 'guest'}
onConfirm={async () => {

View File

@@ -220,6 +220,13 @@ class GameSocket {
useRoom.setState({ code: null, room: null, roomSeq: -1, game: null, result: null, chat: [], pending: null });
}
/** Left the room page (홈, 규칙…) without leaving: the seat stays, but the room no longer counts us as there. */
detach(code: string): void {
if (useRoom.getState().code !== code) return;
this.send({ t: 'detach' });
useRoom.setState({ code: null, room: null, roomSeq: -1, game: null, result: null, chat: [], pending: null });
}
/** Sends a game action; returns false when not connected. */
act(a: unknown): boolean {
const cs = ++this.cs;

View File

@@ -159,7 +159,7 @@ export default function MePage() {
{confirm === 'logout' && (
<ConfirmSheet
title="로그아웃할까요?"
body={me.kind === 'guest' ? '게스트는 로그아웃하면 이 이름과 기록으로 다시 들어올 수 없어요.' : undefined}
body={me.kind === 'guest' ? '게스트 계정은 로그아웃하면 이름과 기록이 모두 지워져요.' : undefined}
confirm="로그아웃"
danger={me.kind === 'guest'}
onConfirm={async () => {

View File

@@ -32,7 +32,9 @@ export default function RoomPage() {
}, [code, me]);
useEffect(() => {
if (me && /^\d{6}$/.test(code)) socket.join(code);
if (!me || !/^\d{6}$/.test(code)) return;
socket.join(code);
return () => socket.detach(code);
}, [code, me]);
if (!me) {