Adds /pricelist under the Management section. Operators drag a supplier price-list file (PDF / CSV / text / image) into the upload zone — or use the explicit Choose file button — and it lands in PocketBase as an attachment on the raw_price_list collection. The table below shows the queue: file, supplier (relation to suppliers), status badge, log summary preview, upload date, and a delete action per row. Layout matches the rest of the app: Card for the upload zone, DataTable for the queue, DeleteDialog for the destructive action. Live updates via the PB subscribe channel so other tabs / the n8n ingest workflow show new rows without a manual refresh. PB schema is sourced from docs/pocketbase/raw_price_list.collection.json. The migration script is idempotent: it creates the collection from the schema file on a fresh deploy, and on a pre-existing one (the live PB already had it, with PB's default null/superuser-only rules) it relaxes the auth rules to @request.auth.id != '' so any logged-in user can CRUD — matching every other collection in the app. Field names match the live PB collection (discovered via admin API): - supplier_file (the file) - supplier_name (relation to suppliers) - status (select: Pending / Processing / Completed / Failed) - log_summary (free text)
81 lines
3.1 KiB
TypeScript
81 lines
3.1 KiB
TypeScript
/**
|
|
* 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<string, unknown> = {};
|
|
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);
|
|
});
|