From c37b8956d4bbc3a4df29f62f60f4fe987152cc76 Mon Sep 17 00:00:00 2001 From: Jurgen Rentinck Date: Sun, 9 Aug 2026 19:50:53 +0200 Subject: [PATCH] Multi tenant DB, registratie per huishouden V1.2 --- controllers/CategoryController.js | 12 +++--- controllers/MenuController.js | 2 +- controllers/ProductController.js | 39 +++++++++++-------- controllers/ShoppingListController.js | 8 ++-- database.sqlite-shm | Bin 32768 -> 32768 bytes database.sqlite-wal | Bin 276072 -> 329632 bytes database/migrate.js | 18 +++++++++ database/seed.js | 21 ++++++++--- repositories/CategoryRepository.js | 37 +++++++++--------- repositories/ProductRepository.js | 50 +++++++++++++++---------- repositories/ShoppingListRepository.js | 7 ++-- services/CategoryService.js | 20 +++++----- services/MenuService.js | 10 ++--- services/ProductService.js | 20 +++++----- sessions.sqlite | Bin 20480 -> 20480 bytes 15 files changed, 148 insertions(+), 96 deletions(-) diff --git a/controllers/CategoryController.js b/controllers/CategoryController.js index b9bd5db..0a03896 100644 --- a/controllers/CategoryController.js +++ b/controllers/CategoryController.js @@ -8,7 +8,7 @@ class CategoryController { res.render("categories/index", { title: "Categorieën", - categories: CategoryService.getCategories() + categories: CategoryService.getCategories(req.user.household_id) }); } @@ -17,7 +17,7 @@ class CategoryController { editForm(req, res) { const categoryId = Number.parseInt(req.params.id, 10); const category = Number.isInteger(categoryId) - ? CategoryService.getCategory(categoryId) + ? CategoryService.getCategory(categoryId, req.user.household_id) : null; if (!category) { @@ -41,7 +41,7 @@ class CategoryController { return res.redirect("/categories"); } - CategoryService.createCategory({ name, icon }); + CategoryService.createCategory({ name, icon, household_id: req.user.household_id }); req.flash("success", "Categorie toegevoegd."); res.redirect("/categories"); } @@ -57,7 +57,7 @@ class CategoryController { return res.redirect("/categories"); } - const result = CategoryService.updateCategory(categoryId, { name, icon }); + const result = CategoryService.updateCategory(categoryId, req.user.household_id, { name, icon }); if (result.changes === 0) { req.flash("error", "Categorie niet gevonden."); @@ -77,7 +77,7 @@ class CategoryController { return res.redirect("/categories"); } - if (CategoryService.categoryHasProducts(categoryId)) { + if (CategoryService.categoryHasProducts(categoryId, req.user.household_id)) { req.flash( "error", "Deze categorie is nog aan producten gekoppeld en kan niet worden verwijderd." @@ -85,7 +85,7 @@ class CategoryController { return res.redirect("/categories"); } - const result = CategoryService.deleteCategory(categoryId); + const result = CategoryService.deleteCategory(categoryId, req.user.household_id); if (result.changes === 0) { req.flash("error", "Categorie niet gevonden."); diff --git a/controllers/MenuController.js b/controllers/MenuController.js index 4d8af53..b709fc6 100644 --- a/controllers/MenuController.js +++ b/controllers/MenuController.js @@ -25,7 +25,7 @@ class MenuController { res.render("menu/preview", { title: "Weekmenu controleren", menu, - newProducts: MenuService.getNewProducts(menu), + newProducts: MenuService.getNewProducts(menu, req.user.household_id), listName: "Weekboodschappen" }); } catch (error) { diff --git a/controllers/ProductController.js b/controllers/ProductController.js index e9c2d3c..cab9126 100644 --- a/controllers/ProductController.js +++ b/controllers/ProductController.js @@ -1,7 +1,8 @@ const ProductService = require("../services/ProductService"); -const db = require("../config/database"); +const CategoryService = require("../services/CategoryService"); + const parseCsv = require("../utils/parseCsv"); class ProductController { @@ -10,11 +11,9 @@ class ProductController { index(req, res) { const products = - ProductService.getProducts(); + ProductService.getProducts(req.user.household_id); - const categories = db.prepare( - "SELECT * FROM categories ORDER BY name" - ).all(); + const categories = CategoryService.getCategories(req.user.household_id); res.render( "products/index", @@ -39,9 +38,15 @@ class ProductController { return res.redirect("/products"); } + if (categoryId !== null && !CategoryService.getCategory(categoryId, req.user.household_id)) { + req.flash("error", "De gekozen categorie bestaat niet."); + return res.redirect("/products"); + } + ProductService.createProduct({ name, - category_id: categoryId + category_id: categoryId, + household_id: req.user.household_id }); req.flash("success", "Product toegevoegd."); @@ -80,7 +85,7 @@ class ProductController { return res.redirect("/products"); } - const categories = db.prepare("SELECT id, name FROM categories").all(); + const categories = CategoryService.getCategories(req.user.household_id); const categoryIds = new Map( categories.map(category => [category.name.trim().toLowerCase(), category.id]) ); @@ -105,7 +110,8 @@ class ProductController { products.push({ name, category_id: categoryId, - barcode: barcodeIndex === -1 ? "" : (row[barcodeIndex] || "").trim() + barcode: barcodeIndex === -1 ? "" : (row[barcodeIndex] || "").trim(), + household_id: req.user.household_id }); }); @@ -123,7 +129,7 @@ class ProductController { editForm(req, res) { const productId = Number.parseInt(req.params.id, 10); const product = Number.isInteger(productId) - ? ProductService.getProduct(productId) + ? ProductService.getProduct(productId, req.user.household_id) : null; if (!product) { @@ -131,9 +137,7 @@ class ProductController { return res.redirect("/products"); } - const categories = db.prepare( - "SELECT * FROM categories ORDER BY name" - ).all(); + const categories = CategoryService.getCategories(req.user.household_id); res.render("products/edit", { title: "Product wijzigen", @@ -156,7 +160,12 @@ class ProductController { return res.redirect("/products"); } - const result = ProductService.updateProduct(productId, { + if (categoryId !== null && !CategoryService.getCategory(categoryId, req.user.household_id)) { + req.flash("error", "De gekozen categorie bestaat niet."); + return res.redirect(`/products/${productId}/edit`); + } + + const result = ProductService.updateProduct(productId, req.user.household_id, { name, category_id: categoryId }); @@ -179,7 +188,7 @@ class ProductController { return res.redirect("/products"); } - if (ProductService.productHasListItems(productId)) { + if (ProductService.productHasListItems(productId, req.user.household_id)) { req.flash( "error", "Dit product staat nog op een boodschappenlijst en kan niet worden verwijderd." @@ -187,7 +196,7 @@ class ProductController { return res.redirect(`/products/${productId}/edit`); } - const result = ProductService.deleteProduct(productId); + const result = ProductService.deleteProduct(productId, req.user.household_id); if (result.changes === 0) { req.flash("error", "Product niet gevonden."); diff --git a/controllers/ShoppingListController.js b/controllers/ShoppingListController.js index eff516d..5fdc169 100644 --- a/controllers/ShoppingListController.js +++ b/controllers/ShoppingListController.js @@ -25,8 +25,8 @@ class ShoppingListController { res.locals.title = list.name; res.render("lists/show", { list, - items: ShoppingListRepository.getItems(list.id), - products: ProductRepository.getAll(), + items: ShoppingListRepository.getItems(list.id, req.user.household_id), + products: ProductRepository.getAll(req.user.household_id), user: req.user }); } @@ -35,7 +35,9 @@ class ShoppingListController { const list = ShoppingListRepository.findById(req.params.id, req.user.household_id); const productId = Number.parseInt(req.body.product_id, 10); const amount = Number.parseFloat(req.body.amount || "1"); - const product = Number.isInteger(productId) ? ProductRepository.findById(productId) : null; + const product = Number.isInteger(productId) + ? ProductRepository.findById(productId, req.user.household_id) + : null; if (!list || !product || !Number.isFinite(amount) || amount <= 0) { req.flash("error", "Ongeldig product of ongeldige hoeveelheid"); return res.redirect(`/lists/${req.params.id}`); diff --git a/database.sqlite-shm b/database.sqlite-shm index f2db13e73507c83c5cf6c37a4f7f39193d3dcf0c..acb1895fb3a56edbbbb62277c3970f7c8b4b67dd 100644 GIT binary patch delta 263 zcmZo@U}|V!s+V}A%K!qxK+MR%AP@kg1%cR%H{p7-x|Q-VF>$YzqU-XOCuuI)U}YLl zs(PSNU;r}rKN5fnZ>$$(oZP_3GjWClFVK3B6Bsywm}~PyMoUL#Uj{!Q<2MsKD}yM5 zIDPs#N+`krbM{_ delta 220 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 { + const columns = db.prepare(`PRAGMA table_info(${table})`).all(); + if (!columns.some(existing => existing.name === column)) { + db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`); + } +}; + +addColumnIfMissing("categories", "household_id", "INTEGER REFERENCES households(id)"); +addColumnIfMissing("products", "household_id", "INTEGER REFERENCES households(id)"); + +const defaultHousehold = db.prepare("SELECT id FROM households ORDER BY id LIMIT 1").get(); +if (defaultHousehold) { + db.prepare("UPDATE categories SET household_id = ? WHERE household_id IS NULL") + .run(defaultHousehold.id); + db.prepare("UPDATE products SET household_id = ? WHERE household_id IS NULL") + .run(defaultHousehold.id); +} + console.log("Database migratie voltooid."); \ No newline at end of file diff --git a/database/seed.js b/database/seed.js index bc1115b..b61b8b8 100644 --- a/database/seed.js +++ b/database/seed.js @@ -19,24 +19,33 @@ const products = [ ["WC papier", "Huishouden"] ]; +const household = db.prepare( + "SELECT id FROM households ORDER BY id LIMIT 1" +).get(); + +if (!household) { + console.log("Seed overgeslagen: er is nog geen huishouden."); + process.exit(0); +} + const insertCategory = db.prepare(` - INSERT OR IGNORE INTO categories (name, icon) VALUES (?, ?) + INSERT OR IGNORE INTO categories (name, icon, household_id) VALUES (?, ?, ?) `); const insertProduct = db.prepare(` - INSERT OR IGNORE INTO products (name, category_id) VALUES (?, ?) + INSERT OR IGNORE INTO products (name, category_id, household_id) VALUES (?, ?, ?) `); db.transaction(() => { for (const category of categories) { - insertCategory.run(category); + insertCategory.run(category[0], category[1], household.id); } for (const [name, categoryName] of products) { const category = db.prepare( - "SELECT id FROM categories WHERE name = ?" - ).get(categoryName); - insertProduct.run(name, category.id); + "SELECT id FROM categories WHERE name = ? AND household_id = ?" + ).get(categoryName, household.id); + insertProduct.run(name, category.id, household.id); } })(); diff --git a/repositories/CategoryRepository.js b/repositories/CategoryRepository.js index 981f7e9..87e4d9e 100644 --- a/repositories/CategoryRepository.js +++ b/repositories/CategoryRepository.js @@ -4,7 +4,7 @@ const db = require("../config/database"); class CategoryRepository { - getAll() { + getAll(householdId) { return db.prepare(` @@ -13,23 +13,26 @@ class CategoryRepository { FROM categories LEFT JOIN products ON products.category_id = categories.id + AND products.household_id = categories.household_id + + WHERE categories.household_id = ? GROUP BY categories.id ORDER BY categories.name - `).all(); + `).all(householdId); } - findById(id) { + findById(id, householdId) { return db.prepare(` - SELECT * FROM categories WHERE id = ? + SELECT * FROM categories WHERE id = ? AND household_id = ? - `).get(id); + `).get(id, householdId); } @@ -38,16 +41,16 @@ class CategoryRepository { return db.prepare(` - INSERT INTO categories (name, icon) + INSERT INTO categories (name, icon, household_id) - VALUES (?, ?) + VALUES (?, ?, ?) - `).run(data.name, data.icon || null); + `).run(data.name, data.icon || null, data.household_id); } - update(id, data) { + update(id, householdId, data) { return db.prepare(` @@ -55,31 +58,31 @@ class CategoryRepository { SET name = ?, icon = ? - WHERE id = ? + WHERE id = ? AND household_id = ? - `).run(data.name, data.icon || null, id); + `).run(data.name, data.icon || null, id, householdId); } - hasProducts(id) { + hasProducts(id, householdId) { return db.prepare(` - SELECT 1 FROM products WHERE category_id = ? LIMIT 1 + SELECT 1 FROM products WHERE category_id = ? AND household_id = ? LIMIT 1 - `).get(id) !== undefined; + `).get(id, householdId) !== undefined; } - delete(id) { + delete(id, householdId) { return db.prepare(` - DELETE FROM categories WHERE id = ? + DELETE FROM categories WHERE id = ? AND household_id = ? - `).run(id); + `).run(id, householdId); } diff --git a/repositories/ProductRepository.js b/repositories/ProductRepository.js index 69cd21a..d7234d0 100644 --- a/repositories/ProductRepository.js +++ b/repositories/ProductRepository.js @@ -4,7 +4,7 @@ const db = require("../config/database"); class ProductRepository { - getAll() { + getAll(householdId) { return db.prepare(` @@ -24,16 +24,18 @@ class ProductRepository { ON categories.id = products.category_id + WHERE products.household_id = ? + ORDER BY products.name - `).all(); + `).all(householdId); } - findById(id) { + findById(id, householdId) { return db.prepare(` @@ -41,9 +43,9 @@ class ProductRepository { FROM products - WHERE id = ? + WHERE id = ? AND household_id = ? - `).get(id); + `).get(id, householdId); } @@ -59,10 +61,11 @@ class ProductRepository { ( name, category_id, - barcode + barcode, + household_id ) - VALUES (?, ?, ?) + VALUES (?, ?, ?, ?) `).run( @@ -70,7 +73,8 @@ class ProductRepository { data.category_id || null, - data.barcode || null + data.barcode || null, + data.household_id ); @@ -81,9 +85,9 @@ class ProductRepository { createMany(products) { const insert = db.prepare(` - INSERT INTO products (name, category_id, barcode) + INSERT INTO products (name, category_id, barcode, household_id) - VALUES (?, ?, ?) + VALUES (?, ?, ?, ?) `); @@ -92,7 +96,8 @@ class ProductRepository { insert.run( product.name, product.category_id || null, - product.barcode || null + product.barcode || null, + product.household_id ); } }); @@ -102,7 +107,7 @@ class ProductRepository { } - update(id, data) { + update(id, householdId, data) { return db.prepare(` @@ -110,7 +115,7 @@ class ProductRepository { SET name = ?, category_id = ? - WHERE id = ? + WHERE id = ? AND household_id = ? `).run( @@ -118,14 +123,15 @@ class ProductRepository { data.category_id || null, - id + id, + householdId ); } - hasListItems(id) { + hasListItems(id, householdId) { return db.prepare(` @@ -133,24 +139,28 @@ class ProductRepository { FROM shopping_list_items - WHERE product_id = ? + WHERE product_id = ? AND EXISTS ( + SELECT 1 FROM shopping_lists + WHERE shopping_lists.id = shopping_list_items.list_id + AND shopping_lists.household_id = ? + ) LIMIT 1 - `).get(id) !== undefined; + `).get(id, householdId) !== undefined; } - delete(id) { + delete(id, householdId) { return db.prepare(` DELETE FROM products - WHERE id = ? + WHERE id = ? AND household_id = ? - `).run(id); + `).run(id, householdId); } diff --git a/repositories/ShoppingListRepository.js b/repositories/ShoppingListRepository.js index 0220924..855b8cd 100644 --- a/repositories/ShoppingListRepository.js +++ b/repositories/ShoppingListRepository.js @@ -22,18 +22,19 @@ class ShoppingListRepository { `).get(id, householdId); } - getItems(listId) { + getItems(listId, householdId) { return db.prepare(` SELECT shopping_list_items.*, products.name AS product_name, categories.name AS category_name, categories.icon AS category_icon FROM shopping_list_items JOIN products ON products.id = shopping_list_items.product_id LEFT JOIN categories ON categories.id = products.category_id - WHERE shopping_list_items.list_id = ? + WHERE shopping_list_items.list_id = ? + AND products.household_id = ? ORDER BY shopping_list_items.checked ASC, shopping_list_items.sort_order ASC, shopping_list_items.created_at ASC - `).all(listId); + `).all(listId, householdId); } create(householdId, name) { diff --git a/services/CategoryService.js b/services/CategoryService.js index 85b7708..016b628 100644 --- a/services/CategoryService.js +++ b/services/CategoryService.js @@ -4,16 +4,16 @@ const CategoryRepository = require("../repositories/CategoryRepository"); class CategoryService { - getCategories() { + getCategories(householdId) { - return CategoryRepository.getAll(); + return CategoryRepository.getAll(householdId); } - getCategory(id) { + getCategory(id, householdId) { - return CategoryRepository.findById(id); + return CategoryRepository.findById(id, householdId); } @@ -25,23 +25,23 @@ class CategoryService { } - updateCategory(id, data) { + updateCategory(id, householdId, data) { - return CategoryRepository.update(id, data); + return CategoryRepository.update(id, householdId, data); } - categoryHasProducts(id) { + categoryHasProducts(id, householdId) { - return CategoryRepository.hasProducts(id); + return CategoryRepository.hasProducts(id, householdId); } - deleteCategory(id) { + deleteCategory(id, householdId) { - return CategoryRepository.delete(id); + return CategoryRepository.delete(id, householdId); } diff --git a/services/MenuService.js b/services/MenuService.js index c95bb16..57d16e6 100644 --- a/services/MenuService.js +++ b/services/MenuService.js @@ -82,9 +82,9 @@ class MenuService { } - getNewProducts(menu) { + getNewProducts(menu, householdId) { const existingNames = new Set( - ProductRepository.getAll().map(product => normalizeName(product.name)) + ProductRepository.getAll(householdId).map(product => normalizeName(product.name)) ); const seen = new Set(); const newProducts = []; @@ -104,12 +104,12 @@ class MenuService { addToShoppingList(householdId, name, menu) { - const products = ProductRepository.getAll(); + const products = ProductRepository.getAll(householdId); const productsByName = new Map( products.map(product => [normalizeName(product.name), product]) ); const insertProduct = db.prepare(` - INSERT INTO products (name, category_id, barcode) VALUES (?, NULL, NULL) + INSERT INTO products (name, category_id, barcode, household_id) VALUES (?, NULL, NULL, ?) `); const insertList = db.prepare(` INSERT INTO shopping_lists (household_id, name) VALUES (?, ?) @@ -132,7 +132,7 @@ class MenuService { let product = productsByName.get(key); if (!product) { - const result = insertProduct.run(ingredient.name); + const result = insertProduct.run(ingredient.name, householdId); product = { id: result.lastInsertRowid, name: ingredient.name }; productsByName.set(key, product); } diff --git a/services/ProductService.js b/services/ProductService.js index 29b53ed..51a4b59 100644 --- a/services/ProductService.js +++ b/services/ProductService.js @@ -5,9 +5,9 @@ require("../repositories/ProductRepository"); class ProductService { - getProducts() { + getProducts(householdId) { - return ProductRepository.getAll(); + return ProductRepository.getAll(householdId); } @@ -26,30 +26,30 @@ class ProductService { } - getProduct(id) { + getProduct(id, householdId) { - return ProductRepository.findById(id); + return ProductRepository.findById(id, householdId); } - updateProduct(id, data) { + updateProduct(id, householdId, data) { - return ProductRepository.update(id, data); + return ProductRepository.update(id, householdId, data); } - productHasListItems(id) { + productHasListItems(id, householdId) { - return ProductRepository.hasListItems(id); + return ProductRepository.hasListItems(id, householdId); } - deleteProduct(id) { + deleteProduct(id, householdId) { - return ProductRepository.delete(id); + return ProductRepository.delete(id, householdId); } diff --git a/sessions.sqlite b/sessions.sqlite index 9bd16f58d4e8c33c2f303acdfe89ced1911d1bb4..82b9c083a5e4f32265a5db3445842649667f6a98 100644 GIT binary patch delta 379 zcmZozz}T>Wae_3X_e2?IM(>RYOXQj5xK~YPSCCcUUd2C=$Bu6^-wj?FK6(BezCJz| z9!dUf{I_|#xzF=DZY)gW_EKT?W%LR3PA$mD4#_R>^!5)bPPPorD)aYB%FTAJD%H); z&xr*osxXN#%W-sZ3{Nc%^z=4OE=do~E%mT4H7!re%!~K4h}!%`TR?>$LyMrPN z<8vUxP++%LT@0&lGruh!I~t>7S$&&4@hJg2V=|+=HJ?R@p}CcjrIn$Ho|%QwP}DN0tRwMGU;X1WHJx&|Wae_3XHyxqKx8wPp@>}VwWJF zFtfrGBlnP$BHffG;R>L