db-model-router 1.0.9 → 1.0.11

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 (99) hide show
  1. package/.codegraph/config.json +143 -0
  2. package/CLAUDE.md +150 -0
  3. package/dbmr.schema.json +3 -0
  4. package/docs/dbmr-schema-spec.md +597 -393
  5. package/package.json +1 -1
  6. package/skill/SKILL.md +1 -1
  7. package/skill/references/dbmr-schema-spec.md +597 -0
  8. package/src/cli/commands/generate.js +6 -6
  9. package/src/cli/diff-engine.js +4 -4
  10. package/src/cli/generate-migration.js +4 -0
  11. package/src/cli/generate-route.js +3 -3
  12. package/src/schema/schema-validator.js +1 -1
  13. package/.env +0 -7
  14. package/db-manager/.dbmanager.sqlite +0 -0
  15. package/db-manager/.dbmanager.sqlite-shm +0 -0
  16. package/db-manager/.dbmanager.sqlite-wal +0 -0
  17. package/db-manager/demo/cockroachdb.env +0 -6
  18. package/db-manager/demo/demo.sqlite +0 -0
  19. package/db-manager/demo/dynamodb.env +0 -7
  20. package/db-manager/demo/mongodb.env +0 -4
  21. package/db-manager/demo/mssql.env +0 -6
  22. package/db-manager/demo/mysql.env +0 -6
  23. package/db-manager/demo/oracle.env +0 -6
  24. package/db-manager/demo/postgres.env +0 -6
  25. package/db-manager/demo/redis.env +0 -4
  26. package/db-manager/demo/seeds/cockroachdb.sql +0 -32
  27. package/db-manager/demo/seeds/mssql.sql +0 -32
  28. package/db-manager/demo/seeds/mysql.sql +0 -32
  29. package/db-manager/demo/seeds/oracle.sql +0 -43
  30. package/db-manager/demo/seeds/postgres.sql +0 -32
  31. package/db-manager/demo/seeds/sqlite3.sql +0 -32
  32. package/db-manager/demo/sqlite3.env +0 -2
  33. package/demo/.dockerignore +0 -7
  34. package/demo/.env.example +0 -15
  35. package/demo/Dockerfile +0 -20
  36. package/demo/app.js +0 -39
  37. package/demo/commons/add_migration.js +0 -43
  38. package/demo/commons/db.js +0 -17
  39. package/demo/commons/migrate.js +0 -68
  40. package/demo/commons/modules.js +0 -18
  41. package/demo/commons/password.js +0 -36
  42. package/demo/commons/security.js +0 -30
  43. package/demo/commons/session.js +0 -13
  44. package/demo/commons/webhook.js +0 -81
  45. package/demo/dbmr.schema.json +0 -338
  46. package/demo/middleware/authenticate.js +0 -14
  47. package/demo/middleware/hasPermission.js +0 -30
  48. package/demo/middleware/logger.js +0 -67
  49. package/demo/middleware/tenantIsolation.js +0 -19
  50. package/demo/migrations/20260510193736_create_migrations_table.sql +0 -6
  51. package/demo/migrations/20260510193737_create_saas_tables.sql +0 -69
  52. package/demo/migrations/20260510193737_create_tables.sql +0 -193
  53. package/demo/models/addresses.js +0 -24
  54. package/demo/models/cart_items.js +0 -20
  55. package/demo/models/carts.js +0 -18
  56. package/demo/models/categories.js +0 -22
  57. package/demo/models/coupons.js +0 -25
  58. package/demo/models/index.js +0 -43
  59. package/demo/models/order_items.js +0 -23
  60. package/demo/models/orders.js +0 -27
  61. package/demo/models/payments.js +0 -23
  62. package/demo/models/product_images.js +0 -20
  63. package/demo/models/product_reviews.js +0 -22
  64. package/demo/models/product_variants.js +0 -22
  65. package/demo/models/products.js +0 -32
  66. package/demo/models/role_permissions.js +0 -17
  67. package/demo/models/roles.js +0 -17
  68. package/demo/models/shipments.js +0 -21
  69. package/demo/models/tenants.js +0 -18
  70. package/demo/models/users.js +0 -23
  71. package/demo/models/webhook_logs.js +0 -22
  72. package/demo/models/webhooks.js +0 -19
  73. package/demo/models/wishlists.js +0 -17
  74. package/demo/openapi.json +0 -7000
  75. package/demo/package-lock.json +0 -3972
  76. package/demo/package.json +0 -46
  77. package/demo/routes/addresses/index.js +0 -10
  78. package/demo/routes/auth/index.js +0 -55
  79. package/demo/routes/carts/cart_items/index.js +0 -11
  80. package/demo/routes/carts/index.js +0 -14
  81. package/demo/routes/categories/index.js +0 -10
  82. package/demo/routes/coupons/index.js +0 -10
  83. package/demo/routes/docs.js +0 -18
  84. package/demo/routes/health.js +0 -35
  85. package/demo/routes/index.js +0 -40
  86. package/demo/routes/orders/index.js +0 -18
  87. package/demo/routes/orders/order_items/index.js +0 -11
  88. package/demo/routes/orders/payments/index.js +0 -11
  89. package/demo/routes/orders/shipments/index.js +0 -11
  90. package/demo/routes/products/index.js +0 -18
  91. package/demo/routes/products/product_images/index.js +0 -11
  92. package/demo/routes/products/product_reviews/index.js +0 -11
  93. package/demo/routes/products/product_variants/index.js +0 -11
  94. package/demo/routes/roles/index.js +0 -75
  95. package/demo/routes/roles/permissions/index.js +0 -47
  96. package/demo/routes/tenants/index.js +0 -45
  97. package/demo/routes/users/index.js +0 -45
  98. package/demo/routes/wishlists/index.js +0 -10
  99. package/demo/seeds/saas-seed.js +0 -329
@@ -1,81 +0,0 @@
1
- import crypto from "crypto";
2
-
3
- /**
4
- * Retry delay schedule in seconds.
5
- * Attempt 0: immediate, 1: 1 min, 2: 5 min, 3: 1 hour, 4: 1 day.
6
- *
7
- * @type {number[]}
8
- */
9
- export const RETRY_DELAYS = [0, 60, 300, 3600, 86400];
10
-
11
- /**
12
- * Sign a webhook payload using HMAC-SHA256.
13
- *
14
- * @param {object} payload - The payload object to sign
15
- * @param {string} secret - The tenant's webhook secret
16
- * @returns {string} Hex-encoded HMAC-SHA256 signature
17
- */
18
- export function signPayload(payload, secret) {
19
- const body = JSON.stringify(payload);
20
- return crypto.createHmac("sha256", secret).update(body).digest("hex");
21
- }
22
-
23
- /**
24
- * Look up the configured webhook for a tenant.
25
- * TODO: Replace this stub with actual database lookup.
26
- */
27
- export async function lookupWebhook(tenantId) {
28
- return null;
29
- }
30
-
31
- /**
32
- * Log a webhook delivery event.
33
- * TODO: Replace this stub with actual database insert into webhook_logs.
34
- */
35
- export async function logWebhookEvent(webhookId, tenantId, eventType, payload, status, responseBody, responseStatusCode) {
36
- // Stub: replace with actual DB insert
37
- }
38
-
39
- /**
40
- * Delay execution for the specified number of milliseconds.
41
- */
42
- export function delay(ms) {
43
- return new Promise((resolve) => setTimeout(resolve, ms));
44
- }
45
-
46
- /**
47
- * Send a webhook notification to the configured endpoint for a tenant.
48
- * Retries delivery up to 5 times with exponential backoff.
49
- */
50
- export async function sendWebhook(tenantId, event, context) {
51
- const webhook = await lookupWebhook(tenantId);
52
- if (!webhook) return;
53
-
54
- const payload = { context, event, timestamp: new Date().toISOString() };
55
- payload.signature = signPayload(payload, webhook.secret);
56
-
57
- for (let attempt = 0; attempt < RETRY_DELAYS.length; attempt++) {
58
- if (attempt > 0) {
59
- await delay(RETRY_DELAYS[attempt] * 1000);
60
- console.log(`Webhook retry attempt ${attempt}, delay: ${RETRY_DELAYS[attempt]}s`);
61
- }
62
- try {
63
- const response = await fetch(webhook.url, {
64
- method: "POST",
65
- headers: { "Content-Type": "application/json", "X-Webhook-Key": webhook.key },
66
- body: JSON.stringify(payload),
67
- });
68
- await logWebhookEvent(
69
- webhook.id, tenantId, event.type, payload,
70
- response.ok ? "success" : "failed",
71
- await response.text(), response.status
72
- );
73
- if (response.ok) return;
74
- } catch (err) {
75
- await logWebhookEvent(
76
- webhook.id, tenantId, event.type, payload,
77
- "error", err.message, null
78
- );
79
- }
80
- }
81
- }
@@ -1,338 +0,0 @@
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
- }
@@ -1,14 +0,0 @@
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;
@@ -1,30 +0,0 @@
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;
@@ -1,67 +0,0 @@
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;
@@ -1,19 +0,0 @@
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
- if (!req.query) req.query = {};
12
- if (!req.body) req.body = {};
13
- req.query.tenant_id = req.session.user.tenant_id;
14
- req.body.tenant_id = req.session.user.tenant_id;
15
- }
16
- next();
17
- }
18
-
19
- export default tenantIsolation;
@@ -1,6 +0,0 @@
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
- );
@@ -1,69 +0,0 @@
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
- );