82 lines
3.3 KiB
TypeScript
82 lines
3.3 KiB
TypeScript
/**
|
|
* One-shot migration: fields for the AI quote assistant.
|
|
*
|
|
* quote_items.ai_fill json (per-line match/fill record)
|
|
* ai_settings.quote_assistant_enabled bool
|
|
* ai_settings.quote_assistant_interval_minutes number
|
|
* ai_settings.quote_assistant_min_confidence text (max 10)
|
|
* ai_settings.quote_assistant_prompt text (max 50000)
|
|
* ai_settings.quote_assistant_last_run_at text (max 40)
|
|
* ai_settings.quote_assistant_last_run_summary json
|
|
*
|
|
* Run with:
|
|
*
|
|
* npm run pb:migrate:quote-assistant
|
|
*
|
|
* Idempotent — fields that already exist are skipped. Manual step on the
|
|
* live PB (like select values and pb_hooks).
|
|
*
|
|
* NOTE: PB text fields with `max: 0` cap at 5000 chars, NOT unlimited, so
|
|
* the prompt field gets an explicit max of 50000.
|
|
*
|
|
* Rollback: delete the fields in the PB admin UI.
|
|
*/
|
|
import "./_env-loader";
|
|
import PocketBase from "pocketbase";
|
|
|
|
const PB_URL = process.env.NEXT_PUBLIC_POCKETBASE_URL || process.env.PB_URL || "http://127.0.0.1:8090";
|
|
const ADMIN_EMAIL = process.env.PB_ADMIN_EMAIL;
|
|
const ADMIN_PASSWORD = process.env.PB_ADMIN_PASSWORD;
|
|
|
|
type FieldDef = Record<string, unknown> & { name: string };
|
|
|
|
const QUOTE_ITEM_FIELDS: FieldDef[] = [
|
|
{ name: "ai_fill", type: "json", required: false, presentable: false, maxSize: 200000 },
|
|
];
|
|
|
|
const AI_SETTINGS_FIELDS: FieldDef[] = [
|
|
{ name: "quote_assistant_enabled", type: "bool", required: false, presentable: false },
|
|
{ name: "quote_assistant_interval_minutes", type: "number", required: false, presentable: false },
|
|
{ name: "quote_assistant_min_confidence", type: "text", required: false, presentable: false, max: 10 },
|
|
{ name: "quote_assistant_prompt", type: "text", required: false, presentable: false, max: 50000 },
|
|
{ name: "quote_assistant_last_run_at", type: "text", required: false, presentable: false, max: 40 },
|
|
{ name: "quote_assistant_last_run_summary", type: "json", required: false, presentable: false, maxSize: 20000 },
|
|
];
|
|
|
|
async function addMissingFields(pb: PocketBase, collectionName: string, wanted: FieldDef[]) {
|
|
const collection = await pb.collections.getOne(collectionName);
|
|
const existing = new Set(collection.fields.map((f: { name: string }) => f.name));
|
|
const missing = wanted.filter((f) => !existing.has(f.name));
|
|
|
|
for (const f of wanted) {
|
|
if (existing.has(f.name)) {
|
|
console.log(`[quote-assistant] ${collectionName}.${f.name} already exists — skipping.`);
|
|
}
|
|
}
|
|
if (missing.length === 0) return;
|
|
|
|
await pb.collections.update(collection.id, {
|
|
fields: [...collection.fields, ...missing],
|
|
});
|
|
for (const f of missing) {
|
|
console.log(`[quote-assistant] ${collectionName}.${f.name} added.`);
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
if (!ADMIN_EMAIL || !ADMIN_PASSWORD) {
|
|
throw new Error("Set PB_ADMIN_EMAIL and PB_ADMIN_PASSWORD (e.g. from .env.local).");
|
|
}
|
|
const pb = new PocketBase(PB_URL);
|
|
await pb.collection("_superusers").authWithPassword(ADMIN_EMAIL, ADMIN_PASSWORD);
|
|
console.log(`[quote-assistant] Authenticated against ${PB_URL}`);
|
|
|
|
await addMissingFields(pb, "quote_items", QUOTE_ITEM_FIELDS);
|
|
await addMissingFields(pb, "ai_settings", AI_SETTINGS_FIELDS);
|
|
console.log("[quote-assistant] Done.");
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error("[quote-assistant] Migration failed:", err);
|
|
process.exit(1);
|
|
});
|