@littlebearapps/platform-admin-sdk 2.1.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.
Files changed (115) hide show
  1. package/README.md +2 -5
  2. package/dist/templates.d.ts +1 -1
  3. package/dist/templates.js +121 -3
  4. package/package.json +1 -1
  5. package/templates/full/dashboard/src/components/notifications/NotificationDropdown.tsx +130 -0
  6. package/templates/full/dashboard/src/components/notifications/NotificationItem.tsx +264 -0
  7. package/templates/full/dashboard/src/components/patterns/PatternInfoButton.tsx +60 -0
  8. package/templates/full/dashboard/src/components/reports/FeatureUsageReport.tsx +339 -0
  9. package/templates/full/dashboard/src/components/search/SearchResultGroup.tsx +46 -0
  10. package/templates/full/dashboard/src/components/search/SearchResultItem.tsx +212 -0
  11. package/templates/full/dashboard/src/pages/api/patterns/[id]/approve.ts +49 -0
  12. package/templates/full/dashboard/src/pages/api/patterns/[id]/reject.ts +50 -0
  13. package/templates/full/dashboard/src/pages/api/reports/digests/stats.ts +38 -0
  14. package/templates/full/dashboard/src/pages/api/reports/digests.ts +39 -0
  15. package/templates/full/dashboard/src/pages/api/search/reindex/[type].ts +56 -0
  16. package/templates/full/dashboard/src/pages/api/test-reports/[id].ts +102 -0
  17. package/templates/full/dashboard/src/pages/feedback.astro +365 -0
  18. package/templates/full/dashboard/src/pages/kiosk.astro +206 -0
  19. package/templates/full/dashboard/src/pages/map.astro +561 -0
  20. package/templates/full/dashboard/src/pages/revenue.astro +72 -0
  21. package/templates/full/dashboard/src/pages/tests.astro +431 -0
  22. package/templates/full/scripts/ops/audit-cost-anomaly.ts +430 -0
  23. package/templates/full/scripts/ops/verify-account-total.ts +256 -0
  24. package/templates/full/tests/integration/feedback-schema.test.ts +361 -0
  25. package/templates/full/tests/integration/r2-archive.test.ts +108 -0
  26. package/templates/shared/.github/workflows/dependabot-automerge.yml +41 -0
  27. package/templates/shared/.github/workflows/validate-controls.yml +27 -0
  28. package/templates/shared/dashboard/src/components/Breadcrumbs.astro +101 -0
  29. package/templates/shared/dashboard/src/components/EmptyState.astro +46 -0
  30. package/templates/shared/dashboard/src/components/ErrorBoundary.astro +79 -0
  31. package/templates/shared/dashboard/src/components/LoadingSkeleton.astro +105 -0
  32. package/templates/shared/dashboard/src/components/PageShell.astro +72 -0
  33. package/templates/shared/dashboard/src/components/SkipLinks.astro +22 -0
  34. package/templates/shared/dashboard/src/components/Toast.astro +170 -0
  35. package/templates/shared/dashboard/src/components/ToastContainer.astro +156 -0
  36. package/templates/shared/dashboard/src/components/costs/ProviderCostsGrid.tsx +401 -0
  37. package/templates/shared/dashboard/src/components/costs/index.ts +4 -0
  38. package/templates/shared/dashboard/src/components/overview/AlertBanner.tsx +94 -0
  39. package/templates/shared/dashboard/src/components/overview/index.ts +9 -0
  40. package/templates/shared/dashboard/src/components/resources/CostChart.tsx +170 -0
  41. package/templates/shared/dashboard/src/components/resources/ProviderCard.tsx +272 -0
  42. package/templates/shared/dashboard/src/components/resources/ProviderDetail.tsx +293 -0
  43. package/templates/shared/dashboard/src/components/settings/SettingsCard.astro +102 -0
  44. package/templates/shared/dashboard/src/components/usage/AllowanceGauge.astro +170 -0
  45. package/templates/shared/dashboard/src/components/usage/AnomalyAlerts.astro +633 -0
  46. package/templates/shared/dashboard/src/components/usage/BillingCycleCountdown.astro +192 -0
  47. package/templates/shared/dashboard/src/components/usage/BurnRateHero.astro +539 -0
  48. package/templates/shared/dashboard/src/components/usage/CircuitBreakerEventLog.astro +542 -0
  49. package/templates/shared/dashboard/src/components/usage/CircuitBreakerPanel.tsx +292 -0
  50. package/templates/shared/dashboard/src/components/usage/CircuitBreakerStatus.astro +669 -0
  51. package/templates/shared/dashboard/src/components/usage/CompactThresholdBanner.astro +531 -0
  52. package/templates/shared/dashboard/src/components/usage/ComparisonModeSelector.astro +651 -0
  53. package/templates/shared/dashboard/src/components/usage/CostBreakdownChart.astro +381 -0
  54. package/templates/shared/dashboard/src/components/usage/CostBreakdownTable.astro +210 -0
  55. package/templates/shared/dashboard/src/components/usage/CostDataTable.astro +0 -0
  56. package/templates/shared/dashboard/src/components/usage/CostDonutChart.astro +311 -0
  57. package/templates/shared/dashboard/src/components/usage/DailyCostChart.astro +632 -0
  58. package/templates/shared/dashboard/src/components/usage/ExportButton.astro +114 -0
  59. package/templates/shared/dashboard/src/components/usage/FeatureBudgetsTable.astro +872 -0
  60. package/templates/shared/dashboard/src/components/usage/FilterBar.astro +190 -0
  61. package/templates/shared/dashboard/src/components/usage/FilterToggles.astro +175 -0
  62. package/templates/shared/dashboard/src/components/usage/GitHubUsageCard.astro +537 -0
  63. package/templates/shared/dashboard/src/components/usage/OverageCostCard.astro +212 -0
  64. package/templates/shared/dashboard/src/components/usage/PlanUtilizationCard.astro +193 -0
  65. package/templates/shared/dashboard/src/components/usage/ProjectCard.astro +640 -0
  66. package/templates/shared/dashboard/src/components/usage/ProjectCardsGrid.astro +272 -0
  67. package/templates/shared/dashboard/src/components/usage/ResourceSearch.astro +279 -0
  68. package/templates/shared/dashboard/src/components/usage/ServiceUtilizationList.astro +604 -0
  69. package/templates/shared/dashboard/src/components/usage/SparklineCard.astro +399 -0
  70. package/templates/shared/dashboard/src/components/usage/StatsHero.astro +600 -0
  71. package/templates/shared/dashboard/src/components/usage/TableFilters.astro +1033 -0
  72. package/templates/shared/dashboard/src/components/usage/ThresholdAlert.astro +271 -0
  73. package/templates/shared/dashboard/src/components/usage/ThresholdSettings.astro +618 -0
  74. package/templates/shared/dashboard/src/components/usage/TopSpenderCard.astro +170 -0
  75. package/templates/shared/dashboard/src/components/usage/UnifiedResourceTable.astro +1737 -0
  76. package/templates/shared/dashboard/src/components/usage/UsageCard.astro +135 -0
  77. package/templates/shared/dashboard/src/components/usage/UsageHealthBanner.astro +387 -0
  78. package/templates/shared/dashboard/src/components/usage/UtilizationBar.astro +159 -0
  79. package/templates/shared/dashboard/src/components/usage/WorkersBreakdownTable.astro +659 -0
  80. package/templates/shared/dashboard/src/components/usage/daily/CostChart.astro +461 -0
  81. package/templates/shared/dashboard/src/components/usage/daily/CostTable.astro +946 -0
  82. package/templates/shared/dashboard/src/components/usage/daily/DailyOverview.astro +1079 -0
  83. package/templates/shared/dashboard/src/components/usage/design-tokens.ts +187 -0
  84. package/templates/shared/dashboard/src/components/usage/filters/InlineDateRange.astro +285 -0
  85. package/templates/shared/dashboard/src/components/usage/filters/PeriodButtons.astro +157 -0
  86. package/templates/shared/dashboard/src/components/usage/filters/ProjectSelect.astro +284 -0
  87. package/templates/shared/dashboard/src/components/usage/scripts/ai-tab-controller.ts +419 -0
  88. package/templates/shared/dashboard/src/components/usage/scripts/constants.ts +60 -0
  89. package/templates/shared/dashboard/src/components/usage/scripts/formatters.ts +62 -0
  90. package/templates/shared/dashboard/src/components/usage/scripts/overview-controller.ts +1633 -0
  91. package/templates/shared/dashboard/src/components/usage/scripts/resource-table-builder.ts +294 -0
  92. package/templates/shared/dashboard/src/components/usage/scripts/tabs-filters-controller.ts +464 -0
  93. package/templates/shared/dashboard/src/components/usage/state/index.ts +55 -0
  94. package/templates/shared/dashboard/src/components/usage/state/usageActions.ts +439 -0
  95. package/templates/shared/dashboard/src/components/usage/state/usageStore.ts +376 -0
  96. package/templates/shared/dashboard/src/components/usage/types.ts +283 -0
  97. package/templates/shared/dashboard/src/components/usage/usage-colors.ts +292 -0
  98. package/templates/shared/dashboard/src/pages/api/usage/ai-models.ts +235 -0
  99. package/templates/shared/dashboard/src/pages/api/usage/billing-context.ts +296 -0
  100. package/templates/shared/scripts/test-telemetry-flow.ts +464 -0
  101. package/templates/shared/tests/e2e/usage-export.test.ts +784 -0
  102. package/templates/shared/tests/e2e/usage-mobile.test.ts +531 -0
  103. package/templates/standard/dashboard/src/components/errors/PriorityBadge.astro +27 -0
  104. package/templates/standard/dashboard/src/components/infrastructure/HealthchecksStatus.tsx +293 -0
  105. package/templates/standard/dashboard/src/components/infrastructure/InfrastructureTabs.tsx +268 -0
  106. package/templates/standard/dashboard/src/pages/analytics.astro +64 -0
  107. package/templates/standard/dashboard/src/pages/api/infrastructure/alerts.ts +85 -0
  108. package/templates/standard/dashboard/src/pages/api/infrastructure/healthchecks/[id]/flips.ts +110 -0
  109. package/templates/standard/dashboard/src/pages/api/infrastructure/healthchecks.ts +101 -0
  110. package/templates/standard/dashboard/src/pages/api/infrastructure/uptime/[id]/response-times.ts +121 -0
  111. package/templates/standard/dashboard/src/pages/api/infrastructure/uptime.ts +89 -0
  112. package/templates/standard/dashboard/src/pages/api/test/service-auth.ts +178 -0
  113. package/templates/standard/tests/integration/connectors.test.ts +241 -0
  114. package/templates/standard/tests/integration/github-monitor.test.ts +143 -0
  115. package/templates/standard/tests/integration/ingestion.test.ts +211 -0
@@ -0,0 +1,296 @@
1
+ /**
2
+ * Billing Context API Endpoint
3
+ *
4
+ * Returns comprehensive billing cycle information with prorated allowances
5
+ * for accurate sub-monthly usage comparisons:
6
+ * - Current billing period dates (start, end)
7
+ * - Days elapsed and remaining
8
+ * - Plan type (free/paid/enterprise)
9
+ * - Prorated allowances for each Cloudflare service
10
+ *
11
+ * @module pages/api/usage/billing-context
12
+ * @created 2026-01-23
13
+ */
14
+
15
+ import type { APIRoute } from 'astro';
16
+ import type { D1Database } from '@cloudflare/workers-types';
17
+ import { CF_ALLOWANCES, type ServiceType } from '../../../lib/usage/allowance-config';
18
+
19
+ // =============================================================================
20
+ // TYPES
21
+ // =============================================================================
22
+
23
+ type PlanType = 'free' | 'paid' | 'enterprise';
24
+
25
+ interface BillingSettings {
26
+ accountId: string;
27
+ planType: PlanType;
28
+ billingCycleDay: number;
29
+ billingCurrency: string;
30
+ baseCostMonthly: number;
31
+ notes: string | null;
32
+ }
33
+
34
+ interface BillingPeriod {
35
+ startDate: string; // ISO date
36
+ endDate: string; // ISO date
37
+ daysInPeriod: number;
38
+ daysElapsed: number;
39
+ daysRemaining: number;
40
+ progress: number; // 0-1
41
+ }
42
+
43
+ interface ServiceAllowance {
44
+ monthly: number;
45
+ prorated: number;
46
+ unit: string;
47
+ isPaidPlan: boolean;
48
+ }
49
+
50
+ interface BillingContextResponse {
51
+ success: true;
52
+ billingPeriod: BillingPeriod;
53
+ planType: PlanType;
54
+ allowances: Record<ServiceType, ServiceAllowance>;
55
+ periodFormatted: string;
56
+ countdownText: string;
57
+ prorationFactors: {
58
+ '24h': number;
59
+ '7d': number;
60
+ '30d': number;
61
+ };
62
+ timestamp: string;
63
+ }
64
+
65
+ interface ErrorResponse {
66
+ success: false;
67
+ error: string;
68
+ code: string;
69
+ }
70
+
71
+ // =============================================================================
72
+ // HELPERS
73
+ // =============================================================================
74
+
75
+ /**
76
+ * Calculate billing period boundaries
77
+ */
78
+ function calculateBillingPeriod(billingCycleDay: number, refDate: Date): BillingPeriod {
79
+ // Normalise to calendar month if 0 or 1
80
+ const cycleDay = billingCycleDay <= 1 ? 1 : Math.min(billingCycleDay, 28);
81
+
82
+ const year = refDate.getFullYear();
83
+ const month = refDate.getMonth();
84
+ const day = refDate.getDate();
85
+
86
+ let startDate: Date;
87
+ let endDate: Date;
88
+
89
+ if (cycleDay === 1) {
90
+ // Calendar month billing
91
+ startDate = new Date(year, month, 1);
92
+ endDate = new Date(year, month + 1, 0); // Last day of current month
93
+ } else {
94
+ // Mid-month billing
95
+ if (day >= cycleDay) {
96
+ startDate = new Date(year, month, cycleDay);
97
+ endDate = new Date(year, month + 1, cycleDay - 1);
98
+ } else {
99
+ startDate = new Date(year, month - 1, cycleDay);
100
+ endDate = new Date(year, month, cycleDay - 1);
101
+ }
102
+ }
103
+
104
+ const daysInPeriod =
105
+ Math.round((endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24)) + 1;
106
+ const daysElapsed =
107
+ Math.round((refDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24)) + 1;
108
+ const daysRemaining = Math.max(0, daysInPeriod - daysElapsed);
109
+ const progress = Math.min(1, daysElapsed / daysInPeriod);
110
+
111
+ return {
112
+ startDate: startDate.toISOString().slice(0, 10),
113
+ endDate: endDate.toISOString().slice(0, 10),
114
+ daysInPeriod,
115
+ daysElapsed,
116
+ daysRemaining,
117
+ progress,
118
+ };
119
+ }
120
+
121
+ /**
122
+ * Format billing period for display
123
+ */
124
+ function formatBillingPeriod(period: BillingPeriod): string {
125
+ const formatter = new Intl.DateTimeFormat('en-AU', { month: 'short', day: 'numeric' });
126
+ const start = new Date(period.startDate);
127
+ const end = new Date(period.endDate);
128
+ return `${formatter.format(start)} - ${formatter.format(end)}`;
129
+ }
130
+
131
+ /**
132
+ * Get billing countdown text
133
+ */
134
+ function getBillingCountdownText(daysRemaining: number): string {
135
+ if (daysRemaining <= 0) return 'Billing reset today';
136
+ if (daysRemaining === 1) return '1 day until billing reset';
137
+ return `${daysRemaining} days until billing reset`;
138
+ }
139
+
140
+ /**
141
+ * Prorate a monthly allowance for a given number of days
142
+ */
143
+ function prorateAllowance(
144
+ monthlyAllowance: number,
145
+ periodDays: number,
146
+ billingDays: number
147
+ ): number {
148
+ if (billingDays <= 0) return monthlyAllowance;
149
+ if (periodDays >= billingDays) return monthlyAllowance;
150
+ if (monthlyAllowance === Infinity) return Infinity;
151
+ if (monthlyAllowance === 0) return 0;
152
+ return Math.round(monthlyAllowance * (periodDays / billingDays));
153
+ }
154
+
155
+ /**
156
+ * Build prorated allowances for all services
157
+ */
158
+ function buildProratedAllowances(
159
+ daysInPeriod: number,
160
+ queryPeriodDays: number = 1
161
+ ): Record<ServiceType, ServiceAllowance> {
162
+ const result = {} as Record<ServiceType, ServiceAllowance>;
163
+
164
+ for (const [key, config] of Object.entries(CF_ALLOWANCES)) {
165
+ const serviceType = key as ServiceType;
166
+ result[serviceType] = {
167
+ monthly: config.monthlyLimit,
168
+ prorated: prorateAllowance(config.monthlyLimit, queryPeriodDays, daysInPeriod),
169
+ unit: config.unit,
170
+ isPaidPlan: config.isPaidPlan,
171
+ };
172
+ }
173
+
174
+ return result;
175
+ }
176
+
177
+ /**
178
+ * Load billing settings from D1 or return defaults
179
+ */
180
+ async function loadBillingSettings(db: D1Database | undefined): Promise<BillingSettings> {
181
+ const defaults: BillingSettings = {
182
+ accountId: 'default',
183
+ planType: 'paid',
184
+ billingCycleDay: 1,
185
+ billingCurrency: 'USD',
186
+ baseCostMonthly: 5.0,
187
+ notes: 'Little Bear Apps - Workers Paid Plan',
188
+ };
189
+
190
+ if (!db) {
191
+ return defaults;
192
+ }
193
+
194
+ try {
195
+ const row = await db
196
+ .prepare(
197
+ `
198
+ SELECT
199
+ account_id,
200
+ plan_type,
201
+ billing_cycle_day,
202
+ billing_currency,
203
+ base_cost_monthly,
204
+ notes
205
+ FROM billing_settings
206
+ WHERE account_id = 'default'
207
+ LIMIT 1
208
+ `
209
+ )
210
+ .first<{
211
+ account_id: string;
212
+ plan_type: string;
213
+ billing_cycle_day: number;
214
+ billing_currency: string;
215
+ base_cost_monthly: number;
216
+ notes: string | null;
217
+ }>();
218
+
219
+ if (row) {
220
+ return {
221
+ accountId: row.account_id,
222
+ planType: row.plan_type as PlanType,
223
+ billingCycleDay: row.billing_cycle_day,
224
+ billingCurrency: row.billing_currency,
225
+ baseCostMonthly: row.base_cost_monthly,
226
+ notes: row.notes,
227
+ };
228
+ }
229
+ } catch (error) {
230
+ // Table may not exist yet (pre-migration)
231
+ console.warn('[BILLING-CONTEXT] billing_settings table not available, using defaults:', error);
232
+ }
233
+
234
+ return defaults;
235
+ }
236
+
237
+ // =============================================================================
238
+ // HANDLER
239
+ // =============================================================================
240
+
241
+ export const GET: APIRoute = async ({ locals, url }) => {
242
+ // Get bindings from runtime environment
243
+ const env = locals.runtime?.env as { PLATFORM_DB?: D1Database } | undefined;
244
+
245
+ // Parse optional query period parameter (for prorated allowance calculation)
246
+ const periodParam = url.searchParams.get('period') ?? '24h';
247
+ let queryPeriodDays = 1;
248
+ if (periodParam === '7d') queryPeriodDays = 7;
249
+ else if (periodParam === '30d') queryPeriodDays = 30;
250
+
251
+ try {
252
+ const settings = await loadBillingSettings(env?.PLATFORM_DB);
253
+ const now = new Date();
254
+ const period = calculateBillingPeriod(settings.billingCycleDay, now);
255
+
256
+ // Build prorated allowances for the query period
257
+ const allowances = buildProratedAllowances(period.daysInPeriod, queryPeriodDays);
258
+
259
+ const response: BillingContextResponse = {
260
+ success: true,
261
+ billingPeriod: period,
262
+ planType: settings.planType,
263
+ allowances,
264
+ periodFormatted: formatBillingPeriod(period),
265
+ countdownText: getBillingCountdownText(period.daysRemaining),
266
+ prorationFactors: {
267
+ '24h': 1 / period.daysInPeriod,
268
+ '7d': Math.min(7 / period.daysInPeriod, 1),
269
+ '30d': Math.min(30 / period.daysInPeriod, 1),
270
+ },
271
+ timestamp: now.toISOString(),
272
+ };
273
+
274
+ return new Response(JSON.stringify(response), {
275
+ status: 200,
276
+ headers: {
277
+ 'Content-Type': 'application/json',
278
+ 'Cache-Control': 'public, max-age=3600', // 1 hour cache (billing period changes slowly)
279
+ },
280
+ });
281
+ } catch (error) {
282
+ const errorMessage = error instanceof Error ? error.message : String(error);
283
+ console.error('[BILLING-CONTEXT] Error:', errorMessage);
284
+
285
+ const response: ErrorResponse = {
286
+ success: false,
287
+ error: 'Failed to load billing context',
288
+ code: 'INTERNAL_ERROR',
289
+ };
290
+
291
+ return new Response(JSON.stringify(response), {
292
+ status: 500,
293
+ headers: { 'Content-Type': 'application/json' },
294
+ });
295
+ }
296
+ };