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.
- package/README.md +150 -11
- package/TODO.md +0 -14
- package/db-manager/.dbmanager.sqlite +0 -0
- package/db-manager/README.md +223 -0
- package/db-manager/adapter-proxy.js +361 -0
- package/db-manager/demo/cockroachdb.env +6 -0
- package/db-manager/demo/demo.sqlite +0 -0
- package/db-manager/demo/dynamodb.env +7 -0
- package/db-manager/demo/mongodb.env +4 -0
- package/db-manager/demo/mssql.env +6 -0
- package/db-manager/demo/mysql.env +6 -0
- package/db-manager/demo/oracle.env +6 -0
- package/db-manager/demo/postgres.env +6 -0
- package/db-manager/demo/redis.env +4 -0
- package/db-manager/demo/seeds/cockroachdb.sql +32 -0
- package/db-manager/demo/seeds/mssql.sql +32 -0
- package/db-manager/demo/seeds/mysql.sql +32 -0
- package/db-manager/demo/seeds/oracle.sql +43 -0
- package/db-manager/demo/seeds/postgres.sql +32 -0
- package/db-manager/demo/seeds/sqlite3.sql +32 -0
- package/db-manager/demo/sqlite3.env +2 -0
- package/db-manager/metadata-db.js +170 -0
- package/db-manager/public/.gitkeep +1 -0
- package/db-manager/public/css/style.css +1413 -0
- package/db-manager/public/js/app.js +1370 -0
- package/db-manager/routes/api.js +388 -0
- package/db-manager/routes/views.js +61 -0
- package/db-manager/server.js +39 -0
- package/db-manager/utils/build-filter-config.js +18 -0
- package/db-manager/utils/csv-export.js +59 -0
- package/db-manager/utils/export-filename.js +39 -0
- package/db-manager/utils/filter-tables.js +20 -0
- package/db-manager/utils/parse-filters.js +93 -0
- package/db-manager/utils/sort-state.js +35 -0
- package/db-manager/views/.gitkeep +1 -0
- package/db-manager/views/dashboard.ejs +53 -0
- package/db-manager/views/history.ejs +52 -0
- package/db-manager/views/index.ejs +35 -0
- package/db-manager/views/layout.ejs +31 -0
- package/db-manager/views/partials/data-panel.ejs +74 -0
- package/db-manager/views/partials/header.ejs +36 -0
- package/db-manager/views/partials/sidebar.ejs +30 -0
- package/db-manager/views/query.ejs +58 -0
- package/dbmr.schema.json +23 -45
- package/demo/.env.example +1 -0
- package/demo/app.js +3 -1
- package/demo/commons/db.js +11 -0
- package/demo/commons/migrate.js +3 -0
- package/demo/commons/modules.js +18 -0
- package/demo/commons/password.js +36 -0
- package/demo/commons/webhook.js +81 -0
- package/demo/dbmr.schema.json +22 -46
- package/demo/middleware/authenticate.js +14 -0
- package/demo/middleware/hasPermission.js +30 -0
- package/demo/middleware/tenantIsolation.js +17 -0
- package/demo/migrations/20260509170349_create_saas_tables.sql +69 -0
- package/demo/migrations/{20260430155809_create_tables.sql → 20260509170349_create_tables.sql} +11 -25
- package/demo/models/addresses.js +5 -3
- package/demo/models/cart_items.js +5 -3
- package/demo/models/carts.js +5 -3
- package/demo/models/categories.js +5 -3
- package/demo/models/coupons.js +5 -3
- package/demo/models/index.js +43 -0
- package/demo/models/order_items.js +4 -2
- package/demo/models/orders.js +5 -3
- package/demo/models/payments.js +5 -3
- package/demo/models/product_images.js +4 -2
- package/demo/models/product_reviews.js +5 -3
- package/demo/models/product_variants.js +5 -3
- package/demo/models/products.js +5 -3
- package/demo/models/role_permissions.js +17 -0
- package/demo/models/roles.js +17 -0
- package/demo/models/shipments.js +5 -3
- package/demo/models/tenants.js +18 -0
- package/demo/models/users.js +12 -8
- package/demo/models/webhook_logs.js +22 -0
- package/demo/models/webhooks.js +19 -0
- package/demo/models/wishlists.js +4 -2
- package/demo/openapi.json +1744 -616
- package/demo/package-lock.json +24 -24
- package/demo/package.json +9 -0
- package/demo/routes/{addresses.js → addresses/index.js} +1 -1
- package/demo/routes/auth/index.js +55 -0
- package/demo/routes/carts/{cart_items.js → cart_items/index.js} +1 -1
- package/demo/routes/{carts.js → carts/index.js} +1 -1
- package/demo/routes/{categories.js → categories/index.js} +1 -1
- package/demo/routes/{coupons.js → coupons/index.js} +1 -1
- package/demo/routes/index.js +39 -24
- package/demo/routes/{orders.js → orders/index.js} +1 -1
- package/demo/routes/orders/{order_items.js → order_items/index.js} +1 -1
- package/demo/routes/orders/{payments.js → payments/index.js} +1 -1
- package/demo/routes/orders/{shipments.js → shipments/index.js} +1 -1
- package/demo/routes/{products.js → products/index.js} +1 -1
- package/demo/routes/products/{product_images.js → product_images/index.js} +1 -1
- package/demo/routes/products/{product_reviews.js → product_reviews/index.js} +1 -1
- package/demo/routes/products/{product_variants.js → product_variants/index.js} +1 -1
- package/demo/routes/roles/index.js +75 -0
- package/demo/routes/roles/permissions/index.js +47 -0
- package/demo/routes/tenants/index.js +45 -0
- package/demo/routes/users/index.js +45 -0
- package/demo/routes/{wishlists.js → wishlists/index.js} +1 -1
- package/demo/seeds/saas-seed.js +329 -0
- package/docker-compose.yml +61 -0
- package/package.json +120 -113
- package/scripts/demo-create.js +1 -1
- package/skill/SKILL.md +119 -3
- package/src/cli/commands/db-manager.js +134 -0
- package/src/cli/commands/generate.js +112 -43
- package/src/cli/commands/help.js +0 -1
- package/src/cli/diff-engine.js +2 -1
- package/src/cli/generate-model.js +9 -4
- package/src/cli/generate-openapi.js +40 -13
- package/src/cli/generate-route.js +61 -22
- package/src/cli/generate-saas-structure.js +122 -0
- package/src/cli/init/generators.js +42 -30
- package/src/cli/init.js +8 -0
- package/src/cli/main.js +8 -1
- package/src/cli/saas/generate-saas-middleware.js +108 -0
- package/src/cli/saas/generate-saas-migrations.js +480 -0
- package/src/cli/saas/generate-saas-models.js +211 -0
- package/src/cli/saas/generate-saas-openapi.js +419 -0
- package/src/cli/saas/generate-saas-routes.js +435 -0
- package/src/cli/saas/generate-saas-seeds.js +243 -0
- package/src/cli/saas/generate-saas-utils.js +176 -0
- package/src/commons/kafka.js +139 -0
- package/src/commons/model.js +29 -9
- package/src/index.js +2 -0
- package/src/mssql/db.js +41 -3
- package/src/mysql/db.js +3 -0
- package/src/postgres/db.js +6 -0
- package/src/sqlite3/db.js +11 -0
- package/demo/docs/llm.md +0 -197
- package/demo/llms.txt +0 -70
- package/demo/routes/users.js +0 -6
- package/src/cli/commands/generate-llm-docs.js +0 -418
- /package/demo/migrations/{20260430155808_create_migrations_table.sql → 20260509170349_create_migrations_table.sql} +0 -0
package/demo/package-lock.json
CHANGED
|
@@ -279,9 +279,9 @@
|
|
|
279
279
|
}
|
|
280
280
|
},
|
|
281
281
|
"node_modules/brace-expansion": {
|
|
282
|
-
"version": "5.0.
|
|
283
|
-
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.
|
|
284
|
-
"integrity": "sha512-
|
|
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.
|
|
579
|
-
"resolved": "https://registry.npmjs.org/db-model-router/-/db-model-router-1.0.
|
|
580
|
-
"integrity": "sha512-
|
|
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.
|
|
593
|
-
"@aws-sdk/lib-dynamodb": "^3.
|
|
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.
|
|
598
|
-
"mssql": "^12.
|
|
599
|
-
"mysql2": "^3.
|
|
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.
|
|
908
|
-
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.
|
|
909
|
-
"integrity": "sha512-
|
|
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.
|
|
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.
|
|
1328
|
-
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.
|
|
1329
|
-
"integrity": "sha512
|
|
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.
|
|
1634
|
-
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.
|
|
1635
|
-
"integrity": "sha512-
|
|
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.
|
|
2224
|
-
"resolved": "https://registry.npmjs.org/semver/-/semver-7.
|
|
2225
|
-
"integrity": "sha512-
|
|
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
|
},
|
|
@@ -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;
|
package/demo/routes/index.js
CHANGED
|
@@ -2,32 +2,38 @@ import express from "express";
|
|
|
2
2
|
|
|
3
3
|
const router = express.Router();
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import
|
|
17
|
-
import
|
|
18
|
-
import
|
|
19
|
-
import
|
|
20
|
-
import
|
|
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;
|
|
@@ -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;
|