feat(web/server): seed a ready-to-open demo project on migrate

A freshly cloned + migrated install had no projects, so there was nothing to
click on. Add seedDemoProject() (run from db:migrate after seedDefaultOwner,
idempotent) that creates a "demo" project and loads three committed fixtures
from web/apps/server/seed-data/ — covering one openable file per editor:

  demo.kicad_sch  -> eeschema (Schematic Editor)
  demo.kicad_pcb  -> pcbnew (PCB Editor)
  demo.kicad_wks  -> pl_editor (Drawing Sheet Editor)

The sch/pcb are the self-contained ecc83 push-pull demo (version-compatible
with this build); the wks is a minimal hand-written drawing sheet. Bytes are
committed so seeding needs no submodule checkout at runtime.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Gergő Törcsvári 2026-06-03 08:52:18 +02:00
commit 0157741660
No known key found for this signature in database
GPG key ID: 8E75F2CDE64E5322
5 changed files with 9762 additions and 2 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,9 @@
(page_layout
(setup (textsize 1.5 1.5)(linewidth 0.15)(textlinewidth 0.15)
(left_margin 10)(right_margin 10)(top_margin 10)(bottom_margin 10))
(rect (name border:Rect) (start 0 0 ltcorner) (end 0 0 rbcorner) (comment "page border"))
(rect (name titleblock:Rect) (start 110 34) (end 2 2) (comment "title block frame"))
(tbtext "KiCad WASM — Dummy Test Drawing Sheet" (name title) (pos 100 22) (font (size 2.5 2.5) (bold)))
(tbtext "pl_editor web smoke test" (name subtitle) (pos 100 14) (font (size 1.5 1.5)))
(tbtext "Sheet: %S / %N" (name sheetnum) (pos 100 6) (font (size 1.3 1.3)))
)

View file

@ -2,7 +2,7 @@ import * as path from "node:path";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
import { migrate } from "drizzle-orm/node-postgres/migrator"; import { migrate } from "drizzle-orm/node-postgres/migrator";
import { db, pool } from "./index.js"; import { db, pool } from "./index.js";
import { seedDefaultOwner } from "./seed.js"; import { seedDefaultOwner, seedDemoProject } from "./seed.js";
const here = path.dirname(fileURLToPath(import.meta.url)); const here = path.dirname(fileURLToPath(import.meta.url));
@ -12,6 +12,8 @@ async function main() {
}); });
const ownerId = await seedDefaultOwner(); const ownerId = await seedDefaultOwner();
console.log(`migrations applied; default owner: ${ownerId}`); console.log(`migrations applied; default owner: ${ownerId}`);
// Give a fresh install a ready-to-open demo project (idempotent).
await seedDemoProject();
await pool.end(); await pool.end();
} }

View file

@ -1,7 +1,14 @@
import * as fs from "node:fs/promises";
import { fileURLToPath } from "node:url";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import { env } from "../env.js"; import { env } from "../env.js";
import { db, pool } from "./index.js"; import { db, pool } from "./index.js";
import { owners } from "./schema.js"; import { owners } from "./schema.js";
import {
createProject,
getProjectRowBySlug,
writeProjectFile,
} from "../services/projects.js";
/** Ensure the default owner namespace exists (no-auth iteration). */ /** Ensure the default owner namespace exists (no-auth iteration). */
export async function seedDefaultOwner(): Promise<string> { export async function seedDefaultOwner(): Promise<string> {
@ -29,11 +36,52 @@ export async function seedDefaultOwner(): Promise<string> {
return row[0].id; return row[0].id;
} }
/**
* Demo project bytes committed at <server>/seed-data/. Resolved relative to this
* module so it works both under tsx (src/db/) and compiled (dist/db/) both are
* two levels below the package root, where seed-data lives.
*/
const SEED_DATA_DIR = new URL("../../seed-data/", import.meta.url);
const DEMO_SLUG = "demo";
const DEMO_NAME = "Demo Project";
/** Committed files → project-relative paths. One per openable tool. */
const DEMO_FILES = [
"demo.kicad_sch", // eeschema (Schematic Editor)
"demo.kicad_pcb", // pcbnew (PCB Editor)
"demo.kicad_wks", // pl_editor (Drawing Sheet Editor)
] as const;
/**
* Seed a ready-to-open demo project so a freshly cloned + migrated install has
* something to click on (and to exercise the tool wiring). Idempotent: skips if
* the "demo" project already exists. Reads bytes from the committed seed-data/
* dir, so it needs no submodule checkout at runtime.
*/
export async function seedDemoProject(): Promise<void> {
if (await getProjectRowBySlug(DEMO_SLUG)) {
console.log(`demo project "${DEMO_SLUG}" already exists — skipping`);
return;
}
const project = await createProject(DEMO_NAME, DEMO_SLUG);
const row = await getProjectRowBySlug(DEMO_SLUG);
if (!row) throw new Error("demo project vanished right after creation");
for (const name of DEMO_FILES) {
const data = await fs.readFile(fileURLToPath(new URL(name, SEED_DATA_DIR)));
await writeProjectFile({ project: row, rawPath: name, data });
console.log(` seeded ${DEMO_SLUG}/${name} (${data.length} bytes)`);
}
console.log(`seeded demo project "${project.slug}" with ${DEMO_FILES.length} files`);
}
// Allow running standalone: `pnpm db:seed`. // Allow running standalone: `pnpm db:seed`.
if (import.meta.url === `file://${process.argv[1]}`) { if (import.meta.url === `file://${process.argv[1]}`) {
seedDefaultOwner() seedDefaultOwner()
.then((id) => { .then(async (id) => {
console.log(`seeded default owner: ${id}`); console.log(`seeded default owner: ${id}`);
await seedDemoProject();
return pool.end(); return pool.end();
}) })
.catch((err) => { .catch((err) => {