@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.
- package/README.md +2 -5
- package/dist/templates.d.ts +1 -1
- package/dist/templates.js +121 -3
- 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/FeatureUsageReport.tsx +339 -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/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/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/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/types.ts +283 -0
- package/templates/shared/dashboard/src/components/usage/usage-colors.ts +292 -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-export.test.ts +784 -0
- package/templates/shared/tests/e2e/usage-mobile.test.ts +531 -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/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
|
@@ -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
|
+
};
|