@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,27 @@
|
|
|
1
|
+
import type { APIRoute } from 'astro';
|
|
2
|
+
|
|
3
|
+
export const GET: APIRoute = async ({ locals }) => {
|
|
4
|
+
const db = (locals.runtime?.env as { PLATFORM_DB?: D1Database } | undefined)?.PLATFORM_DB;
|
|
5
|
+
|
|
6
|
+
if (!db) {
|
|
7
|
+
return new Response(JSON.stringify({ indexed: 0, lastUpdated: null }), {
|
|
8
|
+
headers: { 'Content-Type': 'application/json' },
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const count = await db
|
|
14
|
+
.prepare(`SELECT COUNT(*) as count FROM search_index LIMIT 1`)
|
|
15
|
+
.first<{ count: number }>();
|
|
16
|
+
|
|
17
|
+
return new Response(
|
|
18
|
+
JSON.stringify({ indexed: count?.count ?? 0 }),
|
|
19
|
+
{ headers: { 'Content-Type': 'application/json', 'Cache-Control': 'max-age=60' } }
|
|
20
|
+
);
|
|
21
|
+
} catch {
|
|
22
|
+
return new Response(JSON.stringify({ indexed: 0 }), {
|
|
23
|
+
status: 500,
|
|
24
|
+
headers: { 'Content-Type': 'application/json' },
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { APIRoute } from 'astro';
|
|
2
|
+
|
|
3
|
+
export const GET: APIRoute = async ({ locals }) => {
|
|
4
|
+
const db = (locals.runtime?.env as { PLATFORM_DB?: D1Database } | undefined)?.PLATFORM_DB;
|
|
5
|
+
|
|
6
|
+
if (!db) {
|
|
7
|
+
return new Response(JSON.stringify({ settings: {} }), {
|
|
8
|
+
headers: { 'Content-Type': 'application/json' },
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const rows = await db
|
|
14
|
+
.prepare(
|
|
15
|
+
`SELECT key, value, updated_at
|
|
16
|
+
FROM platform_settings
|
|
17
|
+
ORDER BY key ASC
|
|
18
|
+
LIMIT 200`
|
|
19
|
+
)
|
|
20
|
+
.all<{ key: string; value: string; updated_at: string }>();
|
|
21
|
+
|
|
22
|
+
const settings: Record<string, { value: string; updatedAt: string }> = {};
|
|
23
|
+
for (const row of rows.results ?? []) {
|
|
24
|
+
settings[row.key] = { value: row.value, updatedAt: row.updated_at };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return new Response(
|
|
28
|
+
JSON.stringify({ settings }),
|
|
29
|
+
{ headers: { 'Content-Type': 'application/json', 'Cache-Control': 'max-age=30' } }
|
|
30
|
+
);
|
|
31
|
+
} catch {
|
|
32
|
+
return new Response(JSON.stringify({ settings: {} }), {
|
|
33
|
+
status: 500,
|
|
34
|
+
headers: { 'Content-Type': 'application/json' },
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { APIRoute } from 'astro';
|
|
2
|
+
|
|
3
|
+
export const POST: APIRoute = async ({ request, locals }) => {
|
|
4
|
+
const db = (locals.runtime?.env as { PLATFORM_DB?: D1Database } | undefined)?.PLATFORM_DB;
|
|
5
|
+
|
|
6
|
+
if (!db) {
|
|
7
|
+
return new Response(JSON.stringify({ error: 'Database not available' }), {
|
|
8
|
+
status: 503,
|
|
9
|
+
headers: { 'Content-Type': 'application/json' },
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const body = (await request.json()) as { key?: string; value?: string };
|
|
15
|
+
if (!body.key || body.value === undefined) {
|
|
16
|
+
return new Response(JSON.stringify({ error: 'key and value are required' }), {
|
|
17
|
+
status: 400,
|
|
18
|
+
headers: { 'Content-Type': 'application/json' },
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
await db
|
|
23
|
+
.prepare(
|
|
24
|
+
`INSERT INTO platform_settings (key, value, updated_at)
|
|
25
|
+
VALUES (?, ?, datetime('now'))
|
|
26
|
+
ON CONFLICT (key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at`
|
|
27
|
+
)
|
|
28
|
+
.bind(body.key, body.value)
|
|
29
|
+
.run();
|
|
30
|
+
|
|
31
|
+
return new Response(
|
|
32
|
+
JSON.stringify({ ok: true, key: body.key }),
|
|
33
|
+
{ headers: { 'Content-Type': 'application/json' } }
|
|
34
|
+
);
|
|
35
|
+
} catch {
|
|
36
|
+
return new Response(JSON.stringify({ error: 'Update failed' }), {
|
|
37
|
+
status: 500,
|
|
38
|
+
headers: { 'Content-Type': 'application/json' },
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { APIRoute } from 'astro';
|
|
2
|
+
|
|
3
|
+
export const GET: APIRoute = async ({ locals }) => {
|
|
4
|
+
const db = (locals.runtime?.env as { PLATFORM_DB?: D1Database } | undefined)?.PLATFORM_DB;
|
|
5
|
+
|
|
6
|
+
if (!db) {
|
|
7
|
+
return new Response(JSON.stringify({ nodes: [], edges: [] }), {
|
|
8
|
+
headers: { 'Content-Type': 'application/json' },
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const [workers, bindings] = await Promise.all([
|
|
14
|
+
db
|
|
15
|
+
.prepare(
|
|
16
|
+
`SELECT script_name, project, worker_type, last_seen_at
|
|
17
|
+
FROM resource_project_mapping
|
|
18
|
+
WHERE resource_type = 'worker'
|
|
19
|
+
ORDER BY script_name ASC
|
|
20
|
+
LIMIT 100`
|
|
21
|
+
)
|
|
22
|
+
.all(),
|
|
23
|
+
db
|
|
24
|
+
.prepare(
|
|
25
|
+
`SELECT source_worker, target_worker, binding_type
|
|
26
|
+
FROM service_bindings
|
|
27
|
+
ORDER BY source_worker ASC
|
|
28
|
+
LIMIT 200`
|
|
29
|
+
)
|
|
30
|
+
.all(),
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
const nodes = (workers.results ?? []).map((w: Record<string, unknown>) => ({
|
|
34
|
+
id: w.script_name as string,
|
|
35
|
+
project: w.project as string,
|
|
36
|
+
type: w.worker_type as string,
|
|
37
|
+
lastSeen: w.last_seen_at as string,
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
const edges = (bindings.results ?? []).map((b: Record<string, unknown>) => ({
|
|
41
|
+
from: b.source_worker as string,
|
|
42
|
+
to: b.target_worker as string,
|
|
43
|
+
type: b.binding_type as string,
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
return new Response(
|
|
47
|
+
JSON.stringify({ nodes, edges }),
|
|
48
|
+
{ headers: { 'Content-Type': 'application/json', 'Cache-Control': 'max-age=300' } }
|
|
49
|
+
);
|
|
50
|
+
} catch {
|
|
51
|
+
return new Response(JSON.stringify({ nodes: [], edges: [] }), {
|
|
52
|
+
status: 500,
|
|
53
|
+
headers: { 'Content-Type': 'application/json' },
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
import DashboardLayout from '../../layouts/DashboardLayout.astro';
|
|
3
|
+
import { NotificationList } from '../../components/notifications/NotificationList';
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<DashboardLayout title="Notifications">
|
|
7
|
+
<div class="max-w-4xl mx-auto">
|
|
8
|
+
<h2 class="text-xl font-bold text-gray-900 dark:text-white mb-4">Notifications</h2>
|
|
9
|
+
<NotificationList client:load />
|
|
10
|
+
</div>
|
|
11
|
+
</DashboardLayout>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
-- =============================================================================
|
|
2
|
+
-- 008_auditor.sql — Auditor Reports & Feature Coverage
|
|
3
|
+
-- =============================================================================
|
|
4
|
+
-- Additional tables for FULL tier SDK integration auditor.
|
|
5
|
+
-- Base audit_results table is in shared migration 004_settings_alerts.sql.
|
|
6
|
+
--
|
|
7
|
+
-- Tables:
|
|
8
|
+
-- attribution_reports — Resource-to-project mapping status
|
|
9
|
+
-- feature_coverage_audit — Feature active/dormant/undefined tracking
|
|
10
|
+
-- comprehensive_audit_reports — Weekly aggregated audit findings
|
|
11
|
+
-- =============================================================================
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
-- =============================================================================
|
|
15
|
+
-- ATTRIBUTION REPORTS (from platform-mapper discovery runs)
|
|
16
|
+
-- =============================================================================
|
|
17
|
+
-- Stores resource-to-project attribution status from topology discovery.
|
|
18
|
+
|
|
19
|
+
CREATE TABLE IF NOT EXISTS attribution_reports (
|
|
20
|
+
id TEXT PRIMARY KEY,
|
|
21
|
+
discovery_time TEXT NOT NULL,
|
|
22
|
+
total_resources INTEGER NOT NULL DEFAULT 0,
|
|
23
|
+
attributed_count INTEGER NOT NULL DEFAULT 0,
|
|
24
|
+
unattributed_count INTEGER NOT NULL DEFAULT 0,
|
|
25
|
+
report_json TEXT NOT NULL,
|
|
26
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
CREATE INDEX IF NOT EXISTS idx_attribution_discovery_time
|
|
30
|
+
ON attribution_reports(discovery_time);
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
-- =============================================================================
|
|
34
|
+
-- FEATURE COVERAGE AUDIT
|
|
35
|
+
-- =============================================================================
|
|
36
|
+
-- Tracks which features from budgets.yaml are active vs dormant.
|
|
37
|
+
-- Populated by platform-auditor feature coverage checks.
|
|
38
|
+
|
|
39
|
+
CREATE TABLE IF NOT EXISTS feature_coverage_audit (
|
|
40
|
+
id TEXT PRIMARY KEY,
|
|
41
|
+
audit_time TEXT NOT NULL,
|
|
42
|
+
project TEXT NOT NULL,
|
|
43
|
+
feature TEXT NOT NULL,
|
|
44
|
+
-- Status: active (heartbeats in last 7d), dormant (defined but no heartbeats),
|
|
45
|
+
-- undefined (reporting telemetry but not in config)
|
|
46
|
+
status TEXT NOT NULL CHECK (status IN ('active', 'dormant', 'undefined')),
|
|
47
|
+
last_heartbeat TEXT,
|
|
48
|
+
events_last_7d INTEGER DEFAULT 0,
|
|
49
|
+
defined_budget INTEGER,
|
|
50
|
+
budget_unit TEXT,
|
|
51
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
CREATE INDEX IF NOT EXISTS idx_feature_coverage_project
|
|
55
|
+
ON feature_coverage_audit(project);
|
|
56
|
+
CREATE INDEX IF NOT EXISTS idx_feature_coverage_status
|
|
57
|
+
ON feature_coverage_audit(status);
|
|
58
|
+
CREATE INDEX IF NOT EXISTS idx_feature_coverage_audit_time
|
|
59
|
+
ON feature_coverage_audit(audit_time);
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
-- =============================================================================
|
|
63
|
+
-- COMPREHENSIVE AUDIT REPORTS
|
|
64
|
+
-- =============================================================================
|
|
65
|
+
-- Weekly aggregated reports combining all audit dimensions:
|
|
66
|
+
-- gap analysis, resource attribution, feature coverage, and AI Judge scores.
|
|
67
|
+
|
|
68
|
+
CREATE TABLE IF NOT EXISTS comprehensive_audit_reports (
|
|
69
|
+
id TEXT PRIMARY KEY,
|
|
70
|
+
generated_at TEXT NOT NULL,
|
|
71
|
+
-- Gap summary (from sentinel gap_detection_log)
|
|
72
|
+
gap_events_count INTEGER NOT NULL DEFAULT 0,
|
|
73
|
+
total_missing_hours INTEGER NOT NULL DEFAULT 0,
|
|
74
|
+
worst_gap_day TEXT,
|
|
75
|
+
average_gap_severity TEXT,
|
|
76
|
+
-- Attribution summary (from mapper attribution_reports)
|
|
77
|
+
total_resources INTEGER NOT NULL DEFAULT 0,
|
|
78
|
+
attributed_count INTEGER NOT NULL DEFAULT 0,
|
|
79
|
+
unattributed_count INTEGER NOT NULL DEFAULT 0,
|
|
80
|
+
unattributed_resources TEXT, -- JSON array of {type, name}
|
|
81
|
+
-- Feature coverage summary
|
|
82
|
+
defined_features_count INTEGER NOT NULL DEFAULT 0,
|
|
83
|
+
active_features_count INTEGER NOT NULL DEFAULT 0,
|
|
84
|
+
dormant_features_count INTEGER NOT NULL DEFAULT 0,
|
|
85
|
+
undefined_features_count INTEGER NOT NULL DEFAULT 0,
|
|
86
|
+
-- AI Judge summary
|
|
87
|
+
ai_judge_avg_score REAL,
|
|
88
|
+
ai_judge_recommendations TEXT, -- JSON array
|
|
89
|
+
-- Action items
|
|
90
|
+
action_items_count INTEGER NOT NULL DEFAULT 0,
|
|
91
|
+
critical_items_count INTEGER NOT NULL DEFAULT 0,
|
|
92
|
+
action_items TEXT, -- JSON array
|
|
93
|
+
-- Full report
|
|
94
|
+
report_json TEXT NOT NULL,
|
|
95
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
CREATE INDEX IF NOT EXISTS idx_comprehensive_audit_generated
|
|
99
|
+
ON comprehensive_audit_reports(generated_at);
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
-- Migration 010: Pricing Version Tracking
|
|
2
|
+
--
|
|
3
|
+
-- Purpose: Audit trail for Cloudflare pricing changes with historical recomputation support.
|
|
4
|
+
--
|
|
5
|
+
-- Benefits:
|
|
6
|
+
-- - Track when pricing changes occur (Cloudflare updates rates periodically)
|
|
7
|
+
-- - Recompute historical costs when pricing is corrected
|
|
8
|
+
-- - Audit trail for billing reconciliation
|
|
9
|
+
-- - Support for pricing A/B comparisons
|
|
10
|
+
|
|
11
|
+
-- Create pricing_versions table
|
|
12
|
+
CREATE TABLE IF NOT EXISTS pricing_versions (
|
|
13
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
14
|
+
version_name TEXT NOT NULL, -- Human-readable name (e.g., "2026-01", "2025-workers-update")
|
|
15
|
+
effective_from TEXT NOT NULL, -- ISO date when pricing became effective
|
|
16
|
+
effective_to TEXT, -- NULL = current, ISO date when superseded
|
|
17
|
+
source_url TEXT, -- CF pricing page reference for audit
|
|
18
|
+
pricing_json TEXT NOT NULL, -- Full pricing config as JSON
|
|
19
|
+
allowances_json TEXT NOT NULL, -- Paid plan allowances as JSON
|
|
20
|
+
notes TEXT, -- Optional notes about changes
|
|
21
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
22
|
+
created_by TEXT DEFAULT 'system' -- 'system' for auto-detected, 'manual' for user overrides
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
-- Index for querying current/historical pricing
|
|
26
|
+
CREATE INDEX IF NOT EXISTS idx_pricing_versions_effective
|
|
27
|
+
ON pricing_versions (effective_from, effective_to);
|
|
28
|
+
|
|
29
|
+
-- Add pricing_version_id to daily rollups for cost recomputation
|
|
30
|
+
-- Note: SQLite doesn't support adding constraints in ALTER, so we just add the column
|
|
31
|
+
ALTER TABLE daily_usage_rollups ADD COLUMN pricing_version_id INTEGER;
|
|
32
|
+
|
|
33
|
+
-- Add pricing_version_id to monthly rollups
|
|
34
|
+
ALTER TABLE monthly_usage_rollups ADD COLUMN pricing_version_id INTEGER;
|
|
35
|
+
|
|
36
|
+
-- Seed with current pricing (January 2026)
|
|
37
|
+
INSERT INTO pricing_versions (
|
|
38
|
+
version_name,
|
|
39
|
+
effective_from,
|
|
40
|
+
effective_to,
|
|
41
|
+
source_url,
|
|
42
|
+
pricing_json,
|
|
43
|
+
allowances_json,
|
|
44
|
+
notes,
|
|
45
|
+
created_by
|
|
46
|
+
) VALUES (
|
|
47
|
+
'2026-01-initial',
|
|
48
|
+
'2026-01-01',
|
|
49
|
+
NULL,
|
|
50
|
+
'https://developers.cloudflare.com/workers/platform/pricing/',
|
|
51
|
+
'{
|
|
52
|
+
"workers": {
|
|
53
|
+
"baseCostMonthly": 5.0,
|
|
54
|
+
"includedRequests": 10000000,
|
|
55
|
+
"requestsPerMillion": 0.3,
|
|
56
|
+
"cpuMsPerMillion": 0.02
|
|
57
|
+
},
|
|
58
|
+
"d1": {
|
|
59
|
+
"rowsReadPerBillion": 0.001,
|
|
60
|
+
"rowsWrittenPerMillion": 1.0,
|
|
61
|
+
"storagePerGb": 0.75
|
|
62
|
+
},
|
|
63
|
+
"kv": {
|
|
64
|
+
"readsPerMillion": 0.5,
|
|
65
|
+
"writesPerMillion": 5.0,
|
|
66
|
+
"deletesPerMillion": 5.0,
|
|
67
|
+
"listsPerMillion": 5.0,
|
|
68
|
+
"storagePerGb": 0.5
|
|
69
|
+
},
|
|
70
|
+
"r2": {
|
|
71
|
+
"storagePerGbMonth": 0.015,
|
|
72
|
+
"classAPerMillion": 4.5,
|
|
73
|
+
"classBPerMillion": 0.36
|
|
74
|
+
},
|
|
75
|
+
"durableObjects": {
|
|
76
|
+
"requestsPerMillion": 0.15,
|
|
77
|
+
"gbSecondsPerMillion": 12.5,
|
|
78
|
+
"storagePerGbMonth": 0.2,
|
|
79
|
+
"readsPerMillion": 0.2,
|
|
80
|
+
"writesPerMillion": 1.0,
|
|
81
|
+
"deletesPerMillion": 1.0
|
|
82
|
+
},
|
|
83
|
+
"vectorize": {
|
|
84
|
+
"storedDimensionsPerMillion": 0.01,
|
|
85
|
+
"queriedDimensionsPerMillion": 0.01
|
|
86
|
+
},
|
|
87
|
+
"aiGateway": { "free": true },
|
|
88
|
+
"workersAI": {
|
|
89
|
+
"neuronsPerThousand": 0.011
|
|
90
|
+
},
|
|
91
|
+
"pages": {
|
|
92
|
+
"buildCost": 0.15,
|
|
93
|
+
"bandwidthPerGb": 0.02
|
|
94
|
+
},
|
|
95
|
+
"queues": {
|
|
96
|
+
"messagesPerMillion": 0.4,
|
|
97
|
+
"operationsPerMillion": 0.4
|
|
98
|
+
},
|
|
99
|
+
"workflows": { "free": true }
|
|
100
|
+
}',
|
|
101
|
+
'{
|
|
102
|
+
"d1": { "rowsRead": 25000000000, "rowsWritten": 50000000 },
|
|
103
|
+
"kv": { "reads": 10000000, "writes": 1000000, "deletes": 1000000, "lists": 1000000 },
|
|
104
|
+
"r2": { "storage": 10000000000, "classA": 1000000, "classB": 10000000 },
|
|
105
|
+
"durableObjects": { "requests": 3000000, "gbSeconds": 400000 },
|
|
106
|
+
"vectorize": { "storedDimensions": 30000000, "queriedDimensions": 30000000 }
|
|
107
|
+
}',
|
|
108
|
+
'Initial pricing version seeded from Cloudflare pricing pages (January 2026)',
|
|
109
|
+
'migration'
|
|
110
|
+
);
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
-- Migration 011: Multi-Account Cloudflare Support
|
|
2
|
+
--
|
|
3
|
+
-- Purpose: Registry for managing multiple Cloudflare accounts from a single platform.
|
|
4
|
+
-- Enables federated infrastructure monitoring across accounts.
|
|
5
|
+
--
|
|
6
|
+
-- Use cases:
|
|
7
|
+
-- - Separate accounts for production, staging, client projects
|
|
8
|
+
-- - Consolidated billing and usage visibility across accounts
|
|
9
|
+
-- - Per-account API token management
|
|
10
|
+
--
|
|
11
|
+
-- Architecture:
|
|
12
|
+
-- - Each account has its own CLOUDFLARE_ACCOUNT_ID and API token
|
|
13
|
+
-- - The api_token_env_key column stores the ENV VAR name (not the token itself)
|
|
14
|
+
-- - Workers read the appropriate token from env at runtime
|
|
15
|
+
-- - Primary account is the default for backwards compatibility
|
|
16
|
+
|
|
17
|
+
CREATE TABLE IF NOT EXISTS account_registry (
|
|
18
|
+
account_id TEXT PRIMARY KEY, -- Internal identifier (e.g., "primary", "staging", "client-acme")
|
|
19
|
+
account_name TEXT NOT NULL, -- Human-readable name
|
|
20
|
+
cloudflare_account_id TEXT NOT NULL, -- Cloudflare account ID
|
|
21
|
+
api_token_env_key TEXT NOT NULL, -- ENV var name for the API token (e.g., "CLOUDFLARE_API_TOKEN")
|
|
22
|
+
is_primary INTEGER NOT NULL DEFAULT 0, -- 1 = default account, 0 = secondary
|
|
23
|
+
status TEXT NOT NULL DEFAULT 'active', -- active, disabled
|
|
24
|
+
notes TEXT, -- Optional notes
|
|
25
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
26
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
-- Ensure only one primary account
|
|
30
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_account_registry_primary
|
|
31
|
+
ON account_registry (is_primary) WHERE is_primary = 1;
|
|
32
|
+
|
|
33
|
+
-- Seed with primary account placeholder
|
|
34
|
+
-- TODO: Update cloudflare_account_id with your actual account ID
|
|
35
|
+
INSERT INTO account_registry (
|
|
36
|
+
account_id,
|
|
37
|
+
account_name,
|
|
38
|
+
cloudflare_account_id,
|
|
39
|
+
api_token_env_key,
|
|
40
|
+
is_primary,
|
|
41
|
+
status,
|
|
42
|
+
notes
|
|
43
|
+
) VALUES (
|
|
44
|
+
'primary',
|
|
45
|
+
'Primary Account',
|
|
46
|
+
'YOUR_CLOUDFLARE_ACCOUNT_ID',
|
|
47
|
+
'CLOUDFLARE_API_TOKEN',
|
|
48
|
+
1,
|
|
49
|
+
'active',
|
|
50
|
+
'Default account seeded by migration. Update cloudflare_account_id after deployment.'
|
|
51
|
+
);
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
/**
|
|
3
|
+
* Set KV Pricing Configuration
|
|
4
|
+
*
|
|
5
|
+
* This script sets the Cloudflare pricing configuration in the PLATFORM_CACHE KV namespace.
|
|
6
|
+
* The platform-usage worker reads this configuration at runtime to calculate costs.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* npx tsx scripts/ops/set-kv-pricing.ts [--dry-run]
|
|
10
|
+
*
|
|
11
|
+
* Prerequisites:
|
|
12
|
+
* - wrangler authenticated (npx wrangler login)
|
|
13
|
+
* - KV namespace ID set below (or via PLATFORM_CACHE_KV_ID env var)
|
|
14
|
+
*
|
|
15
|
+
* Pricing sources:
|
|
16
|
+
* - https://developers.cloudflare.com/workers/platform/pricing/
|
|
17
|
+
* - https://developers.cloudflare.com/d1/platform/pricing/
|
|
18
|
+
* - https://developers.cloudflare.com/kv/platform/pricing/
|
|
19
|
+
* - https://developers.cloudflare.com/r2/pricing/
|
|
20
|
+
* - https://developers.cloudflare.com/vectorize/platform/pricing/
|
|
21
|
+
* - https://developers.cloudflare.com/workers-ai/platform/pricing/
|
|
22
|
+
* - https://developers.cloudflare.com/durable-objects/platform/pricing/
|
|
23
|
+
* - https://developers.cloudflare.com/queues/platform/pricing/
|
|
24
|
+
* - https://developers.cloudflare.com/pages/platform/pricing/
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const PRICING_KEY = 'platform-usage:pricing:v1';
|
|
28
|
+
|
|
29
|
+
// TODO: Set your PLATFORM_CACHE KV namespace ID here or via PLATFORM_CACHE_KV_ID env var
|
|
30
|
+
const KV_NAMESPACE_ID = process.env.PLATFORM_CACHE_KV_ID || 'YOUR_PLATFORM_CACHE_KV_ID';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Current Cloudflare pricing (as of January 2026)
|
|
34
|
+
*
|
|
35
|
+
* Notes:
|
|
36
|
+
* - All prices in USD
|
|
37
|
+
* - Workers Paid plan includes 10M requests/month and 30M CPU ms/month
|
|
38
|
+
* - D1: First 25B rows read and 50M rows written are included
|
|
39
|
+
* - KV: First 10M reads, 1M writes, 1M deletes, 1M lists, 1GB storage included
|
|
40
|
+
* - R2: First 10GB storage, 1M Class A ops, 10M Class B ops included
|
|
41
|
+
* - Vectorize: First 10M stored dimensions and 50M queried dimensions included
|
|
42
|
+
* - Workers AI: First 10,000 neurons/day included
|
|
43
|
+
* - Durable Objects: First 1M requests, 400K GB-seconds, 1GB storage included
|
|
44
|
+
* - Queues: First 1M operations included
|
|
45
|
+
* - Pages: 500 builds/month included
|
|
46
|
+
*
|
|
47
|
+
* When Cloudflare updates pricing, update this config and re-run the script.
|
|
48
|
+
* Also create a new row in pricing_versions (migration 010) for audit trail.
|
|
49
|
+
*/
|
|
50
|
+
const PRICING_CONFIG = {
|
|
51
|
+
version: '2026-01-14',
|
|
52
|
+
lastUpdated: new Date().toISOString(),
|
|
53
|
+
source: 'cloudflare-pricing-pages',
|
|
54
|
+
|
|
55
|
+
workers: {
|
|
56
|
+
baseCostMonthly: 5.0,
|
|
57
|
+
includedRequests: 10_000_000,
|
|
58
|
+
requestsPerMillion: 0.3,
|
|
59
|
+
cpuMsPerMillion: 0.02,
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
d1: {
|
|
63
|
+
rowsReadPerBillion: 0.001,
|
|
64
|
+
rowsWrittenPerMillion: 1.0,
|
|
65
|
+
storagePerGb: 0.75,
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
kv: {
|
|
69
|
+
readsPerMillion: 0.5,
|
|
70
|
+
writesPerMillion: 5.0,
|
|
71
|
+
deletesPerMillion: 5.0,
|
|
72
|
+
listsPerMillion: 5.0,
|
|
73
|
+
storagePerGb: 0.5,
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
r2: {
|
|
77
|
+
storagePerGbMonth: 0.015,
|
|
78
|
+
classAPerMillion: 4.5,
|
|
79
|
+
classBPerMillion: 0.36,
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
vectorize: {
|
|
83
|
+
storedDimensionsPerMillion: 0.01,
|
|
84
|
+
queriedDimensionsPerMillion: 0.04,
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
workersAI: {
|
|
88
|
+
neuronsPerThousand: 0.011,
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
durableObjects: {
|
|
92
|
+
requestsPerMillion: 0.15,
|
|
93
|
+
gbSecondsPerMillion: 12.5,
|
|
94
|
+
storagePerGbMonth: 0.2,
|
|
95
|
+
readsPerMillion: 0.2,
|
|
96
|
+
writesPerMillion: 1.0,
|
|
97
|
+
deletesPerMillion: 1.0,
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
queues: {
|
|
101
|
+
messagesPerMillion: 0.4,
|
|
102
|
+
operationsPerMillion: 0.4,
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
pages: {
|
|
106
|
+
buildCost: 0.0,
|
|
107
|
+
bandwidthPerGb: 0.0,
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
async function main() {
|
|
112
|
+
const isDryRun = process.argv.includes('--dry-run');
|
|
113
|
+
|
|
114
|
+
if (KV_NAMESPACE_ID === 'YOUR_PLATFORM_CACHE_KV_ID') {
|
|
115
|
+
console.error('ERROR: Set PLATFORM_CACHE_KV_ID env var or update KV_NAMESPACE_ID in this script.');
|
|
116
|
+
console.error('Find your namespace ID: npx wrangler kv namespace list');
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
console.log('='.repeat(60));
|
|
121
|
+
console.log('Platform Usage Pricing Configuration');
|
|
122
|
+
console.log('='.repeat(60));
|
|
123
|
+
console.log(`Version: ${PRICING_CONFIG.version}`);
|
|
124
|
+
console.log(`KV Key: ${PRICING_KEY}`);
|
|
125
|
+
console.log(`Namespace: ${KV_NAMESPACE_ID}`);
|
|
126
|
+
console.log(`Dry Run: ${isDryRun}`);
|
|
127
|
+
console.log('');
|
|
128
|
+
|
|
129
|
+
console.log('Pricing Summary:');
|
|
130
|
+
console.log('-'.repeat(40));
|
|
131
|
+
console.log(`Workers: $${PRICING_CONFIG.workers.requestsPerMillion}/M requests`);
|
|
132
|
+
console.log(
|
|
133
|
+
`D1: $${PRICING_CONFIG.d1.rowsReadPerBillion}/B rows read, $${PRICING_CONFIG.d1.rowsWrittenPerMillion}/M rows written`
|
|
134
|
+
);
|
|
135
|
+
console.log(
|
|
136
|
+
`KV: $${PRICING_CONFIG.kv.readsPerMillion}/M reads, $${PRICING_CONFIG.kv.writesPerMillion}/M writes`
|
|
137
|
+
);
|
|
138
|
+
console.log(
|
|
139
|
+
`R2: $${PRICING_CONFIG.r2.storagePerGbMonth}/GB storage, $${PRICING_CONFIG.r2.classAPerMillion}/M Class A`
|
|
140
|
+
);
|
|
141
|
+
console.log(`Vectorize: $${PRICING_CONFIG.vectorize.storedDimensionsPerMillion}/M stored dims`);
|
|
142
|
+
console.log(`Workers AI: $${PRICING_CONFIG.workersAI.neuronsPerThousand}/1K neurons`);
|
|
143
|
+
console.log(`Durable Objects: $${PRICING_CONFIG.durableObjects.requestsPerMillion}/M requests`);
|
|
144
|
+
console.log(`Queues: $${PRICING_CONFIG.queues.messagesPerMillion}/M messages`);
|
|
145
|
+
console.log('');
|
|
146
|
+
|
|
147
|
+
if (isDryRun) {
|
|
148
|
+
console.log('[DRY RUN] Would write the following to KV:');
|
|
149
|
+
console.log(JSON.stringify(PRICING_CONFIG, null, 2));
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Write to KV using wrangler
|
|
154
|
+
const { execSync } = await import('child_process');
|
|
155
|
+
const pricingJson = JSON.stringify(PRICING_CONFIG);
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
console.log('Writing pricing to KV...');
|
|
159
|
+
execSync(
|
|
160
|
+
`npx wrangler kv key put "${PRICING_KEY}" '${pricingJson}' --namespace-id ${KV_NAMESPACE_ID} --remote`,
|
|
161
|
+
{ stdio: 'inherit', cwd: process.cwd() }
|
|
162
|
+
);
|
|
163
|
+
console.log('');
|
|
164
|
+
console.log('Pricing configuration saved successfully.');
|
|
165
|
+
console.log('');
|
|
166
|
+
console.log('To verify:');
|
|
167
|
+
console.log(
|
|
168
|
+
` npx wrangler kv key get "${PRICING_KEY}" --namespace-id ${KV_NAMESPACE_ID} --remote`
|
|
169
|
+
);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.error('Failed to write pricing to KV:', error);
|
|
172
|
+
console.log('');
|
|
173
|
+
console.log('Alternative: Copy the JSON below and use Cloudflare dashboard:');
|
|
174
|
+
console.log('1. Go to Workers & Pages > KV > PLATFORM_CACHE');
|
|
175
|
+
console.log(`2. Create key: ${PRICING_KEY}`);
|
|
176
|
+
console.log('3. Paste value:');
|
|
177
|
+
console.log(JSON.stringify(PRICING_CONFIG, null, 2));
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
main();
|