Implement compliance exports and retention overrides
This commit is contained in:
@@ -430,6 +430,23 @@ export type NotificationLogEntry = {
|
||||
is_read?: boolean;
|
||||
};
|
||||
|
||||
export type DataExportSummary = {
|
||||
id: number;
|
||||
scope: 'tenant' | 'event';
|
||||
status: 'pending' | 'processing' | 'ready' | 'failed';
|
||||
include_media: boolean;
|
||||
size_bytes: number | null;
|
||||
created_at: string | null;
|
||||
expires_at: string | null;
|
||||
download_url: string | null;
|
||||
error_message?: string | null;
|
||||
event?: {
|
||||
id: number;
|
||||
slug: string;
|
||||
name: string | Record<string, string>;
|
||||
} | null;
|
||||
};
|
||||
|
||||
export type PaddleTransactionSummary = {
|
||||
id: string | null;
|
||||
status: string | null;
|
||||
@@ -2133,6 +2150,39 @@ function normalizeNotificationLog(entry: JsonValue): NotificationLogEntry | null
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeDataExport(entry: JsonValue): DataExportSummary | null {
|
||||
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const row = entry as Record<string, JsonValue>;
|
||||
const event = row.event;
|
||||
const eventRecord = event && typeof event === 'object' && !Array.isArray(event)
|
||||
? (event as Record<string, JsonValue>)
|
||||
: null;
|
||||
|
||||
return {
|
||||
id: Number(row.id ?? 0),
|
||||
scope: row.scope === 'event' ? 'event' : 'tenant',
|
||||
status: typeof row.status === 'string' ? (row.status as DataExportSummary['status']) : 'pending',
|
||||
include_media: Boolean(row.include_media),
|
||||
size_bytes: typeof row.size_bytes === 'number' ? row.size_bytes : null,
|
||||
created_at: typeof row.created_at === 'string' ? row.created_at : null,
|
||||
expires_at: typeof row.expires_at === 'string' ? row.expires_at : null,
|
||||
download_url: typeof row.download_url === 'string' ? row.download_url : null,
|
||||
error_message: typeof row.error_message === 'string' ? row.error_message : null,
|
||||
event: eventRecord
|
||||
? {
|
||||
id: Number(eventRecord.id ?? 0),
|
||||
slug: typeof eventRecord.slug === 'string' ? eventRecord.slug : '',
|
||||
name: typeof eventRecord.name === 'string' || typeof eventRecord.name === 'object'
|
||||
? (eventRecord.name as DataExportSummary['event']['name'])
|
||||
: '',
|
||||
}
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
export async function listNotificationLogs(options?: {
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
@@ -2178,6 +2228,51 @@ export async function markNotificationLogs(ids: number[], status: 'read' | 'dism
|
||||
});
|
||||
}
|
||||
|
||||
export async function listTenantDataExports(): Promise<DataExportSummary[]> {
|
||||
const response = await authorizedFetch('/api/v1/tenant/exports');
|
||||
const payload = await jsonOrThrow<{ data?: JsonValue[] }>(response, 'Failed to load data exports');
|
||||
const rows = Array.isArray(payload.data) ? payload.data : [];
|
||||
|
||||
return rows
|
||||
.map((row) => normalizeDataExport(row))
|
||||
.filter((row): row is DataExportSummary => Boolean(row));
|
||||
}
|
||||
|
||||
export async function requestTenantDataExport(payload: {
|
||||
scope: 'tenant' | 'event';
|
||||
eventId?: number;
|
||||
includeMedia?: boolean;
|
||||
}): Promise<DataExportSummary | null> {
|
||||
const response = await authorizedFetch('/api/v1/tenant/exports', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
scope: payload.scope,
|
||||
event_id: payload.eventId,
|
||||
include_media: payload.includeMedia,
|
||||
}),
|
||||
});
|
||||
|
||||
const body = await jsonOrThrow<{ data?: JsonValue }>(response, 'Failed to request export');
|
||||
const record = body.data ? normalizeDataExport(body.data) : null;
|
||||
|
||||
return record ?? null;
|
||||
}
|
||||
|
||||
export async function downloadTenantDataExport(downloadUrl: string): Promise<Blob> {
|
||||
const response = await authorizedFetch(downloadUrl, {
|
||||
headers: { 'Accept': 'application/octet-stream' },
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const payload = await safeJson(response);
|
||||
console.error('[API] Failed to download data export', response.status, payload);
|
||||
throw new Error('Failed to download data export');
|
||||
}
|
||||
|
||||
return response.blob();
|
||||
}
|
||||
|
||||
export async function getTenantPaddleTransactions(cursor?: string): Promise<{
|
||||
data: PaddleTransactionSummary[];
|
||||
nextCursor: string | null;
|
||||
|
||||
Reference in New Issue
Block a user