Files
formulation/scripts/remove-pastry-book.mjs
T
nicholas 3c2bc768e0
Build & Deploy Formulation / Build & Push Image (push) Successful in 1m3s
Build & Deploy Formulation / deploy (push) Successful in 19s
refactor: simplify
## Summary

- update recipe data, improve mobile
- simplify app, API; remove bad MCP layer
- consolidate database schema into initial migration

Co-authored-by: Nicholas Ward <nicholaspward@outlook.com>
2026-08-30 20:22:09 -05:00

91 lines
4.1 KiB
JavaScript

import fs from "node:fs";
import path from "node:path";
import { DatabaseSync } from "node:sqlite";
import { writeSiteProjection } from "./lib/site-projection.mjs";
import { exportDatabase } from "../src/lib/backup/export-database.ts";
const root = path.resolve(import.meta.dirname, "..");
const databasePath = path.join(root, "var", "recipe-book.sqlite");
const backupPath = path.join(root, "data", "recipe-book-backup.json");
const db = new DatabaseSync(databasePath, { readOnly: false });
db.exec("PRAGMA foreign_keys = ON");
try {
const book = db.prepare("SELECT * FROM collections WHERE id = ?").get("the_pastry_chefs_little_black_book_vol_1");
if (!book) {
console.log("Collection the_pastry_chefs_little_black_book_vol_1 not found.");
process.exit(0);
}
console.log(`Found collection: "${book.name}" (${book.id})`);
const bookRecipeRows = db.prepare("SELECT recipe_id FROM collection_recipes WHERE collection_id = ?").all(book.id);
const bookRecipeIds = new Set(bookRecipeRows.map(r => r.recipe_id));
console.log(`Recipes to remove: ${bookRecipeIds.size}`);
const allRecipeItems = db.prepare("SELECT recipe_id, ingredient_id, subrecipe_id FROM recipe_items").all();
const externalDependents = allRecipeItems.filter(
item => !bookRecipeIds.has(item.recipe_id) && item.subrecipe_id && bookRecipeIds.has(item.subrecipe_id)
);
if (externalDependents.length > 0) {
throw new Error(`Cannot delete recipes: ${externalDependents.length} external items depend on them: ${JSON.stringify(externalDependents)}`);
}
const nonBookIngredientIds = new Set(
allRecipeItems.filter(item => !bookRecipeIds.has(item.recipe_id) && item.ingredient_id).map(item => item.ingredient_id)
);
const stubIngredients = db.prepare("SELECT id, name, categories_json FROM ingredients WHERE categories_json LIKE '%imported_stub%'").all();
const ingredientsToDelete = stubIngredients.filter(ing => !nonBookIngredientIds.has(ing.id));
const ingredientsToKeepAndClean = stubIngredients.filter(ing => nonBookIngredientIds.has(ing.id));
console.log(`Stub ingredients to delete: ${ingredientsToDelete.length}`);
console.log(`Stub ingredients used elsewhere (keeping and removing 'imported_stub' category): ${ingredientsToKeepAndClean.length} (${ingredientsToKeepAndClean.map(i => i.id).join(", ")})`);
db.exec("BEGIN TRANSACTION");
for (const ing of ingredientsToKeepAndClean) {
const cats = JSON.parse(ing.categories_json ?? "[]").filter(c => c !== "imported_stub");
db.prepare("UPDATE ingredients SET categories_json = ? WHERE id = ?").run(JSON.stringify(cats), ing.id);
}
db.prepare("DELETE FROM collection_recipes WHERE collection_id = ?").run(book.id);
db.prepare("DELETE FROM collections WHERE id = ?").run(book.id);
const deleteRecipeStmt = db.prepare("DELETE FROM recipes WHERE id = ?");
for (const recipeId of bookRecipeIds) {
deleteRecipeStmt.run(recipeId);
}
const deleteIngredientStmt = db.prepare("DELETE FROM ingredients WHERE id = ?");
for (const ing of ingredientsToDelete) {
deleteIngredientStmt.run(ing.id);
}
db.exec("COMMIT");
console.log("Database transaction committed successfully.");
writeSiteProjection(db);
console.log("Site projection updated.");
const backupBundle = exportDatabase(db);
fs.writeFileSync(backupPath, JSON.stringify(backupBundle, null, 2) + "\n", "utf8");
console.log(`Exported updated backup to ${backupPath}`);
const recipeCount = db.prepare("SELECT count(*) as count FROM recipes WHERE deleted_at IS NULL").get();
const ingredientCount = db.prepare("SELECT count(*) as count FROM ingredients WHERE deleted_at IS NULL").get();
const collectionCount = db.prepare("SELECT count(*) as count FROM collections WHERE deleted_at IS NULL").get();
console.log("\n--- Current Database Totals ---");
console.log(`Active Recipes: ${recipeCount.count}`);
console.log(`Active Ingredients: ${ingredientCount.count}`);
console.log(`Active Collections: ${collectionCount.count}`);
} catch (err) {
try { db.exec("ROLLBACK"); } catch {}
console.error("Error during removal:", err);
process.exit(1);
} finally {
db.close();
}