import { SERVER_IP, PB_PORT, PB_EMAIL, PB_PASSWORD } from "../../config.ts"; const PB_ENDPOINT = `http://${SERVER_IP}:${PB_PORT}`; interface FieldDef { name: string type: string required?: boolean unique?: boolean max?: number min?: number values?: string[] maxSelect?: number collectionId?: string cascadeDelete?: boolean } interface CollectionDef { name: string type: string fields: FieldDef[] listRule?: string | null viewRule?: string | null createRule?: string | null updateRule?: string | null deleteRule?: string | null } async function getSuperadminToken(): Promise { const res = await fetch( `${PB_ENDPOINT}/api/collections/_superusers/auth-with-password`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ identity: PB_EMAIL, password: PB_PASSWORD }), }, ); const data = await res.json(); if (!res.ok) throw new Error(`Auth failed: ${JSON.stringify(data)}`); return data.token; } async function createCollection( token: string, col: CollectionDef, ): Promise { const existing = await fetch( `${PB_ENDPOINT}/api/collections?filter=name='${col.name}'`, { headers: { Authorization: `Bearer ${token}` } }, ); const existingData = await existing.json(); if (existingData?.items?.length > 0) { console.log(` ↳ Already exists: ${col.name}`); return existingData.items[0].id; } const res = await fetch(`${PB_ENDPOINT}/api/collections`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify(col), }); const data = await res.json(); if (!res.ok) throw new Error(`Create ${col.name} failed: ${JSON.stringify(data)}`); console.log(` ✓ Created: ${col.name}`); return data.id; } function text(name: string, required = false): FieldDef { return { name, type: "text", required }; } function uniqueText(name: string): FieldDef { return { name, type: "text", required: true, unique: true }; } function number(name: string, required = false): FieldDef { return { name, type: "number", required }; } function bool(name: string): FieldDef { return { name, type: "bool" }; } function date(name: string): FieldDef { return { name, type: "date" }; } function jsonField(name: string): FieldDef { return { name, type: "json" }; } function select(name: string, values: string[], required = false): FieldDef { return { name, type: "select", required, values, maxSelect: 1 }; } function rel(name: string, collectionId: string, required = false): FieldDef { return { name, type: "relation", required, collectionId, maxSelect: 1, cascadeDelete: false, }; } async function main() { console.log("Connecting to PB at", PB_ENDPOINT); const token = await getSuperadminToken(); console.log("Authenticated as superadmin\n"); const ids: Record = {}; // ── Pass 1: Independent collections ── console.log("--- Pass 1: Independent collections ---"); ids.fams = await createCollection(token, { name: "fams", type: "base", listRule: "", viewRule: "", createRule: null, updateRule: null, deleteRule: null, fields: [ text("name", true), uniqueText("slug"), text("inviteCode"), text("stripeCustomerId"), jsonField("featureFlags"), jsonField("seasons"), ], }); // ── Pass 2: Collections that reference fams ── console.log("\n--- Pass 2: fam-scoped collections ---"); ids.settings = await createCollection(token, { name: "settings", type: "base", listRule: "", viewRule: "", createRule: null, updateRule: null, deleteRule: null, fields: [ rel("famId", ids.fams!, true), text("webhookUrl"), ], }); ids.members = await createCollection(token, { name: "members", type: "base", listRule: "", viewRule: "", createRule: null, updateRule: null, deleteRule: null, fields: [ rel("famId", ids.fams!, true), text("name", true), text("color"), text("deviceToken"), text("deviceTokenHint"), ], }); ids.chore_templates = await createCollection(token, { name: "chore_templates", type: "base", listRule: "", viewRule: "", createRule: null, updateRule: null, deleteRule: null, fields: [ rel("famId", ids.fams!, true), text("name", true), text("description"), select("defaultFrequency", ["daily", "weekly"], true), select("defaultType", ["points", "money"], true), number("defaultValue", true), ], }); ids.fam_admins = await createCollection(token, { name: "fam_admins", type: "base", listRule: "", viewRule: "", createRule: null, updateRule: null, deleteRule: null, fields: [ rel("famId", ids.fams!, true), text("userId", true), text("email", true), text("name"), text("color"), ], }); ids.bonus_configs = await createCollection(token, { name: "bonus_configs", type: "base", listRule: "", viewRule: "", createRule: null, updateRule: null, deleteRule: null, fields: [ rel("famId", ids.fams!, true), text("name", true), text("description"), select("target", ["individual", "competitive"], true), select("type", ["threshold", "count", "manual"], true), select("occurrence", ["recurring", "once"], true), select("rewardType", ["points", "cash", "prize"], true), text("rewardValue", true), number("criteriaValue"), select("period", ["weekly", "monthly"]), select("status", ["active", "archived"], true), ], }); // ── Pass 3: Collections with member/template deps ── console.log("\n--- Pass 3: Nested collections ---"); ids.weekly_history = await createCollection(token, { name: "weekly_history", type: "base", listRule: "", viewRule: "", createRule: null, updateRule: null, deleteRule: null, fields: [ rel("famId", ids.fams!, true), rel("memberId", ids.members!, true), date("weekStart"), number("pointsEarned"), number("moneyEarned"), number("choresCompleted"), number("bonusEarned"), ], }); ids.rewards = await createCollection(token, { name: "rewards", type: "base", listRule: "", viewRule: "", createRule: null, updateRule: null, deleteRule: null, fields: [ rel("famId", ids.fams!, true), rel("memberId", ids.members!, true), rel("bonusConfigId", ids.bonus_configs!), text("label", true), number("value", true), select("rewardType", ["cash", "prize", "points"], true), select("status", ["unclaimed", "requested", "claimed"], true), date("claimedAt"), date("requestedAt"), text("date"), ], }); ids.assigned_chores = await createCollection(token, { name: "assigned_chores", type: "base", listRule: "", viewRule: "", createRule: null, updateRule: null, deleteRule: null, fields: [ rel("famId", ids.fams!, true), rel("memberId", ids.members!, true), rel("templateId", ids.chore_templates!, true), select("frequency", ["daily", "weekly"], true), select("type", ["points", "money"], true), number("value", true), text("customName"), jsonField("seasonIds"), ], }); ids.seasons = await createCollection(token, { name: "seasons", type: "base", listRule: "", viewRule: "", createRule: null, updateRule: null, deleteRule: null, fields: [ rel("famId", ids.fams!, true), text("name", true), text("color"), bool("active"), date("autoDisable"), date("autoStart"), ], }); ids.completions = await createCollection(token, { name: "completions", type: "base", listRule: "", viewRule: "", createRule: null, updateRule: null, deleteRule: null, fields: [ rel("famId", ids.fams!, true), rel("memberId", ids.members!, true), rel("assignedChoreId", ids.assigned_chores!, true), date("date"), ], }); console.log("\n✅ All collections created successfully"); console.log("Collection IDs:", ids); } main().catch((err) => { console.error("Seed failed:", err); process.exit(1); });