91 lines
3.6 KiB
JavaScript
91 lines
3.6 KiB
JavaScript
|
|
// Adds the `site` relation on `assets` → `sites`, so every machine can be
|
||
|
|
// assigned to the yard it is actually parked at.
|
||
|
|
//
|
||
|
|
// Idempotent: safe to re-run. Exits non-zero if `sites` is missing, because
|
||
|
|
// creating the relation without a target collection would leave a dangling
|
||
|
|
// field rather than an obvious failure.
|
||
|
|
//
|
||
|
|
// Usage: node scripts/setup-site-assignment.mjs
|
||
|
|
import PocketBase from "pocketbase";
|
||
|
|
import { fileURLToPath } from "node:url";
|
||
|
|
import { dirname, resolve } from "node:path";
|
||
|
|
|
||
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||
|
|
|
||
|
|
const PB = process.env.NEXT_PUBLIC_PB_URL;
|
||
|
|
const EMAIL = process.env.PB_ADMIN;
|
||
|
|
const PASS = process.env.PB_PASS;
|
||
|
|
const missing = [];
|
||
|
|
if (!PB) missing.push("NEXT_PUBLIC_PB_URL");
|
||
|
|
if (!EMAIL) missing.push("PB_ADMIN");
|
||
|
|
if (!PASS) missing.push("PB_PASS");
|
||
|
|
if (missing.length) {
|
||
|
|
console.error(`✗ Missing required env: ${missing.join(", ")}`);
|
||
|
|
console.error(" Copy .env.example to .env.local and fill in.");
|
||
|
|
process.exit(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
const pb = new PocketBase(PB);
|
||
|
|
pb.autoCancellation(false);
|
||
|
|
|
||
|
|
console.log(`→ Connecting to ${PB} as ${EMAIL}`);
|
||
|
|
await pb.collection("_superusers").authWithPassword(EMAIL, PASS);
|
||
|
|
|
||
|
|
const sites = await pb.collections.getOne("sites").catch(() => null);
|
||
|
|
if (!sites) {
|
||
|
|
console.error("✗ Collection `sites` does not exist. Create it first, then re-run.");
|
||
|
|
process.exit(1);
|
||
|
|
}
|
||
|
|
const siteCount = (await pb.collection("sites").getList(1, 1)).totalItems;
|
||
|
|
console.log(` ✓ found \`sites\` with ${siteCount} row(s)`);
|
||
|
|
|
||
|
|
const assets = await pb.collections.getOne("assets");
|
||
|
|
const existing = assets.fields.find((f) => f.name === "site");
|
||
|
|
if (existing) {
|
||
|
|
// PocketBase refuses to repoint an existing relation (it throws
|
||
|
|
// `validation_field_relation_change`). If an earlier run created the field
|
||
|
|
// against the wrong collection, say so instead of silently leaving it broken
|
||
|
|
// — every assignment will fail with `validation_missing_rel_records`.
|
||
|
|
if (existing.collectionId !== sites.id) {
|
||
|
|
const used = (await pb.collection("assets").getFullList()).filter((a) => a.site).length;
|
||
|
|
if (used > 0) {
|
||
|
|
console.error(`✗ assets.site points at the wrong collection and ${used} asset(s) already use it.`);
|
||
|
|
console.error(" Fix by hand: drop the `site` field in the PocketBase UI, then re-run this script.");
|
||
|
|
process.exit(1);
|
||
|
|
}
|
||
|
|
console.log(" · assets.site points at the wrong collection and is unused — recreating it.");
|
||
|
|
assets.fields = assets.fields.filter((f) => f.name !== "site");
|
||
|
|
await pb.collections.update(assets.id, { fields: assets.fields });
|
||
|
|
} else {
|
||
|
|
console.log(" ✓ assets.site already exists and targets `sites` — nothing to do.");
|
||
|
|
process.exit(0);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
const rand = (n) => Math.random().toString(36).slice(2, 2 + n);
|
||
|
|
assets.fields.push({
|
||
|
|
name: "site",
|
||
|
|
type: "relation",
|
||
|
|
required: false,
|
||
|
|
presentable: false,
|
||
|
|
hidden: false,
|
||
|
|
system: false,
|
||
|
|
collectionId: sites.id, // self-relation: a site may be scoped to a parent site
|
||
|
|
minSelect: null,
|
||
|
|
maxSelect: 1,
|
||
|
|
maxSize: 0,
|
||
|
|
// Deleting a site must NEVER delete the machines parked at it. This is the
|
||
|
|
// opposite of monthly_income.asset, which deliberately cascades.
|
||
|
|
cascadeDelete: false,
|
||
|
|
id: "relation" + rand(15),
|
||
|
|
});
|
||
|
|
|
||
|
|
const updated = await pb.collections.update(assets.id, { fields: assets.fields });
|
||
|
|
const field = updated.fields.find((f) => f.name === "site");
|
||
|
|
if (!field) {
|
||
|
|
console.error("✗ assets.site missing after update — PocketBase rejected the change.");
|
||
|
|
process.exit(1);
|
||
|
|
}
|
||
|
|
console.log(` ✓ assets.site added (maxSelect=${field.maxSelect}, cascadeDelete=${field.cascadeDelete})`);
|
||
|
|
console.log("\nNext: assign machines from /plant → Edit → Site, or run `npm run seed`.");
|