db-model-router 1.0.6 → 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 (137) hide show
  1. package/README.md +150 -11
  2. package/TODO.md +0 -15
  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 +22 -44
  45. package/demo/.dockerignore +7 -0
  46. package/demo/.env.example +14 -0
  47. package/demo/Dockerfile +20 -0
  48. package/demo/app.js +39 -0
  49. package/demo/commons/add_migration.js +43 -0
  50. package/demo/commons/db.js +28 -0
  51. package/demo/commons/migrate.js +68 -0
  52. package/demo/commons/modules.js +18 -0
  53. package/demo/commons/password.js +36 -0
  54. package/demo/commons/security.js +30 -0
  55. package/demo/commons/session.js +13 -0
  56. package/demo/commons/webhook.js +81 -0
  57. package/demo/dbmr.schema.json +338 -0
  58. package/demo/middleware/authenticate.js +14 -0
  59. package/demo/middleware/hasPermission.js +30 -0
  60. package/demo/middleware/logger.js +67 -0
  61. package/demo/middleware/tenantIsolation.js +17 -0
  62. package/demo/migrations/20260509170349_create_migrations_table.sql +6 -0
  63. package/demo/migrations/20260509170349_create_saas_tables.sql +69 -0
  64. package/demo/migrations/20260509170349_create_tables.sql +193 -0
  65. package/demo/models/addresses.js +24 -0
  66. package/demo/models/cart_items.js +20 -0
  67. package/demo/models/carts.js +18 -0
  68. package/demo/models/categories.js +22 -0
  69. package/demo/models/coupons.js +25 -0
  70. package/demo/models/index.js +43 -0
  71. package/demo/models/order_items.js +23 -0
  72. package/demo/models/orders.js +27 -0
  73. package/demo/models/payments.js +23 -0
  74. package/demo/models/product_images.js +20 -0
  75. package/demo/models/product_reviews.js +22 -0
  76. package/demo/models/product_variants.js +22 -0
  77. package/demo/models/products.js +32 -0
  78. package/demo/models/role_permissions.js +17 -0
  79. package/demo/models/roles.js +17 -0
  80. package/demo/models/shipments.js +21 -0
  81. package/demo/models/tenants.js +18 -0
  82. package/demo/models/users.js +23 -0
  83. package/demo/models/webhook_logs.js +22 -0
  84. package/demo/models/webhooks.js +19 -0
  85. package/demo/models/wishlists.js +17 -0
  86. package/demo/openapi.json +7000 -0
  87. package/demo/package-lock.json +2810 -0
  88. package/demo/package.json +43 -0
  89. package/demo/routes/addresses/index.js +6 -0
  90. package/demo/routes/auth/index.js +55 -0
  91. package/demo/routes/carts/cart_items/index.js +7 -0
  92. package/demo/routes/carts/index.js +6 -0
  93. package/demo/routes/categories/index.js +6 -0
  94. package/demo/routes/coupons/index.js +6 -0
  95. package/demo/routes/docs.js +18 -0
  96. package/demo/routes/health.js +35 -0
  97. package/demo/routes/index.js +54 -0
  98. package/demo/routes/orders/index.js +6 -0
  99. package/demo/routes/orders/order_items/index.js +7 -0
  100. package/demo/routes/orders/payments/index.js +7 -0
  101. package/demo/routes/orders/shipments/index.js +7 -0
  102. package/demo/routes/products/index.js +6 -0
  103. package/demo/routes/products/product_images/index.js +7 -0
  104. package/demo/routes/products/product_reviews/index.js +7 -0
  105. package/demo/routes/products/product_variants/index.js +7 -0
  106. package/demo/routes/roles/index.js +75 -0
  107. package/demo/routes/roles/permissions/index.js +47 -0
  108. package/demo/routes/tenants/index.js +45 -0
  109. package/demo/routes/users/index.js +45 -0
  110. package/demo/routes/wishlists/index.js +6 -0
  111. package/demo/seeds/saas-seed.js +329 -0
  112. package/docker-compose.yml +61 -0
  113. package/package.json +120 -113
  114. package/scripts/demo-create.js +1 -1
  115. package/skill/SKILL.md +119 -3
  116. package/src/cli/commands/db-manager.js +134 -0
  117. package/src/cli/commands/generate.js +106 -60
  118. package/src/cli/commands/help.js +0 -1
  119. package/src/cli/generate-route.js +60 -21
  120. package/src/cli/generate-saas-structure.js +122 -0
  121. package/src/cli/init/generators.js +6 -0
  122. package/src/cli/init.js +8 -0
  123. package/src/cli/main.js +8 -1
  124. package/src/cli/saas/generate-saas-middleware.js +108 -0
  125. package/src/cli/saas/generate-saas-migrations.js +480 -0
  126. package/src/cli/saas/generate-saas-models.js +211 -0
  127. package/src/cli/saas/generate-saas-openapi.js +419 -0
  128. package/src/cli/saas/generate-saas-routes.js +435 -0
  129. package/src/cli/saas/generate-saas-seeds.js +243 -0
  130. package/src/cli/saas/generate-saas-utils.js +176 -0
  131. package/src/commons/kafka.js +139 -0
  132. package/src/commons/model.js +29 -9
  133. package/src/index.js +2 -0
  134. package/src/mssql/db.js +41 -3
  135. package/src/mysql/db.js +3 -0
  136. package/src/postgres/db.js +6 -0
  137. package/src/cli/generate-db-manager.js +0 -1573
@@ -0,0 +1,338 @@
1
+ {
2
+ "adapter": "sqlite3",
3
+ "framework": "express",
4
+ "options": {
5
+ "rateLimiting": true,
6
+ "helmet": true,
7
+ "logger": true
8
+ },
9
+ "tables": {
10
+ "addresses": {
11
+ "columns": {
12
+ "address_id": "auto_increment",
13
+ "user_id": "required|integer",
14
+ "label": "string",
15
+ "line1": "required|string",
16
+ "line2": "string",
17
+ "city": "required|string",
18
+ "state": "required|string",
19
+ "postal_code": "required|string",
20
+ "country": "required|string",
21
+ "is_default": "boolean",
22
+ "created_at": "datetime",
23
+ "modified_at": "datetime"
24
+ },
25
+ "pk": "address_id",
26
+ "unique": [
27
+ "address_id"
28
+ ],
29
+ "timestamps": {
30
+ "created_at": "created_at",
31
+ "modified_at": "modified_at"
32
+ },
33
+ "parent": null
34
+ },
35
+ "categories": {
36
+ "columns": {
37
+ "category_id": "auto_increment",
38
+ "name": "required|string",
39
+ "slug": "required|string",
40
+ "description": "string",
41
+ "parent_category_id": "integer",
42
+ "image_url": "string",
43
+ "sort_order": "integer",
44
+ "is_active": "boolean",
45
+ "created_at": "datetime",
46
+ "modified_at": "datetime"
47
+ },
48
+ "pk": "category_id",
49
+ "unique": [
50
+ "slug"
51
+ ],
52
+ "timestamps": {
53
+ "created_at": "created_at",
54
+ "modified_at": "modified_at"
55
+ },
56
+ "parent": null
57
+ },
58
+ "products": {
59
+ "columns": {
60
+ "product_id": "auto_increment",
61
+ "category_id": "required|integer",
62
+ "name": "required|string",
63
+ "slug": "required|string",
64
+ "description": "string",
65
+ "short_description": "string",
66
+ "sku": "required|string",
67
+ "price": "required|numeric",
68
+ "compare_at_price": "numeric",
69
+ "cost_price": "numeric",
70
+ "currency": "required|string",
71
+ "stock_quantity": "required|integer",
72
+ "low_stock_threshold": "integer",
73
+ "weight": "numeric",
74
+ "weight_unit": "string",
75
+ "is_active": "boolean",
76
+ "is_featured": "boolean",
77
+ "is_deleted": "boolean",
78
+ "meta": "object",
79
+ "created_at": "datetime",
80
+ "modified_at": "datetime"
81
+ },
82
+ "pk": "product_id",
83
+ "unique": [
84
+ "sku",
85
+ "slug"
86
+ ],
87
+ "softDelete": "is_deleted",
88
+ "timestamps": {
89
+ "created_at": "created_at",
90
+ "modified_at": "modified_at"
91
+ },
92
+ "parent": null
93
+ },
94
+ "product_images": {
95
+ "columns": {
96
+ "product_image_id": "auto_increment",
97
+ "product_id": "required|integer",
98
+ "url": "required|string",
99
+ "alt_text": "string",
100
+ "sort_order": "integer",
101
+ "is_primary": "boolean",
102
+ "created_at": "datetime"
103
+ },
104
+ "pk": "product_image_id",
105
+ "unique": [
106
+ "product_image_id"
107
+ ],
108
+ "timestamps": {
109
+ "created_at": "created_at"
110
+ },
111
+ "parent": "products"
112
+ },
113
+ "product_variants": {
114
+ "columns": {
115
+ "variant_id": "auto_increment",
116
+ "product_id": "required|integer",
117
+ "name": "required|string",
118
+ "sku": "required|string",
119
+ "price": "required|numeric",
120
+ "stock_quantity": "required|integer",
121
+ "attributes": "object",
122
+ "is_active": "boolean",
123
+ "created_at": "datetime",
124
+ "modified_at": "datetime"
125
+ },
126
+ "pk": "variant_id",
127
+ "unique": [
128
+ "sku"
129
+ ],
130
+ "timestamps": {
131
+ "created_at": "created_at",
132
+ "modified_at": "modified_at"
133
+ },
134
+ "parent": "products"
135
+ },
136
+ "product_reviews": {
137
+ "columns": {
138
+ "review_id": "auto_increment",
139
+ "product_id": "required|integer",
140
+ "user_id": "required|integer",
141
+ "rating": "required|integer",
142
+ "title": "string",
143
+ "body": "string",
144
+ "is_verified": "boolean",
145
+ "is_approved": "boolean",
146
+ "created_at": "datetime",
147
+ "modified_at": "datetime"
148
+ },
149
+ "pk": "review_id",
150
+ "unique": [
151
+ "review_id"
152
+ ],
153
+ "timestamps": {
154
+ "created_at": "created_at",
155
+ "modified_at": "modified_at"
156
+ },
157
+ "parent": "products"
158
+ },
159
+ "carts": {
160
+ "columns": {
161
+ "cart_id": "auto_increment",
162
+ "user_id": "integer",
163
+ "session_id": "string",
164
+ "currency": "required|string",
165
+ "created_at": "datetime",
166
+ "modified_at": "datetime"
167
+ },
168
+ "pk": "cart_id",
169
+ "unique": [
170
+ "cart_id"
171
+ ],
172
+ "timestamps": {
173
+ "created_at": "created_at",
174
+ "modified_at": "modified_at"
175
+ },
176
+ "parent": null
177
+ },
178
+ "cart_items": {
179
+ "columns": {
180
+ "cart_item_id": "auto_increment",
181
+ "cart_id": "required|integer",
182
+ "product_id": "required|integer",
183
+ "variant_id": "integer",
184
+ "quantity": "required|integer",
185
+ "unit_price": "required|numeric",
186
+ "created_at": "datetime",
187
+ "modified_at": "datetime"
188
+ },
189
+ "pk": "cart_item_id",
190
+ "unique": [
191
+ "cart_item_id"
192
+ ],
193
+ "timestamps": {
194
+ "created_at": "created_at",
195
+ "modified_at": "modified_at"
196
+ },
197
+ "parent": "carts"
198
+ },
199
+ "orders": {
200
+ "columns": {
201
+ "order_id": "auto_increment",
202
+ "user_id": "required|integer",
203
+ "order_number": "required|string",
204
+ "status": "required|string",
205
+ "subtotal": "required|numeric",
206
+ "tax_amount": "required|numeric",
207
+ "shipping_amount": "required|numeric",
208
+ "discount_amount": "numeric",
209
+ "total": "required|numeric",
210
+ "currency": "required|string",
211
+ "shipping_address_id": "integer",
212
+ "billing_address_id": "integer",
213
+ "notes": "string",
214
+ "created_at": "datetime",
215
+ "modified_at": "datetime"
216
+ },
217
+ "pk": "order_id",
218
+ "unique": [
219
+ "order_number"
220
+ ],
221
+ "timestamps": {
222
+ "created_at": "created_at",
223
+ "modified_at": "modified_at"
224
+ },
225
+ "parent": null
226
+ },
227
+ "order_items": {
228
+ "columns": {
229
+ "order_item_id": "auto_increment",
230
+ "order_id": "required|integer",
231
+ "product_id": "required|integer",
232
+ "variant_id": "integer",
233
+ "product_name": "required|string",
234
+ "sku": "required|string",
235
+ "quantity": "required|integer",
236
+ "unit_price": "required|numeric",
237
+ "total_price": "required|numeric",
238
+ "created_at": "datetime"
239
+ },
240
+ "pk": "order_item_id",
241
+ "unique": [
242
+ "order_item_id"
243
+ ],
244
+ "timestamps": {
245
+ "created_at": "created_at"
246
+ },
247
+ "parent": "orders"
248
+ },
249
+ "payments": {
250
+ "columns": {
251
+ "payment_id": "auto_increment",
252
+ "order_id": "required|integer",
253
+ "method": "required|string",
254
+ "provider": "string",
255
+ "provider_transaction_id": "string",
256
+ "amount": "required|numeric",
257
+ "currency": "required|string",
258
+ "status": "required|string",
259
+ "paid_at": "datetime",
260
+ "created_at": "datetime",
261
+ "modified_at": "datetime"
262
+ },
263
+ "pk": "payment_id",
264
+ "unique": [
265
+ "payment_id"
266
+ ],
267
+ "timestamps": {
268
+ "created_at": "created_at",
269
+ "modified_at": "modified_at"
270
+ },
271
+ "parent": "orders"
272
+ },
273
+ "shipments": {
274
+ "columns": {
275
+ "shipment_id": "auto_increment",
276
+ "order_id": "required|integer",
277
+ "carrier": "required|string",
278
+ "tracking_number": "string",
279
+ "status": "required|string",
280
+ "shipped_at": "datetime",
281
+ "delivered_at": "datetime",
282
+ "created_at": "datetime",
283
+ "modified_at": "datetime"
284
+ },
285
+ "pk": "shipment_id",
286
+ "unique": [
287
+ "shipment_id"
288
+ ],
289
+ "timestamps": {
290
+ "created_at": "created_at",
291
+ "modified_at": "modified_at"
292
+ },
293
+ "parent": "orders"
294
+ },
295
+ "coupons": {
296
+ "columns": {
297
+ "coupon_id": "auto_increment",
298
+ "code": "required|string",
299
+ "description": "string",
300
+ "discount_type": "required|string",
301
+ "discount_value": "required|numeric",
302
+ "min_order_amount": "numeric",
303
+ "max_uses": "integer",
304
+ "used_count": "integer",
305
+ "starts_at": "datetime",
306
+ "expires_at": "datetime",
307
+ "is_active": "boolean",
308
+ "created_at": "datetime",
309
+ "modified_at": "datetime"
310
+ },
311
+ "pk": "coupon_id",
312
+ "unique": [
313
+ "code"
314
+ ],
315
+ "timestamps": {
316
+ "created_at": "created_at",
317
+ "modified_at": "modified_at"
318
+ },
319
+ "parent": null
320
+ },
321
+ "wishlists": {
322
+ "columns": {
323
+ "wishlist_id": "auto_increment",
324
+ "user_id": "required|integer",
325
+ "product_id": "required|integer",
326
+ "created_at": "datetime"
327
+ },
328
+ "pk": "wishlist_id",
329
+ "unique": [
330
+ "wishlist_id"
331
+ ],
332
+ "timestamps": {
333
+ "created_at": "created_at"
334
+ },
335
+ "parent": null
336
+ }
337
+ }
338
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Authentication middleware.
3
+ *
4
+ * Validates that the request has an active session with a user object.
5
+ * Responds with 401 Unauthorized if no valid session exists.
6
+ */
7
+ function authenticate(req, res, next) {
8
+ if (!req.session || !req.session.user) {
9
+ return res.status(401).json({ message: "Unauthorized" });
10
+ }
11
+ next();
12
+ }
13
+
14
+ export default authenticate;
@@ -0,0 +1,30 @@
1
+ import { isValidModule } from "#commons/modules.js";
2
+
3
+ /**
4
+ * Permission validation middleware factory.
5
+ *
6
+ * Returns a middleware function that checks whether the authenticated user
7
+ * has the required permission for the specified module and action.
8
+ * A permission entry with action "global" grants access to any action
9
+ * on that module.
10
+ *
11
+ * @param {string} module - The module name to check permission for
12
+ * @param {string} action - The required action
13
+ * @returns {function} Express middleware function
14
+ */
15
+ function hasPermission(module, action) {
16
+ return (req, res, next) => {
17
+ if (!isValidModule(module)) {
18
+ return res.status(403).json({ message: "Invalid module" });
19
+ }
20
+ const match = req.session.permission.find(
21
+ (p) => p.module === module && (p.action === action || p.action === "global")
22
+ );
23
+ if (!match) {
24
+ return res.status(403).json({ message: "Forbidden" });
25
+ }
26
+ next();
27
+ };
28
+ }
29
+
30
+ export default hasPermission;
@@ -0,0 +1,67 @@
1
+ import winston from "winston";
2
+
3
+ /**
4
+ * Winston logger with Console transport.
5
+ * If LOKI_HOST is set in .env, adds a Loki transport for Grafana visualization.
6
+ */
7
+ const transports = [
8
+ new winston.transports.Console({
9
+ format: winston.format.combine(
10
+ winston.format.colorize(),
11
+ winston.format.printf(({ timestamp, level, message, ...meta }) => {
12
+ const metaStr = Object.keys(meta).length > 1
13
+ ? " " + JSON.stringify(meta)
14
+ : "";
15
+ return `[${timestamp}] [${level}] ${message}${metaStr}`;
16
+ }),
17
+ ),
18
+ }),
19
+ ];
20
+
21
+ // Add Loki transport only when LOKI_HOST is configured
22
+ if (process.env.LOKI_HOST) {
23
+ const { default: LokiTransport } = await import("winston-loki");
24
+ transports.push(
25
+ new LokiTransport({
26
+ host: process.env.LOKI_HOST,
27
+ labels: { app: process.env.APP_NAME || "app" },
28
+ json: true,
29
+ onConnectionError: (err) => console.error("Loki connection error:", err),
30
+ }),
31
+ );
32
+ }
33
+
34
+ const logger = winston.createLogger({
35
+ level: process.env.LOG_LEVEL || "info",
36
+ format: winston.format.combine(
37
+ winston.format.timestamp(),
38
+ winston.format.json(),
39
+ ),
40
+ defaultMeta: { service: process.env.APP_NAME || "app" },
41
+ transports,
42
+ });
43
+
44
+ /**
45
+ * Express middleware that logs every request/response.
46
+ */
47
+ function requestLogger(req, res, next) {
48
+ const start = Date.now();
49
+
50
+ res.on("finish", () => {
51
+ const duration = Date.now() - start;
52
+ const level = res.statusCode >= 400 ? "warn" : "info";
53
+ logger.log({
54
+ level,
55
+ message: `${req.method} ${req.originalUrl} ${res.statusCode} ${duration}ms`,
56
+ method: req.method,
57
+ url: req.originalUrl,
58
+ status: res.statusCode,
59
+ duration,
60
+ });
61
+ });
62
+
63
+ next();
64
+ }
65
+
66
+ requestLogger.logger = logger;
67
+ export default requestLogger;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Tenant isolation middleware.
3
+ *
4
+ * Restricts data access to the user's own tenant unless the user
5
+ * has a global-scoped permission. Injects tenant_id into query
6
+ * and body parameters for non-global users.
7
+ */
8
+ function tenantIsolation(req, res, next) {
9
+ const hasGlobal = req.session.permission.some((p) => p.scope === "global");
10
+ if (!hasGlobal) {
11
+ req.query.tenant_id = req.session.user.tenant_id;
12
+ req.body.tenant_id = req.session.user.tenant_id;
13
+ }
14
+ next();
15
+ }
16
+
17
+ export default tenantIsolation;
@@ -0,0 +1,6 @@
1
+ CREATE TABLE IF NOT EXISTS _migrations (
2
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
3
+ filename VARCHAR(255) NOT NULL UNIQUE,
4
+ executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
5
+ checksum VARCHAR(64) NOT NULL
6
+ );
@@ -0,0 +1,69 @@
1
+ CREATE TABLE IF NOT EXISTS tenants (
2
+ tenant_id INTEGER PRIMARY KEY AUTOINCREMENT,
3
+ name VARCHAR(255) NOT NULL,
4
+ slug VARCHAR(255) NOT NULL,
5
+ attributes TEXT,
6
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
7
+ modified_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
8
+ UNIQUE (slug)
9
+ );
10
+
11
+ CREATE TABLE IF NOT EXISTS roles (
12
+ role_id INTEGER PRIMARY KEY AUTOINCREMENT,
13
+ tenant_id INTEGER,
14
+ name VARCHAR(255) NOT NULL,
15
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
16
+ modified_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
17
+ FOREIGN KEY (tenant_id) REFERENCES tenants(tenant_id),
18
+ UNIQUE (tenant_id, name)
19
+ );
20
+
21
+ CREATE TABLE IF NOT EXISTS users (
22
+ user_id INTEGER PRIMARY KEY AUTOINCREMENT,
23
+ email VARCHAR(255) NOT NULL,
24
+ phone VARCHAR(255),
25
+ password_hash VARCHAR(255) NOT NULL,
26
+ name VARCHAR(255) NOT NULL,
27
+ unique_attribute VARCHAR(255) NOT NULL,
28
+ tenant_id INTEGER,
29
+ role_id INTEGER NOT NULL,
30
+ attributes TEXT,
31
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
32
+ modified_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
33
+ FOREIGN KEY (tenant_id) REFERENCES tenants(tenant_id),
34
+ FOREIGN KEY (role_id) REFERENCES roles(role_id),
35
+ UNIQUE (tenant_id, unique_attribute)
36
+ );
37
+
38
+ CREATE TABLE IF NOT EXISTS role_permissions (
39
+ role_permission_id INTEGER PRIMARY KEY AUTOINCREMENT,
40
+ role_id INTEGER NOT NULL,
41
+ permission TEXT NOT NULL,
42
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
43
+ modified_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
44
+ FOREIGN KEY (role_id) REFERENCES roles(role_id)
45
+ );
46
+
47
+ CREATE TABLE IF NOT EXISTS webhooks (
48
+ webhook_id INTEGER PRIMARY KEY AUTOINCREMENT,
49
+ tenant_id INTEGER NOT NULL,
50
+ url VARCHAR(255) NOT NULL,
51
+ key VARCHAR(255) NOT NULL,
52
+ secret VARCHAR(255) NOT NULL,
53
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
54
+ modified_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
55
+ FOREIGN KEY (tenant_id) REFERENCES tenants(tenant_id)
56
+ );
57
+
58
+ CREATE TABLE IF NOT EXISTS webhook_logs (
59
+ webhook_log_id INTEGER PRIMARY KEY AUTOINCREMENT,
60
+ webhook_id INTEGER NOT NULL,
61
+ tenant_id INTEGER NOT NULL,
62
+ event_type VARCHAR(255) NOT NULL,
63
+ payload TEXT NOT NULL,
64
+ status VARCHAR(255) NOT NULL,
65
+ response_body TEXT,
66
+ response_status_code INTEGER,
67
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
68
+ FOREIGN KEY (webhook_id) REFERENCES webhooks(webhook_id)
69
+ );