컴퓨터 방 자동 정리 + 게스트 계정 삭제(로그아웃·하루 미접속) + 테스트 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:
@@ -150,6 +150,32 @@ export class Users {
|
||||
this.db.query('UPDATE users SET last_seen_at = ? WHERE id = ?').run(now, userId);
|
||||
}
|
||||
|
||||
/** Guests not seen since `before`, oldest first. */
|
||||
idleGuests(before: number, limit = 500): string[] {
|
||||
return this.db
|
||||
.query<{ id: string }, [number, number]>(
|
||||
"SELECT id FROM users WHERE kind = 'guest' AND deleted_at IS NULL AND last_seen_at < ? ORDER BY last_seen_at LIMIT ?",
|
||||
)
|
||||
.all(before, limit)
|
||||
.map((r) => r.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a guest account for good (logout, or idle for a day): the user row, its stats and sessions.
|
||||
* Past games keep the seat (shown as an unknown player). Returns false for non-guests.
|
||||
*/
|
||||
deleteGuest(userId: string): boolean {
|
||||
return this.db.transaction(() => {
|
||||
const row = this.db.query<{ kind: string }, [string]>('SELECT kind FROM users WHERE id = ?').get(userId);
|
||||
if (row?.kind !== 'guest') return false;
|
||||
this.db.query('DELETE FROM user_stats WHERE user_id = ?').run(userId);
|
||||
this.db.query('DELETE FROM sessions WHERE user_id = ?').run(userId);
|
||||
this.db.query('DELETE FROM oauth_accounts WHERE user_id = ?').run(userId);
|
||||
this.db.query('DELETE FROM users WHERE id = ?').run(userId);
|
||||
return true;
|
||||
})();
|
||||
}
|
||||
|
||||
softDelete(userId: string, now = Date.now()): void {
|
||||
this.db.transaction(() => {
|
||||
this.db
|
||||
|
||||
Reference in New Issue
Block a user