@littlebearapps/platform-admin-sdk 2.0.0 → 2.2.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/README.md +4 -7
- package/dist/templates.d.ts +1 -1
- package/dist/templates.js +206 -4
- package/package.json +1 -1
- package/templates/full/dashboard/src/components/notifications/NotificationDropdown.tsx +130 -0
- package/templates/full/dashboard/src/components/notifications/NotificationItem.tsx +264 -0
- package/templates/full/dashboard/src/components/patterns/PatternInfoButton.tsx +60 -0
- package/templates/full/dashboard/src/components/reports/DigestStats.tsx +151 -0
- package/templates/full/dashboard/src/components/reports/FeatureUsageReport.tsx +339 -0
- package/templates/full/dashboard/src/components/reports/HealthTrendsReport.tsx +192 -0
- package/templates/full/dashboard/src/components/search/SearchResultGroup.tsx +46 -0
- package/templates/full/dashboard/src/components/search/SearchResultItem.tsx +212 -0
- package/templates/full/dashboard/src/components/usage/AIModelBreakdown.tsx +364 -0
- package/templates/full/dashboard/src/components/usage/unified/Recommendations.tsx +149 -0
- package/templates/full/dashboard/src/lib/cloudflare/alerting.ts +486 -0
- package/templates/full/dashboard/src/lib/cloudflare/graphql.ts +4785 -0
- package/templates/full/dashboard/src/lib/cloudflare/project-registry.ts +451 -0
- package/templates/full/dashboard/src/lib/notifications/api.ts +197 -0
- package/templates/full/dashboard/src/lib/notifications/types.ts.hbs +97 -0
- package/templates/full/dashboard/src/lib/patterns/api.ts +120 -0
- package/templates/full/dashboard/src/lib/patterns/types.ts +127 -0
- package/templates/full/dashboard/src/lib/reports/types.ts +231 -0
- package/templates/full/dashboard/src/lib/search/api.ts +258 -0
- package/templates/full/dashboard/src/lib/search/types.ts.hbs +115 -0
- package/templates/full/dashboard/src/lib/settings/api.ts.hbs +201 -0
- package/templates/full/dashboard/src/lib/settings/types.ts.hbs +104 -0
- package/templates/full/dashboard/src/lib/usage/allowance-config.ts.hbs +547 -0
- package/templates/full/dashboard/src/lib/usage/providers.ts +331 -0
- package/templates/full/dashboard/src/pages/api/patterns/[id]/approve.ts +49 -0
- package/templates/full/dashboard/src/pages/api/patterns/[id]/reject.ts +50 -0
- package/templates/full/dashboard/src/pages/api/reports/digests/stats.ts +38 -0
- package/templates/full/dashboard/src/pages/api/reports/digests.ts +39 -0
- package/templates/full/dashboard/src/pages/api/search/reindex/[type].ts +56 -0
- package/templates/full/dashboard/src/pages/api/test-reports/[id].ts +102 -0
- package/templates/full/dashboard/src/pages/feedback.astro +365 -0
- package/templates/full/dashboard/src/pages/kiosk.astro +206 -0
- package/templates/full/dashboard/src/pages/map.astro +561 -0
- package/templates/full/dashboard/src/pages/revenue.astro +72 -0
- package/templates/full/dashboard/src/pages/tests.astro +431 -0
- package/templates/full/scripts/ops/audit-cost-anomaly.ts +430 -0
- package/templates/full/scripts/ops/verify-account-total.ts +256 -0
- package/templates/full/tests/integration/feedback-schema.test.ts +361 -0
- package/templates/full/tests/integration/r2-archive.test.ts +108 -0
- package/templates/shared/.github/workflows/dependabot-automerge.yml +41 -0
- package/templates/shared/.github/workflows/validate-controls.yml +27 -0
- package/templates/shared/dashboard/src/components/Breadcrumbs.astro +101 -0
- package/templates/shared/dashboard/src/components/EmptyState.astro +46 -0
- package/templates/shared/dashboard/src/components/ErrorBoundary.astro +79 -0
- package/templates/shared/dashboard/src/components/LoadingSkeleton.astro +105 -0
- package/templates/shared/dashboard/src/components/PageShell.astro +72 -0
- package/templates/shared/dashboard/src/components/SkipLinks.astro +22 -0
- package/templates/shared/dashboard/src/components/Toast.astro +170 -0
- package/templates/shared/dashboard/src/components/ToastContainer.astro +156 -0
- package/templates/shared/dashboard/src/components/costs/ProviderCostsGrid.tsx +401 -0
- package/templates/shared/dashboard/src/components/costs/index.ts +4 -0
- package/templates/shared/dashboard/src/components/overview/AlertBanner.tsx +94 -0
- package/templates/shared/dashboard/src/components/overview/index.ts +9 -0
- package/templates/shared/dashboard/src/components/reports/ReportInfoButton.tsx +98 -0
- package/templates/shared/dashboard/src/components/resources/CostChart.tsx +170 -0
- package/templates/shared/dashboard/src/components/resources/ProviderCard.tsx +272 -0
- package/templates/shared/dashboard/src/components/resources/ProviderDetail.tsx +293 -0
- package/templates/shared/dashboard/src/components/settings/SettingsCard.astro +102 -0
- package/templates/shared/dashboard/src/components/usage/AllowanceGauge.astro +170 -0
- package/templates/shared/dashboard/src/components/usage/AnomalyAlerts.astro +633 -0
- package/templates/shared/dashboard/src/components/usage/BillingCycleCountdown.astro +192 -0
- package/templates/shared/dashboard/src/components/usage/BurnRateHero.astro +539 -0
- package/templates/shared/dashboard/src/components/usage/CircuitBreakerEventLog.astro +542 -0
- package/templates/shared/dashboard/src/components/usage/CircuitBreakerPanel.tsx +292 -0
- package/templates/shared/dashboard/src/components/usage/CircuitBreakerStatus.astro +669 -0
- package/templates/shared/dashboard/src/components/usage/CompactThresholdBanner.astro +531 -0
- package/templates/shared/dashboard/src/components/usage/ComparisonModeSelector.astro +651 -0
- package/templates/shared/dashboard/src/components/usage/CostBreakdownChart.astro +381 -0
- package/templates/shared/dashboard/src/components/usage/CostBreakdownTable.astro +210 -0
- package/templates/shared/dashboard/src/components/usage/CostDataTable.astro +0 -0
- package/templates/shared/dashboard/src/components/usage/CostDonutChart.astro +311 -0
- package/templates/shared/dashboard/src/components/usage/DailyCostChart.astro +632 -0
- package/templates/shared/dashboard/src/components/usage/ExportButton.astro +114 -0
- package/templates/shared/dashboard/src/components/usage/FeatureBudgetsTable.astro +872 -0
- package/templates/shared/dashboard/src/components/usage/FilterBar.astro +190 -0
- package/templates/shared/dashboard/src/components/usage/FilterToggles.astro +175 -0
- package/templates/shared/dashboard/src/components/usage/GitHubUsageCard.astro +537 -0
- package/templates/shared/dashboard/src/components/usage/OverageCostCard.astro +212 -0
- package/templates/shared/dashboard/src/components/usage/PlanUtilizationCard.astro +193 -0
- package/templates/shared/dashboard/src/components/usage/ProjectCard.astro +640 -0
- package/templates/shared/dashboard/src/components/usage/ProjectCardsGrid.astro +272 -0
- package/templates/shared/dashboard/src/components/usage/ResourceSearch.astro +279 -0
- package/templates/shared/dashboard/src/components/usage/ServiceUtilizationList.astro +604 -0
- package/templates/shared/dashboard/src/components/usage/SparklineCard.astro +399 -0
- package/templates/shared/dashboard/src/components/usage/StatsHero.astro +600 -0
- package/templates/shared/dashboard/src/components/usage/TableFilters.astro +1033 -0
- package/templates/shared/dashboard/src/components/usage/ThresholdAlert.astro +271 -0
- package/templates/shared/dashboard/src/components/usage/ThresholdSettings.astro +618 -0
- package/templates/shared/dashboard/src/components/usage/TopSpenderCard.astro +170 -0
- package/templates/shared/dashboard/src/components/usage/UnifiedResourceTable.astro +1737 -0
- package/templates/shared/dashboard/src/components/usage/UsageCard.astro +135 -0
- package/templates/shared/dashboard/src/components/usage/UsageHealthBanner.astro +387 -0
- package/templates/shared/dashboard/src/components/usage/UtilizationBar.astro +159 -0
- package/templates/shared/dashboard/src/components/usage/WorkersBreakdownTable.astro +659 -0
- package/templates/shared/dashboard/src/components/usage/daily/CostChart.astro +461 -0
- package/templates/shared/dashboard/src/components/usage/daily/CostTable.astro +946 -0
- package/templates/shared/dashboard/src/components/usage/daily/DailyOverview.astro +1079 -0
- package/templates/shared/dashboard/src/components/usage/design-tokens.ts +187 -0
- package/templates/shared/dashboard/src/components/usage/filters/InlineDateRange.astro +285 -0
- package/templates/shared/dashboard/src/components/usage/filters/PeriodButtons.astro +157 -0
- package/templates/shared/dashboard/src/components/usage/filters/ProjectSelect.astro +284 -0
- package/templates/shared/dashboard/src/components/usage/react/DashboardShell.tsx +263 -0
- package/templates/shared/dashboard/src/components/usage/react/StatusBadge.tsx +77 -0
- package/templates/shared/dashboard/src/components/usage/react/UsageChart.tsx +391 -0
- package/templates/shared/dashboard/src/components/usage/react/index.ts.hbs +30 -0
- package/templates/shared/dashboard/src/components/usage/react/types.ts +137 -0
- package/templates/shared/dashboard/src/components/usage/scripts/ai-tab-controller.ts +419 -0
- package/templates/shared/dashboard/src/components/usage/scripts/constants.ts +60 -0
- package/templates/shared/dashboard/src/components/usage/scripts/formatters.ts +62 -0
- package/templates/shared/dashboard/src/components/usage/scripts/overview-controller.ts +1633 -0
- package/templates/shared/dashboard/src/components/usage/scripts/resource-table-builder.ts +294 -0
- package/templates/shared/dashboard/src/components/usage/scripts/tabs-filters-controller.ts +464 -0
- package/templates/shared/dashboard/src/components/usage/state/index.ts +55 -0
- package/templates/shared/dashboard/src/components/usage/state/usageActions.ts +439 -0
- package/templates/shared/dashboard/src/components/usage/state/usageStore.ts +376 -0
- package/templates/shared/dashboard/src/components/usage/transformers.ts +478 -0
- package/templates/shared/dashboard/src/components/usage/types.ts +283 -0
- package/templates/shared/dashboard/src/components/usage/unified/AlertBanner.tsx +172 -0
- package/templates/shared/dashboard/src/components/usage/unified/HeroCardsRow.tsx +757 -0
- package/templates/shared/dashboard/src/components/usage/unified/LiveHeader.tsx +169 -0
- package/templates/shared/dashboard/src/components/usage/unified/ProjectsTable.tsx +448 -0
- package/templates/shared/dashboard/src/components/usage/unified/ResourceBreakdown.tsx +236 -0
- package/templates/shared/dashboard/src/components/usage/unified/Sparkline.tsx +127 -0
- package/templates/shared/dashboard/src/components/usage/unified/UnifiedShell.tsx +893 -0
- package/templates/shared/dashboard/src/components/usage/unified/index.ts.hbs +50 -0
- package/templates/shared/dashboard/src/components/usage/unified/types.ts +416 -0
- package/templates/shared/dashboard/src/components/usage/usage-colors.ts +292 -0
- package/templates/shared/dashboard/src/lib/cloudflare/analytics.ts +310 -0
- package/templates/shared/dashboard/src/lib/cloudflare/d1.ts +55 -0
- package/templates/shared/dashboard/src/lib/cloudflare/index.ts.hbs +120 -0
- package/templates/shared/dashboard/src/lib/infrastructure/types.ts +116 -0
- package/templates/shared/dashboard/src/lib/usage/fetchWithDedup.ts +101 -0
- package/templates/shared/dashboard/src/lib/usage/index.ts.hbs +12 -0
- package/templates/shared/dashboard/src/pages/api/usage/ai-models.ts +235 -0
- package/templates/shared/dashboard/src/pages/api/usage/billing-context.ts +296 -0
- package/templates/shared/scripts/test-telemetry-flow.ts +464 -0
- package/templates/shared/tests/e2e/usage-api.test.ts +909 -0
- package/templates/shared/tests/e2e/usage-export.test.ts +784 -0
- package/templates/shared/tests/e2e/usage-mobile.test.ts +531 -0
- package/templates/shared/tests/helpers/mock-storage.ts +166 -0
- package/templates/shared/tests/integration/kv-cache.test.ts +252 -0
- package/templates/shared/tests/integration/platform-usage.test.ts +956 -0
- package/templates/shared/tests/unit/billing.test.ts +331 -0
- package/templates/shared/tests/unit/cloudflare/graphql.test.ts +217 -0
- package/templates/shared/tests/unit/components/usage-transformers.test.ts +473 -0
- package/templates/shared/tests/unit/control.test.ts +226 -0
- package/templates/shared/tests/unit/cost-calculator.test.ts +141 -0
- package/templates/shared/tests/unit/economics.test.ts +365 -0
- package/templates/shared/tests/unit/telemetry-sampling.test.ts +401 -0
- package/templates/standard/dashboard/src/components/errors/PriorityBadge.astro +27 -0
- package/templates/standard/dashboard/src/components/infrastructure/HealthchecksStatus.tsx +293 -0
- package/templates/standard/dashboard/src/components/infrastructure/InfrastructureTabs.tsx +268 -0
- package/templates/standard/dashboard/src/components/reports/CircuitBreakerReport.tsx +474 -0
- package/templates/standard/dashboard/src/components/reports/CostTrendsReport.tsx +229 -0
- package/templates/standard/dashboard/src/components/reports/ErrorTrendsReport.tsx +244 -0
- package/templates/standard/dashboard/src/components/reports/ProjectHealthTable.tsx +251 -0
- package/templates/standard/dashboard/src/components/reports/WarningDigestsTable.tsx +298 -0
- package/templates/standard/dashboard/src/components/usage/react/UsageTable.tsx +385 -0
- package/templates/standard/dashboard/src/components/usage/unified/CircuitBreakerEvents.tsx +305 -0
- package/templates/standard/dashboard/src/components/usage/unified/FeatureBudgets.tsx +472 -0
- package/templates/standard/dashboard/src/lib/errors/api.ts +84 -0
- package/templates/standard/dashboard/src/lib/errors/types.ts +75 -0
- package/templates/standard/dashboard/src/lib/infrastructure/api.ts +141 -0
- package/templates/standard/dashboard/src/lib/infrastructure/gatus.ts.hbs +112 -0
- package/templates/standard/dashboard/src/lib/services/proxy/index.ts +20 -0
- package/templates/standard/dashboard/src/lib/services/proxy/proxy.ts +244 -0
- package/templates/standard/dashboard/src/lib/services/proxy/types.ts +81 -0
- package/templates/standard/dashboard/src/pages/analytics.astro +64 -0
- package/templates/standard/dashboard/src/pages/api/infrastructure/alerts.ts +85 -0
- package/templates/standard/dashboard/src/pages/api/infrastructure/healthchecks/[id]/flips.ts +110 -0
- package/templates/standard/dashboard/src/pages/api/infrastructure/healthchecks.ts +101 -0
- package/templates/standard/dashboard/src/pages/api/infrastructure/uptime/[id]/response-times.ts +121 -0
- package/templates/standard/dashboard/src/pages/api/infrastructure/uptime.ts +89 -0
- package/templates/standard/dashboard/src/pages/api/test/service-auth.ts +178 -0
- package/templates/standard/tests/integration/connectors.test.ts +241 -0
- package/templates/standard/tests/integration/github-monitor.test.ts +143 -0
- package/templates/standard/tests/integration/ingestion.test.ts +211 -0
- package/templates/standard/tests/integration/platform-sentinel.test.ts +497 -0
- package/templates/standard/tests/unit/cloudflare/alerting.test.ts +480 -0
- package/templates/standard/tests/unit/error-collector/dedup.test.ts +350 -0
- package/templates/standard/tests/unit/error-collector/github.test.ts +187 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare Observability Library
|
|
3
|
+
*
|
|
4
|
+
* Provides unified access to Cloudflare metrics, costs, and analytics.
|
|
5
|
+
*
|
|
6
|
+
* @module cloudflare
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// Cost calculator and types
|
|
10
|
+
export {
|
|
11
|
+
CF_PRICING,
|
|
12
|
+
CF_FREE_LIMITS,
|
|
13
|
+
PROJECT_PATTERNS,
|
|
14
|
+
DEFAULT_ALERT_THRESHOLDS,
|
|
15
|
+
identifyProject,
|
|
16
|
+
calculateMonthlyCosts,
|
|
17
|
+
calculateProjectCosts,
|
|
18
|
+
calculateDailyCosts,
|
|
19
|
+
getThresholdLevel,
|
|
20
|
+
analyseThresholds,
|
|
21
|
+
mergeThresholds,
|
|
22
|
+
formatNumber,
|
|
23
|
+
formatCurrency,
|
|
24
|
+
type CostBreakdown,
|
|
25
|
+
type ProjectCostBreakdown,
|
|
26
|
+
type DailyUsageMetrics,
|
|
27
|
+
type ThresholdLevel,
|
|
28
|
+
type ThresholdWarning,
|
|
29
|
+
type ThresholdAnalysis,
|
|
30
|
+
type AlertServiceType,
|
|
31
|
+
type ServiceThreshold,
|
|
32
|
+
type AlertThresholds,
|
|
33
|
+
} from './costs';
|
|
34
|
+
|
|
35
|
+
// D1 helpers
|
|
36
|
+
export { getSystemHealth, type ProjectHealthMap, type HealthCheckRecord } from './d1';
|
|
37
|
+
|
|
38
|
+
// Analytics Engine client
|
|
39
|
+
export {
|
|
40
|
+
queryAnalyticsEngine,
|
|
41
|
+
selectFromAnalytics,
|
|
42
|
+
AnalyticsEngineError,
|
|
43
|
+
type AnalyticsEngineConfig,
|
|
44
|
+
type QueryResult,
|
|
45
|
+
type AnalyticsEngineErrorCode,
|
|
46
|
+
type AnalyticsEngineResponse,
|
|
47
|
+
} from './analytics';
|
|
48
|
+
|
|
49
|
+
{{#if isFull}}
|
|
50
|
+
// GraphQL client and types (Full tier)
|
|
51
|
+
export {
|
|
52
|
+
CloudflareGraphQL,
|
|
53
|
+
type TimePeriod,
|
|
54
|
+
type DateRange,
|
|
55
|
+
type CustomDateRangeParams,
|
|
56
|
+
type CompareMode,
|
|
57
|
+
type WorkersMetrics,
|
|
58
|
+
type D1Metrics,
|
|
59
|
+
type KVMetrics,
|
|
60
|
+
type R2Metrics,
|
|
61
|
+
type DOMetrics,
|
|
62
|
+
type VectorizeInfo,
|
|
63
|
+
type AIGatewayMetrics,
|
|
64
|
+
type PagesMetrics,
|
|
65
|
+
type SparklinePoint,
|
|
66
|
+
type SparklineData,
|
|
67
|
+
type WorkersErrorBreakdown,
|
|
68
|
+
type QueuesMetrics,
|
|
69
|
+
type CacheAnalytics,
|
|
70
|
+
type PeriodComparison,
|
|
71
|
+
type AccountUsage,
|
|
72
|
+
type EnhancedAccountUsage,
|
|
73
|
+
type WorkersAIMetrics,
|
|
74
|
+
type WorkersAISummary,
|
|
75
|
+
type AIGatewaySummary,
|
|
76
|
+
type DailyCostBreakdown,
|
|
77
|
+
type DailyCostData,
|
|
78
|
+
type CloudflareSubscription,
|
|
79
|
+
type WorkersPaidPlanInclusions,
|
|
80
|
+
type CloudflareBillingProfile,
|
|
81
|
+
type CloudflareAccountSubscriptions,
|
|
82
|
+
} from './graphql';
|
|
83
|
+
|
|
84
|
+
// Alerting service and types
|
|
85
|
+
export {
|
|
86
|
+
getSeverityColour,
|
|
87
|
+
getSeverityEmoji,
|
|
88
|
+
formatPercentage,
|
|
89
|
+
generateAlertKey,
|
|
90
|
+
shouldSendAlert,
|
|
91
|
+
buildSlackMessage,
|
|
92
|
+
buildSummarySlackMessage,
|
|
93
|
+
sendSlackAlert,
|
|
94
|
+
evaluateWarning,
|
|
95
|
+
buildEmailHtml,
|
|
96
|
+
buildEmailText,
|
|
97
|
+
type CostSpikeAlert,
|
|
98
|
+
type AlertResult,
|
|
99
|
+
type SlackMessage,
|
|
100
|
+
} from './alerting';
|
|
101
|
+
|
|
102
|
+
// Project registry - D1-backed resource-to-project mapping
|
|
103
|
+
export {
|
|
104
|
+
getProjects,
|
|
105
|
+
getProject,
|
|
106
|
+
identifyProjectFromRegistry,
|
|
107
|
+
getProjectResources,
|
|
108
|
+
getProjectResourcesByType,
|
|
109
|
+
getResourceCountsByProject,
|
|
110
|
+
getResourceCountByType,
|
|
111
|
+
upsertResourceMapping,
|
|
112
|
+
deleteResourceMapping,
|
|
113
|
+
createProject,
|
|
114
|
+
updateProjectStatus,
|
|
115
|
+
clearRegistryCache,
|
|
116
|
+
type Project,
|
|
117
|
+
type ResourceMapping,
|
|
118
|
+
type ResourceType,
|
|
119
|
+
} from './project-registry';
|
|
120
|
+
{{/if}}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Infrastructure Types
|
|
3
|
+
* TypeScript types for infrastructure monitoring dashboard
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type ServiceStatus = 'deployed' | 'development' | 'deprecated' | 'paused';
|
|
7
|
+
export type ServiceType = 'worker' | 'vps-app' | 'd1' | 'kv' | 'r2' | 'vectorize' | 'queue';
|
|
8
|
+
export type MonitorStatus = 'up' | 'down' | 'paused' | 'unknown';
|
|
9
|
+
export type HealthcheckStatus = 'up' | 'down' | 'grace' | 'paused' | 'new';
|
|
10
|
+
|
|
11
|
+
export interface Service {
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
type: ServiceType;
|
|
15
|
+
status: ServiceStatus;
|
|
16
|
+
project: string;
|
|
17
|
+
scriptName?: string;
|
|
18
|
+
schedule?: string | null;
|
|
19
|
+
lastDeployed?: number | null;
|
|
20
|
+
endpoint?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ServiceRegistryStats {
|
|
24
|
+
total: number;
|
|
25
|
+
byStatus: Record<ServiceStatus, number>;
|
|
26
|
+
byType: Record<ServiceType, number>;
|
|
27
|
+
byProject: Record<string, number>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface UptimeMonitor {
|
|
31
|
+
id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
url: string;
|
|
34
|
+
status: MonitorStatus;
|
|
35
|
+
uptimeRatio: number; // Last 7 days (from Gatus)
|
|
36
|
+
responseTime: number; // Latest ms
|
|
37
|
+
lastCheckAt: number;
|
|
38
|
+
createdAt: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface HealthcheckJob {
|
|
42
|
+
id: string;
|
|
43
|
+
name: string;
|
|
44
|
+
slug: string;
|
|
45
|
+
status: HealthcheckStatus;
|
|
46
|
+
lastPing: number | null;
|
|
47
|
+
nextPing: number | null;
|
|
48
|
+
period: number; // seconds
|
|
49
|
+
grace: number; // seconds
|
|
50
|
+
nPings: number;
|
|
51
|
+
tags: string[];
|
|
52
|
+
// Optional flip summary (populated by enhanced API)
|
|
53
|
+
recentFlips?: FlipEvent[];
|
|
54
|
+
flipsToday?: number;
|
|
55
|
+
lastFailure?: number | null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* A flip represents a status change (up→down or down→up)
|
|
60
|
+
* Retrieved from Gatus events API
|
|
61
|
+
*/
|
|
62
|
+
export interface FlipEvent {
|
|
63
|
+
timestamp: number; // Unix timestamp
|
|
64
|
+
up: boolean; // true = went up, false = went down
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* A response time data point from Gatus
|
|
69
|
+
*/
|
|
70
|
+
export interface ResponseTimeDataPoint {
|
|
71
|
+
timestamp: number; // Unix timestamp
|
|
72
|
+
value: number; // Response time in ms
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Response time stats summary
|
|
77
|
+
*/
|
|
78
|
+
export interface ResponseTimeStats {
|
|
79
|
+
avg: number;
|
|
80
|
+
min: number;
|
|
81
|
+
max: number;
|
|
82
|
+
trend: 'improving' | 'stable' | 'degrading';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface Alert {
|
|
86
|
+
id: string;
|
|
87
|
+
type: string;
|
|
88
|
+
severity: 'info' | 'warning' | 'critical';
|
|
89
|
+
source: string;
|
|
90
|
+
message: string;
|
|
91
|
+
createdAt: number;
|
|
92
|
+
acknowledgedAt: number | null;
|
|
93
|
+
resolvedAt: number | null;
|
|
94
|
+
metadata?: Record<string, unknown>;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface InfrastructureStats {
|
|
98
|
+
services: ServiceRegistryStats;
|
|
99
|
+
monitors: {
|
|
100
|
+
total: number;
|
|
101
|
+
up: number;
|
|
102
|
+
down: number;
|
|
103
|
+
averageUptime: number;
|
|
104
|
+
};
|
|
105
|
+
healthchecks: {
|
|
106
|
+
total: number;
|
|
107
|
+
up: number;
|
|
108
|
+
down: number;
|
|
109
|
+
grace: number;
|
|
110
|
+
};
|
|
111
|
+
alerts: {
|
|
112
|
+
total: number;
|
|
113
|
+
unacknowledged: number;
|
|
114
|
+
critical: number;
|
|
115
|
+
};
|
|
116
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch with Request Deduplication
|
|
3
|
+
*
|
|
4
|
+
* Prevents duplicate API calls by:
|
|
5
|
+
* 1. Deduplicating concurrent requests to the same URL
|
|
6
|
+
* 2. Caching responses for a short TTL
|
|
7
|
+
*
|
|
8
|
+
* This is used by the unified dashboard components to prevent
|
|
9
|
+
* multiple components from making redundant API calls.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
interface CacheEntry<T> {
|
|
13
|
+
data: T;
|
|
14
|
+
expiresAt: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// In-flight request tracking (prevents concurrent duplicate requests)
|
|
18
|
+
const pendingRequests = new Map<string, Promise<Response>>();
|
|
19
|
+
|
|
20
|
+
// Response cache with TTL
|
|
21
|
+
const responseCache = new Map<string, CacheEntry<unknown>>();
|
|
22
|
+
|
|
23
|
+
// Default cache TTL: 5 seconds (short enough to stay fresh, long enough to dedupe)
|
|
24
|
+
const DEFAULT_CACHE_TTL = 5000;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Fetch with automatic request deduplication and short-term caching
|
|
28
|
+
*/
|
|
29
|
+
export async function fetchWithDedup<T>(
|
|
30
|
+
url: string,
|
|
31
|
+
options: RequestInit = {},
|
|
32
|
+
cacheTtl: number = DEFAULT_CACHE_TTL
|
|
33
|
+
): Promise<T> {
|
|
34
|
+
const cacheKey = `${options.method || 'GET'}:${url}`;
|
|
35
|
+
|
|
36
|
+
// Check response cache first
|
|
37
|
+
const cached = responseCache.get(cacheKey);
|
|
38
|
+
if (cached && Date.now() < cached.expiresAt) {
|
|
39
|
+
return cached.data as T;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check for in-flight request
|
|
43
|
+
const pending = pendingRequests.get(cacheKey);
|
|
44
|
+
if (pending) {
|
|
45
|
+
const response = await pending;
|
|
46
|
+
const data = await response.clone().json();
|
|
47
|
+
return data as T;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Create new request
|
|
51
|
+
const request = fetch(url, {
|
|
52
|
+
credentials: 'include',
|
|
53
|
+
...options,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
pendingRequests.set(cacheKey, request);
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const response = await request;
|
|
60
|
+
pendingRequests.delete(cacheKey);
|
|
61
|
+
|
|
62
|
+
if (!response.ok) {
|
|
63
|
+
throw new Error(`HTTP ${response.status}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const data = await response.json();
|
|
67
|
+
|
|
68
|
+
// Cache the response
|
|
69
|
+
responseCache.set(cacheKey, {
|
|
70
|
+
data,
|
|
71
|
+
expiresAt: Date.now() + cacheTtl,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return data as T;
|
|
75
|
+
} catch (error) {
|
|
76
|
+
pendingRequests.delete(cacheKey);
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Clear the response cache (useful for forced refresh)
|
|
83
|
+
*/
|
|
84
|
+
export function clearFetchCache(urlPattern?: string): void {
|
|
85
|
+
if (urlPattern) {
|
|
86
|
+
for (const key of responseCache.keys()) {
|
|
87
|
+
if (key.includes(urlPattern)) {
|
|
88
|
+
responseCache.delete(key);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
responseCache.clear();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Check if a URL is currently being fetched
|
|
98
|
+
*/
|
|
99
|
+
export function isFetching(url: string): boolean {
|
|
100
|
+
return pendingRequests.has(`GET:${url}`);
|
|
101
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Model Usage Breakdown API Endpoint
|
|
3
|
+
*
|
|
4
|
+
* Returns per-model usage and cost data from:
|
|
5
|
+
* - Workers AI (workersai_model_daily)
|
|
6
|
+
* - AI Gateway (aigateway_model_daily)
|
|
7
|
+
*
|
|
8
|
+
* @module pages/api/usage/ai-models
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { APIRoute } from 'astro';
|
|
12
|
+
import type { D1Database } from '@cloudflare/workers-types';
|
|
13
|
+
|
|
14
|
+
// =============================================================================
|
|
15
|
+
// TYPES
|
|
16
|
+
// =============================================================================
|
|
17
|
+
|
|
18
|
+
interface WorkersAIModel {
|
|
19
|
+
model: string;
|
|
20
|
+
project: string;
|
|
21
|
+
requests: number;
|
|
22
|
+
inputTokens: number;
|
|
23
|
+
outputTokens: number;
|
|
24
|
+
costUsd: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface AIGatewayModel {
|
|
28
|
+
provider: string;
|
|
29
|
+
model: string;
|
|
30
|
+
gatewayId: string;
|
|
31
|
+
requests: number;
|
|
32
|
+
cachedRequests: number;
|
|
33
|
+
tokensIn: number;
|
|
34
|
+
tokensOut: number;
|
|
35
|
+
costUsd: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface SuccessResponse {
|
|
39
|
+
success: true;
|
|
40
|
+
workersAI: {
|
|
41
|
+
models: WorkersAIModel[];
|
|
42
|
+
totalCostUsd: number;
|
|
43
|
+
totalRequests: number;
|
|
44
|
+
};
|
|
45
|
+
aiGateway: {
|
|
46
|
+
models: AIGatewayModel[];
|
|
47
|
+
totalCostUsd: number;
|
|
48
|
+
totalRequests: number;
|
|
49
|
+
byProvider: Record<string, { requests: number; costUsd: number }>;
|
|
50
|
+
};
|
|
51
|
+
period: { startDate: string; endDate: string };
|
|
52
|
+
responseTimeMs: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface ErrorResponse {
|
|
56
|
+
success: false;
|
|
57
|
+
error: string;
|
|
58
|
+
code: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// =============================================================================
|
|
62
|
+
// HANDLER
|
|
63
|
+
// =============================================================================
|
|
64
|
+
|
|
65
|
+
export const GET: APIRoute = async ({ locals, url }) => {
|
|
66
|
+
const startTime = Date.now();
|
|
67
|
+
|
|
68
|
+
const env = locals.runtime?.env as { PLATFORM_DB?: D1Database } | undefined;
|
|
69
|
+
|
|
70
|
+
if (!env?.PLATFORM_DB) {
|
|
71
|
+
const response: ErrorResponse = {
|
|
72
|
+
success: false,
|
|
73
|
+
error: 'Database binding not available',
|
|
74
|
+
code: 'BINDING_ERROR',
|
|
75
|
+
};
|
|
76
|
+
return new Response(JSON.stringify(response), {
|
|
77
|
+
status: 503,
|
|
78
|
+
headers: { 'Content-Type': 'application/json' },
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Parse query params
|
|
83
|
+
const periodParam = url.searchParams.get('period') ?? '30d';
|
|
84
|
+
const now = new Date();
|
|
85
|
+
let startDate: string;
|
|
86
|
+
|
|
87
|
+
if (periodParam === '7d') {
|
|
88
|
+
const d = new Date(now);
|
|
89
|
+
d.setDate(d.getDate() - 7);
|
|
90
|
+
startDate = d.toISOString().slice(0, 10);
|
|
91
|
+
} else if (periodParam === '24h') {
|
|
92
|
+
startDate = now.toISOString().slice(0, 10);
|
|
93
|
+
} else {
|
|
94
|
+
const d = new Date(now);
|
|
95
|
+
d.setDate(d.getDate() - 30);
|
|
96
|
+
startDate = d.toISOString().slice(0, 10);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const endDate = now.toISOString().slice(0, 10);
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
// Query Workers AI model usage
|
|
103
|
+
const workersAIResult = await env.PLATFORM_DB.prepare(
|
|
104
|
+
`
|
|
105
|
+
SELECT
|
|
106
|
+
model,
|
|
107
|
+
project,
|
|
108
|
+
SUM(requests) as requests,
|
|
109
|
+
SUM(input_tokens) as input_tokens,
|
|
110
|
+
SUM(output_tokens) as output_tokens,
|
|
111
|
+
SUM(cost_usd) as cost_usd
|
|
112
|
+
FROM workersai_model_daily
|
|
113
|
+
WHERE snapshot_date >= ? AND snapshot_date <= ?
|
|
114
|
+
GROUP BY model, project
|
|
115
|
+
ORDER BY cost_usd DESC
|
|
116
|
+
`
|
|
117
|
+
)
|
|
118
|
+
.bind(startDate, endDate)
|
|
119
|
+
.all<{
|
|
120
|
+
model: string;
|
|
121
|
+
project: string;
|
|
122
|
+
requests: number;
|
|
123
|
+
input_tokens: number;
|
|
124
|
+
output_tokens: number;
|
|
125
|
+
cost_usd: number;
|
|
126
|
+
}>();
|
|
127
|
+
|
|
128
|
+
// Query AI Gateway model usage
|
|
129
|
+
const aiGatewayResult = await env.PLATFORM_DB.prepare(
|
|
130
|
+
`
|
|
131
|
+
SELECT
|
|
132
|
+
provider,
|
|
133
|
+
model,
|
|
134
|
+
gateway_id,
|
|
135
|
+
SUM(requests) as requests,
|
|
136
|
+
SUM(cached_requests) as cached_requests,
|
|
137
|
+
SUM(tokens_in) as tokens_in,
|
|
138
|
+
SUM(tokens_out) as tokens_out,
|
|
139
|
+
SUM(cost_usd) as cost_usd
|
|
140
|
+
FROM aigateway_model_daily
|
|
141
|
+
WHERE snapshot_date >= ? AND snapshot_date <= ?
|
|
142
|
+
GROUP BY provider, model, gateway_id
|
|
143
|
+
ORDER BY cost_usd DESC
|
|
144
|
+
`
|
|
145
|
+
)
|
|
146
|
+
.bind(startDate, endDate)
|
|
147
|
+
.all<{
|
|
148
|
+
provider: string;
|
|
149
|
+
model: string;
|
|
150
|
+
gateway_id: string;
|
|
151
|
+
requests: number;
|
|
152
|
+
cached_requests: number;
|
|
153
|
+
tokens_in: number;
|
|
154
|
+
tokens_out: number;
|
|
155
|
+
cost_usd: number;
|
|
156
|
+
}>();
|
|
157
|
+
|
|
158
|
+
// Transform Workers AI results
|
|
159
|
+
const workersAIModels: WorkersAIModel[] = (workersAIResult.results ?? []).map((row) => ({
|
|
160
|
+
model: row.model,
|
|
161
|
+
project: row.project,
|
|
162
|
+
requests: row.requests ?? 0,
|
|
163
|
+
inputTokens: row.input_tokens ?? 0,
|
|
164
|
+
outputTokens: row.output_tokens ?? 0,
|
|
165
|
+
costUsd: row.cost_usd ?? 0,
|
|
166
|
+
}));
|
|
167
|
+
|
|
168
|
+
const workersAITotalCost = workersAIModels.reduce((sum, m) => sum + m.costUsd, 0);
|
|
169
|
+
const workersAITotalRequests = workersAIModels.reduce((sum, m) => sum + m.requests, 0);
|
|
170
|
+
|
|
171
|
+
// Transform AI Gateway results
|
|
172
|
+
const aiGatewayModels: AIGatewayModel[] = (aiGatewayResult.results ?? []).map((row) => ({
|
|
173
|
+
provider: row.provider,
|
|
174
|
+
model: row.model,
|
|
175
|
+
gatewayId: row.gateway_id,
|
|
176
|
+
requests: row.requests ?? 0,
|
|
177
|
+
cachedRequests: row.cached_requests ?? 0,
|
|
178
|
+
tokensIn: row.tokens_in ?? 0,
|
|
179
|
+
tokensOut: row.tokens_out ?? 0,
|
|
180
|
+
costUsd: row.cost_usd ?? 0,
|
|
181
|
+
}));
|
|
182
|
+
|
|
183
|
+
const aiGatewayTotalCost = aiGatewayModels.reduce((sum, m) => sum + m.costUsd, 0);
|
|
184
|
+
const aiGatewayTotalRequests = aiGatewayModels.reduce((sum, m) => sum + m.requests, 0);
|
|
185
|
+
|
|
186
|
+
// Group AI Gateway by provider
|
|
187
|
+
const byProvider: Record<string, { requests: number; costUsd: number }> = {};
|
|
188
|
+
for (const m of aiGatewayModels) {
|
|
189
|
+
if (!byProvider[m.provider]) {
|
|
190
|
+
byProvider[m.provider] = { requests: 0, costUsd: 0 };
|
|
191
|
+
}
|
|
192
|
+
byProvider[m.provider].requests += m.requests;
|
|
193
|
+
byProvider[m.provider].costUsd += m.costUsd;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const response: SuccessResponse = {
|
|
197
|
+
success: true,
|
|
198
|
+
workersAI: {
|
|
199
|
+
models: workersAIModels,
|
|
200
|
+
totalCostUsd: Math.round(workersAITotalCost * 100) / 100,
|
|
201
|
+
totalRequests: workersAITotalRequests,
|
|
202
|
+
},
|
|
203
|
+
aiGateway: {
|
|
204
|
+
models: aiGatewayModels,
|
|
205
|
+
totalCostUsd: Math.round(aiGatewayTotalCost * 100) / 100,
|
|
206
|
+
totalRequests: aiGatewayTotalRequests,
|
|
207
|
+
byProvider,
|
|
208
|
+
},
|
|
209
|
+
period: { startDate, endDate },
|
|
210
|
+
responseTimeMs: Date.now() - startTime,
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
return new Response(JSON.stringify(response), {
|
|
214
|
+
status: 200,
|
|
215
|
+
headers: {
|
|
216
|
+
'Content-Type': 'application/json',
|
|
217
|
+
'Cache-Control': 'public, max-age=300',
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
} catch (error) {
|
|
221
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
222
|
+
console.error('[AI-MODELS] Error:', errorMessage);
|
|
223
|
+
|
|
224
|
+
const response: ErrorResponse = {
|
|
225
|
+
success: false,
|
|
226
|
+
error: 'Failed to fetch AI model usage',
|
|
227
|
+
code: 'INTERNAL_ERROR',
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
return new Response(JSON.stringify(response), {
|
|
231
|
+
status: 500,
|
|
232
|
+
headers: { 'Content-Type': 'application/json' },
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
};
|