@sonicjs-cms/core 3.0.0-beta.22 → 3.0.0-beta.24
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/dist/adapters.cjs +512 -0
- package/dist/adapters.cjs.map +1 -0
- package/dist/adapters.d.cts +342 -0
- package/dist/adapters.d.ts +342 -0
- package/dist/adapters.js +501 -0
- package/dist/adapters.js.map +1 -0
- package/dist/admin-documents-form.template-55V2ZGWT.js +6 -0
- package/dist/{admin-documents-form.template-DDSH6ROU.js.map → admin-documents-form.template-55V2ZGWT.js.map} +1 -1
- package/dist/admin-documents-form.template-YNAVMJCV.cjs +19 -0
- package/dist/{admin-layout-catalyst.template-YQ4EMF2J.js.map → admin-documents-form.template-YNAVMJCV.cjs.map} +1 -1
- package/dist/admin-layout-catalyst.template-3XZXDGF7.cjs +21 -0
- package/dist/{admin-layout-catalyst.template-KDHKVLXR.cjs.map → admin-layout-catalyst.template-3XZXDGF7.cjs.map} +1 -1
- package/dist/admin-layout-catalyst.template-6UQGLCKI.js +7 -0
- package/dist/{admin-documents-form.template-LSZKGA5J.cjs.map → admin-layout-catalyst.template-6UQGLCKI.js.map} +1 -1
- package/dist/{chunk-FTXGZ3BF.cjs → chunk-37V3RET3.cjs} +21 -37
- package/dist/chunk-37V3RET3.cjs.map +1 -0
- package/dist/{chunk-3CT3SUR4.cjs → chunk-A2SN3RVL.cjs} +3 -3
- package/dist/chunk-A2SN3RVL.cjs.map +1 -0
- package/dist/{chunk-CRGUD4KC.cjs → chunk-BBEKHLTW.cjs} +9 -9
- package/dist/{chunk-CRGUD4KC.cjs.map → chunk-BBEKHLTW.cjs.map} +1 -1
- package/dist/{chunk-RMRJGMDE.js → chunk-BHND5PEV.js} +3 -3
- package/dist/{chunk-RMRJGMDE.js.map → chunk-BHND5PEV.js.map} +1 -1
- package/dist/{chunk-OBA2RYZN.js → chunk-BJROXF4N.js} +3 -3
- package/dist/{chunk-OBA2RYZN.js.map → chunk-BJROXF4N.js.map} +1 -1
- package/dist/{chunk-EUJAEB6W.cjs → chunk-BP52BFIV.cjs} +53 -4
- package/dist/chunk-BP52BFIV.cjs.map +1 -0
- package/dist/{chunk-PXNTCCPE.cjs → chunk-E66UVONV.cjs} +8 -8
- package/dist/{chunk-PXNTCCPE.cjs.map → chunk-E66UVONV.cjs.map} +1 -1
- package/dist/{chunk-CHWT5KBK.js → chunk-FHPEM77Y.js} +9 -25
- package/dist/chunk-FHPEM77Y.js.map +1 -0
- package/dist/{chunk-JFLZHOQP.cjs → chunk-I767LVN7.cjs} +531 -419
- package/dist/chunk-I767LVN7.cjs.map +1 -0
- package/dist/{chunk-N32OWET6.cjs → chunk-JMJM7DMM.cjs} +5 -5
- package/dist/{chunk-N32OWET6.cjs.map → chunk-JMJM7DMM.cjs.map} +1 -1
- package/dist/{chunk-QWSR4THH.cjs → chunk-KKNEYIZJ.cjs} +12 -5
- package/dist/chunk-KKNEYIZJ.cjs.map +1 -0
- package/dist/{chunk-73O4GPJ6.js → chunk-LNI3FYSI.js} +12 -5
- package/dist/chunk-LNI3FYSI.js.map +1 -0
- package/dist/{chunk-PYZYU7EK.cjs → chunk-QCJHCSOK.cjs} +36 -2
- package/dist/chunk-QCJHCSOK.cjs.map +1 -0
- package/dist/{chunk-MGHHPNJN.cjs → chunk-QJUJNTIU.cjs} +11 -59
- package/dist/chunk-QJUJNTIU.cjs.map +1 -0
- package/dist/{chunk-DOMXI5PM.js → chunk-RJ2O5IIE.js} +36 -2
- package/dist/chunk-RJ2O5IIE.js.map +1 -0
- package/dist/{chunk-5V62WT6M.js → chunk-THFZSDIU.js} +4 -2
- package/dist/chunk-THFZSDIU.js.map +1 -0
- package/dist/{chunk-2FCKKOA5.js → chunk-UIHIXTIS.js} +3 -3
- package/dist/chunk-UIHIXTIS.js.map +1 -0
- package/dist/{chunk-JQISFW6U.js → chunk-URIBTX27.js} +3 -3
- package/dist/{chunk-JQISFW6U.js.map → chunk-URIBTX27.js.map} +1 -1
- package/dist/{chunk-K623Q6WD.cjs → chunk-UYJQDDEE.cjs} +4 -2
- package/dist/chunk-UYJQDDEE.cjs.map +1 -0
- package/dist/{chunk-6KNTZ67K.js → chunk-VC5XVPLM.js} +234 -126
- package/dist/chunk-VC5XVPLM.js.map +1 -0
- package/dist/{chunk-I2DM27HP.js → chunk-VGEZNFWH.js} +49 -4
- package/dist/chunk-VGEZNFWH.js.map +1 -0
- package/dist/{chunk-GB2Q2DNK.js → chunk-VYQVDL2M.js} +5 -49
- package/dist/chunk-VYQVDL2M.js.map +1 -0
- package/dist/{define-plugin-DsKiGu1q.d.cts → define-plugin-DMcvEy_C.d.cts} +33 -0
- package/dist/{define-plugin-BYIey6sp.d.ts → define-plugin-D_6D9vVZ.d.ts} +33 -0
- package/dist/index.cjs +2587 -326
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -3
- package/dist/index.d.ts +30 -3
- package/dist/index.js +2281 -22
- package/dist/index.js.map +1 -1
- package/dist/middleware.cjs +34 -34
- package/dist/middleware.js +5 -5
- package/dist/migrations-FGXZPKDC.cjs +13 -0
- package/dist/{migrations-UIMA6ZL6.cjs.map → migrations-FGXZPKDC.cjs.map} +1 -1
- package/dist/migrations-Z6D27NAW.js +4 -0
- package/dist/{migrations-HLK2QF4T.js.map → migrations-Z6D27NAW.js.map} +1 -1
- package/dist/plugins.cjs +40 -40
- package/dist/plugins.d.cts +1 -1
- package/dist/plugins.d.ts +1 -1
- package/dist/plugins.js +3 -3
- package/dist/routes.cjs +29 -29
- package/dist/routes.js +9 -9
- package/dist/services.cjs +18 -18
- package/dist/services.js +2 -2
- package/dist/templates.cjs +17 -17
- package/dist/templates.js +3 -3
- package/dist/utils.cjs +3 -3
- package/dist/utils.js +1 -1
- package/migrations/0001_core.sql +7 -1
- package/package.json +10 -3
- package/dist/admin-documents-form.template-DDSH6ROU.js +0 -6
- package/dist/admin-documents-form.template-LSZKGA5J.cjs +0 -19
- package/dist/admin-layout-catalyst.template-KDHKVLXR.cjs +0 -21
- package/dist/admin-layout-catalyst.template-YQ4EMF2J.js +0 -7
- package/dist/chunk-2FCKKOA5.js.map +0 -1
- package/dist/chunk-3CT3SUR4.cjs.map +0 -1
- package/dist/chunk-5V62WT6M.js.map +0 -1
- package/dist/chunk-6KNTZ67K.js.map +0 -1
- package/dist/chunk-73O4GPJ6.js.map +0 -1
- package/dist/chunk-CHWT5KBK.js.map +0 -1
- package/dist/chunk-DOMXI5PM.js.map +0 -1
- package/dist/chunk-EUJAEB6W.cjs.map +0 -1
- package/dist/chunk-FTXGZ3BF.cjs.map +0 -1
- package/dist/chunk-GB2Q2DNK.js.map +0 -1
- package/dist/chunk-I2DM27HP.js.map +0 -1
- package/dist/chunk-JFLZHOQP.cjs.map +0 -1
- package/dist/chunk-K623Q6WD.cjs.map +0 -1
- package/dist/chunk-MGHHPNJN.cjs.map +0 -1
- package/dist/chunk-PYZYU7EK.cjs.map +0 -1
- package/dist/chunk-QWSR4THH.cjs.map +0 -1
- package/dist/migrations-HLK2QF4T.js +0 -4
- package/dist/migrations-UIMA6ZL6.cjs +0 -13
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { init_admin_layout_catalyst_template } from './chunk-THFZSDIU.js';
|
|
2
|
+
export { renderAdminLayoutCatalyst, renderCatalystCheckbox, setBranchLabel } from './chunk-THFZSDIU.js';
|
|
3
|
+
import './chunk-V4OQ3NZ2.js';
|
|
4
|
+
|
|
5
|
+
init_admin_layout_catalyst_template();
|
|
6
|
+
//# sourceMappingURL=admin-layout-catalyst.template-6UQGLCKI.js.map
|
|
7
|
+
//# sourceMappingURL=admin-layout-catalyst.template-6UQGLCKI.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"admin-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"admin-layout-catalyst.template-6UQGLCKI.js"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var chunkLTZ5WYOK_cjs = require('./chunk-LTZ5WYOK.cjs');
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
var
|
|
4
|
+
var chunkQCJHCSOK_cjs = require('./chunk-QCJHCSOK.cjs');
|
|
5
|
+
var chunkA2SN3RVL_cjs = require('./chunk-A2SN3RVL.cjs');
|
|
6
|
+
var chunkUYJQDDEE_cjs = require('./chunk-UYJQDDEE.cjs');
|
|
7
7
|
var chunkJ6JTWD2A_cjs = require('./chunk-J6JTWD2A.cjs');
|
|
8
8
|
var chunkRCQ2HIQD_cjs = require('./chunk-RCQ2HIQD.cjs');
|
|
9
|
-
var
|
|
9
|
+
var chunkKKNEYIZJ_cjs = require('./chunk-KKNEYIZJ.cjs');
|
|
10
10
|
var zod = require('zod');
|
|
11
11
|
var jwt = require('hono/jwt');
|
|
12
12
|
var cookie = require('hono/cookie');
|
|
@@ -72,7 +72,7 @@ var DocumentTypeRegistry = class {
|
|
|
72
72
|
now,
|
|
73
73
|
def.id
|
|
74
74
|
).run();
|
|
75
|
-
await
|
|
75
|
+
await chunkA2SN3RVL_cjs.ensureScalarSchema(this.db, def.id, def.queryableFields ?? []);
|
|
76
76
|
const updated = await this.findById(def.id);
|
|
77
77
|
this.cache.set(def.id, updated);
|
|
78
78
|
return updated;
|
|
@@ -94,7 +94,7 @@ var DocumentTypeRegistry = class {
|
|
|
94
94
|
now,
|
|
95
95
|
now
|
|
96
96
|
).run();
|
|
97
|
-
await
|
|
97
|
+
await chunkA2SN3RVL_cjs.ensureScalarSchema(this.db, def.id, def.queryableFields ?? []);
|
|
98
98
|
const created = await this.findById(def.id);
|
|
99
99
|
this.cache.set(def.id, created);
|
|
100
100
|
return created;
|
|
@@ -148,6 +148,7 @@ async function bootstrapDocumentTypes(db) {
|
|
|
148
148
|
source: "system",
|
|
149
149
|
schema: anyObject,
|
|
150
150
|
settings: {
|
|
151
|
+
versioning: true,
|
|
151
152
|
baseGrants: { public: ["read"], admin: ["read", "create", "update", "delete", "publish", "manage"], editor: ["read", "create", "update", "publish"], viewer: ["read"] },
|
|
152
153
|
maxVersionsPerRoot: 50
|
|
153
154
|
},
|
|
@@ -219,7 +220,8 @@ async function bootstrapDocumentTypes(db) {
|
|
|
219
220
|
internal: true,
|
|
220
221
|
maxVersionsPerRoot: 5,
|
|
221
222
|
baseGrants: {
|
|
222
|
-
|
|
223
|
+
public: ["read"],
|
|
224
|
+
admin: ["read", "create", "update", "delete", "publish", "manage"],
|
|
223
225
|
editor: ["read", "create", "update"],
|
|
224
226
|
author: ["read", "create"],
|
|
225
227
|
viewer: ["read"]
|
|
@@ -310,24 +312,6 @@ async function bootstrapDocumentTypes(db) {
|
|
|
310
312
|
{ name: "path", kind: "scalar", type: "text", column: "q_evt_path" }
|
|
311
313
|
]
|
|
312
314
|
});
|
|
313
|
-
await registry.register({
|
|
314
|
-
id: "media_asset",
|
|
315
|
-
name: "media_asset",
|
|
316
|
-
displayName: "Media Asset",
|
|
317
|
-
description: "Media file metadata (R2 object key + intrinsic properties; URL derived at read time)",
|
|
318
|
-
source: "system",
|
|
319
|
-
schema: anyObject,
|
|
320
|
-
settings: {
|
|
321
|
-
baseGrants: { public: ["read"], admin: ["read", "create", "update", "delete", "publish", "manage"], editor: ["read", "create", "update"] },
|
|
322
|
-
maxVersionsPerRoot: 5
|
|
323
|
-
},
|
|
324
|
-
queryableFields: [
|
|
325
|
-
{ name: "mimeType", kind: "scalar", type: "text", column: "q_media_mime" },
|
|
326
|
-
{ name: "folder", kind: "scalar", type: "text", column: "q_media_folder" },
|
|
327
|
-
{ name: "size", kind: "scalar", type: "integer", column: "q_media_size" },
|
|
328
|
-
{ name: "tags", kind: "facet", type: "text" }
|
|
329
|
-
]
|
|
330
|
-
});
|
|
331
315
|
for (const [id, displayName, description] of [
|
|
332
316
|
["rbac_role", "RBAC Role", "Role record with embedded grants (auth-owned)"],
|
|
333
317
|
["rbac_verb", "RBAC Verb", "Permission verb (auth-owned)"],
|
|
@@ -352,7 +336,7 @@ async function bootstrapDocumentTypes(db) {
|
|
|
352
336
|
}
|
|
353
337
|
async function autoRegisterCollectionDocumentTypes(db) {
|
|
354
338
|
const registry = new DocumentTypeRegistry(db);
|
|
355
|
-
const collections =
|
|
339
|
+
const collections = chunkQCJHCSOK_cjs.getCollectionRegistry().listActive();
|
|
356
340
|
const registered = [];
|
|
357
341
|
for (const collection of collections) {
|
|
358
342
|
if (collection.internal) continue;
|
|
@@ -387,7 +371,7 @@ async function autoRegisterCollectionDocumentTypes(db) {
|
|
|
387
371
|
}
|
|
388
372
|
|
|
389
373
|
// src/middleware/bootstrap.ts
|
|
390
|
-
|
|
374
|
+
chunkUYJQDDEE_cjs.init_admin_layout_catalyst_template();
|
|
391
375
|
var bootstrapComplete = false;
|
|
392
376
|
function verifySecurityConfig(env) {
|
|
393
377
|
const warnings = [];
|
|
@@ -441,11 +425,11 @@ function bootstrapMiddleware(config = {}) {
|
|
|
441
425
|
const host = c.req.header("host") || "";
|
|
442
426
|
const isLocalhost = host.includes("localhost") || host.includes("127.0.0.1");
|
|
443
427
|
const gitBranch = c.env.GIT_BRANCH;
|
|
444
|
-
|
|
428
|
+
chunkUYJQDDEE_cjs.setBranchLabel(isLocalhost && gitBranch ? gitBranch : void 0);
|
|
445
429
|
try {
|
|
446
430
|
console.log("[Bootstrap] Starting system initialization...");
|
|
447
431
|
console.log("[Bootstrap] Checking schema compatibility...");
|
|
448
|
-
const migrationService = new
|
|
432
|
+
const migrationService = new chunkA2SN3RVL_cjs.MigrationService(c.env.DB);
|
|
449
433
|
await migrationService.ensureSchemaCompatibility();
|
|
450
434
|
try {
|
|
451
435
|
const kv = c.env.CACHE_KV;
|
|
@@ -458,8 +442,8 @@ function bootstrapMiddleware(config = {}) {
|
|
|
458
442
|
}
|
|
459
443
|
console.log("[Bootstrap] Populating collection registry...");
|
|
460
444
|
try {
|
|
461
|
-
const configs = await
|
|
462
|
-
|
|
445
|
+
const configs = await chunkQCJHCSOK_cjs.loadCollectionConfigs();
|
|
446
|
+
chunkQCJHCSOK_cjs.getCollectionRegistry().register(configs);
|
|
463
447
|
console.log(`[Bootstrap] Registry populated with ${configs.length} collection(s)`);
|
|
464
448
|
} catch (error) {
|
|
465
449
|
console.error("[Bootstrap] Error populating collection registry:", error);
|
|
@@ -489,7 +473,7 @@ function bootstrapMiddleware(config = {}) {
|
|
|
489
473
|
}
|
|
490
474
|
if (!config.plugins?.disableAll) {
|
|
491
475
|
console.log("[Bootstrap] Bootstrapping core plugins...");
|
|
492
|
-
const bootstrapService = new
|
|
476
|
+
const bootstrapService = new chunkQCJHCSOK_cjs.PluginBootstrapService(c.env.DB);
|
|
493
477
|
const needsBootstrap = await bootstrapService.isBootstrapNeeded();
|
|
494
478
|
if (needsBootstrap) {
|
|
495
479
|
await bootstrapService.bootstrapCorePlugins();
|
|
@@ -500,7 +484,7 @@ function bootstrapMiddleware(config = {}) {
|
|
|
500
484
|
bootstrapComplete = true;
|
|
501
485
|
console.log("[Bootstrap] System initialization completed");
|
|
502
486
|
try {
|
|
503
|
-
const registry =
|
|
487
|
+
const registry = chunkQCJHCSOK_cjs.getCollectionRegistry();
|
|
504
488
|
const collections = registry.listActive();
|
|
505
489
|
const countResult = await c.env.DB.prepare(
|
|
506
490
|
`SELECT type_id, COUNT(*) AS cnt FROM documents
|
|
@@ -533,7 +517,7 @@ function bootstrapMiddleware(config = {}) {
|
|
|
533
517
|
}
|
|
534
518
|
} catch {
|
|
535
519
|
}
|
|
536
|
-
const telemetry =
|
|
520
|
+
const telemetry = chunkQCJHCSOK_cjs.getTelemetryService();
|
|
537
521
|
await telemetry.trackProjectSnapshot({
|
|
538
522
|
installation_id: installationId,
|
|
539
523
|
collection_names: collections.map((c2) => c2.name),
|
|
@@ -541,7 +525,7 @@ function bootstrapMiddleware(config = {}) {
|
|
|
541
525
|
active_plugins: activePlugins,
|
|
542
526
|
field_type_histogram: fieldTypeHistogram,
|
|
543
527
|
doc_total: docTotal,
|
|
544
|
-
sonicjs_version:
|
|
528
|
+
sonicjs_version: chunkKKNEYIZJ_cjs.SONICJS_VERSION
|
|
545
529
|
});
|
|
546
530
|
} catch {
|
|
547
531
|
}
|
|
@@ -1238,5 +1222,5 @@ exports.securityHeadersMiddleware = securityHeadersMiddleware;
|
|
|
1238
1222
|
exports.securityLoggingMiddleware = securityLoggingMiddleware;
|
|
1239
1223
|
exports.validateCsrfToken = validateCsrfToken;
|
|
1240
1224
|
exports.verifySecurityConfig = verifySecurityConfig;
|
|
1241
|
-
//# sourceMappingURL=chunk-
|
|
1242
|
-
//# sourceMappingURL=chunk-
|
|
1225
|
+
//# sourceMappingURL=chunk-37V3RET3.cjs.map
|
|
1226
|
+
//# sourceMappingURL=chunk-37V3RET3.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/services/document-type-registry.ts","../src/services/document-types-seed.ts","../src/middleware/bootstrap.ts","../src/middleware/auth.ts","../src/middleware/metrics.ts","../src/middleware/csrf.ts","../src/middleware/rate-limit.ts","../src/middleware/security-headers.ts","../src/middleware/plugin-middleware.ts","../src/middleware/index.ts"],"names":["ensureScalarSchema","z","getCollectionRegistry","init_admin_layout_catalyst_template","hasHookSystem","getHookSystem","setBranchLabel","MigrationService","loadCollectionConfigs","RbacService","PluginBootstrapService","getTelemetryService","c","SONICJS_VERSION","sign","verify","getCookie","result","setCookie","metricsTracker","JWT_SECRET_FALLBACK"],"mappings":"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","file":"chunk-37V3RET3.cjs","sourcesContent":["import { D1Database } from '@cloudflare/workers-types'\nimport { nanoid } from 'nanoid'\nimport type {\n PluginDocumentType,\n DocumentType,\n DocumentTypeRow,\n} from '../schemas/document'\nimport { ensureScalarSchema } from './document-scalar-schema'\n\nfunction rowToDocumentType(row: DocumentTypeRow): DocumentType {\n return {\n id: row.id,\n name: row.name,\n displayName: row.display_name,\n description: row.description,\n schema: JSON.parse(row.schema),\n queryableFields: JSON.parse(row.queryable_fields),\n settings: JSON.parse(row.settings),\n pluginId: row.plugin_id,\n source: row.source,\n schemaVersion: row.schema_version,\n isSystem: row.is_system === 1,\n isActive: row.is_active === 1,\n isAuth: row.is_auth === 1,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n }\n}\n\nexport class DocumentTypeRegistry {\n private cache = new Map<string, DocumentType>()\n\n constructor(private db: D1Database) {}\n\n // Register or update a document type. Idempotent: bumps schema_version only when schema changes.\n async register(def: PluginDocumentType & { pluginId?: string; source?: 'code' | 'plugin' | 'system' }): Promise<DocumentType> {\n const now = Math.floor(Date.now() / 1000)\n const existing = await this.findById(def.id)\n\n // A z.ZodSchema is not JSON-serializable, so persist a stable serializable shape derived from\n // the type's queryable fields + settings. This is what schemaChanged compares, so schema_version\n // bumps whenever a type's filterable shape changes (and is stamped onto documents.type_version).\n const schemaJson = JSON.stringify({ queryableFields: def.queryableFields ?? [], settings: def.settings ?? {} })\n const queryableJson = JSON.stringify(def.queryableFields ?? [])\n const settingsJson = JSON.stringify(def.settings ?? {})\n\n if (existing) {\n const schemaChanged = schemaJson !== JSON.stringify(existing.schema)\n const newVersion = schemaChanged ? existing.schemaVersion + 1 : existing.schemaVersion\n\n await this.db\n .prepare(\n `UPDATE document_types SET\n display_name = ?,\n description = ?,\n schema = ?,\n queryable_fields = ?,\n settings = ?,\n plugin_id = ?,\n schema_version = ?,\n is_active = 1,\n is_auth = ?,\n updated_at = ?\n WHERE id = ?`,\n )\n .bind(\n def.displayName,\n def.description ?? null,\n schemaJson,\n queryableJson,\n settingsJson,\n def.pluginId ?? null,\n newVersion,\n def.isAuth ? 1 : 0,\n now,\n def.id,\n )\n .run()\n\n // Auto-DDL: ensure VIRTUAL generated columns + indexes for scalar fields.\n await ensureScalarSchema(this.db, def.id, def.queryableFields ?? [])\n\n const updated = await this.findById(def.id)\n this.cache.set(def.id, updated!)\n return updated!\n }\n\n await this.db\n .prepare(\n `INSERT INTO document_types (id, name, display_name, description, schema, queryable_fields, settings, plugin_id, source, schema_version, is_system, is_active, is_auth, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 0, 1, ?, ?, ?)`,\n )\n .bind(\n def.id,\n def.name ?? def.id,\n def.displayName,\n def.description ?? null,\n schemaJson,\n queryableJson,\n settingsJson,\n def.pluginId ?? null,\n def.source ?? 'code',\n def.isAuth ? 1 : 0,\n now,\n now,\n )\n .run()\n\n // Auto-DDL: ensure VIRTUAL generated columns + indexes for scalar fields.\n await ensureScalarSchema(this.db, def.id, def.queryableFields ?? [])\n\n const created = await this.findById(def.id)\n this.cache.set(def.id, created!)\n return created!\n }\n\n async findById(id: string): Promise<DocumentType | null> {\n if (this.cache.has(id)) return this.cache.get(id)!\n\n const row = await this.db\n .prepare('SELECT * FROM document_types WHERE id = ?')\n .bind(id)\n .first<DocumentTypeRow>()\n\n if (!row) return null\n const dt = rowToDocumentType(row)\n this.cache.set(id, dt)\n return dt\n }\n\n async findAll(activeOnly = true): Promise<DocumentType[]> {\n const sql = activeOnly\n ? 'SELECT * FROM document_types WHERE is_active = 1 ORDER BY name'\n : 'SELECT * FROM document_types ORDER BY name'\n\n const result = await this.db.prepare(sql).all<DocumentTypeRow>()\n return (result.results ?? []).map(rowToDocumentType)\n }\n\n async deactivate(id: string): Promise<void> {\n const now = Math.floor(Date.now() / 1000)\n await this.db\n .prepare('UPDATE document_types SET is_active = 0, updated_at = ? WHERE id = ?')\n .bind(now, id)\n .run()\n this.cache.delete(id)\n }\n\n clearCache(): void {\n this.cache.clear()\n }\n}\n","import { D1Database } from '@cloudflare/workers-types'\nimport { z } from 'zod'\nimport { DocumentTypeRegistry } from './document-type-registry'\nimport { getCollectionRegistry } from './collection-registry'\nimport type { Permission } from '../schemas/document'\n\n// Passthrough schema: accepts any JSON object for POC types.\n// Individual fields are validated at the queryable-field level; the full\n// payload schema is a future enhancement (addDocumentType will accept Zod schemas).\nconst anyObject = z.record(z.string(), z.unknown())\n\n// Registers the POC document types idempotently during bootstrap.\n// These are the candidate types from the document model plan.\n// Each call is a no-op if the type already exists and the schema hasn't changed.\nexport async function bootstrapDocumentTypes(db: D1Database): Promise<void> {\n const registry = new DocumentTypeRegistry(db)\n\n // Site settings: internal singleton config — never surfaced in content admin.\n await registry.register({\n id: 'site_settings',\n name: 'site_settings',\n displayName: 'Site Settings',\n description: 'Global site configuration (internal; managed via admin settings UI)',\n source: 'system',\n schema: anyObject,\n settings: {\n internal: true,\n maxVersionsPerRoot: 1,\n baseGrants: { admin: ['read', 'create', 'update', 'delete', 'manage'] },\n },\n queryableFields: [],\n })\n\n // Blog post: the code-managed `blog_post` collection is backed by the document model.\n // The matching id (`blog_post` == collection name) is how content admin detects doc-backing.\n await registry.register({\n id: 'blog_post',\n name: 'blog_post',\n displayName: 'Blog Post',\n description: 'Blog post (document-backed; edited via the content collection UI)',\n source: 'system',\n schema: anyObject,\n settings: {\n versioning: true,\n baseGrants: { public: ['read'], admin: ['read', 'create', 'update', 'delete', 'publish', 'manage'], editor: ['read', 'create', 'update', 'publish'], viewer: ['read'] },\n maxVersionsPerRoot: 50,\n },\n queryableFields: [\n { name: 'difficulty', kind: 'scalar', type: 'text', column: 'q_blog_difficulty' },\n { name: 'author', kind: 'scalar', type: 'text', column: 'q_blog_author' },\n ],\n })\n\n // Plugin registry (document-backed plugin management)\n await registry.register({\n id: 'plugin',\n name: 'plugin',\n displayName: 'Plugin',\n description: 'System plugin record (managed by the plugin bootstrap service)',\n source: 'system',\n schema: anyObject,\n settings: {\n baseGrants: { admin: ['read', 'create', 'update', 'delete', 'publish', 'manage'] },\n maxVersionsPerRoot: 1,\n internal: true,\n },\n queryableFields: [\n { name: 'status', kind: 'scalar', type: 'text', column: 'q_plugin_status' },\n { name: 'category', kind: 'scalar', type: 'text', column: 'q_plugin_category' },\n { name: 'isCore', kind: 'scalar', type: 'integer', column: 'q_plugin_is_core' },\n ],\n })\n\n // Tenant registry (document-backed multi-tenancy; rows managed by the multi-tenant plugin).\n // Tenant records are platform metadata and live under the 'default' tenant themselves.\n // Zero rows until the multi-tenant plugin is activated and used.\n await registry.register({\n id: 'tenant',\n name: 'tenant',\n displayName: 'Tenant',\n description: 'Tenant record (managed by the multi-tenant plugin; slug = tenant id)',\n source: 'system',\n schema: anyObject,\n settings: {\n baseGrants: { admin: ['read', 'create', 'update', 'delete', 'manage'] },\n maxVersionsPerRoot: 1,\n internal: true,\n },\n queryableFields: [\n { name: 'status', kind: 'scalar', type: 'text', column: 'q_tenant_status' },\n { name: 'domain', kind: 'scalar', type: 'text', column: 'q_tenant_domain' },\n ],\n })\n\n // User profile (auth-owned). One document per user, addressed by slug = userId.\n // Replaces the auth_user_profiles table. Typed fields + custom fields live in `data`.\n await registry.register({\n id: 'user_profile',\n name: 'user_profile',\n displayName: 'User Profile',\n description: 'Per-user profile record (auth-owned; one document per user, slug = userId)',\n source: 'system',\n isAuth: true,\n schema: anyObject,\n settings: {\n // Hidden from the content admin surfaces; a single mutable record (no version history).\n internal: true,\n maxVersionsPerRoot: 1,\n pii: true,\n baseGrants: { admin: ['read', 'create', 'update', 'delete', 'manage'] },\n },\n queryableFields: [],\n })\n\n // Media asset (document-authoritative media library). One document per uploaded R2 object.\n // Internal: managed via the media library + media selector, not offered as a content model.\n // Registering here both satisfies the documents.type_id FK and creates the q_media_* columns\n // (DocumentTypeRegistry.register → ensureScalarSchema). Must stay in sync with MEDIA_QUERYABLE\n // in services/media-documents.ts.\n await registry.register({\n id: 'media_asset',\n name: 'media_asset',\n displayName: 'Media Asset',\n description: 'Uploaded media file metadata (managed via the media library; backs an R2 object)',\n source: 'system',\n schema: anyObject,\n settings: {\n internal: true,\n maxVersionsPerRoot: 5,\n baseGrants: {\n public: ['read'],\n admin: ['read', 'create', 'update', 'delete', 'publish', 'manage'],\n editor: ['read', 'create', 'update'],\n author: ['read', 'create'],\n viewer: ['read'],\n },\n },\n queryableFields: [\n { name: 'mimeType', kind: 'scalar', type: 'text', column: 'q_media_mime' },\n { name: 'folder', kind: 'scalar', type: 'text', column: 'q_media_folder' },\n { name: 'size', kind: 'scalar', type: 'integer', column: 'q_media_size' },\n { name: 'tags', kind: 'facet', type: 'text' },\n ],\n })\n\n // Plugin activity log (document-backed; replaces legacy plugin_activity_log table which was never migrated)\n await registry.register({\n id: 'plugin_activity',\n name: 'plugin_activity',\n displayName: 'Plugin Activity',\n description: 'Plugin lifecycle event log (installed/activated/deactivated/settings_updated/error)',\n source: 'system',\n schema: anyObject,\n settings: {\n internal: true,\n maxVersionsPerRoot: 1,\n baseGrants: { admin: ['read', 'create', 'manage'] },\n },\n queryableFields: [\n { name: 'pluginId', kind: 'scalar', type: 'text', column: 'q_plugin_activity_plugin_id' },\n { name: 'action', kind: 'scalar', type: 'text', column: 'q_plugin_activity_action' },\n ],\n })\n\n // Security audit event (document-backed; replaces legacy security_events table)\n await registry.register({\n id: 'security_event',\n name: 'security_event',\n displayName: 'Security Event',\n description: 'Security audit event (login attempts, lockouts, suspicious activity)',\n source: 'system',\n schema: anyObject,\n settings: {\n internal: true,\n maxVersionsPerRoot: 1,\n baseGrants: { admin: ['read', 'create', 'manage'] },\n },\n queryableFields: [\n { name: 'eventType', kind: 'scalar', type: 'text', column: 'q_sa_event_type' },\n { name: 'severity', kind: 'scalar', type: 'text', column: 'q_sa_severity' },\n { name: 'userId', kind: 'scalar', type: 'text', column: 'q_sa_user_id' },\n { name: 'email', kind: 'scalar', type: 'text', column: 'q_sa_email' },\n { name: 'ipAddress', kind: 'scalar', type: 'text', column: 'q_sa_ip_address' },\n { name: 'blocked', kind: 'scalar', type: 'integer', column: 'q_sa_blocked' },\n ],\n })\n\n // API key (document-backed programmatic access tokens; auth-owned, one document per key).\n // Stores only the SHA-256 hash of the secret (q_apikey_hash) — never the plaintext. PII/credential:\n // revoke hard-erases. See services/api-keys.ts (ApiKeyService) for the write/resolve paths.\n await registry.register({\n id: 'api_key',\n name: 'api_key',\n displayName: 'API Key',\n description: 'Programmatic API access key (auth-owned; only the secret hash is stored)',\n source: 'system',\n isAuth: true,\n schema: anyObject,\n settings: {\n internal: true,\n maxVersionsPerRoot: 1,\n pii: true,\n baseGrants: { admin: ['read', 'create', 'update', 'delete', 'manage'] },\n },\n queryableFields: [\n { name: 'keyHash', kind: 'scalar', type: 'text', column: 'q_apikey_hash' },\n { name: 'userId', kind: 'scalar', type: 'text', column: 'q_apikey_user_id' },\n { name: 'revoked', kind: 'scalar', type: 'integer', column: 'q_apikey_revoked' },\n ],\n })\n\n // Analytics event (document-backed; replaces legacy analytics_events table)\n await registry.register({\n id: 'analytics_event',\n name: 'analytics_event',\n displayName: 'Analytics Event',\n description: 'Tracked analytics event (page view, user action, custom event)',\n source: 'system',\n schema: anyObject,\n settings: {\n internal: true,\n maxVersionsPerRoot: 1,\n baseGrants: { admin: ['read', 'create', 'manage'] },\n },\n queryableFields: [\n { name: 'event', kind: 'scalar', type: 'text', column: 'q_evt_event' },\n { name: 'category', kind: 'scalar', type: 'text', column: 'q_evt_category' },\n { name: 'userId', kind: 'scalar', type: 'text', column: 'q_evt_user_id' },\n { name: 'sessionId', kind: 'scalar', type: 'text', column: 'q_evt_session_id' },\n { name: 'path', kind: 'scalar', type: 'text', column: 'q_evt_path' },\n ],\n })\n\n // ── RBAC (auth-owned). 3 document types replace 4 relational tables: ──────────\n // rbac_role slug = roleId, data.grants[] embedded (replaces role_grants)\n // rbac_verb slug = verbId\n // rbac_user_roles slug = userId, data.roleIds[] embedded (replaces user_roles)\n // All internal + is_auth so they never surface in content. See services/rbac.ts.\n for (const [id, displayName, description] of [\n ['rbac_role', 'RBAC Role', 'Role record with embedded grants (auth-owned)'],\n ['rbac_verb', 'RBAC Verb', 'Permission verb (auth-owned)'],\n ['rbac_user_roles', 'RBAC User Roles', \"Per-user role assignments (auth-owned; slug = userId)\"],\n ] as const) {\n await registry.register({\n id,\n name: id,\n displayName,\n description,\n source: 'system',\n isAuth: true,\n schema: anyObject,\n settings: {\n internal: true,\n maxVersionsPerRoot: 1,\n baseGrants: { admin: ['read', 'create', 'update', 'delete', 'manage'] },\n },\n queryableFields: [],\n })\n }\n}\n\n/**\n * Register a document type for every code-defined collection in the registry.\n *\n * Code-defined collections become document-backed automatically so that all\n * content writes against them flow through the documents repository. The\n * document type's id == collection name, matching how content admin detects\n * doc-backing.\n */\nexport async function autoRegisterCollectionDocumentTypes(db: D1Database): Promise<string[]> {\n const registry = new DocumentTypeRegistry(db)\n const collections = getCollectionRegistry().listActive()\n const registered: string[] = []\n\n for (const collection of collections) {\n // Skip system/internal collections that already have explicit document type\n // definitions in bootstrapDocumentTypes (e.g. blog_post is seeded above\n // with its own queryable fields). The explicit registration wins.\n if (collection.internal) continue\n if (collection.name === 'blog_post') continue\n\n try {\n const defaultGrants: Record<string, Permission[]> = {\n admin: ['read', 'create', 'update', 'delete', 'publish', 'manage'],\n editor: ['read', 'create', 'update', 'publish'],\n viewer: ['read'],\n }\n const baseGrants: Record<string, Permission[]> = collection.access\n ? { ...defaultGrants, ...collection.access }\n : defaultGrants\n\n await registry.register({\n id: collection.name,\n name: collection.name,\n displayName: collection.displayName,\n description: collection.description,\n source: 'system',\n schema: anyObject,\n settings: {\n baseGrants,\n maxVersionsPerRoot: 50,\n ...(collection.versioning ? { versioning: true } : {}),\n },\n queryableFields: [],\n })\n registered.push(collection.name)\n } catch (error) {\n console.error(`[document-types-seed] Failed to register collection \"${collection.name}\":`, error)\n }\n }\n\n return registered\n}\n","import { Context, Next } from \"hono\";\nimport { loadCollectionConfigs } from \"../services/collection-loader\";\nimport { getCollectionRegistry } from \"../services/collection-registry\";\nimport { MigrationService } from \"../services/migrations\";\nimport { PluginBootstrapService } from \"../services/plugin-bootstrap\";\nimport { bootstrapDocumentTypes, autoRegisterCollectionDocumentTypes } from \"../services/document-types-seed\";\nimport { getHookSystem, hasHookSystem } from \"../plugins/hooks/hook-system-singleton\";\nimport { getTelemetryService } from \"../services/telemetry-service\"\nimport { SONICJS_VERSION } from \"../utils/version\";\nimport type { SonicJSConfig } from \"../app\";\nimport { setBranchLabel } from \"../templates/layouts/admin-layout-catalyst.template\";\n\ntype Bindings = {\n DB: D1Database;\n KV: KVNamespace;\n JWT_SECRET?: string;\n CORS_ORIGINS?: string;\n ENVIRONMENT?: string;\n};\n\n// Track if bootstrap has been run in this worker instance\nlet bootstrapComplete = false;\n\n/**\n * Verify security-critical environment configuration at startup.\n * Logs warnings in development, throws in production to prevent\n * insecure deployments from silently running.\n */\nexport function verifySecurityConfig(env: Bindings): void {\n const warnings: string[] = [];\n\n // Check JWT secret\n if (!env.JWT_SECRET) {\n warnings.push(\n \"JWT_SECRET is not set — using hardcoded fallback. Set via `wrangler secret put JWT_SECRET`\"\n );\n } else if (env.JWT_SECRET.includes(\"change-in-production\")) {\n warnings.push(\n \"JWT_SECRET contains the default value — tokens are forgeable. Generate a strong random secret\"\n );\n }\n\n // Check CORS origins\n if (!env.CORS_ORIGINS) {\n warnings.push(\n \"CORS_ORIGINS is not set — all cross-origin API requests will be rejected\"\n );\n }\n\n // Check environment designation\n if (!env.ENVIRONMENT) {\n warnings.push(\n \"ENVIRONMENT is not set — HSTS header will not be applied. Set to \\\"production\\\" or \\\"development\\\"\"\n );\n }\n\n if (warnings.length === 0) {\n return;\n }\n\n const isProduction = env.ENVIRONMENT === \"production\";\n\n for (const warning of warnings) {\n console.warn(`[SonicJS Security] ${warning}`);\n }\n\n if (isProduction) {\n // In production, a missing or default JWT_SECRET is a hard failure —\n // every token issued would be forgeable by anyone reading the source code.\n const hasCritical =\n !env.JWT_SECRET || env.JWT_SECRET.includes(\"change-in-production\");\n if (hasCritical) {\n throw new Error(\n \"[SonicJS Security] CRITICAL: Production deployment is missing a secure JWT_SECRET. \" +\n \"Set it via `wrangler secret put JWT_SECRET` before deploying.\"\n );\n }\n }\n}\n\n/**\n * Bootstrap middleware that ensures system initialization\n * Runs once per worker instance\n */\nexport function bootstrapMiddleware(config: SonicJSConfig = {}) {\n return async (c: Context<{ Bindings: Bindings; Variables: { hookSystem?: unknown } }>, next: Next) => {\n // Attach the hook system to the request BEFORE any heavy bootstrap work\n // runs, so anything that emits a hook during bootstrap (cron cold starts,\n // RBAC seed, document-type registration, plugin onBoot via createPluginWirer)\n // sees a live bus instead of a no-op. The process singleton was published at\n // app-factory time (app.ts); we only forward it onto the request here.\n if (hasHookSystem()) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Variables typed loosely here; concrete type lives in app.ts\n (c as any).set(\"hookSystem\", getHookSystem());\n }\n\n // Skip if already bootstrapped in this worker instance\n if (bootstrapComplete) {\n return next();\n }\n\n // Skip bootstrap for static assets and health checks\n const path = c.req.path;\n if (\n path.startsWith(\"/images/\") ||\n path.startsWith(\"/assets/\") ||\n path === \"/health\" ||\n path.endsWith(\".js\") ||\n path.endsWith(\".css\") ||\n path.endsWith(\".png\") ||\n path.endsWith(\".jpg\") ||\n path.endsWith(\".ico\")\n ) {\n return next();\n }\n\n // Show branch label automatically on localhost — no env var needed\n const host = c.req.header('host') || '';\n const isLocalhost = host.includes('localhost') || host.includes('127.0.0.1');\n const gitBranch = (c.env as any).GIT_BRANCH as string | undefined;\n setBranchLabel(isLocalhost && gitBranch ? gitBranch : undefined);\n\n try {\n console.log(\"[Bootstrap] Starting system initialization...\");\n\n // 1. Run idempotent schema compatibility repairs. Migration state and\n // migration execution are owned by Cloudflare D1/Wrangler.\n console.log(\"[Bootstrap] Checking schema compatibility...\");\n const migrationService = new MigrationService(c.env.DB);\n await migrationService.ensureSchemaCompatibility();\n\n // 1a. Wire the CACHE_KV binding into the cache plugin's singleton store so\n // cache writes survive isolate evictions. Memory-only cache is per-isolate\n // and ephemeral, which makes /admin/cache appear \"empty\" after restarts.\n try {\n const kv = (c.env as any).CACHE_KV;\n if (kv) {\n const { setGlobalKVNamespace } = await import(\n \"../plugins/cache/services/cache\"\n );\n setGlobalKVNamespace(kv);\n }\n } catch (error) {\n console.error(\"[Bootstrap] Error wiring CACHE_KV namespace:\", error);\n }\n\n // 2. Populate the in-memory collection registry from code-defined configs.\n // This is the source of truth going forward; the DB `collections` table is\n // being decommissioned (see docs/ai/plans/drop-db-collections-plan.md).\n console.log(\"[Bootstrap] Populating collection registry...\");\n try {\n const configs = await loadCollectionConfigs();\n getCollectionRegistry().register(configs);\n console.log(`[Bootstrap] Registry populated with ${configs.length} collection(s)`);\n } catch (error) {\n console.error(\"[Bootstrap] Error populating collection registry:\", error);\n }\n\n // 3. Register document types (idempotent)\n console.log(\"[Bootstrap] Registering document types...\");\n try {\n await bootstrapDocumentTypes(c.env.DB);\n } catch (error) {\n console.error(\"[Bootstrap] Error registering document types:\", error);\n }\n\n // 2c. Repair legacy users that have password_hash in auth_user but no\n // auth_account credential row (registered before Better Auth migration).\n try {\n await repairMissingCredentialAccounts(c.env.DB)\n } catch (error) {\n console.error('[Bootstrap] Error repairing credential accounts:', error)\n }\n\n // 3a. Seed system RBAC roles/verbs/grants as documents (idempotent).\n try {\n const { RbacService } = await import(\"../services/rbac\");\n await new RbacService(c.env.DB, (c.env as any).CACHE_KV).ensureSystemRbacSeed();\n } catch (error) {\n console.error(\"[Bootstrap] Error seeding RBAC documents:\", error);\n }\n\n // 3b. Make every content collection document-backed (so all new content goes to `documents`).\n try {\n const auto = await autoRegisterCollectionDocumentTypes(c.env.DB);\n if (auto.length) console.log(`[Bootstrap] Document-backed collections registered: ${auto.join(\", \")}`);\n } catch (error) {\n console.error(\"[Bootstrap] Error auto-registering collection document types:\", error);\n }\n\n // 4. Bootstrap core plugins (unless disableAll is set)\n if (!config.plugins?.disableAll) {\n console.log(\"[Bootstrap] Bootstrapping core plugins...\");\n const bootstrapService = new PluginBootstrapService(c.env.DB);\n\n // Check if bootstrap is needed\n const needsBootstrap = await bootstrapService.isBootstrapNeeded();\n if (needsBootstrap) {\n await bootstrapService.bootstrapCorePlugins();\n }\n } else {\n console.log(\"[Bootstrap] Plugin bootstrap skipped (disableAll is true)\");\n }\n\n // Mark bootstrap as complete for this worker instance\n bootstrapComplete = true;\n console.log(\"[Bootstrap] System initialization completed\");\n\n // Fire project snapshot telemetry (fire-and-forget, never blocks boot)\n try {\n const registry = getCollectionRegistry();\n const collections = registry.listActive();\n\n // Count docs per collection type from D1\n const countResult = await c.env.DB.prepare(\n `SELECT type_id, COUNT(*) AS cnt FROM documents\n WHERE is_current_draft = 1 AND deleted_at IS NULL GROUP BY type_id`\n ).all<{ type_id: string; cnt: number }>();\n const countMap: Record<string, number> = {};\n let docTotal = 0;\n for (const row of (countResult.results ?? [])) {\n countMap[row.type_id] = row.cnt;\n docTotal += row.cnt;\n }\n\n // Field type histogram across all collection schemas\n const fieldTypeHistogram: Record<string, number> = {};\n for (const col of collections) {\n const props = (col.schema as any)?.properties ?? {};\n for (const field of Object.values(props) as any[]) {\n const ft: string = field?.type ?? 'unknown';\n fieldTypeHistogram[ft] = (fieldTypeHistogram[ft] ?? 0) + 1;\n }\n }\n\n // Plugin names from config\n const activePlugins = (config.plugins?.register ?? []).map((p: any) => p.name ?? 'unknown');\n\n // Stable installation ID via KV (generated once, persisted)\n let installationId = 'unknown';\n try {\n const kv = c.env.KV as KVNamespace | undefined;\n if (kv) {\n installationId = (await kv.get('_sonicjs_installation_id')) ?? '';\n if (!installationId) {\n installationId = crypto.randomUUID();\n await kv.put('_sonicjs_installation_id', installationId);\n }\n }\n } catch { /* KV not available */ }\n\n const telemetry = getTelemetryService();\n await telemetry.trackProjectSnapshot({\n installation_id: installationId,\n collection_names: collections.map(c => c.name),\n collection_counts: countMap,\n active_plugins: activePlugins,\n field_type_histogram: fieldTypeHistogram,\n doc_total: docTotal,\n sonicjs_version: SONICJS_VERSION,\n });\n } catch { /* silent — telemetry must never break boot */ }\n } catch (error) {\n console.error(\"[Bootstrap] Error during system initialization:\", error);\n // Don't prevent the app from starting, but log the error\n }\n\n // 4. Verify security configuration (outside try/catch so critical\n // errors in production propagate and prevent insecure deployments)\n verifySecurityConfig(c.env as Bindings);\n\n return next();\n };\n}\n\n/**\n * Reset bootstrap flag (useful for testing)\n */\nexport function resetBootstrap() {\n bootstrapComplete = false;\n}\n\n/**\n * Find auth_user rows that have a password_hash but no auth_account credential\n * row (created before Better Auth migration) and repair them. Idempotent —\n * INSERT OR IGNORE means re-runs are safe.\n */\nasync function repairMissingCredentialAccounts(db: D1Database): Promise<void> {\n const { results } = await db.prepare(`\n SELECT u.id, u.password_hash\n FROM auth_user u\n WHERE u.password_hash IS NOT NULL AND u.password_hash != ''\n AND NOT EXISTS (\n SELECT 1 FROM auth_account a\n WHERE a.user_id = u.id AND a.provider_id = 'credential'\n )\n `).all()\n\n if (!results.length) return\n\n console.log(`[Bootstrap] Repairing ${results.length} user(s) missing credential auth_account rows`)\n const nowSec = Math.floor(Date.now() / 1000)\n for (const user of results as Array<{ id: string; password_hash: string }>) {\n await db.prepare(`\n INSERT OR IGNORE INTO auth_account (id, user_id, account_id, provider_id, password, created_at, updated_at)\n VALUES (?, ?, ?, 'credential', ?, ?, ?)\n `).bind(`cred-${user.id}`, user.id, user.id, user.password_hash, nowSec, nowSec).run()\n }\n console.log(`[Bootstrap] Credential account repair complete (${results.length} repaired)`)\n}\n","import { sign, verify } from 'hono/jwt'\nimport { Context, Next } from 'hono'\nimport { getCookie, setCookie } from 'hono/cookie'\nimport { RbacService } from '../services/rbac'\n\ntype JWTPayload = {\n userId: string\n email: string\n role: string\n exp: number\n iat: number\n}\n\n// Fallback JWT secret for local development only (no wrangler secret set)\nconst JWT_SECRET_FALLBACK = 'your-super-secret-jwt-key-change-in-production'\n\n// Default JWT TTL: 30 days. Can be overridden via JWT_EXPIRES_IN env var.\nconst DEFAULT_JWT_EXPIRES_IN_SECONDS = 60 * 60 * 24 * 30\n\n/**\n * Parse a TTL string like \"30d\", \"12h\", \"3600s\", or a bare number-of-seconds\n * into a seconds value. Returns null if the input is missing/unparseable.\n */\nfunction parseDuration(input: string | number | undefined | null): number | null {\n if (input === undefined || input === null || input === '') return null\n if (typeof input === 'number' && Number.isFinite(input) && input > 0) {\n return Math.floor(input)\n }\n const raw = String(input).trim()\n if (/^\\d+$/.test(raw)) {\n const n = parseInt(raw, 10)\n return n > 0 ? n : null\n }\n const match = raw.match(/^(\\d+)\\s*(s|sec|secs|seconds|m|min|mins|minutes|h|hr|hrs|hours|d|day|days)$/i)\n if (!match) return null\n const value = parseInt(match[1]!, 10)\n const unit = match[2]!.toLowerCase()\n if (unit.startsWith('s')) return value\n if (unit.startsWith('m')) return value * 60\n if (unit.startsWith('h')) return value * 60 * 60\n if (unit.startsWith('d')) return value * 60 * 60 * 24\n return null\n}\n\n/**\n * Resolve the JWT expiry in seconds from the environment.\n * Honors `JWT_EXPIRES_IN` (seconds or \"30d\"/\"12h\"/\"3600s\") with a 30-day default.\n */\nexport function getJwtExpirySeconds(env?: Record<string, any> | null): number {\n const configured = parseDuration(env?.JWT_EXPIRES_IN)\n return configured ?? DEFAULT_JWT_EXPIRES_IN_SECONDS\n}\n\n/**\n * Resolve the JWT expiry in seconds. Precedence: `JWT_EXPIRES_IN` env var\n * (authoritative ceiling) → `settings.security.jwtExpiresIn` DB value\n * (admin-configurable) → 30-day default.\n *\n * The env var wins so operators can cap runtime overrides — admins can adjust\n * the TTL from /admin/settings/security, but an env var, if set, always wins.\n * DB failures fall back to env/default so auth never breaks if the settings\n * table is unreachable.\n */\nexport async function getJwtExpirySecondsFromDb(\n db: { prepare: (query: string) => any } | null | undefined,\n env?: Record<string, any> | null\n): Promise<number> {\n const envParsed = parseDuration(env?.JWT_EXPIRES_IN)\n if (envParsed) return envParsed\n\n if (db) {\n try {\n const row = await db\n .prepare(\"SELECT data FROM documents WHERE type_id = 'site_settings' AND slug = 'security' AND tenant_id = 'default' AND is_current_draft = 1 AND deleted_at IS NULL\")\n .first() as { data: string } | null\n if (row?.data) {\n const data = JSON.parse(row.data)\n const parsed = parseDuration(data.jwtExpiresIn)\n if (parsed) return parsed\n }\n } catch (err) {\n console.warn('Failed to read jwtExpiresIn from settings, falling back to default:', err)\n }\n }\n return DEFAULT_JWT_EXPIRES_IN_SECONDS\n}\n\n/**\n * Resolve the refresh grace window (seconds) for `/auth/refresh`. Precedence:\n * `JWT_REFRESH_GRACE_SECONDS` env var → `settings.security.jwtRefreshGraceSeconds`\n * DB value → 7-day default.\n */\nexport async function getJwtRefreshGraceSecondsFromDb(\n db: { prepare: (query: string) => any } | null | undefined,\n env?: Record<string, any> | null\n): Promise<number> {\n const DEFAULT_GRACE = 60 * 60 * 24 * 7\n const envParsed = parseDuration(env?.JWT_REFRESH_GRACE_SECONDS)\n if (envParsed) return envParsed\n\n if (db) {\n try {\n const row = await db\n .prepare(\"SELECT data FROM documents WHERE type_id = 'site_settings' AND slug = 'security' AND tenant_id = 'default' AND is_current_draft = 1 AND deleted_at IS NULL\")\n .first() as { data: string } | null\n if (row?.data) {\n const data = JSON.parse(row.data)\n const parsed = parseDuration(data.jwtRefreshGraceSeconds?.toString())\n if (parsed) return parsed\n }\n } catch (err) {\n console.warn('Failed to read jwtRefreshGraceSeconds from settings:', err)\n }\n }\n return DEFAULT_GRACE\n}\n\n/**\n * Decode a JWT payload without verifying the signature. Returns null on any\n * parsing failure. Callers MUST independently verify the signature before\n * trusting this value — used from the grace-window refresh path where the\n * signature is verified explicitly via `verifyHs256Signature`.\n */\nfunction decodeJwtPayload(token: string): JWTPayload | null {\n try {\n const parts = token.split('.')\n if (parts.length !== 3) return null\n const b64 = parts[1]!.replace(/-/g, '+').replace(/_/g, '/')\n const padded = b64 + '='.repeat((4 - (b64.length % 4)) % 4)\n const json = atob(padded)\n const obj = JSON.parse(json)\n if (!obj || typeof obj.exp !== 'number') return null\n return obj as JWTPayload\n } catch {\n return null\n }\n}\n\nfunction base64UrlToBytes(b64url: string): Uint8Array {\n const b64 = b64url.replace(/-/g, '+').replace(/_/g, '/')\n const padded = b64 + '='.repeat((4 - (b64.length % 4)) % 4)\n const bin = atob(padded)\n const bytes = new Uint8Array(bin.length)\n for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i)\n return bytes\n}\n\n/**\n * Verify a JWT's HS256 signature using Web Crypto, independent of hono/jwt.\n * Returns true iff the signature matches the header.payload portion.\n */\nasync function verifyHs256Signature(token: string, secret: string): Promise<boolean> {\n try {\n const parts = token.split('.')\n if (parts.length !== 3) return false\n const encoder = new TextEncoder()\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['verify']\n )\n const signature = base64UrlToBytes(parts[2]!)\n const message = encoder.encode(`${parts[0]}.${parts[1]}`)\n return await crypto.subtle.verify('HMAC', key, signature, message)\n } catch {\n return false\n }\n}\n\nexport class AuthManager {\n static async generateToken(\n userId: string,\n email: string,\n role: string,\n secret?: string,\n expiresInSeconds?: number\n ): Promise<string> {\n const ttl = expiresInSeconds && expiresInSeconds > 0\n ? Math.floor(expiresInSeconds)\n : DEFAULT_JWT_EXPIRES_IN_SECONDS\n const now = Math.floor(Date.now() / 1000)\n const payload: JWTPayload = {\n userId,\n email,\n role,\n exp: now + ttl,\n iat: now\n }\n\n return await sign(payload, secret || JWT_SECRET_FALLBACK, 'HS256')\n }\n\n /**\n * Verify a token's signature and expiration.\n *\n * IMPORTANT: pass the `JWT_SECRET` binding (e.g. `c.env.JWT_SECRET`) as the\n * `secret` argument. If omitted, this falls back to a development-only\n * placeholder secret — tokens signed with the real `JWT_SECRET` will then\n * silently fail verification. From inside a Hono handler prefer\n * `AuthManager.verifyAuthRequest(c)`, which handles header/cookie extraction\n * and pulls the secret from `c.env` for you.\n *\n * If `graceSeconds` > 0, tokens whose `exp` is within the grace window\n * (i.e. expired by no more than `graceSeconds`) are still returned. This\n * supports a sliding-session refresh endpoint that accepts recently-expired\n * tokens. Signature failures always return null.\n */\n static async verifyToken(\n token: string,\n secret?: string,\n graceSeconds: number = 0\n ): Promise<JWTPayload | null> {\n const effectiveSecret = secret || JWT_SECRET_FALLBACK\n try {\n let payload: JWTPayload | null = null\n try {\n payload = await verify(token, effectiveSecret, 'HS256') as JWTPayload\n } catch (verifyError: any) {\n // hono/jwt checks `exp` before signature, so a bad-signature token\n // that happens to be expired will throw JwtTokenExpired here. For\n // the grace window, we still require a valid HS256 signature before\n // accepting the payload.\n const name = verifyError?.name || ''\n const message = String(verifyError?.message || '')\n const isExpired = name === 'JwtTokenExpired' || message.includes('expired')\n if (!isExpired || graceSeconds <= 0) {\n throw verifyError\n }\n const signatureValid = await verifyHs256Signature(token, effectiveSecret)\n if (!signatureValid) return null\n const decoded = decodeJwtPayload(token)\n if (!decoded) return null\n payload = decoded\n }\n\n if (!payload) return null\n\n const now = Math.floor(Date.now() / 1000)\n if (payload.exp < now - Math.max(0, Math.floor(graceSeconds))) {\n return null\n }\n\n return payload\n } catch (error) {\n console.error('Token verification failed:', error)\n return null\n }\n }\n\n /**\n * Verify the JWT on an incoming Hono request using the `JWT_SECRET`\n * binding from `c.env`. Reads the token from the `Authorization: Bearer …`\n * header first, then falls back to the `auth_token` cookie. Returns the\n * decoded payload, or null when the token is missing, malformed, expired,\n * or signed with a different secret.\n *\n * Use this from custom Hono routes mounted alongside SonicJS — it\n * resolves the secret the same way `requireAuth()` does, without forcing\n * the caller to plumb it through manually.\n */\n static async verifyAuthRequest(c: Context): Promise<JWTPayload | null> {\n let token = c.req.header('Authorization')?.replace('Bearer ', '')\n if (!token) {\n token = getCookie(c, 'auth_token')\n }\n if (!token) return null\n const secret = (c.env as any)?.JWT_SECRET\n return await AuthManager.verifyToken(token, secret)\n }\n\n static async hashPassword(password: string): Promise<string> {\n const iterations = 100000\n const salt = new Uint8Array(16)\n crypto.getRandomValues(salt)\n\n const encoder = new TextEncoder()\n const keyMaterial = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(password),\n 'PBKDF2',\n false,\n ['deriveBits']\n )\n\n const hashBuffer = await crypto.subtle.deriveBits(\n {\n name: 'PBKDF2',\n salt,\n iterations,\n hash: 'SHA-256'\n },\n keyMaterial,\n 256\n )\n\n const saltHex = Array.from(salt).map(b => b.toString(16).padStart(2, '0')).join('')\n const hashHex = Array.from(new Uint8Array(hashBuffer)).map(b => b.toString(16).padStart(2, '0')).join('')\n\n return `pbkdf2:${iterations}:${saltHex}:${hashHex}`\n }\n\n static async hashPasswordLegacy(password: string): Promise<string> {\n const encoder = new TextEncoder()\n const data = encoder.encode(password + 'salt-change-in-production')\n const hashBuffer = await crypto.subtle.digest('SHA-256', data)\n const hashArray = Array.from(new Uint8Array(hashBuffer))\n return hashArray.map(b => b.toString(16).padStart(2, '0')).join('')\n }\n\n static async verifyPassword(password: string, storedHash: string): Promise<boolean> {\n if (storedHash.startsWith('pbkdf2:')) {\n // PBKDF2 format: pbkdf2:<iterations>:<salt_hex>:<hash_hex>\n const parts = storedHash.split(':')\n if (parts.length !== 4) return false\n\n const iterationsStr = parts[1]!\n const saltHex = parts[2]!\n const expectedHashHex = parts[3]!\n const iterations = parseInt(iterationsStr, 10)\n\n const saltBytes = saltHex.match(/.{2}/g)\n if (!saltBytes) return false\n const salt = new Uint8Array(saltBytes.map(byte => parseInt(byte, 16)))\n\n const encoder = new TextEncoder()\n const keyMaterial = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(password),\n 'PBKDF2',\n false,\n ['deriveBits']\n )\n\n const hashBuffer = await crypto.subtle.deriveBits(\n {\n name: 'PBKDF2',\n salt,\n iterations,\n hash: 'SHA-256'\n },\n keyMaterial,\n 256\n )\n\n const actualHashHex = Array.from(new Uint8Array(hashBuffer)).map(b => b.toString(16).padStart(2, '0')).join('')\n\n // Constant-time comparison\n if (actualHashHex.length !== expectedHashHex.length) return false\n let result = 0\n for (let i = 0; i < actualHashHex.length; i++) {\n result |= actualHashHex.charCodeAt(i) ^ expectedHashHex.charCodeAt(i)\n }\n return result === 0\n }\n\n // Legacy SHA-256 format (no colons in hash)\n const legacyHash = await this.hashPasswordLegacy(password)\n // Constant-time comparison for legacy too\n if (legacyHash.length !== storedHash.length) return false\n let result = 0\n for (let i = 0; i < legacyHash.length; i++) {\n result |= legacyHash.charCodeAt(i) ^ storedHash.charCodeAt(i)\n }\n return result === 0\n }\n\n static isLegacyHash(storedHash: string): boolean {\n return !storedHash.startsWith('pbkdf2:')\n }\n\n /**\n * Set authentication cookie - useful for plugins implementing alternative auth methods\n * @param c - Hono context\n * @param token - JWT token to set in cookie\n * @param options - Optional cookie configuration\n */\n static setAuthCookie(c: Context, token: string, options?: {\n maxAge?: number\n secure?: boolean\n httpOnly?: boolean\n sameSite?: 'Strict' | 'Lax' | 'None'\n }): void {\n setCookie(c, 'auth_token', token, {\n httpOnly: options?.httpOnly ?? true,\n secure: options?.secure ?? true,\n sameSite: options?.sameSite ?? 'Strict',\n maxAge: options?.maxAge ?? getJwtExpirySeconds((c as any)?.env)\n })\n }\n}\n\n// Middleware to require authentication.\n// The Better Auth session middleware (app.ts) has already populated c.get('user')\n// from the session cookie; here we just enforce its presence.\nexport const requireAuth = () => {\n return async (c: Context, next: Next) => {\n const user = c.get('user') as JWTPayload | undefined\n\n if (!user) {\n const acceptHeader = c.req.header('Accept') || ''\n if (acceptHeader.includes('text/html')) {\n return c.redirect('/auth/login?error=Please login to access the admin area')\n }\n return c.json({ error: 'Authentication required' }, 401)\n }\n\n return await next()\n }\n}\n\n// Middleware to require specific role. Must run after requireAuth / the session\n// middleware so c.get('user') is set.\nexport const requireRole = (requiredRole: string | string[]) => {\n return async (c: Context, next: Next) => {\n const user = c.get('user') as JWTPayload | undefined\n\n if (!user) {\n const acceptHeader = c.req.header('Accept') || ''\n if (acceptHeader.includes('text/html')) {\n return c.redirect('/auth/login?error=Please login to access the admin area')\n }\n return c.json({ error: 'Authentication required' }, 401)\n }\n\n const roles = Array.isArray(requiredRole) ? requiredRole : [requiredRole]\n\n if (!roles.includes(user.role)) {\n const acceptHeader = c.req.header('Accept') || ''\n if (acceptHeader.includes('text/html')) {\n return c.redirect('/auth/login?error=You do not have permission to access this area')\n }\n return c.json({ error: 'Insufficient permissions' }, 403)\n }\n\n return await next()\n }\n}\n\n// Middleware to require a live RBAC grant for the signed-in user. This is the\n// dynamic replacement for legacy `users.role` checks on admin routes.\nexport const requireRbac = (resource: string, verb: string) => {\n return async (c: Context, next: Next) => {\n const user = c.get('user') as JWTPayload | undefined\n\n if (!user) {\n const acceptHeader = c.req.header('Accept') || ''\n if (acceptHeader.includes('text/html')) {\n return c.redirect('/auth/login?error=Please login to access the admin area')\n }\n return c.json({ error: 'Authentication required' }, 401)\n }\n\n // Fast path: use pre-computed perms from the /admin/* middleware (avoids DB hit).\n const cachedPerms = (c as any).get('rbacPerms') as string[] | undefined\n let allowed: boolean\n if (cachedPerms !== undefined) {\n allowed = cachedPerms.includes(`${resource}:${verb}`)\n } else {\n allowed = await new RbacService((c.env as any).DB).can(user.userId, resource, verb)\n }\n\n if (!allowed) {\n const acceptHeader = c.req.header('Accept') || ''\n if (acceptHeader.includes('text/html')) {\n return c.redirect('/auth/login?error=You do not have permission to access this area')\n }\n return c.json({ error: 'Insufficient permissions' }, 403)\n }\n\n return await next()\n }\n}\n\n// Optional auth middleware. The session middleware already sets c.get('user')\n// when a valid session exists, so this is a no-op kept for API compatibility.\nexport const optionalAuth = () => {\n return async (_c: Context, next: Next) => {\n return await next()\n }\n}\n","import { MiddlewareHandler } from 'hono'\nimport { metricsTracker } from '../utils/metrics'\n\n/**\n * Middleware to track all HTTP requests for real-time analytics\n * Excludes the metrics endpoint itself to avoid inflating the count\n */\nexport const metricsMiddleware = (): MiddlewareHandler => {\n return async (c, next) => {\n const path = new URL(c.req.url).pathname\n\n // Don't track the metrics endpoint itself to avoid self-inflating counts\n if (path !== '/admin/dashboard/api/metrics') {\n metricsTracker.recordRequest()\n }\n\n // Continue with the request\n await next()\n }\n}\n","/**\n * CSRF Protection Middleware — Signed Double-Submit Cookie\n *\n * Stateless CSRF protection for Cloudflare Workers (no session store needed).\n * Token format: `<nonce>.<hmac>` where HMAC-SHA256 is keyed with JWT_SECRET.\n *\n * Flow:\n * GET — ensureCsrfCookie(): reuse existing valid cookie or set a new one\n * POST/PUT/DELETE/PATCH — validate X-CSRF-Token header === csrf_token cookie, HMAC valid\n *\n * Exempt:\n * - Safe methods (GET, HEAD, OPTIONS)\n * - Auth routes that create sessions (/auth/login*, /auth/register*, etc.)\n * - Public form submissions (/forms/*, /api/forms/*) — NOT /admin/forms/*\n * - Requests with no auth_token cookie (Bearer-only or API-key-only)\n */\n\nimport type { Context, Next } from 'hono'\nimport { getCookie, setCookie } from 'hono/cookie'\n\n// Fallback secret — mirrors auth.ts behavior for local dev without wrangler secret\nconst JWT_SECRET_FALLBACK = 'your-super-secret-jwt-key-change-in-production'\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/** Convert ArrayBuffer to URL-safe base64 (no padding). */\nexport function arrayBufferToBase64Url(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer)\n let binary = ''\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]!)\n }\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\n/** Import a string key for HMAC-SHA256. */\nasync function getHmacKey(secret: string): Promise<CryptoKey> {\n const encoder = new TextEncoder()\n return crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify']\n )\n}\n\n// ============================================================================\n// Token Generation & Validation\n// ============================================================================\n\n/**\n * Generate a signed CSRF token: `<nonce>.<hmac_signature>`\n * - nonce = 32 random bytes, base64url-encoded\n * - signature = HMAC-SHA256(nonce, secret), base64url-encoded\n */\nexport async function generateCsrfToken(secret: string): Promise<string> {\n const nonceBytes = new Uint8Array(32)\n crypto.getRandomValues(nonceBytes)\n const nonce = arrayBufferToBase64Url(nonceBytes.buffer)\n\n const key = await getHmacKey(secret)\n const encoder = new TextEncoder()\n const signatureBuffer = await crypto.subtle.sign('HMAC', key, encoder.encode(nonce))\n const signature = arrayBufferToBase64Url(signatureBuffer)\n\n return `${nonce}.${signature}`\n}\n\n/**\n * Validate a signed CSRF token.\n *\n * Checks that the token has the correct `<nonce>.<signature>` format and that\n * the HMAC signature is valid for the given secret. Uses crypto.subtle.verify\n * which provides constant-time comparison.\n *\n * NOTE: No expiry check here — by design. The security property of signed\n * double-submit comes from the unpredictability of the nonce + the\n * secret-bound HMAC, not from time-bounding. The cookie's maxAge (86400s)\n * handles expiry at the browser level.\n */\nexport async function validateCsrfToken(token: string, secret: string): Promise<boolean> {\n if (!token || typeof token !== 'string') return false\n\n const dotIndex = token.indexOf('.')\n if (dotIndex === -1) return false\n\n const nonce = token.substring(0, dotIndex)\n const signature = token.substring(dotIndex + 1)\n\n if (!nonce || !signature) return false\n\n try {\n const key = await getHmacKey(secret)\n const encoder = new TextEncoder()\n\n // Decode the signature from base64url\n const sigPadded = signature.replace(/-/g, '+').replace(/_/g, '/')\n const sigBinary = atob(sigPadded)\n const sigBytes = new Uint8Array(sigBinary.length)\n for (let i = 0; i < sigBinary.length; i++) {\n sigBytes[i] = sigBinary.charCodeAt(i)\n }\n\n // crypto.subtle.verify is constant-time\n return await crypto.subtle.verify('HMAC', key, sigBytes.buffer, encoder.encode(nonce))\n } catch {\n return false\n }\n}\n\n// ============================================================================\n// Default Exempt Paths\n// ============================================================================\n\nconst DEFAULT_EXEMPT_PATHS = [\n '/auth/login',\n '/auth/register',\n '/auth/seed-admin',\n '/test-seed-defaults',\n '/test-cleanup',\n '/auth/accept-invitation',\n '/auth/reset-password',\n '/auth/request-password-reset',\n '/auth/otp',\n '/auth/magic-link',\n '/auth/sign-out',\n '/auth/sign-in',\n '/auth/sign-up',\n '/auth/get-session',\n '/auth/verify',\n '/api/stripe/webhook',\n '/api/events',\n]\n\n/**\n * Check whether a request path is exempt from CSRF validation.\n * - Exact match or startsWith for auth routes (e.g. /auth/login/form)\n * - /forms/* and /api/forms/* are exempt (public submissions)\n * - /api/search* is exempt (read-only POST for complex query params)\n * - /admin/forms/* is NOT exempt\n */\nfunction isExemptPath(path: string, extraExemptPaths: string[] = []): boolean {\n // Public form routes — NOT /admin/forms/*\n if (path.startsWith('/forms/') || path.startsWith('/api/forms/') || path === '/forms' || path === '/api/forms') {\n return true\n }\n\n // Search API — read-only POST (includes /api/search/click, /api/search/facet-click)\n if (path.startsWith('/api/search')) {\n return true\n }\n\n const allExempt = [...DEFAULT_EXEMPT_PATHS, ...extraExemptPaths]\n for (const exempt of allExempt) {\n if (path === exempt || path.startsWith(exempt + '/')) {\n return true\n }\n }\n\n return false\n}\n\n// ============================================================================\n// Middleware\n// ============================================================================\n\nexport interface CsrfOptions {\n /** Additional paths to exempt from CSRF validation. */\n exemptPaths?: string[]\n}\n\n/**\n * CSRF protection middleware (Signed Double-Submit Cookie).\n *\n * - GET/HEAD/OPTIONS: ensure a valid csrf_token cookie exists\n * - POST/PUT/DELETE/PATCH: validate X-CSRF-Token header matches cookie, HMAC valid\n * - Exempt: auth routes, public /forms/*, Bearer-only, API-key-only\n */\nexport function csrfProtection(options: CsrfOptions = {}) {\n return async (c: Context, next: Next): Promise<Response | void> => {\n const method = c.req.method.toUpperCase()\n const path = new URL(c.req.url).pathname\n const secret = c.env?.JWT_SECRET || JWT_SECRET_FALLBACK\n\n // Warn if using fallback secret in production\n if (c.env?.ENVIRONMENT === 'production' && !c.env?.JWT_SECRET) {\n console.warn(\n '[CSRF] WARNING: JWT_SECRET is not set in production. ' +\n 'CSRF tokens are signed with the fallback key, which is insecure.'\n )\n }\n\n // Safe methods — just ensure cookie, then pass through\n if (method === 'GET' || method === 'HEAD' || method === 'OPTIONS') {\n await ensureCsrfCookie(c, secret)\n await next()\n return\n }\n\n // Exempt paths — pass through without validation\n if (isExemptPath(path, options.exemptPaths)) {\n await next()\n return\n }\n\n // Bearer-only or API-key-only requests (no auth_token cookie) — exempt\n const authCookie = getCookie(c, 'auth_token')\n if (!authCookie) {\n await next()\n return\n }\n\n // Requests with an Authorization header use token-based auth — the cookie\n // is incidental and CSRF protection is unnecessary (the attacker cannot\n // forge the Authorization header from a cross-origin page).\n const authHeader = c.req.header('Authorization')\n if (authHeader) {\n await next()\n return\n }\n\n // State-changing request with cookie auth — validate CSRF\n const cookieToken = getCookie(c, 'csrf_token')\n let headerToken = c.req.header('X-CSRF-Token')\n\n // Fallback: check _csrf field in form-encoded body (regular HTML form submissions)\n if (!headerToken) {\n const contentType = c.req.header('Content-Type') || ''\n if (contentType.includes('application/x-www-form-urlencoded') || contentType.includes('multipart/form-data')) {\n try {\n const body = await c.req.parseBody()\n headerToken = body['_csrf'] as string | undefined\n } catch {\n // Body not parseable — leave headerToken undefined\n }\n }\n }\n\n if (!cookieToken || !headerToken) {\n return csrfError(c, 'CSRF token missing')\n }\n\n if (cookieToken !== headerToken) {\n return csrfError(c, 'CSRF token mismatch')\n }\n\n const isValid = await validateCsrfToken(cookieToken, secret)\n if (!isValid) {\n return csrfError(c, 'CSRF token invalid')\n }\n\n await next()\n }\n}\n\n/**\n * Ensure a valid CSRF cookie exists. Check-then-reuse: if the existing cookie\n * has a valid HMAC signature, reuse it (no new Set-Cookie header). Only\n * generate a fresh token when the cookie is missing or has an invalid signature.\n */\nasync function ensureCsrfCookie(c: Context, secret: string): Promise<void> {\n const existing = getCookie(c, 'csrf_token')\n\n if (existing) {\n const isValid = await validateCsrfToken(existing, secret)\n if (isValid) {\n // Reuse existing valid token — no Set-Cookie needed\n c.set('csrfToken', existing)\n return\n }\n }\n\n // Generate fresh token\n const token = await generateCsrfToken(secret)\n c.set('csrfToken', token)\n\n const isDev = c.env?.ENVIRONMENT === 'development' || !c.env?.ENVIRONMENT\n setCookie(c, 'csrf_token', token, {\n httpOnly: false, // JS must read this cookie\n secure: !isDev,\n sameSite: 'Strict',\n path: '/',\n maxAge: 86400, // 24 hours — browser-side expiry\n })\n}\n\n/** Return a 403 CSRF error — HTML for browser requests, JSON for API. */\nfunction csrfError(c: Context, message: string): Response {\n const accept = c.req.header('Accept') || ''\n if (accept.includes('text/html')) {\n return c.html(\n `<!DOCTYPE html><html><head><title>403 Forbidden</title></head>` +\n `<body><h1>403 Forbidden</h1><p>${message}</p></body></html>`,\n 403\n )\n }\n return c.json({ error: message, status: 403 }, 403)\n}\n","import { Context, Next } from 'hono'\n\ninterface RateLimitOptions {\n max: number\n windowMs: number\n keyPrefix: string\n}\n\ninterface RateLimitEntry {\n count: number\n resetAt: number\n}\n\n/**\n * KV-based sliding window rate limiter middleware.\n * Gracefully skips if CACHE_KV binding is not available.\n */\nexport function rateLimit(options: RateLimitOptions) {\n const { max, windowMs, keyPrefix } = options\n\n return async (c: Context, next: Next) => {\n const kv = (c.env as any)?.CACHE_KV\n if (!kv) {\n // No KV binding available — skip rate limiting\n return await next()\n }\n\n const ip = c.req.header('cf-connecting-ip') || c.req.header('x-forwarded-for') || 'unknown'\n const key = `ratelimit:${keyPrefix}:${ip}`\n\n try {\n const now = Date.now()\n const stored = await kv.get(key, 'json') as RateLimitEntry | null\n\n let entry: RateLimitEntry\n if (stored && stored.resetAt > now) {\n entry = stored\n } else {\n entry = { count: 0, resetAt: now + windowMs }\n }\n\n entry.count++\n\n // Calculate TTL in seconds (KV expiration)\n const ttlSeconds = Math.ceil((entry.resetAt - now) / 1000)\n\n if (entry.count > max) {\n // Store the updated count even when rejecting\n await kv.put(key, JSON.stringify(entry), { expirationTtl: Math.max(ttlSeconds, 60) })\n\n const retryAfter = Math.ceil((entry.resetAt - now) / 1000)\n c.header('Retry-After', String(retryAfter))\n c.header('X-RateLimit-Limit', String(max))\n c.header('X-RateLimit-Remaining', '0')\n c.header('X-RateLimit-Reset', String(Math.ceil(entry.resetAt / 1000)))\n return c.json({ error: 'Too many requests. Please try again later.' }, 429)\n }\n\n await kv.put(key, JSON.stringify(entry), { expirationTtl: Math.max(ttlSeconds, 60) })\n\n c.header('X-RateLimit-Limit', String(max))\n c.header('X-RateLimit-Remaining', String(max - entry.count))\n c.header('X-RateLimit-Reset', String(Math.ceil(entry.resetAt / 1000)))\n\n return await next()\n } catch (error) {\n // Rate limiting should never break the app\n console.error('Rate limiter error (non-fatal):', error)\n return await next()\n }\n }\n}\n","import { Context, Next } from 'hono'\n\n/**\n * Security headers middleware.\n * Sets standard security headers on every response.\n * Skips HSTS in development to avoid local dev issues.\n */\nexport const securityHeadersMiddleware = () => {\n return async (c: Context, next: Next) => {\n await next()\n\n c.header('X-Content-Type-Options', 'nosniff')\n c.header('X-Frame-Options', 'SAMEORIGIN')\n c.header('Referrer-Policy', 'strict-origin-when-cross-origin')\n c.header('Permissions-Policy', 'camera=(), microphone=(), geolocation=()')\n\n // Only set HSTS in non-development environments\n const environment = (c.env as any)?.ENVIRONMENT\n if (environment !== 'development') {\n c.header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains')\n }\n }\n}\n","/**\n * Plugin Middleware\n *\n * Provides middleware functions for checking plugin status and enforcing plugin requirements\n */\n\nimport type { D1Database } from '@cloudflare/workers-types'\n\n/**\n * Check if a plugin is active\n * @param db - The D1 database instance\n * @param pluginId - The plugin ID to check\n * @returns Promise<boolean> - True if the plugin is active, false otherwise\n */\nexport async function isPluginActive(db: D1Database, pluginId: string): Promise<boolean> {\n try {\n // documents table is the authoritative source — PluginService writes here on install/activate.\n // Sidebar nav uses the same query pattern; plugins table is unreliable (may not exist).\n const docResult = await db\n .prepare(\n `SELECT json_extract(data, '$.status') as status FROM documents\n WHERE slug = ? AND type_id = 'plugin' AND tenant_id = 'default'\n AND is_current_draft = 1 AND deleted_at IS NULL`\n )\n .bind(pluginId)\n .first()\n return (docResult as any)?.status === 'active'\n } catch (error) {\n console.error(`[isPluginActive] Error checking plugin status for ${pluginId}:`, error)\n return false\n }\n}\n\n/**\n * Middleware to require a plugin to be active\n * Throws an error if the plugin is not active\n * @param db - The D1 database instance\n * @param pluginId - The plugin ID to check\n * @throws Error if plugin is not active\n */\nexport async function requireActivePlugin(db: D1Database, pluginId: string): Promise<void> {\n const isActive = await isPluginActive(db, pluginId)\n if (!isActive) {\n throw new Error(`Plugin '${pluginId}' is required but is not active`)\n }\n}\n\n/**\n * Middleware to require multiple plugins to be active\n * Throws an error if any plugin is not active\n * @param db - The D1 database instance\n * @param pluginIds - Array of plugin IDs to check\n * @throws Error if any plugin is not active\n */\nexport async function requireActivePlugins(db: D1Database, pluginIds: string[]): Promise<void> {\n for (const pluginId of pluginIds) {\n await requireActivePlugin(db, pluginId)\n }\n}\n\n/**\n * Get all active plugins\n * @param db - The D1 database instance\n * @returns Promise<any[]> - Array of active plugin records\n */\nexport async function getActivePlugins(db: D1Database): Promise<any[]> {\n try {\n const { results } = await db\n .prepare(\n `SELECT slug as id, json_extract(data, '$.status') as status, data FROM documents\n WHERE type_id = 'plugin' AND tenant_id = 'default'\n AND q_plugin_status = 'active' AND is_current_draft = 1 AND deleted_at IS NULL`\n )\n .all()\n\n return results || []\n } catch (error) {\n console.error('[getActivePlugins] Error fetching active plugins:', error)\n return []\n }\n}\n","/**\n * Middleware Module Exports\n *\n * Request processing middleware for SonicJS\n *\n * Note: Most middleware is currently in the monolith and will be migrated later.\n * For now, we only export the bootstrap middleware which is used for system initialization.\n */\n\n// Bootstrap middleware\nexport { bootstrapMiddleware, verifySecurityConfig } from './bootstrap'\n\n// Auth middleware\nexport {\n AuthManager,\n requireAuth,\n requireRole,\n optionalAuth,\n getJwtExpirySeconds,\n getJwtExpirySecondsFromDb,\n getJwtRefreshGraceSecondsFromDb,\n} from './auth'\n\n// Metrics middleware\nexport { metricsMiddleware } from './metrics'\n\n// CSRF protection middleware\nexport { csrfProtection, generateCsrfToken, validateCsrfToken } from './csrf'\n\n// Rate limiting middleware\nexport { rateLimit } from './rate-limit'\n\n// Re-export types and functions that are referenced but implemented in monolith\n// These are placeholder exports to maintain API compatibility\nexport type Permission = string\nexport type UserPermissions = {\n userId: string\n permissions: Permission[]\n}\n\n// Middleware stubs - these return pass-through middleware that call next()\nexport const loggingMiddleware: any = () => async (_c: any, next: any) => await next()\nexport const detailedLoggingMiddleware: any = () => async (_c: any, next: any) => await next()\nexport const securityLoggingMiddleware: any = () => async (_c: any, next: any) => await next()\nexport const performanceLoggingMiddleware: any = () => async (_c: any, next: any) => await next()\nexport const cacheHeaders: any = () => async (_c: any, next: any) => await next()\nexport const compressionMiddleware: any = async (_c: any, next: any) => await next()\nexport { securityHeadersMiddleware as securityHeaders } from './security-headers'\n\n// Other stubs\nexport const PermissionManager: any = {}\nexport const requirePermission: any = () => async (_c: any, next: any) => await next()\nexport const requireAnyPermission: any = () => async (_c: any, next: any) => await next()\nexport const logActivity: any = () => {}\nexport { requireActivePlugin, requireActivePlugins, getActivePlugins, isPluginActive } from './plugin-middleware'\n"]}
|
|
@@ -7,7 +7,7 @@ var bundledMigrations = [
|
|
|
7
7
|
name: "Core",
|
|
8
8
|
filename: "0001_core.sql",
|
|
9
9
|
description: "Migration 0001: Core",
|
|
10
|
-
sql: "-- Migration 0001: Auth tables\n-- auth_user, auth_session, auth_account, auth_verification + BA plugin tables + RBAC + auth support.\n-- Only auth_* prefixed tables live here. All content lives in document_* tables (0002_documents.sql).\n\n-- \u2500\u2500 auth_user \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n-- BA user model + SonicJS domain columns as BA additionalFields.\nCREATE TABLE IF NOT EXISTS auth_user (\n id TEXT PRIMARY KEY,\n name TEXT,\n email TEXT NOT NULL UNIQUE,\n email_verified INTEGER NOT NULL DEFAULT 0,\n image TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n -- SonicJS additionalFields\n first_name TEXT NOT NULL,\n last_name TEXT NOT NULL,\n role TEXT NOT NULL DEFAULT 'viewer',\n -- Platform super-admin: bypasses the multi-tenant membership gate, uses global roles in every\n -- tenant. Opt-in (default 0); intentionally NOT derived from the 'admin' role.\n is_super_admin INTEGER NOT NULL DEFAULT 0,\n avatar TEXT,\n password_hash TEXT,\n is_active INTEGER NOT NULL DEFAULT 1,\n last_login_at INTEGER,\n phone TEXT,\n bio TEXT,\n timezone TEXT DEFAULT 'UTC',\n language TEXT DEFAULT 'en',\n email_notifications INTEGER DEFAULT 1,\n theme TEXT DEFAULT 'dark',\n invitation_token TEXT,\n invited_by TEXT,\n invited_at INTEGER,\n accepted_invitation_at INTEGER,\n failed_login_count INTEGER NOT NULL DEFAULT 0,\n locked_until INTEGER\n);\n\nCREATE INDEX IF NOT EXISTS idx_auth_user_email ON auth_user(email);\nCREATE INDEX IF NOT EXISTS idx_auth_user_role ON auth_user(role);\nCREATE INDEX IF NOT EXISTS idx_auth_user_invitation_token ON auth_user(invitation_token);\nCREATE INDEX IF NOT EXISTS idx_auth_user_locked_until ON auth_user(locked_until) WHERE locked_until IS NOT NULL;\n\n-- \u2500\u2500 auth_session \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nCREATE TABLE IF NOT EXISTS auth_session (\n id TEXT PRIMARY KEY,\n user_id TEXT NOT NULL REFERENCES auth_user(id) ON DELETE CASCADE,\n token TEXT NOT NULL UNIQUE,\n expires_at INTEGER NOT NULL,\n ip_address TEXT,\n user_agent TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_auth_session_user_id ON auth_session(user_id);\nCREATE INDEX IF NOT EXISTS idx_auth_session_token ON auth_session(token);\nCREATE INDEX IF NOT EXISTS idx_auth_session_expires_at ON auth_session(expires_at);\n\n-- \u2500\u2500 auth_account \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nCREATE TABLE IF NOT EXISTS auth_account (\n id TEXT PRIMARY KEY,\n user_id TEXT NOT NULL REFERENCES auth_user(id) ON DELETE CASCADE,\n account_id TEXT NOT NULL,\n provider_id TEXT NOT NULL,\n access_token TEXT,\n refresh_token TEXT,\n access_token_expires_at INTEGER,\n refresh_token_expires_at INTEGER,\n scope TEXT,\n id_token TEXT,\n password TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_auth_account_user_id ON auth_account(user_id);\nCREATE INDEX IF NOT EXISTS idx_auth_account_provider ON auth_account(provider_id, account_id);\n\n-- \u2500\u2500 auth_verification \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n-- Covers email verification, password reset, magic-link tokens, OTP codes.\nCREATE TABLE IF NOT EXISTS auth_verification (\n id TEXT PRIMARY KEY,\n identifier TEXT NOT NULL,\n value TEXT NOT NULL,\n expires_at INTEGER NOT NULL,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_auth_verification_identifier ON auth_verification(identifier);\n\n-- \u2500\u2500 BA plugin tables \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nCREATE TABLE IF NOT EXISTS auth_two_factor (\n id TEXT PRIMARY KEY,\n secret TEXT NOT NULL,\n backup_codes TEXT NOT NULL,\n user_id TEXT NOT NULL REFERENCES auth_user(id) ON DELETE CASCADE,\n verified INTEGER NOT NULL DEFAULT 1,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_auth_two_factor_user_id ON auth_two_factor(user_id);\n\nCREATE TABLE IF NOT EXISTS auth_tenant (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n slug TEXT NOT NULL UNIQUE,\n logo TEXT,\n metadata TEXT,\n -- SonicJS tenant-resolution fields (BA organization additionalFields):\n status TEXT NOT NULL DEFAULT 'active',\n domain TEXT,\n notes TEXT NOT NULL DEFAULT '',\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_auth_tenant_domain ON auth_tenant(domain);\n\nCREATE TABLE IF NOT EXISTS auth_tenant_member (\n id TEXT PRIMARY KEY,\n tenant_id TEXT NOT NULL REFERENCES auth_tenant(id) ON DELETE CASCADE,\n user_id TEXT NOT NULL REFERENCES auth_user(id) ON DELETE CASCADE,\n role TEXT NOT NULL DEFAULT 'member',\n email TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n UNIQUE(tenant_id, user_id)\n);\nCREATE INDEX IF NOT EXISTS idx_auth_tenant_member_tenant ON auth_tenant_member(tenant_id);\nCREATE INDEX IF NOT EXISTS idx_auth_tenant_member_user ON auth_tenant_member(user_id);\n\nCREATE TABLE IF NOT EXISTS auth_tenant_invitation (\n id TEXT PRIMARY KEY,\n tenant_id TEXT NOT NULL REFERENCES auth_tenant(id) ON DELETE CASCADE,\n email TEXT NOT NULL,\n role TEXT NOT NULL DEFAULT 'member',\n status TEXT NOT NULL DEFAULT 'pending',\n expires_at INTEGER NOT NULL,\n inviter_id TEXT REFERENCES auth_user(id) ON DELETE SET NULL,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_auth_tenant_invitation_tenant ON auth_tenant_invitation(tenant_id);\nCREATE INDEX IF NOT EXISTS idx_auth_tenant_invitation_email ON auth_tenant_invitation(email);\n\nCREATE TABLE IF NOT EXISTS auth_tenant_team (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n tenant_id TEXT NOT NULL REFERENCES auth_tenant(id) ON DELETE CASCADE,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n);\n\n-- \u2500\u2500 RBAC \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n-- RBAC roles, verbs, and user-role assignments are document-backed (is_auth doc\n-- types rbac_role / rbac_verb / rbac_user_roles \u2014 see services/rbac.ts). The\n-- system roles/verbs/grants are seeded at bootstrap by RbacService.ensureSystemRbacSeed().\n-- No auth_rbac_* tables.\n\n-- \u2500\u2500 Auth support tables \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nCREATE TABLE IF NOT EXISTS auth_password_history (\n id TEXT PRIMARY KEY,\n user_id TEXT NOT NULL REFERENCES auth_user(id) ON DELETE CASCADE,\n password_hash TEXT NOT NULL,\n created_at INTEGER NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_auth_password_history_user_id ON auth_password_history(user_id);\n\nCREATE TABLE IF NOT EXISTS auth_api_tokens (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n token TEXT NOT NULL UNIQUE,\n user_id TEXT NOT NULL REFERENCES auth_user(id),\n permissions TEXT NOT NULL,\n expires_at INTEGER,\n last_used_at INTEGER,\n created_at INTEGER NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_auth_api_tokens_user ON auth_api_tokens(user_id);\nCREATE INDEX IF NOT EXISTS idx_auth_api_tokens_token ON auth_api_tokens(token);\n\n-- User profiles moved to the document model: a `user_profile` document (is_auth type),\n-- one per user, addressed by slug = userId. See services/document-types-seed.ts and\n-- plugins/core-plugins/user-profiles/user-profile-document.ts. No auth_user_profiles table.\n"
|
|
10
|
+
sql: "-- Migration 0001: Auth tables\n-- auth_user, auth_session, auth_account, auth_verification + BA plugin tables + RBAC + auth support.\n-- Only auth_* prefixed tables live here. All content lives in document_* tables (0002_documents.sql).\n\n-- \u2500\u2500 auth_user \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n-- BA user model + SonicJS domain columns as BA additionalFields.\nCREATE TABLE IF NOT EXISTS auth_user (\n id TEXT PRIMARY KEY,\n name TEXT,\n email TEXT NOT NULL UNIQUE,\n email_verified INTEGER NOT NULL DEFAULT 0,\n image TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n -- SonicJS additionalFields\n first_name TEXT NOT NULL,\n last_name TEXT NOT NULL,\n role TEXT NOT NULL DEFAULT 'viewer',\n -- Platform super-admin: bypasses the multi-tenant membership gate, uses global roles in every\n -- tenant. Opt-in (default 0); intentionally NOT derived from the 'admin' role.\n is_super_admin INTEGER NOT NULL DEFAULT 0,\n avatar TEXT,\n password_hash TEXT,\n is_active INTEGER NOT NULL DEFAULT 1,\n last_login_at INTEGER,\n phone TEXT,\n bio TEXT,\n timezone TEXT DEFAULT 'UTC',\n language TEXT DEFAULT 'en',\n email_notifications INTEGER DEFAULT 1,\n theme TEXT DEFAULT 'dark',\n -- Password reset flow (routes/auth.ts)\n password_reset_token TEXT,\n password_reset_expires INTEGER,\n invitation_token TEXT,\n invited_by TEXT,\n invited_at INTEGER,\n accepted_invitation_at INTEGER,\n -- Account lockout: reset on success; set on threshold failures\n failed_login_count INTEGER NOT NULL DEFAULT 0,\n locked_until INTEGER,\n -- 2FA enrollment flag (twoFactor BA plugin)\n two_factor_enabled INTEGER NOT NULL DEFAULT 0\n);\n\nCREATE INDEX IF NOT EXISTS idx_auth_user_email ON auth_user(email);\nCREATE INDEX IF NOT EXISTS idx_auth_user_role ON auth_user(role);\nCREATE INDEX IF NOT EXISTS idx_auth_user_invitation_token ON auth_user(invitation_token);\nCREATE INDEX IF NOT EXISTS idx_auth_user_locked_until ON auth_user(locked_until) WHERE locked_until IS NOT NULL;\n\n-- \u2500\u2500 auth_session \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nCREATE TABLE IF NOT EXISTS auth_session (\n id TEXT PRIMARY KEY,\n user_id TEXT NOT NULL REFERENCES auth_user(id) ON DELETE CASCADE,\n token TEXT NOT NULL UNIQUE,\n expires_at INTEGER NOT NULL,\n ip_address TEXT,\n user_agent TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_auth_session_user_id ON auth_session(user_id);\nCREATE INDEX IF NOT EXISTS idx_auth_session_token ON auth_session(token);\nCREATE INDEX IF NOT EXISTS idx_auth_session_expires_at ON auth_session(expires_at);\n\n-- \u2500\u2500 auth_account \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nCREATE TABLE IF NOT EXISTS auth_account (\n id TEXT PRIMARY KEY,\n user_id TEXT NOT NULL REFERENCES auth_user(id) ON DELETE CASCADE,\n account_id TEXT NOT NULL,\n provider_id TEXT NOT NULL,\n access_token TEXT,\n refresh_token TEXT,\n access_token_expires_at INTEGER,\n refresh_token_expires_at INTEGER,\n scope TEXT,\n id_token TEXT,\n password TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_auth_account_user_id ON auth_account(user_id);\nCREATE INDEX IF NOT EXISTS idx_auth_account_provider ON auth_account(provider_id, account_id);\n\n-- \u2500\u2500 auth_verification \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n-- Covers email verification, password reset, magic-link tokens, OTP codes.\nCREATE TABLE IF NOT EXISTS auth_verification (\n id TEXT PRIMARY KEY,\n identifier TEXT NOT NULL,\n value TEXT NOT NULL,\n expires_at INTEGER NOT NULL,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_auth_verification_identifier ON auth_verification(identifier);\n\n-- \u2500\u2500 BA plugin tables \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nCREATE TABLE IF NOT EXISTS auth_two_factor (\n id TEXT PRIMARY KEY,\n secret TEXT NOT NULL,\n backup_codes TEXT NOT NULL,\n user_id TEXT NOT NULL REFERENCES auth_user(id) ON DELETE CASCADE,\n verified INTEGER NOT NULL DEFAULT 1,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_auth_two_factor_user_id ON auth_two_factor(user_id);\n\nCREATE TABLE IF NOT EXISTS auth_tenant (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n slug TEXT NOT NULL UNIQUE,\n logo TEXT,\n metadata TEXT,\n -- SonicJS tenant-resolution fields (BA organization additionalFields):\n status TEXT NOT NULL DEFAULT 'active',\n domain TEXT,\n notes TEXT NOT NULL DEFAULT '',\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_auth_tenant_domain ON auth_tenant(domain);\n\nCREATE TABLE IF NOT EXISTS auth_tenant_member (\n id TEXT PRIMARY KEY,\n tenant_id TEXT NOT NULL REFERENCES auth_tenant(id) ON DELETE CASCADE,\n user_id TEXT NOT NULL REFERENCES auth_user(id) ON DELETE CASCADE,\n role TEXT NOT NULL DEFAULT 'member',\n email TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n UNIQUE(tenant_id, user_id)\n);\nCREATE INDEX IF NOT EXISTS idx_auth_tenant_member_tenant ON auth_tenant_member(tenant_id);\nCREATE INDEX IF NOT EXISTS idx_auth_tenant_member_user ON auth_tenant_member(user_id);\n\nCREATE TABLE IF NOT EXISTS auth_tenant_invitation (\n id TEXT PRIMARY KEY,\n tenant_id TEXT NOT NULL REFERENCES auth_tenant(id) ON DELETE CASCADE,\n email TEXT NOT NULL,\n role TEXT NOT NULL DEFAULT 'member',\n status TEXT NOT NULL DEFAULT 'pending',\n expires_at INTEGER NOT NULL,\n inviter_id TEXT REFERENCES auth_user(id) ON DELETE SET NULL,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_auth_tenant_invitation_tenant ON auth_tenant_invitation(tenant_id);\nCREATE INDEX IF NOT EXISTS idx_auth_tenant_invitation_email ON auth_tenant_invitation(email);\n\nCREATE TABLE IF NOT EXISTS auth_tenant_team (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n tenant_id TEXT NOT NULL REFERENCES auth_tenant(id) ON DELETE CASCADE,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n);\n\n-- \u2500\u2500 RBAC \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n-- RBAC roles, verbs, and user-role assignments are document-backed (is_auth doc\n-- types rbac_role / rbac_verb / rbac_user_roles \u2014 see services/rbac.ts). The\n-- system roles/verbs/grants are seeded at bootstrap by RbacService.ensureSystemRbacSeed().\n-- No auth_rbac_* tables.\n\n-- \u2500\u2500 Auth support tables \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nCREATE TABLE IF NOT EXISTS auth_password_history (\n id TEXT PRIMARY KEY,\n user_id TEXT NOT NULL REFERENCES auth_user(id) ON DELETE CASCADE,\n password_hash TEXT NOT NULL,\n created_at INTEGER NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_auth_password_history_user_id ON auth_password_history(user_id);\n\nCREATE TABLE IF NOT EXISTS auth_api_tokens (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n token TEXT NOT NULL UNIQUE,\n user_id TEXT NOT NULL REFERENCES auth_user(id),\n permissions TEXT NOT NULL,\n expires_at INTEGER,\n last_used_at INTEGER,\n created_at INTEGER NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_auth_api_tokens_user ON auth_api_tokens(user_id);\nCREATE INDEX IF NOT EXISTS idx_auth_api_tokens_token ON auth_api_tokens(token);\n\n-- User profiles moved to the document model: a `user_profile` document (is_auth type),\n-- one per user, addressed by slug = userId. See services/document-types-seed.ts and\n-- plugins/core-plugins/user-profiles/user-profile-document.ts. No auth_user_profiles table.\n"
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
id: "0002",
|
|
@@ -273,5 +273,5 @@ var MigrationService = class {
|
|
|
273
273
|
|
|
274
274
|
exports.MigrationService = MigrationService;
|
|
275
275
|
exports.ensureScalarSchema = ensureScalarSchema;
|
|
276
|
-
//# sourceMappingURL=chunk-
|
|
277
|
-
//# sourceMappingURL=chunk-
|
|
276
|
+
//# sourceMappingURL=chunk-A2SN3RVL.cjs.map
|
|
277
|
+
//# sourceMappingURL=chunk-A2SN3RVL.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/db/migrations-bundle.ts","../src/services/document-scalar-schema.ts","../src/services/migrations.ts"],"names":[],"mappings":"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","file":"chunk-A2SN3RVL.cjs","sourcesContent":["/**\n * AUTO-GENERATED FILE - DO NOT EDIT\n * Generated by: scripts/generate-migrations.ts\n * Generated at: 2026-07-03T00:03:07.627Z\n *\n * This file contains all migration SQL bundled for use in Cloudflare Workers\n * where filesystem access is not available at runtime.\n */\n\nexport interface BundledMigration {\n id: string\n name: string\n filename: string\n description: string\n sql: string\n}\n\nexport const bundledMigrations: BundledMigration[] = [\n {\n id: '0001',\n name: 'Core',\n filename: '0001_core.sql',\n description: 'Migration 0001: Core',\n sql: \"-- Migration 0001: Auth tables\\n-- auth_user, auth_session, auth_account, auth_verification + BA plugin tables + RBAC + auth support.\\n-- Only auth_* prefixed tables live here. All content lives in document_* tables (0002_documents.sql).\\n\\n-- ── auth_user ────────────────────────────────────────────────────────────────\\n-- BA user model + SonicJS domain columns as BA additionalFields.\\nCREATE TABLE IF NOT EXISTS auth_user (\\n id TEXT PRIMARY KEY,\\n name TEXT,\\n email TEXT NOT NULL UNIQUE,\\n email_verified INTEGER NOT NULL DEFAULT 0,\\n image TEXT,\\n created_at INTEGER NOT NULL,\\n updated_at INTEGER NOT NULL,\\n -- SonicJS additionalFields\\n first_name TEXT NOT NULL,\\n last_name TEXT NOT NULL,\\n role TEXT NOT NULL DEFAULT 'viewer',\\n -- Platform super-admin: bypasses the multi-tenant membership gate, uses global roles in every\\n -- tenant. Opt-in (default 0); intentionally NOT derived from the 'admin' role.\\n is_super_admin INTEGER NOT NULL DEFAULT 0,\\n avatar TEXT,\\n password_hash TEXT,\\n is_active INTEGER NOT NULL DEFAULT 1,\\n last_login_at INTEGER,\\n phone TEXT,\\n bio TEXT,\\n timezone TEXT DEFAULT 'UTC',\\n language TEXT DEFAULT 'en',\\n email_notifications INTEGER DEFAULT 1,\\n theme TEXT DEFAULT 'dark',\\n -- Password reset flow (routes/auth.ts)\\n password_reset_token TEXT,\\n password_reset_expires INTEGER,\\n invitation_token TEXT,\\n invited_by TEXT,\\n invited_at INTEGER,\\n accepted_invitation_at INTEGER,\\n -- Account lockout: reset on success; set on threshold failures\\n failed_login_count INTEGER NOT NULL DEFAULT 0,\\n locked_until INTEGER,\\n -- 2FA enrollment flag (twoFactor BA plugin)\\n two_factor_enabled INTEGER NOT NULL DEFAULT 0\\n);\\n\\nCREATE INDEX IF NOT EXISTS idx_auth_user_email ON auth_user(email);\\nCREATE INDEX IF NOT EXISTS idx_auth_user_role ON auth_user(role);\\nCREATE INDEX IF NOT EXISTS idx_auth_user_invitation_token ON auth_user(invitation_token);\\nCREATE INDEX IF NOT EXISTS idx_auth_user_locked_until ON auth_user(locked_until) WHERE locked_until IS NOT NULL;\\n\\n-- ── auth_session ─────────────────────────────────────────────────────────────\\nCREATE TABLE IF NOT EXISTS auth_session (\\n id TEXT PRIMARY KEY,\\n user_id TEXT NOT NULL REFERENCES auth_user(id) ON DELETE CASCADE,\\n token TEXT NOT NULL UNIQUE,\\n expires_at INTEGER NOT NULL,\\n ip_address TEXT,\\n user_agent TEXT,\\n created_at INTEGER NOT NULL,\\n updated_at INTEGER NOT NULL\\n);\\nCREATE INDEX IF NOT EXISTS idx_auth_session_user_id ON auth_session(user_id);\\nCREATE INDEX IF NOT EXISTS idx_auth_session_token ON auth_session(token);\\nCREATE INDEX IF NOT EXISTS idx_auth_session_expires_at ON auth_session(expires_at);\\n\\n-- ── auth_account ─────────────────────────────────────────────────────────────\\nCREATE TABLE IF NOT EXISTS auth_account (\\n id TEXT PRIMARY KEY,\\n user_id TEXT NOT NULL REFERENCES auth_user(id) ON DELETE CASCADE,\\n account_id TEXT NOT NULL,\\n provider_id TEXT NOT NULL,\\n access_token TEXT,\\n refresh_token TEXT,\\n access_token_expires_at INTEGER,\\n refresh_token_expires_at INTEGER,\\n scope TEXT,\\n id_token TEXT,\\n password TEXT,\\n created_at INTEGER NOT NULL,\\n updated_at INTEGER NOT NULL\\n);\\nCREATE INDEX IF NOT EXISTS idx_auth_account_user_id ON auth_account(user_id);\\nCREATE INDEX IF NOT EXISTS idx_auth_account_provider ON auth_account(provider_id, account_id);\\n\\n-- ── auth_verification ────────────────────────────────────────────────────────\\n-- Covers email verification, password reset, magic-link tokens, OTP codes.\\nCREATE TABLE IF NOT EXISTS auth_verification (\\n id TEXT PRIMARY KEY,\\n identifier TEXT NOT NULL,\\n value TEXT NOT NULL,\\n expires_at INTEGER NOT NULL,\\n created_at INTEGER NOT NULL,\\n updated_at INTEGER NOT NULL\\n);\\nCREATE INDEX IF NOT EXISTS idx_auth_verification_identifier ON auth_verification(identifier);\\n\\n-- ── BA plugin tables ──────────────────────────────────────────────────────────\\n\\nCREATE TABLE IF NOT EXISTS auth_two_factor (\\n id TEXT PRIMARY KEY,\\n secret TEXT NOT NULL,\\n backup_codes TEXT NOT NULL,\\n user_id TEXT NOT NULL REFERENCES auth_user(id) ON DELETE CASCADE,\\n verified INTEGER NOT NULL DEFAULT 1,\\n created_at INTEGER NOT NULL,\\n updated_at INTEGER NOT NULL\\n);\\nCREATE INDEX IF NOT EXISTS idx_auth_two_factor_user_id ON auth_two_factor(user_id);\\n\\nCREATE TABLE IF NOT EXISTS auth_tenant (\\n id TEXT PRIMARY KEY,\\n name TEXT NOT NULL,\\n slug TEXT NOT NULL UNIQUE,\\n logo TEXT,\\n metadata TEXT,\\n -- SonicJS tenant-resolution fields (BA organization additionalFields):\\n status TEXT NOT NULL DEFAULT 'active',\\n domain TEXT,\\n notes TEXT NOT NULL DEFAULT '',\\n created_at INTEGER NOT NULL,\\n updated_at INTEGER NOT NULL\\n);\\nCREATE INDEX IF NOT EXISTS idx_auth_tenant_domain ON auth_tenant(domain);\\n\\nCREATE TABLE IF NOT EXISTS auth_tenant_member (\\n id TEXT PRIMARY KEY,\\n tenant_id TEXT NOT NULL REFERENCES auth_tenant(id) ON DELETE CASCADE,\\n user_id TEXT NOT NULL REFERENCES auth_user(id) ON DELETE CASCADE,\\n role TEXT NOT NULL DEFAULT 'member',\\n email TEXT,\\n created_at INTEGER NOT NULL,\\n updated_at INTEGER NOT NULL,\\n UNIQUE(tenant_id, user_id)\\n);\\nCREATE INDEX IF NOT EXISTS idx_auth_tenant_member_tenant ON auth_tenant_member(tenant_id);\\nCREATE INDEX IF NOT EXISTS idx_auth_tenant_member_user ON auth_tenant_member(user_id);\\n\\nCREATE TABLE IF NOT EXISTS auth_tenant_invitation (\\n id TEXT PRIMARY KEY,\\n tenant_id TEXT NOT NULL REFERENCES auth_tenant(id) ON DELETE CASCADE,\\n email TEXT NOT NULL,\\n role TEXT NOT NULL DEFAULT 'member',\\n status TEXT NOT NULL DEFAULT 'pending',\\n expires_at INTEGER NOT NULL,\\n inviter_id TEXT REFERENCES auth_user(id) ON DELETE SET NULL,\\n created_at INTEGER NOT NULL,\\n updated_at INTEGER NOT NULL\\n);\\nCREATE INDEX IF NOT EXISTS idx_auth_tenant_invitation_tenant ON auth_tenant_invitation(tenant_id);\\nCREATE INDEX IF NOT EXISTS idx_auth_tenant_invitation_email ON auth_tenant_invitation(email);\\n\\nCREATE TABLE IF NOT EXISTS auth_tenant_team (\\n id TEXT PRIMARY KEY,\\n name TEXT NOT NULL,\\n tenant_id TEXT NOT NULL REFERENCES auth_tenant(id) ON DELETE CASCADE,\\n created_at INTEGER NOT NULL,\\n updated_at INTEGER NOT NULL\\n);\\n\\n-- ── RBAC ─────────────────────────────────────────────────────────────────────\\n-- RBAC roles, verbs, and user-role assignments are document-backed (is_auth doc\\n-- types rbac_role / rbac_verb / rbac_user_roles — see services/rbac.ts). The\\n-- system roles/verbs/grants are seeded at bootstrap by RbacService.ensureSystemRbacSeed().\\n-- No auth_rbac_* tables.\\n\\n-- ── Auth support tables ───────────────────────────────────────────────────────\\nCREATE TABLE IF NOT EXISTS auth_password_history (\\n id TEXT PRIMARY KEY,\\n user_id TEXT NOT NULL REFERENCES auth_user(id) ON DELETE CASCADE,\\n password_hash TEXT NOT NULL,\\n created_at INTEGER NOT NULL\\n);\\nCREATE INDEX IF NOT EXISTS idx_auth_password_history_user_id ON auth_password_history(user_id);\\n\\nCREATE TABLE IF NOT EXISTS auth_api_tokens (\\n id TEXT PRIMARY KEY,\\n name TEXT NOT NULL,\\n token TEXT NOT NULL UNIQUE,\\n user_id TEXT NOT NULL REFERENCES auth_user(id),\\n permissions TEXT NOT NULL,\\n expires_at INTEGER,\\n last_used_at INTEGER,\\n created_at INTEGER NOT NULL\\n);\\nCREATE INDEX IF NOT EXISTS idx_auth_api_tokens_user ON auth_api_tokens(user_id);\\nCREATE INDEX IF NOT EXISTS idx_auth_api_tokens_token ON auth_api_tokens(token);\\n\\n-- User profiles moved to the document model: a `user_profile` document (is_auth type),\\n-- one per user, addressed by slug = userId. See services/document-types-seed.ts and\\n-- plugins/core-plugins/user-profiles/user-profile-document.ts. No auth_user_profiles table.\\n\"\n },\n {\n id: '0002',\n name: 'Documents',\n filename: '0002_documents.sql',\n description: 'Migration 0002: Documents',\n sql: \"-- Migration 0002: Document Schema (v3 greenfield)\\n-- Contains only the new document data model tables, generated columns, and indexes.\\n\\n-- Document type registry\\nCREATE TABLE IF NOT EXISTS document_types (\\n id TEXT PRIMARY KEY,\\n name TEXT NOT NULL UNIQUE,\\n display_name TEXT NOT NULL,\\n description TEXT,\\n schema TEXT NOT NULL DEFAULT '{}',\\n queryable_fields TEXT NOT NULL DEFAULT '[]',\\n settings TEXT NOT NULL DEFAULT '{}',\\n plugin_id TEXT,\\n source TEXT NOT NULL DEFAULT 'code' CHECK (source IN ('code', 'plugin', 'system')),\\n schema_version INTEGER NOT NULL DEFAULT 1,\\n is_system INTEGER NOT NULL DEFAULT 0,\\n is_active INTEGER NOT NULL DEFAULT 1,\\n is_auth INTEGER NOT NULL DEFAULT 0,\\n created_at INTEGER NOT NULL DEFAULT (unixepoch()),\\n updated_at INTEGER NOT NULL DEFAULT (unixepoch())\\n);\\n\\nCREATE INDEX IF NOT EXISTS idx_document_types_plugin ON document_types(plugin_id);\\nCREATE INDEX IF NOT EXISTS idx_document_types_active ON document_types(is_active);\\n\\n-- Documents: canonical document rows and historical versions.\\nCREATE TABLE IF NOT EXISTS documents (\\n id TEXT PRIMARY KEY,\\n root_id TEXT NOT NULL,\\n type_id TEXT NOT NULL REFERENCES document_types(id),\\n type_version INTEGER NOT NULL DEFAULT 1,\\n\\n version_of_id TEXT REFERENCES documents(id),\\n version_number INTEGER NOT NULL DEFAULT 1,\\n\\n is_current_draft INTEGER NOT NULL DEFAULT 1,\\n is_published INTEGER NOT NULL DEFAULT 0,\\n status TEXT NOT NULL DEFAULT 'draft' CHECK (status IN ('draft', 'published', 'archived')),\\n\\n parent_root_id TEXT NOT NULL DEFAULT '',\\n slug TEXT,\\n path TEXT,\\n title TEXT,\\n zone TEXT,\\n sort_order INTEGER NOT NULL DEFAULT 0,\\n visible INTEGER NOT NULL DEFAULT 1,\\n\\n published_at INTEGER,\\n scheduled_at INTEGER,\\n expires_at INTEGER,\\n deleted_at INTEGER,\\n\\n tenant_id TEXT NOT NULL DEFAULT 'default',\\n locale TEXT NOT NULL DEFAULT 'default',\\n translation_group_id TEXT NOT NULL DEFAULT '',\\n\\n data TEXT NOT NULL DEFAULT '{}',\\n metadata TEXT NOT NULL DEFAULT '{}',\\n\\n owner_id TEXT,\\n created_by TEXT,\\n updated_by TEXT,\\n created_at INTEGER NOT NULL DEFAULT (unixepoch()),\\n updated_at INTEGER NOT NULL DEFAULT (unixepoch())\\n);\\n\\n-- Queryable scalar fields (VIRTUAL generated columns) and their q_* filter indexes\\n-- are AUTO-GENERATED at runtime from each document type's queryableFields config —\\n-- see DocumentTypeRegistry.register() -> ensureScalarSchema() (document-scalar-schema.ts).\\n-- Do not hand-add q_* columns/indexes here; declare the field in the type instead.\\n\\n-- Revision chain\\nCREATE INDEX IF NOT EXISTS idx_documents_root ON documents(root_id, version_number DESC);\\n\\n-- List / lifecycle\\nCREATE INDEX IF NOT EXISTS idx_documents_published ON documents(tenant_id, type_id, locale, is_published)\\n WHERE is_published = 1 AND deleted_at IS NULL;\\nCREATE INDEX IF NOT EXISTS idx_documents_drafts ON documents(tenant_id, type_id, status, is_current_draft)\\n WHERE is_current_draft = 1;\\nCREATE INDEX IF NOT EXISTS idx_documents_parent ON documents(tenant_id, parent_root_id, sort_order, is_published);\\nCREATE INDEX IF NOT EXISTS idx_documents_path ON documents(tenant_id, path);\\nCREATE INDEX IF NOT EXISTS idx_documents_translation ON documents(translation_group_id, locale);\\nCREATE INDEX IF NOT EXISTS idx_documents_deleted ON documents(deleted_at);\\nCREATE INDEX IF NOT EXISTS idx_documents_scheduled ON documents(scheduled_at) WHERE scheduled_at IS NOT NULL;\\nCREATE INDEX IF NOT EXISTS idx_documents_expires ON documents(expires_at) WHERE expires_at IS NOT NULL;\\n\\n-- Stable keyset/cursor pagination for published lists\\nCREATE INDEX IF NOT EXISTS idx_documents_published_cursor\\n ON documents(tenant_id, type_id, updated_at DESC, id DESC)\\n WHERE is_published = 1 AND deleted_at IS NULL;\\n\\n-- (q_* generated-column filter indexes are auto-created at runtime — see note above.)\\n\\n-- Partial unique indexes: the hard concurrency guarantees for draft/publish invariants.\\nCREATE UNIQUE INDEX IF NOT EXISTS idx_documents_one_current_draft\\n ON documents(root_id) WHERE is_current_draft = 1;\\nCREATE UNIQUE INDEX IF NOT EXISTS idx_documents_one_published\\n ON documents(root_id) WHERE is_published = 1;\\nCREATE UNIQUE INDEX IF NOT EXISTS idx_documents_unique_version\\n ON documents(root_id, version_number);\\nCREATE UNIQUE INDEX IF NOT EXISTS idx_documents_unique_slug\\n ON documents(tenant_id, locale, type_id, parent_root_id, slug)\\n WHERE is_current_draft = 1 AND deleted_at IS NULL AND slug IS NOT NULL;\\nCREATE UNIQUE INDEX IF NOT EXISTS idx_documents_one_translation_per_locale\\n ON documents(tenant_id, translation_group_id, locale)\\n WHERE is_current_draft = 1 AND translation_group_id <> '';\\n\\n-- Document references: typed document-to-document edges.\\nCREATE TABLE IF NOT EXISTS document_references (\\n id TEXT PRIMARY KEY,\\n tenant_id TEXT NOT NULL,\\n from_root_id TEXT NOT NULL,\\n from_document_id TEXT NOT NULL REFERENCES documents(id) ON DELETE CASCADE,\\n field_name TEXT NOT NULL,\\n ordinal INTEGER NOT NULL DEFAULT 0,\\n to_root_id TEXT NOT NULL,\\n ref_strength TEXT NOT NULL DEFAULT 'weak' CHECK (ref_strength IN ('strong', 'weak')),\\n created_at INTEGER NOT NULL DEFAULT (unixepoch())\\n);\\n\\nCREATE INDEX IF NOT EXISTS idx_docref_to ON document_references(tenant_id, to_root_id);\\nCREATE INDEX IF NOT EXISTS idx_docref_from ON document_references(from_document_id);\\nCREATE UNIQUE INDEX IF NOT EXISTS idx_docref_unique\\n ON document_references(from_document_id, field_name, ordinal);\\n\\n-- Document facets: indexed rows for multi-valued scalar fields (e.g. tags arrays).\\nCREATE TABLE IF NOT EXISTS document_facets (\\n id TEXT PRIMARY KEY,\\n tenant_id TEXT NOT NULL,\\n document_id TEXT NOT NULL REFERENCES documents(id) ON DELETE CASCADE,\\n root_id TEXT NOT NULL,\\n type_id TEXT NOT NULL,\\n field_name TEXT NOT NULL,\\n ordinal INTEGER NOT NULL DEFAULT 0,\\n value_text TEXT,\\n value_number REAL,\\n created_at INTEGER NOT NULL DEFAULT (unixepoch())\\n);\\n\\nCREATE INDEX IF NOT EXISTS idx_facets_lookup ON document_facets(tenant_id, type_id, field_name, value_text);\\nCREATE INDEX IF NOT EXISTS idx_facets_doc ON document_facets(document_id);\\nCREATE UNIQUE INDEX IF NOT EXISTS idx_facets_unique\\n ON document_facets(document_id, field_name, ordinal);\\n\\n-- Document permissions: per-document ACL overrides.\\nCREATE TABLE IF NOT EXISTS document_permissions (\\n id TEXT PRIMARY KEY,\\n tenant_id TEXT NOT NULL,\\n root_id TEXT NOT NULL,\\n principal_type TEXT NOT NULL CHECK (principal_type IN ('user', 'role', 'group', 'public', 'token')),\\n principal_id TEXT NOT NULL,\\n permission TEXT NOT NULL CHECK (permission IN ('read', 'create', 'update', 'delete', 'publish', 'manage')),\\n effect TEXT NOT NULL DEFAULT 'allow' CHECK (effect IN ('allow', 'deny')),\\n inherited INTEGER NOT NULL DEFAULT 0,\\n created_at INTEGER NOT NULL DEFAULT (unixepoch()),\\n created_by TEXT\\n);\\n\\nCREATE INDEX IF NOT EXISTS idx_document_permissions_root ON document_permissions(tenant_id, root_id);\\nCREATE INDEX IF NOT EXISTS idx_document_permissions_principal\\n ON document_permissions(tenant_id, principal_type, principal_id, permission);\\nCREATE UNIQUE INDEX IF NOT EXISTS idx_document_permissions_unique\\n ON document_permissions(root_id, principal_type, principal_id, permission);\\n\"\n }\n]\n\n// Map for quick lookup by ID\nexport const migrationsByIdMap = new Map<string, BundledMigration>(\n bundledMigrations.map(m => [m.id, m])\n)\n\n// Get migration SQL by ID\nexport function getMigrationSQLById(id: string): string | null {\n return migrationsByIdMap.get(id)?.sql ?? null\n}\n\n// Get all migration info (without SQL for lighter payloads)\nexport function getMigrationList(): Array<Omit<BundledMigration, 'sql'>> {\n return bundledMigrations.map(({ sql, ...rest }) => rest)\n}\n","import { D1Database } from '@cloudflare/workers-types'\nimport type { QueryableField } from '../schemas/document'\n\n// Identifiers and JSON paths are interpolated into DDL (they cannot be bound), so\n// they are format-guarded here. Source is trusted code config, not user input —\n// this is defense-in-depth, mirroring document-repository.ts.\nconst SAFE_IDENTIFIER = /^[a-z_][a-z0-9_]*$/\n\n/** Map a queryable field's logical type to a SQLite column affinity. */\nfunction affinity(type?: QueryableField['type']): 'TEXT' | 'INTEGER' | 'REAL' {\n if (type === 'number') return 'REAL'\n if (type === 'integer' || type === 'boolean' || type === 'date') return 'INTEGER'\n return 'TEXT'\n}\n\nconst slug = (s: string) => s.toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '')\n\n/**\n * Authoritative generated-column name for a scalar field. An explicit `column`\n * always wins (back-compat with every existing type definition); otherwise it is\n * derived deterministically from the type id + field name. The repository reads\n * the same `column`/derivation when building filter SQL, so the two never drift.\n */\nexport function resolveColumn(typeId: string, f: QueryableField): string {\n if (f.column) return f.column\n const name = `q_${slug(typeId)}_${slug(f.name)}`\n return name.length <= 60 ? name : `q_${slug(typeId).slice(0, 20)}_${slug(f.name).slice(0, 20)}`\n}\n\n/**\n * Idempotently ensure the `documents` table has a VIRTUAL generated column and a\n * filter/sort index for each of a type's scalar queryable fields. Safe to call on\n * every registration and every bootstrap: existing columns/indexes are skipped,\n * and a concurrent add surfaces as a swallowed \"duplicate column name\".\n *\n * Facet and reference fields need no DDL (generic document_facets /\n * document_references tables), so they are ignored here.\n *\n * Returns the columns it actually created (empty when all already existed).\n */\nexport async function ensureScalarSchema(\n db: D1Database,\n typeId: string,\n fields: QueryableField[],\n): Promise<string[]> {\n const scalars = fields.filter((f) => f.kind === 'scalar')\n if (scalars.length === 0) return []\n\n // pragma_table_info does NOT list VIRTUAL generated columns — use table_xinfo, which does.\n let existing = new Set<string>()\n try {\n const info = await db.prepare(\"SELECT name FROM pragma_table_xinfo('documents')\").all()\n existing = new Set((info?.results ?? []).map((r: any) => r.name))\n } catch {\n // table_xinfo unavailable — fall back to attempting every ALTER (duplicate errors swallowed).\n }\n\n const added: string[] = []\n for (const f of scalars) {\n const col = resolveColumn(typeId, f)\n if (!SAFE_IDENTIFIER.test(col)) {\n console.error(`[scalar-schema] unsafe column name '${col}' for ${typeId}.${f.name} — skipped`)\n continue\n }\n const path = f.path ?? `$.${f.name}`\n if (path.includes(\"'\")) {\n console.error(`[scalar-schema] unsafe json path for ${col} (${typeId}.${f.name}) — skipped`)\n continue\n }\n\n if (!existing.has(col)) {\n try {\n await db\n .prepare(`ALTER TABLE documents ADD COLUMN ${col} ${affinity(f.type)} AS (json_extract(data, '${path}')) VIRTUAL`)\n .run()\n added.push(col)\n console.log(`[scalar-schema] added documents.${col} for type '${typeId}'`)\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error)\n if (!msg.includes('duplicate column name')) {\n console.error(`[scalar-schema] failed to add documents.${col}:`, msg)\n continue\n }\n }\n }\n\n // One general index per column: the leading (tenant_id, type_id, col) prefix\n // serves every equality filter the repository builds; the trailing\n // (updated_at DESC, id DESC) matches the default keyset sort/cursor. Non-partial\n // so a single index covers both draft and published lists.\n try {\n await db\n .prepare(`CREATE INDEX IF NOT EXISTS idx_${col} ON documents(tenant_id, type_id, ${col}, updated_at DESC, id DESC)`)\n .run()\n } catch (error) {\n console.error(`[scalar-schema] failed to create idx_${col}:`, error instanceof Error ? error.message : String(error))\n }\n }\n return added\n}\n","import { D1Database } from '@cloudflare/workers-types'\nimport { bundledMigrations } from '../db/migrations-bundle'\nimport { ensureScalarSchema } from './document-scalar-schema'\nimport type { QueryableField } from '../schemas/document'\n\nexport interface Migration {\n id: string\n name: string\n filename: string\n description?: string\n applied: boolean\n appliedAt?: string\n size?: number\n}\n\nexport interface MigrationStatus {\n totalMigrations: number\n appliedMigrations: number\n pendingMigrations: number\n lastApplied?: string\n migrations: Migration[]\n}\n\nexport class MigrationService {\n constructor(private db: D1Database) {}\n\n /**\n * Cloudflare D1 owns migration bookkeeping through `d1_migrations`.\n * SonicJS intentionally does not create its own tracking table.\n */\n async initializeMigrationsTable(): Promise<void> {\n // Kept as a no-op for compatibility with older callers.\n }\n\n /**\n * Get all available migrations from the bundled migrations\n */\n async getAvailableMigrations(): Promise<Migration[]> {\n const migrations: Migration[] = []\n const appliedMigrations = await this.getD1AppliedMigrations()\n await this.ensureSchemaCompatibility()\n\n // Use bundled migrations as the source of truth\n for (const bundled of bundledMigrations) {\n const applied = appliedMigrations.has(bundled.id)\n const appliedData = appliedMigrations.get(bundled.id)\n\n migrations.push({\n id: bundled.id,\n name: bundled.name,\n filename: bundled.filename,\n description: bundled.description,\n applied,\n appliedAt: applied ? appliedData?.applied_at : undefined,\n size: bundled.sql.length\n })\n }\n\n return migrations\n }\n\n /**\n * Read Wrangler/D1's canonical migration table. If the table is absent, no\n * migrations have been applied by the supported migration runner yet.\n */\n private async getD1AppliedMigrations(): Promise<Map<string, any>> {\n try {\n const appliedResult = await this.db.prepare(\n 'SELECT name, applied_at FROM d1_migrations ORDER BY applied_at ASC'\n ).all()\n\n return new Map(\n (appliedResult.results ?? [])\n .map((row: any) => {\n const filename = String(row.name ?? '')\n const id = filename.match(/^(\\d+)/)?.[1]\n if (!id) return null\n return [id, {\n id,\n name: filename,\n filename,\n applied_at: row.applied_at\n }]\n })\n .filter((entry): entry is [string, any] => entry !== null)\n )\n } catch (error) {\n return new Map()\n }\n }\n\n /**\n * Run idempotent compatibility repairs that are safe outside migration state.\n */\n async ensureSchemaCompatibility(): Promise<void> {\n if (await this.checkTablesExist(['documents'])) {\n await this.ensureDocumentGeneratedColumns()\n }\n }\n\n /**\n * Ensure the `documents` table exposes every queryable VIRTUAL generated column + index (D45).\n * Data-driven repair: reconciles from each active type's `queryable_fields` rather than a hardcoded\n * list, so it stays in sync with whatever types are registered. Generation of these columns is owned\n * by DocumentTypeRegistry.register() (via ensureScalarSchema); this pass is a bootstrap safety net for\n * a DB that has document_types rows but lost columns (e.g. table rebuilt). Idempotent.\n */\n private async ensureDocumentGeneratedColumns(): Promise<void> {\n if (!(await this.checkTablesExist(['document_types']))) return\n const rows = await this.db\n .prepare('SELECT id, queryable_fields FROM document_types WHERE is_active = 1')\n .all<{ id: string; queryable_fields: string }>()\n for (const row of rows.results ?? []) {\n let fields: QueryableField[]\n try {\n fields = JSON.parse(row.queryable_fields)\n } catch {\n continue\n }\n await ensureScalarSchema(this.db, row.id, fields)\n }\n }\n\n /**\n * Check if specific tables exist in the database\n */\n private async checkTablesExist(tableNames: string[]): Promise<boolean> {\n try {\n for (const tableName of tableNames) {\n const result = await this.db.prepare(\n `SELECT name FROM sqlite_master WHERE type='table' AND name=?`\n ).bind(tableName).first()\n\n if (!result) {\n return false\n }\n }\n return true\n } catch (error) {\n return false\n }\n }\n\n /**\n * Check if a specific column exists in a table\n */\n private async checkColumnExists(tableName: string, columnName: string): Promise<boolean> {\n try {\n const result = await this.db.prepare(\n `SELECT * FROM pragma_table_info(?) WHERE name = ?`\n ).bind(tableName, columnName).first()\n\n return !!result\n } catch (error) {\n return false\n }\n }\n\n /**\n * Get migration status summary\n */\n async getMigrationStatus(): Promise<MigrationStatus> {\n const migrations = await this.getAvailableMigrations()\n const appliedMigrations = migrations.filter(m => m.applied)\n const pendingMigrations = migrations.filter(m => !m.applied)\n\n const lastApplied = appliedMigrations.length > 0\n ? appliedMigrations[appliedMigrations.length - 1]?.appliedAt\n : undefined\n\n return {\n totalMigrations: migrations.length,\n appliedMigrations: appliedMigrations.length,\n pendingMigrations: pendingMigrations.length,\n lastApplied,\n migrations\n }\n }\n\n /**\n * D1 migration state is managed by Wrangler.\n */\n async markMigrationApplied(migrationId: string, name: string, filename: string): Promise<void> {\n void migrationId\n void name\n void filename\n }\n\n /**\n * D1 migration state is managed by Wrangler.\n */\n async removeMigrationApplied(migrationId: string): Promise<void> {\n void migrationId\n }\n\n /**\n * Check if a specific migration has been applied\n */\n async isMigrationApplied(migrationId: string): Promise<boolean> {\n const appliedMigrations = await this.getD1AppliedMigrations()\n return appliedMigrations.has(migrationId)\n }\n\n /**\n * Get the last applied migration\n */\n async getLastAppliedMigration(): Promise<Migration | null> {\n const migrations = await this.getAvailableMigrations()\n return migrations.filter(m => m.applied).at(-1) ?? null\n }\n\n /**\n * Run pending migrations\n */\n async runPendingMigrations(): Promise<{ success: boolean; message: string; applied: string[]; errors: string[] }> {\n return {\n success: false,\n message: 'Migrations are managed by Cloudflare D1. Run `wrangler d1 migrations apply DB --local` or `wrangler d1 migrations apply DB --remote`.',\n applied: [],\n errors: []\n }\n }\n\n /**\n * Validate database schema\n */\n async validateSchema(): Promise<{ valid: boolean; issues: string[] }> {\n const issues: string[] = []\n\n // Basic table existence checks\n const requiredTables = [\n 'users', 'documents', 'document_types'\n ]\n\n for (const table of requiredTables) {\n try {\n await this.db.prepare(`SELECT COUNT(*) FROM ${table} LIMIT 1`).first()\n } catch (error) {\n issues.push(`Missing table: ${table}`)\n }\n }\n\n return {\n valid: issues.length === 0,\n issues\n }\n }\n}\n"]}
|