feat(diagnostics): Admin dashboard client library (Phase 3.4)
- querySessions, createSession, getSession, updateSession, cancelSession - getTraces, getLogs, getScreenshots - Full TypeScript types for all diagnostics entities
This commit is contained in:
parent
fc8f8d33dc
commit
a5d68decdb
299
dashboards/admin-web/src/lib/diagnostics-client.ts
Normal file
299
dashboards/admin-web/src/lib/diagnostics-client.ts
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
/**
|
||||||
|
* Diagnostics Client Library
|
||||||
|
* Admin dashboard client for debug sessions, traces, and logs
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { createApiClient } from '@bytelyst/api-client';
|
||||||
|
import type { ApiClient } from '@bytelyst/api-client';
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Types
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
export interface DebugSession {
|
||||||
|
id: string;
|
||||||
|
productId: string;
|
||||||
|
targetUserId?: string;
|
||||||
|
targetAnonymousId?: string;
|
||||||
|
targetDeviceId?: string;
|
||||||
|
targetSessionId?: string;
|
||||||
|
status: 'pending' | 'active' | 'paused' | 'completed' | 'cancelled';
|
||||||
|
collectionLevel: 'standard' | 'debug' | 'trace';
|
||||||
|
captureLogs: boolean;
|
||||||
|
captureNetwork: boolean;
|
||||||
|
captureScreenshots: boolean;
|
||||||
|
screenshotOnError: boolean;
|
||||||
|
maxDurationMinutes: number;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
startedAt?: string;
|
||||||
|
endedAt?: string;
|
||||||
|
expiresAt: string;
|
||||||
|
logCount: number;
|
||||||
|
traceCount: number;
|
||||||
|
screenshotCount: number;
|
||||||
|
createdBy: string;
|
||||||
|
updatedBy?: string;
|
||||||
|
userConsent?: {
|
||||||
|
consentedAt: string;
|
||||||
|
consentMethod: 'prompt' | 'pre_consent' | 'auto';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateSessionRequest {
|
||||||
|
targetUserId?: string;
|
||||||
|
targetAnonymousId?: string;
|
||||||
|
targetDeviceId?: string;
|
||||||
|
targetSessionId?: string;
|
||||||
|
collectionLevel: 'standard' | 'debug' | 'trace';
|
||||||
|
captureLogs?: boolean;
|
||||||
|
captureNetwork?: boolean;
|
||||||
|
captureScreenshots?: boolean;
|
||||||
|
screenshotOnError?: boolean;
|
||||||
|
maxDurationMinutes?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateSessionRequest {
|
||||||
|
status?: 'active' | 'paused' | 'completed' | 'cancelled';
|
||||||
|
collectionLevel?: 'standard' | 'debug' | 'trace';
|
||||||
|
captureLogs?: boolean;
|
||||||
|
captureNetwork?: boolean;
|
||||||
|
captureScreenshots?: boolean;
|
||||||
|
screenshotOnError?: boolean;
|
||||||
|
maxDurationMinutes?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QuerySessionsOptions {
|
||||||
|
productId: string;
|
||||||
|
status?: string;
|
||||||
|
targetUserId?: string;
|
||||||
|
targetDeviceId?: string;
|
||||||
|
startDate?: string;
|
||||||
|
endDate?: string;
|
||||||
|
limit?: number;
|
||||||
|
offset?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QuerySessionsResult {
|
||||||
|
sessions: DebugSession[];
|
||||||
|
total: number;
|
||||||
|
limit: number;
|
||||||
|
offset: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TraceSpan {
|
||||||
|
id: string;
|
||||||
|
sessionId: string;
|
||||||
|
productId: string;
|
||||||
|
traceId: string;
|
||||||
|
parentId?: string;
|
||||||
|
spanId: string;
|
||||||
|
name: string;
|
||||||
|
kind?: 'internal' | 'server' | 'client' | 'producer' | 'consumer';
|
||||||
|
startTime: string;
|
||||||
|
endTime?: string;
|
||||||
|
durationMs?: number;
|
||||||
|
attributes: Record<string, unknown>;
|
||||||
|
status: 'ok' | 'error' | 'unset';
|
||||||
|
statusMessage?: string;
|
||||||
|
events?: Array<{
|
||||||
|
name: string;
|
||||||
|
timestamp: string;
|
||||||
|
attributes?: Record<string, unknown>;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LogEntry {
|
||||||
|
id: string;
|
||||||
|
sessionId: string;
|
||||||
|
productId: string;
|
||||||
|
level: 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
||||||
|
message: string;
|
||||||
|
messageHash?: string;
|
||||||
|
timestamp: string;
|
||||||
|
receivedAt?: string;
|
||||||
|
module: string;
|
||||||
|
file?: string;
|
||||||
|
line?: number;
|
||||||
|
function?: string;
|
||||||
|
threadId?: string;
|
||||||
|
correlationId?: string;
|
||||||
|
context: Record<string, unknown>;
|
||||||
|
redaction?: {
|
||||||
|
fieldsRedacted: string[];
|
||||||
|
patternsMatched: string[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QueryTracesOptions {
|
||||||
|
sessionId: string;
|
||||||
|
productId: string;
|
||||||
|
startTime?: string;
|
||||||
|
endTime?: string;
|
||||||
|
name?: string;
|
||||||
|
status?: string;
|
||||||
|
limit?: number;
|
||||||
|
offset?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QueryLogsOptions {
|
||||||
|
sessionId: string;
|
||||||
|
productId: string;
|
||||||
|
levels?: ('debug' | 'info' | 'warn' | 'error' | 'fatal')[];
|
||||||
|
module?: string;
|
||||||
|
search?: string;
|
||||||
|
startTime?: string;
|
||||||
|
endTime?: string;
|
||||||
|
limit?: number;
|
||||||
|
offset?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Client Factory
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
export interface DiagnosticsClientConfig {
|
||||||
|
baseUrl: string;
|
||||||
|
productId: string;
|
||||||
|
getAuthToken: () => string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createDiagnosticsClient(config: DiagnosticsClientConfig) {
|
||||||
|
const client = createApiClient({
|
||||||
|
baseUrl: config.baseUrl,
|
||||||
|
getToken: config.getAuthToken,
|
||||||
|
}) as ApiClient;
|
||||||
|
|
||||||
|
return {
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Sessions
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async querySessions(options: QuerySessionsOptions): Promise<QuerySessionsResult> {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.set('productId', options.productId);
|
||||||
|
if (options.status) params.set('status', options.status);
|
||||||
|
if (options.targetUserId) params.set('targetUserId', options.targetUserId);
|
||||||
|
if (options.targetDeviceId) params.set('targetDeviceId', options.targetDeviceId);
|
||||||
|
if (options.startDate) params.set('startDate', options.startDate);
|
||||||
|
if (options.endDate) params.set('endDate', options.endDate);
|
||||||
|
if (options.limit) params.set('limit', options.limit.toString());
|
||||||
|
if (options.offset) params.set('offset', options.offset.toString());
|
||||||
|
|
||||||
|
const result = await client.safeFetch<QuerySessionsResult>(`/api/diagnostics/sessions?${params.toString()}`);
|
||||||
|
if (result.error) throw new Error(result.error);
|
||||||
|
return result.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
async createSession(request: CreateSessionRequest): Promise<DebugSession> {
|
||||||
|
const result = await client.safeFetch<DebugSession>('/api/diagnostics/sessions', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(request),
|
||||||
|
});
|
||||||
|
if (result.error) throw new Error(result.error);
|
||||||
|
return result.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
async getSession(sessionId: string): Promise<DebugSession> {
|
||||||
|
const result = await client.safeFetch<DebugSession>(`/api/diagnostics/sessions/${sessionId}`);
|
||||||
|
if (result.error) throw new Error(result.error);
|
||||||
|
return result.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
async updateSession(sessionId: string, request: UpdateSessionRequest): Promise<DebugSession> {
|
||||||
|
const result = await client.safeFetch<DebugSession>(`/api/diagnostics/sessions/${sessionId}`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(request),
|
||||||
|
});
|
||||||
|
if (result.error) throw new Error(result.error);
|
||||||
|
return result.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
async cancelSession(sessionId: string, reason?: string): Promise<void> {
|
||||||
|
const result = await client.safeFetch<void>(`/api/diagnostics/sessions/${sessionId}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ reason }),
|
||||||
|
});
|
||||||
|
if (result.error) throw new Error(result.error);
|
||||||
|
},
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Traces
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async getTraces(options: QueryTracesOptions): Promise<{ traces: TraceSpan[]; total: number }> {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.set('productId', options.productId);
|
||||||
|
if (options.startTime) params.set('startTime', options.startTime);
|
||||||
|
if (options.endTime) params.set('endTime', options.endTime);
|
||||||
|
if (options.name) params.set('name', options.name);
|
||||||
|
if (options.status) params.set('status', options.status);
|
||||||
|
if (options.limit) params.set('limit', options.limit.toString());
|
||||||
|
if (options.offset) params.set('offset', options.offset.toString());
|
||||||
|
|
||||||
|
const result = await client.safeFetch<{ traces: TraceSpan[]; total: number }>(
|
||||||
|
`/api/diagnostics/sessions/${options.sessionId}/traces?${params.toString()}`
|
||||||
|
);
|
||||||
|
if (result.error) throw new Error(result.error);
|
||||||
|
return result.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Logs
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async getLogs(options: QueryLogsOptions): Promise<{ logs: LogEntry[]; total: number }> {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.set('productId', options.productId);
|
||||||
|
if (options.levels) params.set('levels', options.levels.join(','));
|
||||||
|
if (options.module) params.set('module', options.module);
|
||||||
|
if (options.search) params.set('search', options.search);
|
||||||
|
if (options.startTime) params.set('startTime', options.startTime);
|
||||||
|
if (options.endTime) params.set('endTime', options.endTime);
|
||||||
|
if (options.limit) params.set('limit', options.limit.toString());
|
||||||
|
if (options.offset) params.set('offset', options.offset.toString());
|
||||||
|
|
||||||
|
const result = await client.safeFetch<{ logs: LogEntry[]; total: number }>(
|
||||||
|
`/api/diagnostics/sessions/${options.sessionId}/logs?${params.toString()}`
|
||||||
|
);
|
||||||
|
if (result.error) throw new Error(result.error);
|
||||||
|
return result.data!;
|
||||||
|
},
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Screenshots
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async getScreenshots(sessionId: string, productId: string): Promise<
|
||||||
|
Array<{
|
||||||
|
id: string;
|
||||||
|
blobUrl: string;
|
||||||
|
capturedAt: string;
|
||||||
|
trigger: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}>
|
||||||
|
> {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.set('productId', productId);
|
||||||
|
|
||||||
|
const result = await client.safeFetch<
|
||||||
|
Array<{
|
||||||
|
id: string;
|
||||||
|
blobUrl: string;
|
||||||
|
capturedAt: string;
|
||||||
|
trigger: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}>
|
||||||
|
>(`/api/diagnostics/sessions/${sessionId}/screenshots?${params.toString()}`);
|
||||||
|
if (result.error) throw new Error(result.error);
|
||||||
|
return result.data!;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DiagnosticsClient = ReturnType<typeof createDiagnosticsClient>;
|
||||||
Loading…
Reference in New Issue
Block a user