관리자 사용자 목록: 게스트 삭제 예정 시각 표시
게스트는 사이트를 나가고 하루 지나면 자동 삭제된다. 목록과 상세에 마지막 접속 + 24시간을 보여 주고, 접속 중이거나 방에 자리가 있으면 삭제 보류로 표시한다. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
/** Admin site tests (docs/14-admin.md §9). */
|
||||
import { afterEach, describe, expect, test } from 'bun:test';
|
||||
import { Client, TEST_SUPERADMINS, api, boot, guest, sleep } from './test-utils';
|
||||
import { GUEST_IDLE_MS } from './server';
|
||||
|
||||
const [SUPER, SUPER2] = TEST_SUPERADMINS;
|
||||
type Booted = ReturnType<typeof boot>;
|
||||
@@ -238,4 +239,20 @@ describe('site settings, rooms, users', () => {
|
||||
const actions = (await api(s.base, '/api/admin/audit', { cookie: sup.cookie })).body.entries.map((e: any) => e.action);
|
||||
expect(actions).toEqual(['user.unban', 'user.ban', 'user.rename']);
|
||||
});
|
||||
|
||||
test('superadmin sees when an idle guest will be deleted; none for members or guests still online', async () => {
|
||||
const s = start();
|
||||
const sup = await discordLogin(s, SUPER);
|
||||
const away = await guest(s.base, '떠난손님');
|
||||
const here = await guest(s.base, '있는손님');
|
||||
const c = await new Client(s.base, here.cookie, here.id).connect();
|
||||
clients.push(c);
|
||||
const seen = (s.db.query('SELECT last_seen_at AS t FROM users WHERE id = ?').get(away.id) as { t: number }).t;
|
||||
const list = (await api(s.base, '/api/admin/users', { cookie: sup.cookie })).body.users;
|
||||
const byId = (id: string) => list.find((u: any) => u.id === id);
|
||||
expect(byId(away.id).deleteAt).toBe(seen + GUEST_IDLE_MS);
|
||||
expect(byId(here.id).deleteAt).toBeNull();
|
||||
expect(byId(sup.id).deleteAt).toBeNull();
|
||||
expect((await api(s.base, `/api/admin/users/${away.id}`, { cookie: sup.cookie })).body.user.deleteAt).toBe(seen + GUEST_IDLE_MS);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,6 +18,8 @@ export interface AdminDeps {
|
||||
settings: SettingsStore;
|
||||
games: Record<string, AnyGameDefinition>;
|
||||
disconnectUser: (userId: string) => void;
|
||||
/** When an idle guest will be deleted; null for members and for guests still online or seated. */
|
||||
guestDeleteAt: (u: { id: string; kind: 'guest' | 'member'; last_seen_at: number }) => number | null;
|
||||
stats: () => Record<string, unknown>;
|
||||
}
|
||||
|
||||
@@ -145,6 +147,7 @@ export function createAdminApp(d: AdminDeps) {
|
||||
banned: u.banned_at ? { at: u.banned_at, reason: u.banned_reason } : null,
|
||||
createdAt: u.created_at,
|
||||
lastSeenAt: u.last_seen_at,
|
||||
deleteAt: d.guestDeleteAt(u),
|
||||
})),
|
||||
});
|
||||
});
|
||||
@@ -161,6 +164,7 @@ export function createAdminApp(d: AdminDeps) {
|
||||
banned: u.banned_at ? { at: u.banned_at, reason: u.banned_reason } : null,
|
||||
createdAt: u.created_at,
|
||||
lastSeenAt: u.last_seen_at,
|
||||
deleteAt: d.guestDeleteAt(u),
|
||||
activeRoom: d.rooms.activeRoomFor(u.id),
|
||||
},
|
||||
stats: d.users.stats(u.id),
|
||||
|
||||
@@ -64,11 +64,12 @@ export function startServer(opts: StartOptions) {
|
||||
gateway.closeUser(id);
|
||||
if (users.deleteGuest(id)) log('info', 'guest removed', { user: id });
|
||||
};
|
||||
/** Still on the site (socket open) or still holding a seat: not idle. */
|
||||
const guestKept = (id: string) => gateway.isOnline(id) || !!rooms.activeRoomFor(id);
|
||||
/** Guests who closed the site and stayed away a day are deleted (docs/05 §4). */
|
||||
const purgeIdleGuests = (now = Date.now()) => {
|
||||
for (const id of users.idleGuests(now - GUEST_IDLE_MS)) {
|
||||
// Still on the site (socket open) or still holding a seat: not idle.
|
||||
if (gateway.isOnline(id) || rooms.activeRoomFor(id)) continue;
|
||||
if (guestKept(id)) continue;
|
||||
removeGuest(id);
|
||||
}
|
||||
};
|
||||
@@ -95,6 +96,7 @@ export function startServer(opts: StartOptions) {
|
||||
audit,
|
||||
games: GAMES,
|
||||
disconnectUser: (id) => gateway.disconnectUser(id),
|
||||
guestDeleteAt: (u) => (u.kind !== 'guest' || guestKept(u.id) ? null : u.last_seen_at + GUEST_IDLE_MS),
|
||||
stats: () => {
|
||||
const all = rooms.all();
|
||||
const day = Date.now() - 24 * 3600_000;
|
||||
|
||||
Reference in New Issue
Block a user