101 lines
3.4 KiB
TypeScript
101 lines
3.4 KiB
TypeScript
interface AuditEntry {
|
|
action: string;
|
|
targetId: string | null;
|
|
details: string;
|
|
}
|
|
|
|
const actionLabels: Record<string, string> = {
|
|
setup: 'Administrator created',
|
|
login: 'Signed in',
|
|
'login.failed': 'Sign-in failed',
|
|
logout: 'Signed out',
|
|
'sessions.revoked': 'Sessions ended',
|
|
'user.created': 'User created',
|
|
'user.updated': 'Access updated',
|
|
'password.changed': 'Password changed',
|
|
request: 'Action requested'
|
|
};
|
|
|
|
const requestActions: Record<string, string> = {
|
|
'POST /api/configs/inspect': 'Inspect config',
|
|
'POST /api/scripts/inspect': 'Inspect script',
|
|
'POST /api/servers': 'Create server',
|
|
'DELETE /api/servers/[name]': 'Remove server',
|
|
'POST /api/servers/[name]/probe-rcon': 'Check server connection',
|
|
'POST /api/users': 'Create user',
|
|
'PATCH /api/users/[id]': 'Update user access',
|
|
'POST /api/users/[id]/revoke-sessions': 'Sign out sessions',
|
|
'POST /api/auth/logout': 'Sign out',
|
|
'POST /api/auth/password': 'Change password',
|
|
'PUT /api/settings': 'Save settings',
|
|
'PATCH /api/settings': 'Save settings',
|
|
'POST /api/settings/retention/preview': 'Preview data cleanup',
|
|
'POST /api/plugins/github/import': 'Import plugin',
|
|
'PUT /api/server-keys': 'Save server key',
|
|
'DELETE /api/server-keys': 'Remove server key'
|
|
};
|
|
|
|
const requestAreas: Record<string, string> = {
|
|
auth: 'account',
|
|
autoscaling: 'autoscaling',
|
|
bank: 'bank',
|
|
betas: 'beta',
|
|
blueprints: 'blueprint',
|
|
campaigns: 'campaign',
|
|
configs: 'config',
|
|
'deployment-collections': 'server collection',
|
|
deployments: 'campaign',
|
|
'game-files': 'game files',
|
|
hosts: 'host',
|
|
maintenance: 'maintenance',
|
|
moderation: 'moderation',
|
|
plugins: 'plugin',
|
|
plutonium: 'game update',
|
|
provisioning: 'server operation',
|
|
rebalancing: 'rebalancing',
|
|
scripts: 'script',
|
|
'server-keys': 'server key',
|
|
servers: 'server',
|
|
settings: 'settings',
|
|
users: 'user',
|
|
watchdog: 'recovery'
|
|
};
|
|
|
|
export function auditPresentation(event: AuditEntry): {
|
|
label: string;
|
|
requestAction: string | null;
|
|
details: string;
|
|
} {
|
|
let parsed: unknown;
|
|
try {
|
|
parsed = JSON.parse(event.details);
|
|
} catch {
|
|
parsed = null;
|
|
}
|
|
const fields =
|
|
parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
? (parsed as Record<string, unknown>)
|
|
: null;
|
|
const request = event.action === 'request';
|
|
const method = typeof fields?.method === 'string' ? fields.method.toUpperCase() : '';
|
|
const route = event.targetId ?? (typeof fields?.path === 'string' ? fields.path : '');
|
|
const area = route.startsWith('/api/') ? requestAreas[route.split('/')[2]] : undefined;
|
|
// Older entries only recorded the route and HTTP method, so body-driven
|
|
// actions cannot safely be inferred more precisely than their feature area.
|
|
const requestAction = request
|
|
? (requestActions[`${method} ${route}`] ?? (area ? `Manage ${area}` : 'Other action'))
|
|
: null;
|
|
const details = request
|
|
? JSON.stringify(
|
|
{ ...(fields ?? { recordedDetails: event.details }), routeId: event.targetId },
|
|
null,
|
|
2
|
|
)
|
|
: fields
|
|
? Object.keys(fields).length
|
|
? JSON.stringify(fields, null, 2)
|
|
: ''
|
|
: event.details;
|
|
return { label: actionLabels[event.action] ?? event.action, requestAction, details };
|
|
}
|