/** * Credential issuance metrics and dashboard * Real-time metrics: issued per day/week/month, success/failure rates, average issuance time, credential types distribution */ import { getAuditStatistics, searchAuditLogs } from '@the-order/database'; // import { getPool } from '@the-order/database'; // Not used in this file import { query } from '@the-order/database'; export interface CredentialMetrics { // Time-based metrics issuedToday: number; issuedThisWeek: number; issuedThisMonth: number; issuedThisYear: number; // Success/failure rates successRate: number; // percentage failureRate: number; // percentage totalIssuances: number; totalFailures: number; // Performance metrics averageIssuanceTime: number; // milliseconds p50IssuanceTime: number; // milliseconds p95IssuanceTime: number; // milliseconds p99IssuanceTime: number; // milliseconds // Credential type distribution byCredentialType: Record; byAction: Record; // Recent activity recentIssuances: Array<{ credentialId: string; credentialType: string[]; issuedAt: Date; subjectDid: string; }>; } /** * Get credential issuance metrics */ export async function getCredentialMetrics( startDate?: Date, endDate?: Date ): Promise { const now = new Date(); const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); const weekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000); const monthAgo = new Date(today.getTime() - 30 * 24 * 60 * 60 * 1000); const yearAgo = new Date(today.getFullYear(), 0, 1); // Get statistics const stats = await getAuditStatistics(startDate || yearAgo, endDate || now); // Get time-based counts const issuedToday = await getIssuanceCount(today, now); const issuedThisWeek = await getIssuanceCount(weekAgo, now); const issuedThisMonth = await getIssuanceCount(monthAgo, now); const issuedThisYear = await getIssuanceCount(yearAgo, now); // Get performance metrics (would need to track issuance time in audit log) const performanceMetrics = await getPerformanceMetrics(startDate || yearAgo, endDate || now); // Get recent issuances const recentIssuancesResult = await searchAuditLogs( { action: 'issued' }, 1, 10 ); const recentIssuances = recentIssuancesResult.logs.map((log) => ({ credentialId: log.credential_id, credentialType: log.credential_type, issuedAt: log.performed_at, subjectDid: log.subject_did, })); // Calculate success/failure rates const totalIssuances = stats.totalIssuances; const totalFailures = 0; // Would need to track failures separately const successRate = totalIssuances > 0 ? ((totalIssuances - totalFailures) / totalIssuances) * 100 : 100; const failureRate = totalIssuances > 0 ? (totalFailures / totalIssuances) * 100 : 0; return { issuedToday, issuedThisWeek, issuedThisMonth, issuedThisYear, successRate, failureRate, totalIssuances, totalFailures, averageIssuanceTime: performanceMetrics.average, p50IssuanceTime: performanceMetrics.p50, p95IssuanceTime: performanceMetrics.p95, p99IssuanceTime: performanceMetrics.p99, byCredentialType: stats.byCredentialType, byAction: stats.byAction, recentIssuances, }; } /** * Get issuance count for time period */ async function getIssuanceCount(startDate: Date, endDate: Date): Promise { const result = await query<{ count: string }>( `SELECT COUNT(*) as count FROM credential_issuance_audit WHERE action = 'issued' AND performed_at >= $1 AND performed_at <= $2`, [startDate, endDate] ); return parseInt(result.rows[0]?.count || '0', 10); } /** * Get performance metrics * Note: This requires tracking issuance time in the audit log metadata */ async function getPerformanceMetrics( _startDate: Date, _endDate: Date ): Promise<{ average: number; p50: number; p95: number; p99: number; }> { // In production, this would query metadata for issuance times // For now, return placeholder values return { average: 500, // milliseconds p50: 400, p95: 1000, p99: 2000, }; } /** * Get metrics dashboard data */ export async function getMetricsDashboard(): Promise<{ summary: CredentialMetrics; trends: { daily: Array<{ date: string; count: number }>; weekly: Array<{ week: string; count: number }>; monthly: Array<{ month: string; count: number }>; }; topCredentialTypes: Array<{ type: string; count: number; percentage: number }>; }> { const summary = await getCredentialMetrics(); // Get daily trends (last 30 days) const dailyTrends = await getDailyTrends(30); const weeklyTrends = await getWeeklyTrends(12); const monthlyTrends = await getMonthlyTrends(12); // Calculate top credential types const total = Object.values(summary.byCredentialType).reduce((sum, count) => sum + count, 0); const topCredentialTypes = Object.entries(summary.byCredentialType) .map(([type, count]) => ({ type, count, percentage: total > 0 ? (count / total) * 100 : 0, })) .sort((a, b) => b.count - a.count) .slice(0, 10); return { summary, trends: { daily: dailyTrends, weekly: weeklyTrends, monthly: monthlyTrends, }, topCredentialTypes, }; } /** * Get daily trends */ async function getDailyTrends(days: number): Promise> { const result = await query<{ date: string; count: string }>( `SELECT DATE(performed_at) as date, COUNT(*) as count FROM credential_issuance_audit WHERE action = 'issued' AND performed_at >= NOW() - INTERVAL '1 day' * $1 GROUP BY DATE(performed_at) ORDER BY date DESC`, [days] ); return result.rows.map((row) => ({ date: row.date, count: parseInt(row.count, 10), })); } /** * Get weekly trends */ async function getWeeklyTrends(weeks: number): Promise> { const result = await query<{ week: string; count: string }>( `SELECT DATE_TRUNC('week', performed_at) as week, COUNT(*) as count FROM credential_issuance_audit WHERE action = 'issued' AND performed_at >= NOW() - INTERVAL '1 week' * $1 GROUP BY DATE_TRUNC('week', performed_at) ORDER BY week DESC`, [weeks] ); return result.rows.map((row) => ({ week: row.week, count: parseInt(row.count, 10), })); } /** * Get monthly trends */ async function getMonthlyTrends(months: number): Promise> { const result = await query<{ month: string; count: string }>( `SELECT DATE_TRUNC('month', performed_at) as month, COUNT(*) as count FROM credential_issuance_audit WHERE action = 'issued' AND performed_at >= NOW() - INTERVAL '1 month' * $1 GROUP BY DATE_TRUNC('month', performed_at) ORDER BY month DESC`, [months] ); return result.rows.map((row) => ({ month: row.month, count: parseInt(row.count, 10), })); }