db-model-router 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/README.md +150 -11
  2. package/TODO.md +0 -14
  3. package/db-manager/.dbmanager.sqlite +0 -0
  4. package/db-manager/README.md +223 -0
  5. package/db-manager/adapter-proxy.js +361 -0
  6. package/db-manager/demo/cockroachdb.env +6 -0
  7. package/db-manager/demo/demo.sqlite +0 -0
  8. package/db-manager/demo/dynamodb.env +7 -0
  9. package/db-manager/demo/mongodb.env +4 -0
  10. package/db-manager/demo/mssql.env +6 -0
  11. package/db-manager/demo/mysql.env +6 -0
  12. package/db-manager/demo/oracle.env +6 -0
  13. package/db-manager/demo/postgres.env +6 -0
  14. package/db-manager/demo/redis.env +4 -0
  15. package/db-manager/demo/seeds/cockroachdb.sql +32 -0
  16. package/db-manager/demo/seeds/mssql.sql +32 -0
  17. package/db-manager/demo/seeds/mysql.sql +32 -0
  18. package/db-manager/demo/seeds/oracle.sql +43 -0
  19. package/db-manager/demo/seeds/postgres.sql +32 -0
  20. package/db-manager/demo/seeds/sqlite3.sql +32 -0
  21. package/db-manager/demo/sqlite3.env +2 -0
  22. package/db-manager/metadata-db.js +170 -0
  23. package/db-manager/public/.gitkeep +1 -0
  24. package/db-manager/public/css/style.css +1413 -0
  25. package/db-manager/public/js/app.js +1370 -0
  26. package/db-manager/routes/api.js +388 -0
  27. package/db-manager/routes/views.js +61 -0
  28. package/db-manager/server.js +39 -0
  29. package/db-manager/utils/build-filter-config.js +18 -0
  30. package/db-manager/utils/csv-export.js +59 -0
  31. package/db-manager/utils/export-filename.js +39 -0
  32. package/db-manager/utils/filter-tables.js +20 -0
  33. package/db-manager/utils/parse-filters.js +93 -0
  34. package/db-manager/utils/sort-state.js +35 -0
  35. package/db-manager/views/.gitkeep +1 -0
  36. package/db-manager/views/dashboard.ejs +53 -0
  37. package/db-manager/views/history.ejs +52 -0
  38. package/db-manager/views/index.ejs +35 -0
  39. package/db-manager/views/layout.ejs +31 -0
  40. package/db-manager/views/partials/data-panel.ejs +74 -0
  41. package/db-manager/views/partials/header.ejs +36 -0
  42. package/db-manager/views/partials/sidebar.ejs +30 -0
  43. package/db-manager/views/query.ejs +58 -0
  44. package/dbmr.schema.json +23 -45
  45. package/demo/.env.example +1 -0
  46. package/demo/app.js +3 -1
  47. package/demo/commons/db.js +11 -0
  48. package/demo/commons/migrate.js +3 -0
  49. package/demo/commons/modules.js +18 -0
  50. package/demo/commons/password.js +36 -0
  51. package/demo/commons/webhook.js +81 -0
  52. package/demo/dbmr.schema.json +22 -46
  53. package/demo/middleware/authenticate.js +14 -0
  54. package/demo/middleware/hasPermission.js +30 -0
  55. package/demo/middleware/tenantIsolation.js +17 -0
  56. package/demo/migrations/20260509170349_create_saas_tables.sql +69 -0
  57. package/demo/migrations/{20260430155809_create_tables.sql → 20260509170349_create_tables.sql} +11 -25
  58. package/demo/models/addresses.js +5 -3
  59. package/demo/models/cart_items.js +5 -3
  60. package/demo/models/carts.js +5 -3
  61. package/demo/models/categories.js +5 -3
  62. package/demo/models/coupons.js +5 -3
  63. package/demo/models/index.js +43 -0
  64. package/demo/models/order_items.js +4 -2
  65. package/demo/models/orders.js +5 -3
  66. package/demo/models/payments.js +5 -3
  67. package/demo/models/product_images.js +4 -2
  68. package/demo/models/product_reviews.js +5 -3
  69. package/demo/models/product_variants.js +5 -3
  70. package/demo/models/products.js +5 -3
  71. package/demo/models/role_permissions.js +17 -0
  72. package/demo/models/roles.js +17 -0
  73. package/demo/models/shipments.js +5 -3
  74. package/demo/models/tenants.js +18 -0
  75. package/demo/models/users.js +12 -8
  76. package/demo/models/webhook_logs.js +22 -0
  77. package/demo/models/webhooks.js +19 -0
  78. package/demo/models/wishlists.js +4 -2
  79. package/demo/openapi.json +1744 -616
  80. package/demo/package-lock.json +24 -24
  81. package/demo/package.json +9 -0
  82. package/demo/routes/{addresses.js → addresses/index.js} +1 -1
  83. package/demo/routes/auth/index.js +55 -0
  84. package/demo/routes/carts/{cart_items.js → cart_items/index.js} +1 -1
  85. package/demo/routes/{carts.js → carts/index.js} +1 -1
  86. package/demo/routes/{categories.js → categories/index.js} +1 -1
  87. package/demo/routes/{coupons.js → coupons/index.js} +1 -1
  88. package/demo/routes/index.js +39 -24
  89. package/demo/routes/{orders.js → orders/index.js} +1 -1
  90. package/demo/routes/orders/{order_items.js → order_items/index.js} +1 -1
  91. package/demo/routes/orders/{payments.js → payments/index.js} +1 -1
  92. package/demo/routes/orders/{shipments.js → shipments/index.js} +1 -1
  93. package/demo/routes/{products.js → products/index.js} +1 -1
  94. package/demo/routes/products/{product_images.js → product_images/index.js} +1 -1
  95. package/demo/routes/products/{product_reviews.js → product_reviews/index.js} +1 -1
  96. package/demo/routes/products/{product_variants.js → product_variants/index.js} +1 -1
  97. package/demo/routes/roles/index.js +75 -0
  98. package/demo/routes/roles/permissions/index.js +47 -0
  99. package/demo/routes/tenants/index.js +45 -0
  100. package/demo/routes/users/index.js +45 -0
  101. package/demo/routes/{wishlists.js → wishlists/index.js} +1 -1
  102. package/demo/seeds/saas-seed.js +329 -0
  103. package/docker-compose.yml +61 -0
  104. package/package.json +120 -113
  105. package/scripts/demo-create.js +1 -1
  106. package/skill/SKILL.md +119 -3
  107. package/src/cli/commands/db-manager.js +134 -0
  108. package/src/cli/commands/generate.js +112 -43
  109. package/src/cli/commands/help.js +0 -1
  110. package/src/cli/diff-engine.js +2 -1
  111. package/src/cli/generate-model.js +9 -4
  112. package/src/cli/generate-openapi.js +40 -13
  113. package/src/cli/generate-route.js +61 -22
  114. package/src/cli/generate-saas-structure.js +122 -0
  115. package/src/cli/init/generators.js +42 -30
  116. package/src/cli/init.js +8 -0
  117. package/src/cli/main.js +8 -1
  118. package/src/cli/saas/generate-saas-middleware.js +108 -0
  119. package/src/cli/saas/generate-saas-migrations.js +480 -0
  120. package/src/cli/saas/generate-saas-models.js +211 -0
  121. package/src/cli/saas/generate-saas-openapi.js +419 -0
  122. package/src/cli/saas/generate-saas-routes.js +435 -0
  123. package/src/cli/saas/generate-saas-seeds.js +243 -0
  124. package/src/cli/saas/generate-saas-utils.js +176 -0
  125. package/src/commons/kafka.js +139 -0
  126. package/src/commons/model.js +29 -9
  127. package/src/index.js +2 -0
  128. package/src/mssql/db.js +41 -3
  129. package/src/mysql/db.js +3 -0
  130. package/src/postgres/db.js +6 -0
  131. package/src/sqlite3/db.js +11 -0
  132. package/demo/docs/llm.md +0 -197
  133. package/demo/llms.txt +0 -70
  134. package/demo/routes/users.js +0 -6
  135. package/src/cli/commands/generate-llm-docs.js +0 -418
  136. /package/demo/migrations/{20260430155808_create_migrations_table.sql → 20260509170349_create_migrations_table.sql} +0 -0
@@ -279,9 +279,9 @@
279
279
  }
280
280
  },
281
281
  "node_modules/brace-expansion": {
282
- "version": "5.0.5",
283
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
284
- "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
282
+ "version": "5.0.6",
283
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
284
+ "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
285
285
  "dev": true,
286
286
  "license": "MIT",
287
287
  "dependencies": {
@@ -575,9 +575,9 @@
575
575
  }
576
576
  },
577
577
  "node_modules/db-model-router": {
578
- "version": "1.0.4",
579
- "resolved": "https://registry.npmjs.org/db-model-router/-/db-model-router-1.0.4.tgz",
580
- "integrity": "sha512-mK4GSdkybMeyxd/EM3O5bGWAGmLHk0CJSwLD10OhnOTnVN6jOOIM17gdqCFVN5dMg3W15pCP+rm8fbUl/AowuQ==",
578
+ "version": "1.0.6",
579
+ "resolved": "https://registry.npmjs.org/db-model-router/-/db-model-router-1.0.6.tgz",
580
+ "integrity": "sha512-8GUCYQbWqhPpMOuweHJ3eLIzovJQDIiFRYJ2kM52KR/ryt7EM1Pn0tC+rk+kr92hdDnpVy5wXhEwKE8nxOuM1Q==",
581
581
  "license": "Apache-2.0",
582
582
  "dependencies": {
583
583
  "dotenv": "^10.0.0",
@@ -589,14 +589,14 @@
589
589
  "db-model-router": "src/cli/main.js"
590
590
  },
591
591
  "peerDependencies": {
592
- "@aws-sdk/client-dynamodb": "^3.1029.0",
593
- "@aws-sdk/lib-dynamodb": "^3.1029.0",
592
+ "@aws-sdk/client-dynamodb": "^3.1039.0",
593
+ "@aws-sdk/lib-dynamodb": "^3.1039.0",
594
594
  "better-sqlite3": "^12.9.0",
595
595
  "express": "^4.17.2 || ^5.0.0",
596
596
  "ioredis": "^5.10.1",
597
- "mongodb": "^7.1.1",
598
- "mssql": "^12.2.1",
599
- "mysql2": "^3.14.4",
597
+ "mongodb": "^7.2.0",
598
+ "mssql": "^12.5.0",
599
+ "mysql2": "^3.22.3",
600
600
  "oracledb": "^6.10.0",
601
601
  "pg": "^8.20.0",
602
602
  "ultimate-express": "^2.0.0"
@@ -904,12 +904,12 @@
904
904
  }
905
905
  },
906
906
  "node_modules/express-rate-limit": {
907
- "version": "8.4.1",
908
- "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.4.1.tgz",
909
- "integrity": "sha512-NGVYwQSAyEQgzxX1iCM978PP9AdO/hW93gMcF6ZwQCm+rFvLsBH6w4xcXWTcliS8La5EPRN3p9wzItqBwJrfNw==",
907
+ "version": "8.5.1",
908
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.1.tgz",
909
+ "integrity": "sha512-5O6KYmyJEpuPJV5hNTXKbAHWRqrzyu+OI3vUnSd2kXFubIVpG7ezpgxQy76Zo5GQZtrQBg86hF+CM/NX+cioiQ==",
910
910
  "license": "MIT",
911
911
  "dependencies": {
912
- "ip-address": "10.1.0"
912
+ "ip-address": "^10.2.0"
913
913
  },
914
914
  "engines": {
915
915
  "node": ">= 16"
@@ -1324,9 +1324,9 @@
1324
1324
  }
1325
1325
  },
1326
1326
  "node_modules/ip-address": {
1327
- "version": "10.1.0",
1328
- "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
1329
- "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
1327
+ "version": "10.2.0",
1328
+ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz",
1329
+ "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==",
1330
1330
  "license": "MIT",
1331
1331
  "engines": {
1332
1332
  "node": ">= 12"
@@ -1630,9 +1630,9 @@
1630
1630
  }
1631
1631
  },
1632
1632
  "node_modules/node-abi": {
1633
- "version": "3.89.0",
1634
- "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.89.0.tgz",
1635
- "integrity": "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==",
1633
+ "version": "3.92.0",
1634
+ "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.92.0.tgz",
1635
+ "integrity": "sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==",
1636
1636
  "license": "MIT",
1637
1637
  "dependencies": {
1638
1638
  "semver": "^7.3.5"
@@ -2220,9 +2220,9 @@
2220
2220
  "license": "MIT"
2221
2221
  },
2222
2222
  "node_modules/semver": {
2223
- "version": "7.7.4",
2224
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
2225
- "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
2223
+ "version": "7.8.0",
2224
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz",
2225
+ "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==",
2226
2226
  "license": "ISC",
2227
2227
  "bin": {
2228
2228
  "semver": "bin/semver.js"
package/demo/package.json CHANGED
@@ -17,6 +17,14 @@
17
17
  "author": "",
18
18
  "license": "ISC",
19
19
  "type": "module",
20
+ "imports": {
21
+ "#root/*.js": "./*.js",
22
+ "#models": "./models/index.js",
23
+ "#models/*.js": "./models/*.js",
24
+ "#routes/*.js": "./routes/*.js",
25
+ "#commons/*.js": "./commons/*.js",
26
+ "#middleware/*.js": "./middleware/*.js"
27
+ },
20
28
  "dependencies": {
21
29
  "db-model-router": "latest",
22
30
  "dotenv": "latest",
@@ -25,6 +33,7 @@
25
33
  "better-sqlite3": "latest",
26
34
  "express-rate-limit": "latest",
27
35
  "helmet": "latest",
36
+ "kafkajs": "latest",
28
37
  "winston": "latest",
29
38
  "swagger-ui-express": "latest"
30
39
  },
@@ -1,5 +1,5 @@
1
1
  import dbModelRouter from "db-model-router";
2
- import addresses from "../models/addresses.js";
2
+ import { addresses } from "#models";
3
3
 
4
4
  const { route } = dbModelRouter;
5
5
 
@@ -0,0 +1,55 @@
1
+ import express from "express";
2
+ import authenticate from "#middleware/authenticate.js";
3
+ import { verifyPassword } from "#commons/password.js";
4
+ import { users, roles, role_permissions } from "#models";
5
+
6
+ const router = express.Router();
7
+
8
+ // POST /api/auth/login - Authenticate user and create session
9
+ router.post("/login", async (req, res) => {
10
+ try {
11
+ const { email, password } = req.body;
12
+
13
+ if (!email || !password) {
14
+ return res.status(401).json({ message: "Invalid credentials" });
15
+ }
16
+
17
+ const userResults = await users.findAll({ email });
18
+ const user = Array.isArray(userResults) ? userResults[0] : (userResults?.data?.[0] ?? null);
19
+
20
+ if (!user) {
21
+ return res.status(401).json({ message: "Invalid credentials" });
22
+ }
23
+
24
+ const isValid = await verifyPassword(password, user.password_hash);
25
+ if (!isValid) {
26
+ return res.status(401).json({ message: "Invalid credentials" });
27
+ }
28
+
29
+ const role = await roles.findById(user.role_id);
30
+ const permsResult = await role_permissions.findAll({ role_id: user.role_id });
31
+ const permissionList = Array.isArray(permsResult) ? permsResult : (permsResult?.data ?? []);
32
+
33
+ req.session.user = user;
34
+ req.session.role = role;
35
+ req.session.permission = permissionList.map((p) =>
36
+ typeof p.permission === "string" ? JSON.parse(p.permission) : p.permission
37
+ );
38
+
39
+ res.json({ message: "Login successful", user: { id: user.user_id, email: user.email, name: user.name } });
40
+ } catch (err) {
41
+ res.status(500).json({ message: err.message });
42
+ }
43
+ });
44
+
45
+ // POST /api/auth/logout - Destroy session
46
+ router.post("/logout", authenticate, (req, res) => {
47
+ req.session.destroy((err) => {
48
+ if (err) {
49
+ return res.status(500).json({ message: "Failed to destroy session" });
50
+ }
51
+ res.json({ message: "Logout successful" });
52
+ });
53
+ });
54
+
55
+ export default router;
@@ -1,5 +1,5 @@
1
1
  import dbModelRouter from "db-model-router";
2
- import cart_items from "../../models/cart_items.js";
2
+ import { cart_items } from "#models";
3
3
 
4
4
  const { route } = dbModelRouter;
5
5
 
@@ -1,5 +1,5 @@
1
1
  import dbModelRouter from "db-model-router";
2
- import carts from "../models/carts.js";
2
+ import { carts } from "#models";
3
3
 
4
4
  const { route } = dbModelRouter;
5
5
 
@@ -1,5 +1,5 @@
1
1
  import dbModelRouter from "db-model-router";
2
- import categories from "../models/categories.js";
2
+ import { categories } from "#models";
3
3
 
4
4
  const { route } = dbModelRouter;
5
5
 
@@ -1,5 +1,5 @@
1
1
  import dbModelRouter from "db-model-router";
2
- import coupons from "../models/coupons.js";
2
+ import { coupons } from "#models";
3
3
 
4
4
  const { route } = dbModelRouter;
5
5
 
@@ -2,32 +2,38 @@ import express from "express";
2
2
 
3
3
  const router = express.Router();
4
4
 
5
- import addressesRoute from "./addresses.js";
6
- import cartsRoute from "./carts.js";
7
- import categoriesRoute from "./categories.js";
8
- import couponsRoute from "./coupons.js";
9
- import ordersRoute from "./orders.js";
10
- import productsRoute from "./products.js";
11
- import usersRoute from "./users.js";
12
- import wishlistsRoute from "./wishlists.js";
13
- import product_imagesChildRoute from "./products/product_images.js";
14
- import product_variantsChildRoute from "./products/product_variants.js";
15
- import product_reviewsChildRoute from "./products/product_reviews.js";
16
- import cart_itemsChildRoute from "./carts/cart_items.js";
17
- import order_itemsChildRoute from "./orders/order_items.js";
18
- import paymentsChildRoute from "./orders/payments.js";
19
- import shipmentsChildRoute from "./orders/shipments.js";
20
- import docsRoute from "./docs.js";
5
+ // SaaS auth & CRUD routes
6
+ import authRoute from "#routes/auth/index.js";
7
+ import saasUsersRoute from "#routes/users/index.js";
8
+ import saasTenantsRoute from "#routes/tenants/index.js";
9
+ import saasRolesRoute from "#routes/roles/index.js";
10
+ import saasPermissionsRoute from "#routes/roles/permissions/index.js";
11
+
12
+ // Schema-generated routes
13
+ import addressesRoute from "#routes/addresses/index.js";
14
+ import cartsRoute from "#routes/carts/index.js";
15
+ import categoriesRoute from "#routes/categories/index.js";
16
+ import couponsRoute from "#routes/coupons/index.js";
17
+ import ordersRoute from "#routes/orders/index.js";
18
+ import productsRoute from "#routes/products/index.js";
19
+ import wishlistsRoute from "#routes/wishlists/index.js";
20
+ import product_imagesChildRoute from "#routes/products/product_images/index.js";
21
+ import product_variantsChildRoute from "#routes/products/product_variants/index.js";
22
+ import product_reviewsChildRoute from "#routes/products/product_reviews/index.js";
23
+ import cart_itemsChildRoute from "#routes/carts/cart_items/index.js";
24
+ import order_itemsChildRoute from "#routes/orders/order_items/index.js";
25
+ import paymentsChildRoute from "#routes/orders/payments/index.js";
26
+ import shipmentsChildRoute from "#routes/orders/shipments/index.js";
27
+ import docsRoute from "#routes/docs.js";
28
+
29
+ // SaaS routes
30
+ router.use("/auth", authRoute);
31
+ router.use("/users", saasUsersRoute);
32
+ router.use("/tenants", saasTenantsRoute);
33
+ router.use("/roles", saasRolesRoute);
34
+ router.use("/roles/:role_id/permissions", saasPermissionsRoute);
21
35
 
22
36
  router.use("/docs", docsRoute);
23
- router.use("/addresses", addressesRoute);
24
- router.use("/carts", cartsRoute);
25
- router.use("/categories", categoriesRoute);
26
- router.use("/coupons", couponsRoute);
27
- router.use("/orders", ordersRoute);
28
- router.use("/products", productsRoute);
29
- router.use("/users", usersRoute);
30
- router.use("/wishlists", wishlistsRoute);
31
37
  router.use("/products/:product_id/product_images", product_imagesChildRoute);
32
38
  router.use("/products/:product_id/product_variants", product_variantsChildRoute);
33
39
  router.use("/products/:product_id/product_reviews", product_reviewsChildRoute);
@@ -36,4 +42,13 @@ router.use("/orders/:order_id/order_items", order_itemsChildRoute);
36
42
  router.use("/orders/:order_id/payments", paymentsChildRoute);
37
43
  router.use("/orders/:order_id/shipments", shipmentsChildRoute);
38
44
 
45
+ // Schema-generated routes
46
+ router.use("/addresses", addressesRoute);
47
+ router.use("/carts", cartsRoute);
48
+ router.use("/categories", categoriesRoute);
49
+ router.use("/coupons", couponsRoute);
50
+ router.use("/orders", ordersRoute);
51
+ router.use("/products", productsRoute);
52
+ router.use("/wishlists", wishlistsRoute);
53
+
39
54
  export default router;
@@ -1,5 +1,5 @@
1
1
  import dbModelRouter from "db-model-router";
2
- import orders from "../models/orders.js";
2
+ import { orders } from "#models";
3
3
 
4
4
  const { route } = dbModelRouter;
5
5
 
@@ -1,5 +1,5 @@
1
1
  import dbModelRouter from "db-model-router";
2
- import order_items from "../../models/order_items.js";
2
+ import { order_items } from "#models";
3
3
 
4
4
  const { route } = dbModelRouter;
5
5
 
@@ -1,5 +1,5 @@
1
1
  import dbModelRouter from "db-model-router";
2
- import payments from "../../models/payments.js";
2
+ import { payments } from "#models";
3
3
 
4
4
  const { route } = dbModelRouter;
5
5
 
@@ -1,5 +1,5 @@
1
1
  import dbModelRouter from "db-model-router";
2
- import shipments from "../../models/shipments.js";
2
+ import { shipments } from "#models";
3
3
 
4
4
  const { route } = dbModelRouter;
5
5
 
@@ -1,5 +1,5 @@
1
1
  import dbModelRouter from "db-model-router";
2
- import products from "../models/products.js";
2
+ import { products } from "#models";
3
3
 
4
4
  const { route } = dbModelRouter;
5
5
 
@@ -1,5 +1,5 @@
1
1
  import dbModelRouter from "db-model-router";
2
- import product_images from "../../models/product_images.js";
2
+ import { product_images } from "#models";
3
3
 
4
4
  const { route } = dbModelRouter;
5
5
 
@@ -1,5 +1,5 @@
1
1
  import dbModelRouter from "db-model-router";
2
- import product_reviews from "../../models/product_reviews.js";
2
+ import { product_reviews } from "#models";
3
3
 
4
4
  const { route } = dbModelRouter;
5
5
 
@@ -1,5 +1,5 @@
1
1
  import dbModelRouter from "db-model-router";
2
- import product_variants from "../../models/product_variants.js";
2
+ import { product_variants } from "#models";
3
3
 
4
4
  const { route } = dbModelRouter;
5
5
 
@@ -0,0 +1,75 @@
1
+ import express from "express";
2
+ import authenticate from "#middleware/authenticate.js";
3
+ import tenantIsolation from "#middleware/tenantIsolation.js";
4
+ import hasPermission from "#middleware/hasPermission.js";
5
+ import { roles } from "#models";
6
+
7
+ const router = express.Router();
8
+
9
+ function userHasGlobalPermission(req) {
10
+ return req.session.permission.some((p) => p.scope === "global");
11
+ }
12
+
13
+ function guardSystemRole(req, res, role) {
14
+ if (role.tenant_id === null && !userHasGlobalPermission(req)) {
15
+ res.status(403).json({ message: "Cannot modify system roles" });
16
+ return true;
17
+ }
18
+ return false;
19
+ }
20
+
21
+ function guardGlobalPermissionEscalation(req, res) {
22
+ const permissions = req.body.permissions || [];
23
+ const hasGlobalEntry = permissions.some((p) => p.scope === "global");
24
+ if (hasGlobalEntry && !userHasGlobalPermission(req)) {
25
+ res.status(403).json({ message: "Cannot assign global permissions" });
26
+ return true;
27
+ }
28
+ return false;
29
+ }
30
+
31
+ router.get("/", authenticate, tenantIsolation, hasPermission("roles", "read"), async (req, res) => {
32
+ try {
33
+ const results = await roles.findAll(req.query);
34
+ res.json(results);
35
+ } catch (err) {
36
+ res.status(500).json({ message: err.message });
37
+ }
38
+ });
39
+
40
+ router.post("/", authenticate, tenantIsolation, hasPermission("roles", "write"), async (req, res) => {
41
+ try {
42
+ if (guardGlobalPermissionEscalation(req, res)) return;
43
+ const result = await roles.create(req.body);
44
+ res.status(201).json(result);
45
+ } catch (err) {
46
+ res.status(500).json({ message: err.message });
47
+ }
48
+ });
49
+
50
+ router.put("/:id", authenticate, tenantIsolation, hasPermission("roles", "update"), async (req, res) => {
51
+ try {
52
+ const role = await roles.findById(req.params.id);
53
+ if (!role) return res.status(404).json({ message: "Role not found" });
54
+ if (guardSystemRole(req, res, role)) return;
55
+ if (guardGlobalPermissionEscalation(req, res)) return;
56
+ const result = await roles.update(req.params.id, req.body);
57
+ res.json(result);
58
+ } catch (err) {
59
+ res.status(500).json({ message: err.message });
60
+ }
61
+ });
62
+
63
+ router.delete("/:id", authenticate, tenantIsolation, hasPermission("roles", "delete"), async (req, res) => {
64
+ try {
65
+ const role = await roles.findById(req.params.id);
66
+ if (!role) return res.status(404).json({ message: "Role not found" });
67
+ if (guardSystemRole(req, res, role)) return;
68
+ const result = await roles.delete(req.params.id);
69
+ res.json(result);
70
+ } catch (err) {
71
+ res.status(500).json({ message: err.message });
72
+ }
73
+ });
74
+
75
+ export default router;
@@ -0,0 +1,47 @@
1
+ import express from "express";
2
+ import authenticate from "#middleware/authenticate.js";
3
+ import tenantIsolation from "#middleware/tenantIsolation.js";
4
+ import hasPermission from "#middleware/hasPermission.js";
5
+ import { role_permissions } from "#models";
6
+
7
+ const router = express.Router({ mergeParams: true });
8
+
9
+ router.get("/", authenticate, tenantIsolation, hasPermission("permissions", "read"), async (req, res) => {
10
+ try {
11
+ const query = { ...req.query, role_id: req.params.role_id };
12
+ const results = await role_permissions.findAll(query);
13
+ res.json(results);
14
+ } catch (err) {
15
+ res.status(500).json({ message: err.message });
16
+ }
17
+ });
18
+
19
+ router.post("/", authenticate, tenantIsolation, hasPermission("permissions", "write"), async (req, res) => {
20
+ try {
21
+ const data = { ...req.body, role_id: req.params.role_id };
22
+ const result = await role_permissions.create(data);
23
+ res.status(201).json(result);
24
+ } catch (err) {
25
+ res.status(500).json({ message: err.message });
26
+ }
27
+ });
28
+
29
+ router.put("/:permission_id", authenticate, tenantIsolation, hasPermission("permissions", "update"), async (req, res) => {
30
+ try {
31
+ const result = await role_permissions.update(req.params.permission_id, req.body);
32
+ res.json(result);
33
+ } catch (err) {
34
+ res.status(500).json({ message: err.message });
35
+ }
36
+ });
37
+
38
+ router.delete("/:permission_id", authenticate, tenantIsolation, hasPermission("permissions", "delete"), async (req, res) => {
39
+ try {
40
+ const result = await role_permissions.delete(req.params.permission_id);
41
+ res.json(result);
42
+ } catch (err) {
43
+ res.status(500).json({ message: err.message });
44
+ }
45
+ });
46
+
47
+ export default router;
@@ -0,0 +1,45 @@
1
+ import express from "express";
2
+ import authenticate from "#middleware/authenticate.js";
3
+ import tenantIsolation from "#middleware/tenantIsolation.js";
4
+ import hasPermission from "#middleware/hasPermission.js";
5
+ import { tenants } from "#models";
6
+
7
+ const router = express.Router();
8
+
9
+ router.get("/", authenticate, tenantIsolation, hasPermission("tenants", "read"), async (req, res) => {
10
+ try {
11
+ const results = await tenants.findAll(req.query);
12
+ res.json(results);
13
+ } catch (err) {
14
+ res.status(500).json({ message: err.message });
15
+ }
16
+ });
17
+
18
+ router.post("/", authenticate, tenantIsolation, hasPermission("tenants", "write"), async (req, res) => {
19
+ try {
20
+ const result = await tenants.create(req.body);
21
+ res.status(201).json(result);
22
+ } catch (err) {
23
+ res.status(500).json({ message: err.message });
24
+ }
25
+ });
26
+
27
+ router.put("/:id", authenticate, tenantIsolation, hasPermission("tenants", "update"), async (req, res) => {
28
+ try {
29
+ const result = await tenants.update(req.params.id, req.body);
30
+ res.json(result);
31
+ } catch (err) {
32
+ res.status(500).json({ message: err.message });
33
+ }
34
+ });
35
+
36
+ router.delete("/:id", authenticate, tenantIsolation, hasPermission("tenants", "delete"), async (req, res) => {
37
+ try {
38
+ const result = await tenants.delete(req.params.id);
39
+ res.json(result);
40
+ } catch (err) {
41
+ res.status(500).json({ message: err.message });
42
+ }
43
+ });
44
+
45
+ export default router;
@@ -0,0 +1,45 @@
1
+ import express from "express";
2
+ import authenticate from "#middleware/authenticate.js";
3
+ import tenantIsolation from "#middleware/tenantIsolation.js";
4
+ import hasPermission from "#middleware/hasPermission.js";
5
+ import { users } from "#models";
6
+
7
+ const router = express.Router();
8
+
9
+ router.get("/", authenticate, tenantIsolation, hasPermission("users", "read"), async (req, res) => {
10
+ try {
11
+ const results = await users.findAll(req.query);
12
+ res.json(results);
13
+ } catch (err) {
14
+ res.status(500).json({ message: err.message });
15
+ }
16
+ });
17
+
18
+ router.post("/", authenticate, tenantIsolation, hasPermission("users", "write"), async (req, res) => {
19
+ try {
20
+ const result = await users.create(req.body);
21
+ res.status(201).json(result);
22
+ } catch (err) {
23
+ res.status(500).json({ message: err.message });
24
+ }
25
+ });
26
+
27
+ router.put("/:id", authenticate, tenantIsolation, hasPermission("users", "update"), async (req, res) => {
28
+ try {
29
+ const result = await users.update(req.params.id, req.body);
30
+ res.json(result);
31
+ } catch (err) {
32
+ res.status(500).json({ message: err.message });
33
+ }
34
+ });
35
+
36
+ router.delete("/:id", authenticate, tenantIsolation, hasPermission("users", "delete"), async (req, res) => {
37
+ try {
38
+ const result = await users.delete(req.params.id);
39
+ res.json(result);
40
+ } catch (err) {
41
+ res.status(500).json({ message: err.message });
42
+ }
43
+ });
44
+
45
+ export default router;
@@ -1,5 +1,5 @@
1
1
  import dbModelRouter from "db-model-router";
2
- import wishlists from "../models/wishlists.js";
2
+ import { wishlists } from "#models";
3
3
 
4
4
  const { route } = dbModelRouter;
5
5