/** * Migration — create / repair the `raw_price_list` PocketBase collection. * * Two responsibilities: * * 1. Create the collection from `docs/pocketbase/raw_price_list.collection.json` * if it doesn't exist. Schema is the single source of truth; this script * strips the leading `_comment` keys before sending to PB. * * 2. On a pre-existing collection (the user's case — the live PB had the * collection already, but with PB's default `null` superuser-only rules), * relax the rules to `@request.auth.id != ''` so any logged-in user can * CRUD. We DON'T touch the schema on a pre-existing collection — the * field names there are the source of truth (the live one uses * `supplier_file` and `supplier_name` as a relation). * * Idempotent — safe to re-run. * * Run with: * npm run pb:migrate:raw-price-list */ import fs from "node:fs"; import path from "node:path"; import PocketBase from "pocketbase"; const PB_URL = process.env.NEXT_PUBLIC_POCKETBASE_URL || "http://127.0.0.1:8090"; const ADMIN_EMAIL = process.env.PB_ADMIN_EMAIL; const ADMIN_PASSWORD = process.env.PB_ADMIN_PASSWORD; if (!ADMIN_EMAIL || !ADMIN_PASSWORD) { throw new Error("PB_ADMIN_EMAIL and PB_ADMIN_PASSWORD are required."); } const ROOT = path.resolve(process.cwd(), "docs", "pocketbase"); const AUTH_RULE = "@request.auth.id != ''"; async function main() { const pb = new PocketBase(PB_URL); await pb.collection("_superusers").authWithPassword(ADMIN_EMAIL, ADMIN_PASSWORD); const existing = await pb.collections.getList(1, 1, { filter: `name = "raw_price_list"` }); if (existing.items.length === 0) { // ── Create from schema file ───────────────────────────────── const def = JSON.parse( fs.readFileSync(path.join(ROOT, "raw_price_list.collection.json"), "utf8"), ); const sanitized = JSON.parse( JSON.stringify(def, (key, value) => (key.startsWith("_") ? undefined : value)), ); delete sanitized.id; // let PB assign its own 15-char id const created = await pb.collections.create(sanitized); console.log(`Created raw_price_list (${created.id}) with auth rules = @request.auth.id != ''`); return; } // ── Pre-existing collection: relax the rules ───────────────── const col = existing.items[0]; const updates: Record = {}; if (col.listRule !== AUTH_RULE) updates.listRule = AUTH_RULE; if (col.viewRule !== AUTH_RULE) updates.viewRule = AUTH_RULE; if (col.createRule !== AUTH_RULE) updates.createRule = AUTH_RULE; if (col.updateRule !== AUTH_RULE) updates.updateRule = AUTH_RULE; if (col.deleteRule !== AUTH_RULE) updates.deleteRule = AUTH_RULE; if (Object.keys(updates).length === 0) { console.log(`raw_price_list (${col.id}) already has correct auth rules, nothing to do.`); return; } await pb.collections.update(col.id, updates); console.log(`Relaxed raw_price_list (${col.id}) auth rules → ${AUTH_RULE}`); console.log("Pricelist migration complete."); } main().catch((err) => { console.error(err); process.exit(1); });