117 lines
3.7 KiB
TypeScript
117 lines
3.7 KiB
TypeScript
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<T extends string>(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<T> {
|
|
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<BankAccount>;
|
|
transactions: BankPage<BankTransfer>;
|
|
pending: BankPage<BankPending>;
|
|
detail: {
|
|
account: BankAccount;
|
|
pending: BankPending | null;
|
|
history: BankPage<BankTransfer>;
|
|
} | null;
|
|
}
|