export const bankPageSize = 25; export const bankAttentionAgeMs = 5 * 60_000; export interface BankQuery { view: 'accounts' | 'transactions' | 'pending'; q: string; balance: '' | 'funded' | 'limit' | 'pending'; sort: 'balance' | 'recent'; game: '' | 't5' | 't6'; server: string; operation: '' | 'deposit' | 'withdraw'; from: string; to: string; offset: number; player: string; historyOffset: number; } export function readBankQuery(params: URLSearchParams): BankQuery { function choice(key: string, values: readonly T[], fallback: T): T { const value = params.get(key) || fallback; if (!values.includes(value as T)) throw new Error(`Invalid ${key} filter.`); return value as T; } function text(key: string, max: number) { const value = (params.get(key) ?? '').trim(); if (value.length > max) throw new Error(`${key} is too long.`); return value; } function offset(key: string) { const value = Number(params.get(key) || 0); if (!Number.isSafeInteger(value) || value < 0) throw new Error('Invalid page offset.'); return Math.floor(value / bankPageSize) * bankPageSize; } function date(key: string) { const value = text(key, 10); if ( value && (!/^\d{4}-\d{2}-\d{2}$/.test(value) || !Number.isFinite(Date.parse(value)) || new Date(value).toISOString().slice(0, 10) !== value) ) throw new Error('Use a valid date in YYYY-MM-DD format.'); return value; } const from = date('from'); const to = date('to'); if (from && to && from > to) throw new Error('The end date must follow the start date.'); return { view: choice('view', ['accounts', 'transactions', 'pending'], 'accounts'), q: text('q', 100), balance: choice('balance', ['', 'funded', 'limit', 'pending'], ''), sort: choice('sort', ['balance', 'recent'], 'balance'), game: choice('game', ['', 't5', 't6'], ''), server: text('server', 256), operation: choice('operation', ['', 'deposit', 'withdraw'], ''), from, to, offset: offset('offset'), player: text('player', 128), historyOffset: offset('historyOffset') }; } export interface BankIdentity { guid: string; name: string | null; } export interface BankAccount extends BankIdentity { balance: number; available: number; pendingOperation: 'deposit' | 'withdraw' | null; pendingAmount: number | null; lastActivity: string | null; lastAmount: number | null; } export interface BankTransfer extends BankIdentity { id: number; server: string; game: string | null; matchId: number | null; matchAvailable: number; amount: number; balance: number; createdAt: string; } export interface BankPending extends BankIdentity { requestId: string; server: string; game: string | null; matchId: number | null; matchAvailable: number; operation: 'deposit' | 'withdraw'; amount: number; createdAt: string; } export interface BankPage { entries: T[]; total: number; offset: number; } export interface BankOverview { query: BankQuery; maximum: number | null; checkedAt: string; summary: { totalBanked: number; fundedAccounts: number; transfers24h: number; pending: number }; servers: string[]; accounts: BankPage; transactions: BankPage; pending: BankPage; detail: { account: BankAccount; pending: BankPending | null; history: BankPage; } | null; }