This commit is contained in:
+143
@@ -0,0 +1,143 @@
|
||||
export const afkPageSize = 25;
|
||||
export const afkLedgerKinds = [
|
||||
'free_grant',
|
||||
'purchase',
|
||||
'reward',
|
||||
'reserve',
|
||||
'start',
|
||||
'release',
|
||||
'unresolved',
|
||||
'consume',
|
||||
'correction'
|
||||
] as const;
|
||||
export type AfkLedgerKind = (typeof afkLedgerKinds)[number];
|
||||
export type AfkSessionState = 'reserved' | 'started' | 'settled' | 'unresolved' | 'corrected';
|
||||
|
||||
export interface AfkQuery {
|
||||
view: 'accounts' | 'sessions' | 'ledger';
|
||||
q: string;
|
||||
balance: '' | 'saved' | 'reserved' | 'unresolved';
|
||||
sort: 'saved' | 'available' | 'recent';
|
||||
game: '' | 't5' | 't6';
|
||||
server: string;
|
||||
state: '' | AfkSessionState;
|
||||
kind: '' | AfkLedgerKind;
|
||||
from: string;
|
||||
to: string;
|
||||
offset: number;
|
||||
player: string;
|
||||
}
|
||||
|
||||
export function readAfkQuery(params: URLSearchParams): AfkQuery {
|
||||
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 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 offset = Number(params.get('offset') || 0);
|
||||
if (!Number.isSafeInteger(offset) || offset < 0) throw new Error('Invalid page offset.');
|
||||
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', 'sessions', 'ledger'], 'accounts'),
|
||||
q: text('q', 100),
|
||||
balance: choice('balance', ['', 'saved', 'reserved', 'unresolved'], ''),
|
||||
sort: choice('sort', ['saved', 'available', 'recent'], 'saved'),
|
||||
game: choice('game', ['', 't5', 't6'], ''),
|
||||
server: text('server', 256),
|
||||
state: choice(
|
||||
'state',
|
||||
['', 'reserved', 'started', 'settled', 'unresolved', 'corrected'],
|
||||
''
|
||||
),
|
||||
kind: choice('kind', ['', ...afkLedgerKinds], ''),
|
||||
from,
|
||||
to,
|
||||
offset: Math.floor(offset / afkPageSize) * afkPageSize,
|
||||
player: text('player', 128)
|
||||
};
|
||||
}
|
||||
|
||||
export interface AfkIdentity {
|
||||
guid: string;
|
||||
name: string | null;
|
||||
}
|
||||
export interface AfkAccount extends AfkIdentity {
|
||||
freeSeconds: number;
|
||||
savedSeconds: number;
|
||||
availableSeconds: number;
|
||||
reservedSeconds: number;
|
||||
reservedSavedSeconds: number;
|
||||
unresolvedSessions: number;
|
||||
lastActivity: string | null;
|
||||
freeRenewsAt: string | null;
|
||||
}
|
||||
export interface AfkSession extends AfkIdentity {
|
||||
sessionId: string;
|
||||
server: string;
|
||||
game: string | null;
|
||||
matchId: number | null;
|
||||
matchAvailable: number;
|
||||
state: AfkSessionState;
|
||||
authorizedSeconds: number;
|
||||
consumedSeconds: number | null;
|
||||
chargedSeconds: number | null;
|
||||
confirmedSeconds: number;
|
||||
endReason: string | null;
|
||||
createdAt: string;
|
||||
startedAt: string | null;
|
||||
endedAt: string | null;
|
||||
}
|
||||
export interface AfkLedgerEntry extends AfkIdentity {
|
||||
id: number;
|
||||
server: string;
|
||||
game: string | null;
|
||||
matchId: number | null;
|
||||
matchAvailable: number;
|
||||
kind: AfkLedgerKind;
|
||||
freeDelta: number;
|
||||
savedDelta: number;
|
||||
freeSeconds: number;
|
||||
savedSeconds: number;
|
||||
operationId: string;
|
||||
sessionId: string | null;
|
||||
eventAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
export interface AfkPage<T> {
|
||||
entries: T[];
|
||||
total: number;
|
||||
offset: number;
|
||||
}
|
||||
export interface AfkOverview {
|
||||
query: AfkQuery;
|
||||
checkedAt: string;
|
||||
capacitySeconds: number;
|
||||
summary: {
|
||||
accounts: number;
|
||||
savedSeconds: number;
|
||||
reservedSeconds: number;
|
||||
unresolvedSessions: number;
|
||||
};
|
||||
servers: string[];
|
||||
accounts: AfkPage<AfkAccount>;
|
||||
sessions: AfkPage<AfkSession>;
|
||||
ledger: AfkPage<AfkLedgerEntry>;
|
||||
}
|
||||
Reference in New Issue
Block a user