Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions controlplane/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -915,6 +915,8 @@ async function handleRequest(request: Request, env: EnvWithBindings, ctx: Pick<E
if (authError) return authError;
try {
await initializeDatabase(env.DB);
// Seed curated starter templates on desktop (no-op on cloud + after first run).
await templates.seedStarterTemplates(env);
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : String(err);
console.error('[init-db] FAILED:', msg, err);
Expand Down
46 changes: 46 additions & 0 deletions controlplane/src/templates/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import type { Env } from '../types';
import { scrubItemContent, type DashboardItemType } from './scrubber';
import starterTemplates from './starter-templates.json';

/**
* Ensure the status and viewport_json columns exist on dashboard_templates.
Expand Down Expand Up @@ -59,6 +60,51 @@ export interface TemplateEdge {
targetHandle?: string;
}

// Starter templates seeded into a fresh desktop DB — verbatim copies of the
// curated prod templates (the cloud has real user-submitted templates; desktop
// starts empty). Block/edge/viewport data lives in starter-templates.json.

/**
* Seed the curated starter templates into a fresh desktop DB. Desktop only
* (SURFACE_TOKEN is set only by the desktop app) and once per DB (a schema
* marker, so a user who deletes them isn't re-seeded). Called from /init-db.
*/
export async function seedStarterTemplates(env: Env): Promise<void> {
if (!env.SURFACE_TOKEN) return;
await ensureTemplateColumns(env);

const marker = 'seed_desktop_starter_templates_v2';
const already = await env.DB.prepare(
`SELECT 1 FROM schema_migrations WHERE name = ?`
).bind(marker).first();
if (already) return;

// Drop the earlier hand-made starters, superseded by these prod copies.
await env.DB.prepare(
`DELETE FROM dashboard_templates WHERE id IN
('starter-agentic-coding', 'starter-automation', 'starter-documentation')`
).run();

const now = new Date().toISOString();
for (const t of starterTemplates) {
await env.DB.prepare(
`INSERT INTO dashboard_templates
(id, name, description, category, author_id, author_name,
items_json, edges_json, viewport_json, item_count, is_featured, status, created_at, updated_at)
VALUES (?, ?, ?, ?, 'orcabot', 'Orcabot', ?, ?, ?, ?, 1, 'approved', ?, ?)
ON CONFLICT(id) DO NOTHING`
).bind(
t.id, t.name, t.description, t.category,
JSON.stringify(t.items), JSON.stringify(t.edges),
t.viewport ? JSON.stringify(t.viewport) : null,
t.itemCount, now, now
).run();
}
await env.DB.prepare(
`INSERT INTO schema_migrations (name) VALUES (?)`
).bind(marker).run();
}

/**
* Dashboard template (summary for listing)
*/
Expand Down
Loading
Loading