Fix inconsistent line endings across all project files for cross-environment consistency. Add complete unit test suite covering core utilities, API routes, and server services. Add Playwright E2E test setup with smoke, auth, and agent API tests. Update all project configuration files to match standardized formatting. Fix MCP API error handling to properly propagate HTTP status codes and use correct JSON-RPC error codes. Add VSCode workspace configuration, extensions, and example MCP config files. Update .gitignore to exclude new test and build artifacts.
131 lines
6.2 KiB
TypeScript
131 lines
6.2 KiB
TypeScript
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 adminEmail = ADMIN_EMAIL;
|
|
const adminPassword = ADMIN_PASSWORD;
|
|
|
|
const ROOT = path.resolve(process.cwd(), "docs", "pocketbase");
|
|
|
|
async function main() {
|
|
const pb = new PocketBase(PB_URL);
|
|
await pb.collection("_superusers").authWithPassword(adminEmail, adminPassword);
|
|
|
|
const collections = await pb.collections.getFullList();
|
|
const byName = new Map(collections.map((c) => [c.name, c.id]));
|
|
|
|
const replacers: Record<string, string> = {
|
|
REPLACE_ORDERS_COLLECTION_ID: byName.get("orders") || "",
|
|
REPLACE_QUOTE_ITEMS_COLLECTION_ID: byName.get("quote_items") || "",
|
|
REPLACE_PURCHASE_ORDERS_COLLECTION_ID: byName.get("purchase_orders") || "",
|
|
REPLACE_SUPPLIERS_COLLECTION_ID: byName.get("suppliers") || "",
|
|
REPLACE_CUSTOMERS_COLLECTION_ID: byName.get("customers") || "",
|
|
REPLACE_COMPANIES_COLLECTION_ID: byName.get("companies") || "",
|
|
};
|
|
|
|
for (const key of Object.keys(replacers)) {
|
|
if (!replacers[key]) {
|
|
throw new Error(`Missing required collection for token ${key}`);
|
|
}
|
|
}
|
|
|
|
const orderItemsDef = JSON.parse(fs.readFileSync(path.join(ROOT, "order_items.collection.json"), "utf8"));
|
|
const existingOrderItems = collections.find((c) => c.name === "order_items");
|
|
if (existingOrderItems) {
|
|
console.log("order_items already exists. Delete it first if you want a clean replace.");
|
|
} else {
|
|
const normalized = JSON.parse(JSON.stringify(orderItemsDef)
|
|
.replaceAll("REPLACE_ORDERS_COLLECTION_ID", replacers.REPLACE_ORDERS_COLLECTION_ID)
|
|
.replaceAll("REPLACE_QUOTE_ITEMS_COLLECTION_ID", replacers.REPLACE_QUOTE_ITEMS_COLLECTION_ID)
|
|
.replaceAll("REPLACE_PURCHASE_ORDERS_COLLECTION_ID", replacers.REPLACE_PURCHASE_ORDERS_COLLECTION_ID)
|
|
.replaceAll("REPLACE_SUPPLIERS_COLLECTION_ID", replacers.REPLACE_SUPPLIERS_COLLECTION_ID));
|
|
|
|
const created = await pb.collections.create(normalized);
|
|
console.log(`Created ${created.name} (${created.id})`);
|
|
byName.set(created.name, created.id);
|
|
}
|
|
|
|
const collectionPoolDef = JSON.parse(fs.readFileSync(path.join(ROOT, "collection_pool.collection.json"), "utf8"));
|
|
const existingPool = (await pb.collections.getFullList()).find((c) => c.name === "collection_pool");
|
|
if (existingPool) {
|
|
console.log("collection_pool already exists, skipping create.");
|
|
} else {
|
|
const orderItemsId = byName.get("order_items");
|
|
if (!orderItemsId) {
|
|
throw new Error("order_items was not found/created; cannot create collection_pool.");
|
|
}
|
|
|
|
const normalizedPool = JSON.parse(JSON.stringify(collectionPoolDef)
|
|
.replaceAll("REPLACE_ORDERS_COLLECTION_ID", replacers.REPLACE_ORDERS_COLLECTION_ID)
|
|
.replaceAll("REPLACE_ORDER_ITEMS_COLLECTION_ID", orderItemsId)
|
|
.replaceAll("REPLACE_SUPPLIERS_COLLECTION_ID", replacers.REPLACE_SUPPLIERS_COLLECTION_ID));
|
|
|
|
const createdPool = await pb.collections.create(normalizedPool);
|
|
console.log(`Created ${createdPool.name} (${createdPool.id})`);
|
|
byName.set(createdPool.name, createdPool.id);
|
|
}
|
|
|
|
const deliveryNotesDef = JSON.parse(fs.readFileSync(path.join(ROOT, "delivery_notes.collection.json"), "utf8"));
|
|
const existingDeliveryNotes = (await pb.collections.getFullList()).find((c) => c.name === "delivery_notes");
|
|
if (existingDeliveryNotes) {
|
|
console.log("delivery_notes already exists, skipping create.");
|
|
byName.set(existingDeliveryNotes.name, existingDeliveryNotes.id);
|
|
} else {
|
|
const normalizedDeliveryNotes = JSON.parse(JSON.stringify(deliveryNotesDef)
|
|
.replaceAll("REPLACE_ORDERS_COLLECTION_ID", replacers.REPLACE_ORDERS_COLLECTION_ID)
|
|
.replaceAll("REPLACE_COMPANIES_COLLECTION_ID", replacers.REPLACE_COMPANIES_COLLECTION_ID)
|
|
.replaceAll("REPLACE_CUSTOMERS_COLLECTION_ID", replacers.REPLACE_CUSTOMERS_COLLECTION_ID));
|
|
|
|
const createdDeliveryNotes = await pb.collections.create(normalizedDeliveryNotes);
|
|
console.log(`Created ${createdDeliveryNotes.name} (${createdDeliveryNotes.id})`);
|
|
byName.set(createdDeliveryNotes.name, createdDeliveryNotes.id);
|
|
}
|
|
|
|
const deliveryNoteItemsDef = JSON.parse(fs.readFileSync(path.join(ROOT, "delivery_note_items.collection.json"), "utf8"));
|
|
const existingDeliveryNoteItems = (await pb.collections.getFullList()).find((c) => c.name === "delivery_note_items");
|
|
if (existingDeliveryNoteItems) {
|
|
console.log("delivery_note_items already exists, skipping create.");
|
|
} else {
|
|
const deliveryNotesId = byName.get("delivery_notes");
|
|
const orderItemsId = byName.get("order_items");
|
|
if (!deliveryNotesId || !orderItemsId) {
|
|
throw new Error("delivery_notes and order_items are required before creating delivery_note_items.");
|
|
}
|
|
|
|
const normalizedDeliveryNoteItems = JSON.parse(JSON.stringify(deliveryNoteItemsDef)
|
|
.replaceAll("REPLACE_DELIVERY_NOTES_COLLECTION_ID", deliveryNotesId)
|
|
.replaceAll("REPLACE_ORDER_ITEMS_COLLECTION_ID", orderItemsId));
|
|
|
|
const createdDeliveryNoteItems = await pb.collections.create(normalizedDeliveryNoteItems);
|
|
console.log(`Created ${createdDeliveryNoteItems.name} (${createdDeliveryNoteItems.id})`);
|
|
}
|
|
|
|
const repairsDef = JSON.parse(fs.readFileSync(path.join(ROOT, "repairs.collection.json"), "utf8"));
|
|
const existingRepairs = (await pb.collections.getFullList()).find((c) => c.name === "repairs");
|
|
if (existingRepairs) {
|
|
console.log("repairs already exists, skipping create.");
|
|
} else {
|
|
const normalizedRepairs = JSON.parse(JSON.stringify(repairsDef)
|
|
.replaceAll("REPLACE_CUSTOMERS_COLLECTION_ID", replacers.REPLACE_CUSTOMERS_COLLECTION_ID)
|
|
.replaceAll("REPLACE_COMPANIES_COLLECTION_ID", replacers.REPLACE_COMPANIES_COLLECTION_ID)
|
|
.replaceAll("REPLACE_ORDERS_COLLECTION_ID", replacers.REPLACE_ORDERS_COLLECTION_ID));
|
|
|
|
const createdRepairs = await pb.collections.create(normalizedRepairs);
|
|
console.log(`Created ${createdRepairs.name} (${createdRepairs.id})`);
|
|
}
|
|
|
|
console.log("Migration complete.");
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error(err);
|
|
process.exit(1);
|
|
});
|