@littlebearapps/platform-admin-sdk 1.4.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/templates.d.ts +1 -1
- package/dist/templates.js +232 -2
- package/package.json +1 -1
- package/templates/full/config/audit-targets.yaml +72 -0
- package/templates/full/dashboard/src/components/notifications/NotificationBell.tsx +30 -0
- package/templates/full/dashboard/src/components/notifications/NotificationList.tsx +116 -0
- package/templates/full/dashboard/src/components/notifications/index.ts +2 -0
- package/templates/full/dashboard/src/components/patterns/ActivePatterns.tsx +62 -0
- package/templates/full/dashboard/src/components/patterns/PatternStats.tsx +60 -0
- package/templates/full/dashboard/src/components/patterns/PatternTabs.tsx +116 -0
- package/templates/full/dashboard/src/components/patterns/SuggestionsQueue.tsx +115 -0
- package/templates/full/dashboard/src/components/patterns/SystemPatterns.tsx +52 -0
- package/templates/full/dashboard/src/components/patterns/index.ts +5 -0
- package/templates/full/dashboard/src/components/reports/GapDetectionReport.tsx +69 -0
- package/templates/full/dashboard/src/components/reports/SdkAuditReport.tsx +72 -0
- package/templates/full/dashboard/src/components/reports/index.ts +2 -0
- package/templates/full/dashboard/src/components/search/SearchModal.tsx +108 -0
- package/templates/full/dashboard/src/pages/api/notifications/[id]/read.ts +37 -0
- package/templates/full/dashboard/src/pages/api/notifications/index.ts +47 -0
- package/templates/full/dashboard/src/pages/api/notifications/read-all.ts +28 -0
- package/templates/full/dashboard/src/pages/api/notifications/unread-count.ts +31 -0
- package/templates/full/dashboard/src/pages/api/patterns/approve.ts +55 -0
- package/templates/full/dashboard/src/pages/api/patterns/cache-refresh.ts +38 -0
- package/templates/full/dashboard/src/pages/api/patterns/discover.ts +36 -0
- package/templates/full/dashboard/src/pages/api/patterns/index.ts +36 -0
- package/templates/full/dashboard/src/pages/api/patterns/ready-for-review.ts +39 -0
- package/templates/full/dashboard/src/pages/api/patterns/reject.ts +54 -0
- package/templates/full/dashboard/src/pages/api/patterns/stats.ts +39 -0
- package/templates/full/dashboard/src/pages/api/patterns/suggestions.ts +43 -0
- package/templates/full/dashboard/src/pages/api/reports/audit.ts +45 -0
- package/templates/full/dashboard/src/pages/api/reports/usage.ts +52 -0
- package/templates/full/dashboard/src/pages/api/search/index.ts +74 -0
- package/templates/full/dashboard/src/pages/api/search/reindex.ts +28 -0
- package/templates/full/dashboard/src/pages/api/search/stats.ts +27 -0
- package/templates/full/dashboard/src/pages/api/settings/index.ts +37 -0
- package/templates/full/dashboard/src/pages/api/settings/update.ts +41 -0
- package/templates/full/dashboard/src/pages/api/topology/index.ts +56 -0
- package/templates/full/dashboard/src/pages/notifications.astro +11 -0
- package/templates/full/migrations/008_auditor.sql +99 -0
- package/templates/full/migrations/010_pricing_versions.sql +110 -0
- package/templates/full/migrations/011_multi_account.sql +51 -0
- package/templates/full/scripts/ops/set-kv-pricing.ts +182 -0
- package/templates/full/scripts/ops/universal-backfill.ts +147 -0
- package/templates/full/workers/lib/ai-judge-schema.ts +181 -0
- package/templates/full/workers/lib/auditor/comprehensive-report.ts +407 -0
- package/templates/full/workers/lib/auditor/feature-coverage.ts +348 -0
- package/templates/full/workers/lib/auditor/index.ts +9 -0
- package/templates/full/workers/lib/auditor/types.ts +167 -0
- package/templates/full/workers/platform-auditor.ts +1071 -0
- package/templates/full/wrangler.auditor.jsonc.hbs +75 -0
- package/templates/shared/.github/workflows/contract-check.yml.hbs +42 -0
- package/templates/shared/.github/workflows/dashboard-deploy.yml.hbs +39 -0
- package/templates/shared/.github/workflows/platform-check.yml.hbs +28 -0
- package/templates/shared/.github/workflows/security.yml +33 -0
- package/templates/shared/config/observability.yaml.hbs +276 -0
- package/templates/shared/contracts/schemas/envelope.v1.schema.json +64 -0
- package/templates/shared/contracts/schemas/error_report.v1.schema.json +65 -0
- package/templates/shared/contracts/types/telemetry-envelope.types.ts +139 -0
- package/templates/shared/dashboard/astro.config.mjs +21 -0
- package/templates/shared/dashboard/package.json.hbs +29 -0
- package/templates/shared/dashboard/src/components/Header.astro +29 -0
- package/templates/shared/dashboard/src/components/Nav.astro.hbs +59 -0
- package/templates/shared/dashboard/src/components/infrastructure/AlertHistory.tsx +57 -0
- package/templates/shared/dashboard/src/components/infrastructure/InfrastructureStats.tsx +73 -0
- package/templates/shared/dashboard/src/components/infrastructure/ServiceRegistry.tsx +55 -0
- package/templates/shared/dashboard/src/components/infrastructure/UptimeStatus.tsx +56 -0
- package/templates/shared/dashboard/src/components/infrastructure/index.ts +4 -0
- package/templates/shared/dashboard/src/components/overview/ActivityFeed.tsx +134 -0
- package/templates/shared/dashboard/src/components/overview/CostQuadrant.tsx +131 -0
- package/templates/shared/dashboard/src/components/overview/ErrorsQuadrant.tsx +113 -0
- package/templates/shared/dashboard/src/components/overview/HealthQuadrant.tsx +87 -0
- package/templates/shared/dashboard/src/components/overview/MissionControl.tsx +139 -0
- package/templates/shared/dashboard/src/components/resources/AllowanceStatus.tsx +44 -0
- package/templates/shared/dashboard/src/components/resources/CostCentreOverview.tsx +42 -0
- package/templates/shared/dashboard/src/components/resources/ResourceTabs.tsx +69 -0
- package/templates/shared/dashboard/src/components/resources/index.ts +3 -0
- package/templates/shared/dashboard/src/components/settings/SettingsCard.tsx +21 -0
- package/templates/shared/dashboard/src/components/settings/index.ts +1 -0
- package/templates/shared/dashboard/src/components/ui/AlertBanner.tsx +39 -0
- package/templates/shared/dashboard/src/components/ui/Breadcrumbs.tsx +27 -0
- package/templates/shared/dashboard/src/components/ui/EmptyState.tsx +26 -0
- package/templates/shared/dashboard/src/components/ui/ErrorBoundary.tsx +42 -0
- package/templates/shared/dashboard/src/components/ui/LoadingSkeleton.tsx +18 -0
- package/templates/shared/dashboard/src/components/ui/PageShell.tsx +26 -0
- package/templates/shared/dashboard/src/components/ui/Sparkline.tsx +127 -0
- package/templates/shared/dashboard/src/components/ui/StatusDot.tsx +21 -0
- package/templates/shared/dashboard/src/components/ui/Toast.tsx +44 -0
- package/templates/shared/dashboard/src/components/ui/index.ts +9 -0
- package/templates/shared/dashboard/src/components/usage/AnomaliesWidget.tsx +68 -0
- package/templates/shared/dashboard/src/components/usage/HourlyUsageChart.tsx +55 -0
- package/templates/shared/dashboard/src/components/usage/PlanAllowanceDashboard.tsx +67 -0
- package/templates/shared/dashboard/src/components/usage/ProjectCostBreakdown.tsx +55 -0
- package/templates/shared/dashboard/src/components/usage/index.ts +4 -0
- package/templates/shared/dashboard/src/env.d.ts.hbs +34 -0
- package/templates/shared/dashboard/src/layouts/DashboardLayout.astro +37 -0
- package/templates/shared/dashboard/src/lib/cloudflare/costs.ts +21 -0
- package/templates/shared/dashboard/src/lib/fetch.ts +29 -0
- package/templates/shared/dashboard/src/lib/types.ts +72 -0
- package/templates/shared/dashboard/src/middleware/auth.ts +100 -0
- package/templates/shared/dashboard/src/middleware/index.ts +1 -0
- package/templates/shared/dashboard/src/pages/api/costs/overview.ts +65 -0
- package/templates/shared/dashboard/src/pages/api/costs/providers.ts +47 -0
- package/templates/shared/dashboard/src/pages/api/infrastructure/services.ts +55 -0
- package/templates/shared/dashboard/src/pages/api/infrastructure/stats.ts +99 -0
- package/templates/shared/dashboard/src/pages/api/overview/summary.ts +311 -0
- package/templates/shared/dashboard/src/pages/api/usage/allowances.ts +56 -0
- package/templates/shared/dashboard/src/pages/api/usage/anomalies.ts +45 -0
- package/templates/shared/dashboard/src/pages/api/usage/billing.ts +53 -0
- package/templates/shared/dashboard/src/pages/api/usage/circuit-breakers.ts +44 -0
- package/templates/shared/dashboard/src/pages/api/usage/granular.ts +50 -0
- package/templates/shared/dashboard/src/pages/api/usage/hourly.ts +45 -0
- package/templates/shared/dashboard/src/pages/api/usage/projects.ts +51 -0
- package/templates/shared/dashboard/src/pages/api/usage/status.ts +42 -0
- package/templates/shared/dashboard/src/pages/api/user/identity.ts +11 -0
- package/templates/shared/dashboard/src/pages/dashboard.astro +11 -0
- package/templates/shared/dashboard/src/pages/index.astro +3 -0
- package/templates/shared/dashboard/src/pages/resources.astro +11 -0
- package/templates/shared/dashboard/src/pages/settings/index.astro +28 -0
- package/templates/shared/dashboard/src/pages/settings/notifications.astro +34 -0
- package/templates/shared/dashboard/src/pages/settings/thresholds.astro +39 -0
- package/templates/shared/dashboard/src/pages/settings/usage.astro +28 -0
- package/templates/shared/dashboard/src/styles/global.css +29 -0
- package/templates/shared/dashboard/tailwind.config.mjs +9 -0
- package/templates/shared/dashboard/tsconfig.json +9 -0
- package/templates/shared/dashboard/wrangler.json.hbs +47 -0
- package/templates/shared/docs/architecture.md +89 -0
- package/templates/shared/docs/post-deploy-runbook.md +126 -0
- package/templates/shared/docs/troubleshooting.md +91 -0
- package/templates/shared/package.json.hbs +17 -1
- package/templates/shared/scripts/ops/backfill-cloudflare-daily.ts +145 -0
- package/templates/shared/scripts/ops/backfill-cloudflare-hourly.ts +473 -0
- package/templates/shared/scripts/ops/backfill-monthly-rollups.ts +125 -0
- package/templates/shared/scripts/ops/discover-graphql-datasets.ts +482 -0
- package/templates/shared/scripts/ops/reset-budget-state.ts +279 -0
- package/templates/shared/scripts/ops/validate-controls.js +141 -0
- package/templates/shared/scripts/ops/validate-pipeline.ts +237 -0
- package/templates/shared/scripts/ops/verify-account-completeness.ts +236 -0
- package/templates/shared/scripts/validate-schemas.js +61 -0
- package/templates/shared/tests/contract/validate-schemas.test.ts +130 -0
- package/templates/shared/tests/fixtures/telemetry-envelope-invalid.json +9 -0
- package/templates/shared/tests/fixtures/telemetry-envelope-valid.json +27 -0
- package/templates/shared/tests/helpers/mock-d1.ts +61 -0
- package/templates/shared/tests/helpers/mock-kv.ts +37 -0
- package/templates/shared/tests/unit/workers/batch-persistence.test.ts +133 -0
- package/templates/shared/tests/unit/workers/budget-enforcement.test.ts +214 -0
- package/templates/shared/vitest.config.ts +18 -0
- package/templates/shared/workers/lib/usage/collectors/anthropic.ts +114 -0
- package/templates/shared/workers/lib/usage/collectors/apify.ts +96 -0
- package/templates/shared/workers/lib/usage/collectors/custom-http.ts +151 -0
- package/templates/shared/workers/lib/usage/collectors/deepseek.ts +92 -0
- package/templates/shared/workers/lib/usage/collectors/gemini.ts +263 -0
- package/templates/shared/workers/lib/usage/collectors/github.ts +362 -0
- package/templates/shared/workers/lib/usage/collectors/index.ts +31 -15
- package/templates/shared/workers/lib/usage/collectors/minimax.ts +106 -0
- package/templates/shared/workers/lib/usage/collectors/openai.ts +171 -0
- package/templates/shared/workers/lib/usage/collectors/resend.ts +79 -0
- package/templates/shared/workers/lib/usage/collectors/stripe.ts +192 -0
- package/templates/shared/workers/lib/usage/shared/types.ts +46 -0
- package/templates/shared/workers/platform-usage.ts +98 -8
- package/templates/standard/dashboard/src/components/errors/ErrorStats.tsx +53 -0
- package/templates/standard/dashboard/src/components/errors/ErrorsTable.tsx +133 -0
- package/templates/standard/dashboard/src/components/errors/index.ts +2 -0
- package/templates/standard/dashboard/src/components/health/CircuitBreakerEvents.tsx +69 -0
- package/templates/standard/dashboard/src/components/health/CircuitBreakerPanel.tsx +97 -0
- package/templates/standard/dashboard/src/components/health/DlqStatusCard.tsx +52 -0
- package/templates/standard/dashboard/src/components/health/HealthTabs.tsx +86 -0
- package/templates/standard/dashboard/src/components/health/index.ts +4 -0
- package/templates/standard/dashboard/src/lib/errors.ts +28 -0
- package/templates/standard/dashboard/src/pages/api/errors/[fingerprint]/mute.ts +49 -0
- package/templates/standard/dashboard/src/pages/api/errors/[fingerprint]/resolve.ts +36 -0
- package/templates/standard/dashboard/src/pages/api/errors/[fingerprint].ts +55 -0
- package/templates/standard/dashboard/src/pages/api/errors/index.ts +58 -0
- package/templates/standard/dashboard/src/pages/api/errors/stats.ts +55 -0
- package/templates/standard/dashboard/src/pages/api/health/audit-history.ts +37 -0
- package/templates/standard/dashboard/src/pages/api/health/dlq.ts +43 -0
- package/templates/standard/dashboard/src/pages/circuit-breakers.astro +13 -0
- package/templates/standard/dashboard/src/pages/errors.astro +13 -0
- package/templates/standard/dashboard/src/pages/health.astro +11 -0
- package/templates/standard/migrations/009_topology_mapper.sql +65 -0
- package/templates/standard/tests/unit/error-collector/capture.test.ts +106 -0
- package/templates/standard/tests/unit/error-collector/fingerprint.test.ts +155 -0
- package/templates/standard/workers/lib/error-collector/email-health-alerts.ts +37 -3
- package/templates/standard/workers/lib/error-collector/gap-alerts.ts +32 -1
- package/templates/standard/workers/lib/mapper/attribution-check.ts +339 -0
- package/templates/standard/workers/lib/mapper/index.ts +7 -0
- package/templates/standard/workers/platform-mapper.ts +482 -0
- package/templates/standard/workers/platform-sdk-test-client.ts +125 -0
- package/templates/standard/wrangler.mapper.jsonc.hbs +85 -0
- package/templates/standard/wrangler.sdk-test-client.jsonc.hbs +62 -0
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comprehensive Audit Report Module
|
|
3
|
+
*
|
|
4
|
+
* Aggregates findings from all audit dimensions:
|
|
5
|
+
* - Gap detection (platform-sentinel gap_detection_log)
|
|
6
|
+
* - Attribution checking (attribution_reports)
|
|
7
|
+
* - Feature coverage audit
|
|
8
|
+
* - AI Judge results
|
|
9
|
+
*
|
|
10
|
+
* Produces ranked action items sorted by severity.
|
|
11
|
+
*
|
|
12
|
+
* @module workers/lib/auditor/comprehensive-report
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { Logger } from '@littlebearapps/platform-consumer-sdk';
|
|
16
|
+
import type { FeatureCoverageReport } from './feature-coverage';
|
|
17
|
+
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// Types
|
|
20
|
+
// =============================================================================
|
|
21
|
+
|
|
22
|
+
/** Environment bindings (generic to work with both raw Env and TrackedEnv) */
|
|
23
|
+
export interface ComprehensiveReportEnv {
|
|
24
|
+
PLATFORM_DB: {
|
|
25
|
+
prepare: (query: string) => {
|
|
26
|
+
bind: (...args: unknown[]) => {
|
|
27
|
+
run: () => Promise<unknown>;
|
|
28
|
+
all: <T>() => Promise<{ results?: T[] }>;
|
|
29
|
+
first: <T>() => Promise<T | null>;
|
|
30
|
+
};
|
|
31
|
+
all: <T>() => Promise<{ results?: T[] }>;
|
|
32
|
+
first: <T>() => Promise<T | null>;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
PLATFORM_CACHE: {
|
|
36
|
+
get: (key: string) => Promise<string | null>;
|
|
37
|
+
put: (key: string, value: string, options?: { expirationTtl?: number }) => Promise<void>;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type ActionSeverity = 'critical' | 'high' | 'medium' | 'low';
|
|
42
|
+
|
|
43
|
+
export interface ActionItem {
|
|
44
|
+
category: 'gap' | 'attribution' | 'feature' | 'ai_judge';
|
|
45
|
+
severity: ActionSeverity;
|
|
46
|
+
title: string;
|
|
47
|
+
description: string;
|
|
48
|
+
recommendation: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface ComprehensiveReport {
|
|
52
|
+
id: string;
|
|
53
|
+
generatedAt: string;
|
|
54
|
+
gapSummary: {
|
|
55
|
+
eventsCount: number;
|
|
56
|
+
totalMissingHours: number;
|
|
57
|
+
worstGapDay: string | null;
|
|
58
|
+
averageSeverity: string;
|
|
59
|
+
};
|
|
60
|
+
attributionSummary: {
|
|
61
|
+
totalResources: number;
|
|
62
|
+
attributedCount: number;
|
|
63
|
+
unattributedCount: number;
|
|
64
|
+
unattributedResources: Array<{ type: string; name: string }>;
|
|
65
|
+
};
|
|
66
|
+
featureCoverageSummary: {
|
|
67
|
+
definedCount: number;
|
|
68
|
+
activeCount: number;
|
|
69
|
+
dormantCount: number;
|
|
70
|
+
undefinedCount: number;
|
|
71
|
+
};
|
|
72
|
+
aiJudgeSummary: {
|
|
73
|
+
averageScore: number | null;
|
|
74
|
+
recommendations: string[];
|
|
75
|
+
};
|
|
76
|
+
actionItems: ActionItem[];
|
|
77
|
+
criticalItemsCount: number;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// =============================================================================
|
|
81
|
+
// Main Report Generator
|
|
82
|
+
// =============================================================================
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Generate comprehensive audit report aggregating all dimensions.
|
|
86
|
+
*/
|
|
87
|
+
export async function generateComprehensiveReport(
|
|
88
|
+
env: ComprehensiveReportEnv,
|
|
89
|
+
featureCoverage: FeatureCoverageReport | null,
|
|
90
|
+
log: Logger
|
|
91
|
+
): Promise<ComprehensiveReport> {
|
|
92
|
+
const id = crypto.randomUUID();
|
|
93
|
+
const generatedAt = new Date().toISOString();
|
|
94
|
+
const actionItems: ActionItem[] = [];
|
|
95
|
+
|
|
96
|
+
// 1. Gap detection summary (last 7 days)
|
|
97
|
+
const gapSummary = await getGapSummary(env, log);
|
|
98
|
+
|
|
99
|
+
if (gapSummary.totalMissingHours > 0) {
|
|
100
|
+
actionItems.push({
|
|
101
|
+
category: 'gap',
|
|
102
|
+
severity: gapSummary.totalMissingHours > 10 ? 'high' : 'medium',
|
|
103
|
+
title: `${gapSummary.totalMissingHours} missing hourly snapshots in last 7 days`,
|
|
104
|
+
description: `Gap detection found ${gapSummary.eventsCount} gap events. Worst day: ${gapSummary.worstGapDay || 'N/A'}`,
|
|
105
|
+
recommendation: 'Run backfill via npm run backfill or investigate collection issues',
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// 2. Attribution summary (latest report)
|
|
110
|
+
const attributionSummary = await getAttributionSummary(env, log);
|
|
111
|
+
|
|
112
|
+
if (attributionSummary.unattributedCount > 0) {
|
|
113
|
+
actionItems.push({
|
|
114
|
+
category: 'attribution',
|
|
115
|
+
severity: attributionSummary.unattributedCount > 5 ? 'high' : 'medium',
|
|
116
|
+
title: `${attributionSummary.unattributedCount} unattributed Cloudflare resources`,
|
|
117
|
+
description: `Resources not mapped to any project: ${attributionSummary.unattributedResources
|
|
118
|
+
.map((r) => `${r.type}:${r.name}`)
|
|
119
|
+
.slice(0, 5)
|
|
120
|
+
.join(', ')}${attributionSummary.unattributedCount > 5 ? '...' : ''}`,
|
|
121
|
+
recommendation: 'Add resource-to-project mappings in services.yaml',
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 3. Feature coverage summary
|
|
126
|
+
const featureCoverageSummary = featureCoverage
|
|
127
|
+
? {
|
|
128
|
+
definedCount: featureCoverage.summary.totalDefined,
|
|
129
|
+
activeCount: featureCoverage.summary.active,
|
|
130
|
+
dormantCount: featureCoverage.summary.dormant,
|
|
131
|
+
undefinedCount: featureCoverage.summary.undefined,
|
|
132
|
+
}
|
|
133
|
+
: { definedCount: 0, activeCount: 0, dormantCount: 0, undefinedCount: 0 };
|
|
134
|
+
|
|
135
|
+
if (featureCoverageSummary.dormantCount > 0) {
|
|
136
|
+
actionItems.push({
|
|
137
|
+
category: 'feature',
|
|
138
|
+
severity: 'low',
|
|
139
|
+
title: `${featureCoverageSummary.dormantCount} dormant features defined`,
|
|
140
|
+
description: 'Features defined in budgets.yaml but not sending heartbeats in 7 days',
|
|
141
|
+
recommendation: 'Review if these features are still used or can be removed from config',
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (featureCoverageSummary.undefinedCount > 0) {
|
|
146
|
+
actionItems.push({
|
|
147
|
+
category: 'feature',
|
|
148
|
+
severity: 'medium',
|
|
149
|
+
title: `${featureCoverageSummary.undefinedCount} undefined features reporting`,
|
|
150
|
+
description: 'Features sending telemetry but not defined in budgets.yaml',
|
|
151
|
+
recommendation: 'Add budget definitions for these features in budgets.yaml',
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// 4. AI Judge summary (latest results)
|
|
156
|
+
const aiJudgeSummary = await getAIJudgeSummary(env, log);
|
|
157
|
+
|
|
158
|
+
if (aiJudgeSummary.recommendations.length > 0) {
|
|
159
|
+
for (const rec of aiJudgeSummary.recommendations.slice(0, 3)) {
|
|
160
|
+
actionItems.push({
|
|
161
|
+
category: 'ai_judge',
|
|
162
|
+
severity: 'medium',
|
|
163
|
+
title: 'AI Judge recommendation',
|
|
164
|
+
description: rec,
|
|
165
|
+
recommendation: 'Review and implement AI Judge suggestions',
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Sort by severity
|
|
171
|
+
const severityOrder: Record<ActionSeverity, number> = {
|
|
172
|
+
critical: 0,
|
|
173
|
+
high: 1,
|
|
174
|
+
medium: 2,
|
|
175
|
+
low: 3,
|
|
176
|
+
};
|
|
177
|
+
actionItems.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
|
|
178
|
+
|
|
179
|
+
const report: ComprehensiveReport = {
|
|
180
|
+
id,
|
|
181
|
+
generatedAt,
|
|
182
|
+
gapSummary,
|
|
183
|
+
attributionSummary,
|
|
184
|
+
featureCoverageSummary,
|
|
185
|
+
aiJudgeSummary,
|
|
186
|
+
actionItems,
|
|
187
|
+
criticalItemsCount: actionItems.filter((i) => i.severity === 'critical').length,
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
log.info('Generated comprehensive audit report', {
|
|
191
|
+
id,
|
|
192
|
+
actionItems: actionItems.length,
|
|
193
|
+
criticalItems: report.criticalItemsCount,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
return report;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// =============================================================================
|
|
200
|
+
// Data Aggregation Helpers
|
|
201
|
+
// =============================================================================
|
|
202
|
+
|
|
203
|
+
/** Get gap detection summary from last 7 days */
|
|
204
|
+
async function getGapSummary(
|
|
205
|
+
env: ComprehensiveReportEnv,
|
|
206
|
+
log: Logger
|
|
207
|
+
): Promise<ComprehensiveReport['gapSummary']> {
|
|
208
|
+
try {
|
|
209
|
+
const result = await env.PLATFORM_DB.prepare(
|
|
210
|
+
`
|
|
211
|
+
SELECT
|
|
212
|
+
COUNT(*) as events_count,
|
|
213
|
+
SUM(missing_hours_count) as total_missing_hours,
|
|
214
|
+
MAX(CASE WHEN missing_hours_count = (
|
|
215
|
+
SELECT MAX(missing_hours_count) FROM gap_detection_log
|
|
216
|
+
WHERE detection_time > datetime('now', '-7 days')
|
|
217
|
+
) THEN date(detection_time) END) as worst_gap_day,
|
|
218
|
+
AVG(CASE severity
|
|
219
|
+
WHEN 'critical' THEN 3
|
|
220
|
+
WHEN 'warning' THEN 2
|
|
221
|
+
WHEN 'ok' THEN 1
|
|
222
|
+
ELSE 0
|
|
223
|
+
END) as avg_severity_score
|
|
224
|
+
FROM gap_detection_log
|
|
225
|
+
WHERE detection_time > datetime('now', '-7 days')
|
|
226
|
+
`
|
|
227
|
+
).first<{
|
|
228
|
+
events_count: number;
|
|
229
|
+
total_missing_hours: number;
|
|
230
|
+
worst_gap_day: string | null;
|
|
231
|
+
avg_severity_score: number;
|
|
232
|
+
}>();
|
|
233
|
+
|
|
234
|
+
if (!result) {
|
|
235
|
+
return { eventsCount: 0, totalMissingHours: 0, worstGapDay: null, averageSeverity: 'ok' };
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const avgScore = result.avg_severity_score ?? 0;
|
|
239
|
+
let averageSeverity = 'ok';
|
|
240
|
+
if (avgScore >= 2.5) averageSeverity = 'critical';
|
|
241
|
+
else if (avgScore >= 1.5) averageSeverity = 'warning';
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
eventsCount: result.events_count ?? 0,
|
|
245
|
+
totalMissingHours: result.total_missing_hours ?? 0,
|
|
246
|
+
worstGapDay: result.worst_gap_day,
|
|
247
|
+
averageSeverity,
|
|
248
|
+
};
|
|
249
|
+
} catch (error) {
|
|
250
|
+
log.error('Failed to get gap summary', error);
|
|
251
|
+
return { eventsCount: 0, totalMissingHours: 0, worstGapDay: null, averageSeverity: 'ok' };
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** Get attribution summary from latest report */
|
|
256
|
+
async function getAttributionSummary(
|
|
257
|
+
env: ComprehensiveReportEnv,
|
|
258
|
+
log: Logger
|
|
259
|
+
): Promise<ComprehensiveReport['attributionSummary']> {
|
|
260
|
+
try {
|
|
261
|
+
const result = await env.PLATFORM_DB.prepare(
|
|
262
|
+
`
|
|
263
|
+
SELECT report_json
|
|
264
|
+
FROM attribution_reports
|
|
265
|
+
ORDER BY discovery_time DESC
|
|
266
|
+
LIMIT 1
|
|
267
|
+
`
|
|
268
|
+
).first<{ report_json: string }>();
|
|
269
|
+
|
|
270
|
+
if (!result?.report_json) {
|
|
271
|
+
return { totalResources: 0, attributedCount: 0, unattributedCount: 0, unattributedResources: [] };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const report = JSON.parse(result.report_json) as {
|
|
275
|
+
totalResources: number;
|
|
276
|
+
attributedCount: number;
|
|
277
|
+
unattributedCount: number;
|
|
278
|
+
unattributed: Array<{ resource: { resourceType: string; resourceName: string } }>;
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
return {
|
|
282
|
+
totalResources: report.totalResources,
|
|
283
|
+
attributedCount: report.attributedCount,
|
|
284
|
+
unattributedCount: report.unattributedCount,
|
|
285
|
+
unattributedResources: (report.unattributed || []).map((a) => ({
|
|
286
|
+
type: a.resource.resourceType,
|
|
287
|
+
name: a.resource.resourceName,
|
|
288
|
+
})),
|
|
289
|
+
};
|
|
290
|
+
} catch (error) {
|
|
291
|
+
log.error('Failed to get attribution summary', error);
|
|
292
|
+
return { totalResources: 0, attributedCount: 0, unattributedCount: 0, unattributedResources: [] };
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** Get AI Judge summary from latest audit results */
|
|
297
|
+
async function getAIJudgeSummary(
|
|
298
|
+
env: ComprehensiveReportEnv,
|
|
299
|
+
log: Logger
|
|
300
|
+
): Promise<ComprehensiveReport['aiJudgeSummary']> {
|
|
301
|
+
try {
|
|
302
|
+
const result = await env.PLATFORM_DB.prepare(
|
|
303
|
+
`
|
|
304
|
+
SELECT
|
|
305
|
+
AVG(ai_judge_score) as avg_score,
|
|
306
|
+
GROUP_CONCAT(ai_judge_summary) as summaries
|
|
307
|
+
FROM audit_results
|
|
308
|
+
WHERE created_at > datetime('now', '-7 days')
|
|
309
|
+
AND ai_judge_score IS NOT NULL
|
|
310
|
+
`
|
|
311
|
+
).first<{ avg_score: number | null; summaries: string | null }>();
|
|
312
|
+
|
|
313
|
+
if (!result?.avg_score) {
|
|
314
|
+
return { averageScore: null, recommendations: [] };
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const summaries = result.summaries?.split(',') ?? [];
|
|
318
|
+
const uniqueRecommendations = [...new Set(summaries)].filter((s) => s.trim()).slice(0, 5);
|
|
319
|
+
|
|
320
|
+
return {
|
|
321
|
+
averageScore: Math.round(result.avg_score),
|
|
322
|
+
recommendations: uniqueRecommendations,
|
|
323
|
+
};
|
|
324
|
+
} catch (error) {
|
|
325
|
+
log.error('Failed to get AI Judge summary', error);
|
|
326
|
+
return { averageScore: null, recommendations: [] };
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// =============================================================================
|
|
331
|
+
// Storage
|
|
332
|
+
// =============================================================================
|
|
333
|
+
|
|
334
|
+
/** Store comprehensive audit report in D1 */
|
|
335
|
+
export async function storeComprehensiveReport(
|
|
336
|
+
env: ComprehensiveReportEnv,
|
|
337
|
+
report: ComprehensiveReport,
|
|
338
|
+
log: Logger
|
|
339
|
+
): Promise<void> {
|
|
340
|
+
try {
|
|
341
|
+
await env.PLATFORM_DB.prepare(
|
|
342
|
+
`
|
|
343
|
+
INSERT INTO comprehensive_audit_reports (
|
|
344
|
+
id, generated_at,
|
|
345
|
+
gap_events_count, total_missing_hours, worst_gap_day, average_gap_severity,
|
|
346
|
+
total_resources, attributed_count, unattributed_count, unattributed_resources,
|
|
347
|
+
defined_features_count, active_features_count, dormant_features_count, undefined_features_count,
|
|
348
|
+
ai_judge_avg_score, ai_judge_recommendations,
|
|
349
|
+
action_items_count, critical_items_count, action_items,
|
|
350
|
+
report_json
|
|
351
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
352
|
+
`
|
|
353
|
+
)
|
|
354
|
+
.bind(
|
|
355
|
+
report.id,
|
|
356
|
+
report.generatedAt,
|
|
357
|
+
report.gapSummary.eventsCount,
|
|
358
|
+
report.gapSummary.totalMissingHours,
|
|
359
|
+
report.gapSummary.worstGapDay,
|
|
360
|
+
report.gapSummary.averageSeverity,
|
|
361
|
+
report.attributionSummary.totalResources,
|
|
362
|
+
report.attributionSummary.attributedCount,
|
|
363
|
+
report.attributionSummary.unattributedCount,
|
|
364
|
+
JSON.stringify(report.attributionSummary.unattributedResources),
|
|
365
|
+
report.featureCoverageSummary.definedCount,
|
|
366
|
+
report.featureCoverageSummary.activeCount,
|
|
367
|
+
report.featureCoverageSummary.dormantCount,
|
|
368
|
+
report.featureCoverageSummary.undefinedCount,
|
|
369
|
+
report.aiJudgeSummary.averageScore,
|
|
370
|
+
JSON.stringify(report.aiJudgeSummary.recommendations),
|
|
371
|
+
report.actionItems.length,
|
|
372
|
+
report.criticalItemsCount,
|
|
373
|
+
JSON.stringify(report.actionItems),
|
|
374
|
+
JSON.stringify(report)
|
|
375
|
+
)
|
|
376
|
+
.run();
|
|
377
|
+
|
|
378
|
+
log.info('Stored comprehensive audit report', { id: report.id });
|
|
379
|
+
} catch (error) {
|
|
380
|
+
log.error('Failed to store comprehensive report', error);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/** Get latest comprehensive report from D1 */
|
|
385
|
+
export async function getLatestComprehensiveReport(
|
|
386
|
+
env: ComprehensiveReportEnv,
|
|
387
|
+
log: Logger
|
|
388
|
+
): Promise<ComprehensiveReport | null> {
|
|
389
|
+
try {
|
|
390
|
+
const result = await env.PLATFORM_DB.prepare(
|
|
391
|
+
`
|
|
392
|
+
SELECT report_json
|
|
393
|
+
FROM comprehensive_audit_reports
|
|
394
|
+
ORDER BY generated_at DESC
|
|
395
|
+
LIMIT 1
|
|
396
|
+
`
|
|
397
|
+
).first<{ report_json: string }>();
|
|
398
|
+
|
|
399
|
+
if (result?.report_json) {
|
|
400
|
+
return JSON.parse(result.report_json) as ComprehensiveReport;
|
|
401
|
+
}
|
|
402
|
+
return null;
|
|
403
|
+
} catch (error) {
|
|
404
|
+
log.error('Failed to get latest comprehensive report', error);
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
407
|
+
}
|