@sonicjs-cms/core 3.0.0-beta.2 → 3.0.0-beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/README.md +48 -49
  2. package/dist/admin-documents-form.template-DDSH6ROU.js +6 -0
  3. package/dist/{admin-documents-form.template-NLSI6Z42.js.map → admin-documents-form.template-DDSH6ROU.js.map} +1 -1
  4. package/dist/admin-documents-form.template-LSZKGA5J.cjs +19 -0
  5. package/dist/{admin-layout-catalyst.template-WHJGSWWD.js.map → admin-documents-form.template-LSZKGA5J.cjs.map} +1 -1
  6. package/dist/{filter-bar.template-DlVYMk-T.d.ts → admin-layout-catalyst.template-DrwDUfsE.d.cts} +25 -1
  7. package/dist/{filter-bar.template-DlVYMk-T.d.cts → admin-layout-catalyst.template-DrwDUfsE.d.ts} +25 -1
  8. package/dist/admin-layout-catalyst.template-KDHKVLXR.cjs +21 -0
  9. package/dist/{admin-layout-catalyst.template-ZK5HD545.cjs.map → admin-layout-catalyst.template-KDHKVLXR.cjs.map} +1 -1
  10. package/dist/admin-layout-catalyst.template-YQ4EMF2J.js +7 -0
  11. package/dist/{admin-documents-form.template-KN7JF66Q.cjs.map → admin-layout-catalyst.template-YQ4EMF2J.js.map} +1 -1
  12. package/dist/{app-Bo0X1OWX.d.ts → app-01M0dIwt.d.ts} +1 -1
  13. package/dist/{app-Do66yCcV.d.cts → app-aj39TGHB.d.cts} +1 -1
  14. package/dist/{chunk-ZEZ245PW.js → chunk-2XAQTPAE.js} +4 -236
  15. package/dist/chunk-2XAQTPAE.js.map +1 -0
  16. package/dist/{chunk-BDDABDAB.cjs → chunk-36Z3AKX3.cjs} +3 -239
  17. package/dist/chunk-36Z3AKX3.cjs.map +1 -0
  18. package/dist/{chunk-BLMTL57B.js → chunk-3TMTYJAJ.js} +26 -4
  19. package/dist/chunk-3TMTYJAJ.js.map +1 -0
  20. package/dist/{chunk-ZGGXCFR6.js → chunk-4LVVMVQI.js} +282 -4
  21. package/dist/chunk-4LVVMVQI.js.map +1 -0
  22. package/dist/{chunk-RZ6H7OZK.js → chunk-4UYJPHSV.js} +87 -12
  23. package/dist/chunk-4UYJPHSV.js.map +1 -0
  24. package/dist/{chunk-NLJVSER2.js → chunk-5U75XP74.js} +3 -2
  25. package/dist/chunk-5U75XP74.js.map +1 -0
  26. package/dist/{chunk-H2AXVCLS.cjs → chunk-5UXU3LP4.cjs} +141 -74
  27. package/dist/chunk-5UXU3LP4.cjs.map +1 -0
  28. package/dist/{chunk-3TB6AT6X.js → chunk-5V62WT6M.js} +35 -4
  29. package/dist/chunk-5V62WT6M.js.map +1 -0
  30. package/dist/{chunk-6OUHGKFD.js → chunk-7OTOXWEB.js} +3 -3
  31. package/dist/{chunk-6OUHGKFD.js.map → chunk-7OTOXWEB.js.map} +1 -1
  32. package/dist/{chunk-VXE42MYF.cjs → chunk-7RUW244J.cjs} +710 -434
  33. package/dist/chunk-7RUW244J.cjs.map +1 -0
  34. package/dist/{chunk-XW56B23A.cjs → chunk-A4KUJ3LD.cjs} +4 -4
  35. package/dist/{chunk-XW56B23A.cjs.map → chunk-A4KUJ3LD.cjs.map} +1 -1
  36. package/dist/{chunk-LRZIAW7U.cjs → chunk-AOUVUF32.cjs} +3 -3
  37. package/dist/chunk-AOUVUF32.cjs.map +1 -0
  38. package/dist/{chunk-WULONYGB.cjs → chunk-CRGUD4KC.cjs} +9 -9
  39. package/dist/{chunk-WULONYGB.cjs.map → chunk-CRGUD4KC.cjs.map} +1 -1
  40. package/dist/{chunk-AAWNRBRB.cjs → chunk-FCLKX2OS.cjs} +95 -20
  41. package/dist/chunk-FCLKX2OS.cjs.map +1 -0
  42. package/dist/{chunk-YHSQVQXX.js → chunk-GICS5AIO.js} +135 -68
  43. package/dist/chunk-GICS5AIO.js.map +1 -0
  44. package/dist/{chunk-NUKJ54GA.cjs → chunk-GMMCCIQO.cjs} +6 -6
  45. package/dist/{chunk-NUKJ54GA.cjs.map → chunk-GMMCCIQO.cjs.map} +1 -1
  46. package/dist/{chunk-NMPEMSU4.js → chunk-JEX3UPZL.js} +3 -3
  47. package/dist/chunk-JEX3UPZL.js.map +1 -0
  48. package/dist/{chunk-6JQOUUOB.js → chunk-JQISFW6U.js} +3 -3
  49. package/dist/{chunk-6JQOUUOB.js.map → chunk-JQISFW6U.js.map} +1 -1
  50. package/dist/{chunk-NAVPFIG5.cjs → chunk-K623Q6WD.cjs} +35 -3
  51. package/dist/chunk-K623Q6WD.cjs.map +1 -0
  52. package/dist/{chunk-JEQ7FLOD.cjs → chunk-KIGVVO6N.cjs} +16 -10
  53. package/dist/chunk-KIGVVO6N.cjs.map +1 -0
  54. package/dist/{chunk-QAYFOER6.cjs → chunk-KVBHB7QP.cjs} +285 -3
  55. package/dist/chunk-KVBHB7QP.cjs.map +1 -0
  56. package/dist/{chunk-2CB4KY7I.cjs → chunk-LTZ5WYOK.cjs} +29 -7
  57. package/dist/chunk-LTZ5WYOK.cjs.map +1 -0
  58. package/dist/{chunk-DNQCEKUK.cjs → chunk-N32OWET6.cjs} +5 -5
  59. package/dist/{chunk-DNQCEKUK.cjs.map → chunk-N32OWET6.cjs.map} +1 -1
  60. package/dist/{chunk-HDWE5FRJ.js → chunk-OBA2RYZN.js} +3 -3
  61. package/dist/{chunk-HDWE5FRJ.js.map → chunk-OBA2RYZN.js.map} +1 -1
  62. package/dist/{chunk-DSA4UX5B.cjs → chunk-OJC4G5JE.cjs} +3 -2
  63. package/dist/chunk-OJC4G5JE.cjs.map +1 -0
  64. package/dist/{chunk-MVIZJOO5.cjs → chunk-PXNTCCPE.cjs} +8 -8
  65. package/dist/{chunk-MVIZJOO5.cjs.map → chunk-PXNTCCPE.cjs.map} +1 -1
  66. package/dist/{chunk-EF2NQUIQ.js → chunk-RMRJGMDE.js} +3 -3
  67. package/dist/{chunk-EF2NQUIQ.js.map → chunk-RMRJGMDE.js.map} +1 -1
  68. package/dist/{chunk-IVPRUGTY.js → chunk-SOZUG52O.js} +4 -4
  69. package/dist/{chunk-IVPRUGTY.js.map → chunk-SOZUG52O.js.map} +1 -1
  70. package/dist/{chunk-GCDZZNIN.js → chunk-UWUP2VMT.js} +16 -10
  71. package/dist/chunk-UWUP2VMT.js.map +1 -0
  72. package/dist/{chunk-IESEVHXL.js → chunk-VVYVFCML.js} +12 -3
  73. package/dist/chunk-VVYVFCML.js.map +1 -0
  74. package/dist/{chunk-HIKBY7MS.cjs → chunk-XWVDSM2L.cjs} +12 -3
  75. package/dist/chunk-XWVDSM2L.cjs.map +1 -0
  76. package/dist/{chunk-VD2EA3WT.js → chunk-ZP77LTN4.js} +343 -68
  77. package/dist/chunk-ZP77LTN4.js.map +1 -0
  78. package/dist/{collection-config-JgHOpFCG.d.ts → collection-config-BTzAvKt2.d.cts} +25 -0
  79. package/dist/{collection-config-JgHOpFCG.d.cts → collection-config-BTzAvKt2.d.ts} +25 -0
  80. package/dist/config-6FB2BXMP.js +6 -0
  81. package/dist/{config-HFXANXCC.js.map → config-6FB2BXMP.js.map} +1 -1
  82. package/dist/config-OCH7S4WK.cjs +19 -0
  83. package/dist/{config-ON6FNMYX.cjs.map → config-OCH7S4WK.cjs.map} +1 -1
  84. package/dist/{define-plugin-BzNHc1ZI.d.ts → define-plugin-BYIey6sp.d.ts} +1 -1
  85. package/dist/{define-plugin-IWDKYaVm.d.cts → define-plugin-DsKiGu1q.d.cts} +1 -1
  86. package/dist/document-projection-4HG75S5S.js +4 -0
  87. package/dist/{document-projection-YYMC6I4U.js.map → document-projection-4HG75S5S.js.map} +1 -1
  88. package/dist/document-projection-JQHSTSFD.cjs +13 -0
  89. package/dist/{document-projection-TDWRJX3Z.cjs.map → document-projection-JQHSTSFD.cjs.map} +1 -1
  90. package/dist/index.cjs +1119 -440
  91. package/dist/index.cjs.map +1 -1
  92. package/dist/index.d.cts +200 -12
  93. package/dist/index.d.ts +200 -12
  94. package/dist/index.js +803 -137
  95. package/dist/index.js.map +1 -1
  96. package/dist/middleware.cjs +37 -34
  97. package/dist/middleware.d.cts +2 -2
  98. package/dist/middleware.d.ts +2 -2
  99. package/dist/middleware.js +8 -5
  100. package/dist/migrations-HMX5SPN2.js +4 -0
  101. package/dist/{migrations-WCAVBD7C.js.map → migrations-HMX5SPN2.js.map} +1 -1
  102. package/dist/migrations-V73ARYV5.cjs +13 -0
  103. package/dist/{migrations-NJJWQUKK.cjs.map → migrations-V73ARYV5.cjs.map} +1 -1
  104. package/dist/{plugin-bootstrap-B8ThJU21.d.cts → plugin-bootstrap-BHWQ2df8.d.cts} +1 -1
  105. package/dist/{plugin-bootstrap-qu8hJgUt.d.ts → plugin-bootstrap-Bj4fxX1q.d.ts} +1 -1
  106. package/dist/plugins.cjs +40 -40
  107. package/dist/plugins.d.cts +2 -2
  108. package/dist/plugins.d.ts +2 -2
  109. package/dist/plugins.js +3 -3
  110. package/dist/rbac-7QZ4JZ2E.cjs +14 -0
  111. package/dist/{rbac-VONLJJKB.cjs.map → rbac-7QZ4JZ2E.cjs.map} +1 -1
  112. package/dist/rbac-Y43IEQOP.js +5 -0
  113. package/dist/{rbac-O73MFKDA.js.map → rbac-Y43IEQOP.js.map} +1 -1
  114. package/dist/routes.cjs +34 -34
  115. package/dist/routes.d.cts +2 -2
  116. package/dist/routes.d.ts +2 -2
  117. package/dist/routes.js +14 -14
  118. package/dist/services.cjs +42 -42
  119. package/dist/services.d.cts +16 -3
  120. package/dist/services.d.ts +16 -3
  121. package/dist/services.js +3 -3
  122. package/dist/{telemetry-B9vIV4wh.d.cts → telemetry-Cku1ax74.d.cts} +1 -1
  123. package/dist/{telemetry-B9vIV4wh.d.ts → telemetry-Cku1ax74.d.ts} +1 -1
  124. package/dist/templates.cjs +17 -17
  125. package/dist/templates.d.cts +2 -24
  126. package/dist/templates.d.ts +2 -24
  127. package/dist/templates.js +3 -3
  128. package/dist/{types-Dea1eNxU.d.ts → types-DT7XN5TX.d.cts} +1 -1
  129. package/dist/{types-Dea1eNxU.d.cts → types-DT7XN5TX.d.ts} +1 -1
  130. package/dist/types.d.cts +2 -2
  131. package/dist/types.d.ts +2 -2
  132. package/dist/utils.cjs +8 -8
  133. package/dist/utils.d.cts +2 -2
  134. package/dist/utils.d.ts +2 -2
  135. package/dist/utils.js +2 -2
  136. package/package.json +1 -1
  137. package/dist/admin-documents-form.template-KN7JF66Q.cjs +0 -19
  138. package/dist/admin-documents-form.template-NLSI6Z42.js +0 -6
  139. package/dist/admin-layout-catalyst.template-WHJGSWWD.js +0 -7
  140. package/dist/admin-layout-catalyst.template-ZK5HD545.cjs +0 -17
  141. package/dist/chunk-2CB4KY7I.cjs.map +0 -1
  142. package/dist/chunk-3TB6AT6X.js.map +0 -1
  143. package/dist/chunk-AAWNRBRB.cjs.map +0 -1
  144. package/dist/chunk-BDDABDAB.cjs.map +0 -1
  145. package/dist/chunk-BLMTL57B.js.map +0 -1
  146. package/dist/chunk-DSA4UX5B.cjs.map +0 -1
  147. package/dist/chunk-GCDZZNIN.js.map +0 -1
  148. package/dist/chunk-H2AXVCLS.cjs.map +0 -1
  149. package/dist/chunk-HIKBY7MS.cjs.map +0 -1
  150. package/dist/chunk-IESEVHXL.js.map +0 -1
  151. package/dist/chunk-JEQ7FLOD.cjs.map +0 -1
  152. package/dist/chunk-LRZIAW7U.cjs.map +0 -1
  153. package/dist/chunk-NAVPFIG5.cjs.map +0 -1
  154. package/dist/chunk-NLJVSER2.js.map +0 -1
  155. package/dist/chunk-NMPEMSU4.js.map +0 -1
  156. package/dist/chunk-QAYFOER6.cjs.map +0 -1
  157. package/dist/chunk-RZ6H7OZK.js.map +0 -1
  158. package/dist/chunk-VD2EA3WT.js.map +0 -1
  159. package/dist/chunk-VXE42MYF.cjs.map +0 -1
  160. package/dist/chunk-YHSQVQXX.js.map +0 -1
  161. package/dist/chunk-ZEZ245PW.js.map +0 -1
  162. package/dist/chunk-ZGGXCFR6.js.map +0 -1
  163. package/dist/config-HFXANXCC.js +0 -6
  164. package/dist/config-ON6FNMYX.cjs +0 -19
  165. package/dist/document-projection-TDWRJX3Z.cjs +0 -13
  166. package/dist/document-projection-YYMC6I4U.js +0 -4
  167. package/dist/migrations-NJJWQUKK.cjs +0 -13
  168. package/dist/migrations-WCAVBD7C.js +0 -4
  169. package/dist/rbac-O73MFKDA.js +0 -5
  170. package/dist/rbac-VONLJJKB.cjs +0 -14
package/dist/index.js CHANGED
@@ -1,41 +1,43 @@
1
- import { createAuth } from './chunk-IVPRUGTY.js';
2
- import { getEmailService, hasEmailService, setEmailService, invalidateVariablesCache, resolveVariablesInObject, resolveDocScope, denyIfNotAllowed, DocumentRepository, dispatchHookEvent, getCustomData, renderTable, getVariablesCached, setVariablesCache, TenantService, isValidMemberRole, renderConfirmationDialog, getConfirmationDialogScript, VALID_MEMBER_ROLES, userProfilesPlugin, lexicalEditorPlugin, api_media_default, api_system_default, apiDocumentsRoutes, api_default, adminDocumentsRoutes, admin_api_default, adminCollectionsRoutes, adminSettingsRoutes, router2, admin_content_default, adminMediaRoutes, router, adminPluginRoutes, adminLogsRoutes, userRoutes, auth_default, test_cleanup_default } from './chunk-VD2EA3WT.js';
3
- export { ROUTES_INFO, admin_api_default as adminApiRoutes, adminCheckboxRoutes, adminCollectionsRoutes, admin_content_default as adminContentRoutes, router as adminDashboardRoutes, adminLogsRoutes, adminMediaRoutes, adminPluginRoutes, adminSettingsRoutes, userRoutes as adminUsersRoutes, api_content_crud_default as apiContentCrudRoutes, api_media_default as apiMediaRoutes, api_default as apiRoutes, api_system_default as apiSystemRoutes, auth_default as authRoutes, createUserProfilesPlugin, defineUserProfile, getUserProfileConfig, userProfilesPlugin } from './chunk-VD2EA3WT.js';
4
- import './chunk-IESEVHXL.js';
5
- import './chunk-EF2NQUIQ.js';
6
- import { SettingsService, setAppInstance } from './chunk-ZEZ245PW.js';
7
- export { Logger, getLogger, initLogger } from './chunk-ZEZ245PW.js';
1
+ import { createAuth } from './chunk-SOZUG52O.js';
2
+ import { getEmailService, hasEmailService, setEmailService, invalidateVariablesCache, resolveVariablesInObject, resolveDocScope, denyIfNotAllowed, DocumentRepository, dispatchHookEvent, getCustomData, createDocumentSchema, renderTable, getVariablesCached, setVariablesCache, TenantService, isValidMemberRole, renderConfirmationDialog, getConfirmationDialogScript, VALID_MEMBER_ROLES, userProfilesPlugin, lexicalEditorPlugin, api_media_default, api_system_default, apiDocumentsRoutes, api_default, adminDocumentsRoutes, admin_api_default, adminCollectionsRoutes, adminSettingsRoutes, router2, admin_content_default, adminMediaRoutes, router, adminPluginRoutes, adminLogsRoutes, userRoutes, auth_default, test_cleanup_default } from './chunk-ZP77LTN4.js';
3
+ export { DocumentRepository, ROUTES_INFO, admin_api_default as adminApiRoutes, adminCheckboxRoutes, adminCollectionsRoutes, admin_content_default as adminContentRoutes, router as adminDashboardRoutes, adminLogsRoutes, adminMediaRoutes, adminPluginRoutes, adminSettingsRoutes, userRoutes as adminUsersRoutes, api_content_crud_default as apiContentCrudRoutes, api_media_default as apiMediaRoutes, api_default as apiRoutes, api_system_default as apiSystemRoutes, auth_default as authRoutes, createUserProfilesPlugin, defineUserProfile, getUserProfileConfig, userProfilesPlugin } from './chunk-ZP77LTN4.js';
4
+ import './chunk-VVYVFCML.js';
5
+ import './chunk-RMRJGMDE.js';
6
+ import { SettingsService, setAppInstance } from './chunk-2XAQTPAE.js';
7
+ export { Logger, getLogger, initLogger } from './chunk-2XAQTPAE.js';
8
8
  import { getAllCacheStats, CACHE_CONFIGS, getCacheService, clearAllCaches, parseCacheKey } from './chunk-K25XHMM3.js';
9
9
  import { schema_exports } from './chunk-AI663NBO.js';
10
10
  export { apiTokens, content, contentVersions, insertContentSchema, insertLogConfigSchema, insertMediaSchema, insertPluginActivityLogSchema, insertPluginAssetSchema, insertPluginHookSchema, insertPluginRouteSchema, insertPluginSchema, insertSystemLogSchema, insertUserSchema, insertWorkflowHistorySchema, logConfig, media, pluginActivityLog, pluginAssets, pluginHooks, pluginRoutes, plugins, selectContentSchema, selectLogConfigSchema, selectMediaSchema, selectPluginActivityLogSchema, selectPluginAssetSchema, selectPluginHookSchema, selectPluginRouteSchema, selectPluginSchema, selectSystemLogSchema, selectUserSchema, selectWorkflowHistorySchema, systemLogs, users, workflowHistory } from './chunk-AI663NBO.js';
11
- import { requireAuth, DocumentTypeRegistry, requireRbac, requireRole, getJwtExpirySecondsFromDb, AuthManager, metricsMiddleware, bootstrapMiddleware, securityHeadersMiddleware, csrfProtection } from './chunk-RZ6H7OZK.js';
12
- export { AuthManager, PermissionManager, bootstrapDocumentTypes, bootstrapMiddleware, cacheHeaders, compressionMiddleware, detailedLoggingMiddleware, getActivePlugins, isPluginActive, logActivity, loggingMiddleware, optionalAuth, performanceLoggingMiddleware, requireActivePlugin, requireActivePlugins, requireAnyPermission, requireAuth, requirePermission, requireRole, securityHeadersMiddleware as securityHeaders, securityLoggingMiddleware } from './chunk-RZ6H7OZK.js';
13
- import { RbacService, DocumentsService } from './chunk-BLMTL57B.js';
14
- export { RbacService } from './chunk-BLMTL57B.js';
15
- import { nanoid } from './chunk-GCDZZNIN.js';
16
- import { PluginService, PLUGIN_REGISTRY, invalidateTenantCache, getCollectionRegistry, TENANT_COOKIE, MULTI_TENANT_PLUGIN_ID, collectionRecordToRow, tenantMiddleware } from './chunk-ZGGXCFR6.js';
17
- export { PluginBootstrapService, PluginService as PluginServiceClass, getAvailableCollectionNames, loadCollectionConfig, loadCollectionConfigs, registerCollections, validateCollectionConfig } from './chunk-ZGGXCFR6.js';
18
- export { MigrationService } from './chunk-NLJVSER2.js';
19
- export { renderFilterBar } from './chunk-6JQOUUOB.js';
20
- import { renderAdminLayout } from './chunk-HDWE5FRJ.js';
21
- export { getConfirmationDialogScript, renderAlert, renderConfirmationDialog, renderForm, renderFormField, renderPagination, renderTable } from './chunk-HDWE5FRJ.js';
22
- import { init_admin_layout_catalyst_template, renderAdminLayoutCatalyst } from './chunk-3TB6AT6X.js';
23
- import { TurnstileService, HookSystemImpl, createPluginWirer, registerPluginRoutes, setPluginMenu } from './chunk-YHSQVQXX.js';
24
- export { HookSystemImpl, HookUtils, PluginDependencyCycleError, PluginManager as PluginManagerClass, PluginRegistryImpl, PluginValidator as PluginValidatorClass, RegisterPluginsError, ScopedHookSystem as ScopedHookSystemClass, collectCronSchedules, collectCrons, createScheduledHandler, dispatchCronTick, getPluginMenu, registerPlugins, resetPluginMenu, resolvePluginMenuItems, setPluginMenu, topoSort } from './chunk-YHSQVQXX.js';
25
- import { definePlugin, setPluginDefinitions } from './chunk-6OUHGKFD.js';
26
- export { applySchemaDefaults, definePlugin, isDefinedPlugin, parseConfigSchema, parseFormDataToSettings, renderSchemaFields } from './chunk-6OUHGKFD.js';
11
+ import { requireAuth, DocumentTypeRegistry, requireRbac, requireRole, getJwtExpirySecondsFromDb, AuthManager, metricsMiddleware, bootstrapMiddleware, securityHeadersMiddleware, csrfProtection } from './chunk-4UYJPHSV.js';
12
+ export { AuthManager, PermissionManager, bootstrapDocumentTypes, bootstrapMiddleware, cacheHeaders, compressionMiddleware, detailedLoggingMiddleware, getActivePlugins, isPluginActive, logActivity, loggingMiddleware, optionalAuth, performanceLoggingMiddleware, requireActivePlugin, requireActivePlugins, requireAnyPermission, requireAuth, requirePermission, requireRole, securityHeadersMiddleware as securityHeaders, securityLoggingMiddleware } from './chunk-4UYJPHSV.js';
13
+ import { RbacService, DocumentsService } from './chunk-3TMTYJAJ.js';
14
+ export { DocumentsService, RbacService } from './chunk-3TMTYJAJ.js';
15
+ import { nanoid } from './chunk-UWUP2VMT.js';
16
+ import { PluginService, PLUGIN_REGISTRY, invalidateTenantCache, getCollectionRegistry, TENANT_COOKIE, MULTI_TENANT_PLUGIN_ID, collectionRecordToRow, tenantMiddleware } from './chunk-4LVVMVQI.js';
17
+ export { PluginBootstrapService, PluginService as PluginServiceClass, getAvailableCollectionNames, loadCollectionConfig, loadCollectionConfigs, registerCollections, validateCollectionConfig } from './chunk-4LVVMVQI.js';
18
+ export { MigrationService } from './chunk-5U75XP74.js';
19
+ export { renderFilterBar } from './chunk-JQISFW6U.js';
20
+ import { renderAdminLayout } from './chunk-OBA2RYZN.js';
21
+ export { getConfirmationDialogScript, renderAlert, renderConfirmationDialog, renderForm, renderFormField, renderPagination, renderTable } from './chunk-OBA2RYZN.js';
22
+ import { init_admin_layout_catalyst_template, renderAdminLayoutCatalyst } from './chunk-5V62WT6M.js';
23
+ export { renderAdminLayoutCatalyst } from './chunk-5V62WT6M.js';
24
+ import { TurnstileService, HookSystemImpl, createPluginWirer, registerPluginRoutes, setPluginMenu, resolvePluginMenuItems } from './chunk-GICS5AIO.js';
25
+ export { HookSystemImpl, HookUtils, PluginDependencyCycleError, PluginManager as PluginManagerClass, PluginRegistryImpl, PluginValidator as PluginValidatorClass, RegisterPluginsError, ScopedHookSystem as ScopedHookSystemClass, collectCronSchedules, collectCrons, createScheduledHandler, dispatchCronTick, getPluginMenu, registerPlugins, resetPluginMenu, resolvePluginMenuItems, setPluginMenu, topoSort } from './chunk-GICS5AIO.js';
26
+ import { definePlugin, setPluginDefinitions } from './chunk-7OTOXWEB.js';
27
+ export { applySchemaDefaults, definePlugin, isDefinedPlugin, parseConfigSchema, parseFormDataToSettings, renderSchemaFields } from './chunk-7OTOXWEB.js';
27
28
  import { setHookSystem } from './chunk-RNZFGN4R.js';
28
29
  export { getHookSystem, getTypedHooks, hasHookSystem, resetHookSystem, setHookSystem } from './chunk-RNZFGN4R.js';
29
30
  export { QueryFilterBuilder, TemplateRenderer, buildQuery, renderTemplate, templateRenderer } from './chunk-QZGABF2M.js';
30
- import './chunk-X7ZAEI5S.js';
31
31
  export { metricsTracker } from './chunk-FICTAGD4.js';
32
- import { getCoreVersion, package_default } from './chunk-NMPEMSU4.js';
33
- export { SONICJS_VERSION, getCoreVersion } from './chunk-NMPEMSU4.js';
32
+ import './chunk-X7ZAEI5S.js';
33
+ import { getCoreVersion, package_default } from './chunk-JEX3UPZL.js';
34
+ export { SONICJS_VERSION, getCoreVersion } from './chunk-JEX3UPZL.js';
34
35
  import { escapeHtml, sanitizeInput } from './chunk-TQABQWOP.js';
35
36
  export { escapeHtml, sanitizeInput, sanitizeObject } from './chunk-TQABQWOP.js';
36
37
  export { HOOKS } from './chunk-QXOZI5Q2.js';
37
38
  import './chunk-V4OQ3NZ2.js';
38
39
  import { Hono } from 'hono';
40
+ import { cors } from 'hono/cors';
39
41
  import { z } from 'zod';
40
42
  import { setCookie, getCookie } from 'hono/cookie';
41
43
  import { html } from 'hono/html';
@@ -49,6 +51,7 @@ var DatabaseToolsService = class {
49
51
  constructor(db) {
50
52
  this.db = db;
51
53
  }
54
+ db;
52
55
  /**
53
56
  * Get database statistics
54
57
  */
@@ -190,9 +193,9 @@ var DatabaseToolsService = class {
190
193
  }
191
194
  }
192
195
  /**
193
- * Get table data with optional pagination and sorting
196
+ * Get table data with optional pagination, sorting, and search
194
197
  */
195
- async getTableData(tableName, limit = 100, offset = 0, sortColumn, sortDirection = "asc") {
198
+ async getTableData(tableName, limit = 100, offset = 0, sortColumn, sortDirection = "asc", search) {
196
199
  try {
197
200
  const tables = await this.getTables();
198
201
  if (!tables.includes(tableName)) {
@@ -203,14 +206,26 @@ var DatabaseToolsService = class {
203
206
  if (sortColumn && !columns.includes(sortColumn)) {
204
207
  sortColumn = void 0;
205
208
  }
206
- const countResult = await this.db.prepare(`SELECT COUNT(*) as count FROM ${tableName}`).first();
209
+ const searchTerm = search?.trim();
210
+ let whereClause = "";
211
+ let bindings = [];
212
+ if (searchTerm) {
213
+ const conditions = columns.map(() => `CAST(? AS TEXT) IS NOT NULL AND CAST(? AS TEXT) LIKE ?`).join(" OR ");
214
+ const colConditions = columns.map((col) => `CAST("${col}" AS TEXT) LIKE ?`).join(" OR ");
215
+ whereClause = ` WHERE (${colConditions})`;
216
+ bindings = columns.map(() => `%${searchTerm}%`);
217
+ }
218
+ const countQuery = `SELECT COUNT(*) as count FROM "${tableName}"${whereClause}`;
219
+ const countStmt = this.db.prepare(countQuery);
220
+ const countResult = bindings.length > 0 ? await countStmt.bind(...bindings).first() : await countStmt.first();
207
221
  const totalRows = countResult?.count || 0;
208
- let query = `SELECT * FROM ${tableName}`;
222
+ let query = `SELECT * FROM "${tableName}"${whereClause}`;
209
223
  if (sortColumn) {
210
- query += ` ORDER BY ${sortColumn} ${sortDirection.toUpperCase()}`;
224
+ query += ` ORDER BY "${sortColumn}" ${sortDirection.toUpperCase()}`;
211
225
  }
212
226
  query += ` LIMIT ${limit} OFFSET ${offset}`;
213
- const dataResult = await this.db.prepare(query).all();
227
+ const dataStmt = this.db.prepare(query);
228
+ const dataResult = bindings.length > 0 ? await dataStmt.bind(...bindings).all() : await dataStmt.all();
214
229
  return {
215
230
  tableName,
216
231
  columns,
@@ -314,6 +329,43 @@ function renderDatabaseTablePage(data) {
314
329
  </div>
315
330
  </div>
316
331
 
332
+ <!-- Search bar -->
333
+ <div class="flex items-center gap-3">
334
+ <div class="relative flex-1 max-w-xl">
335
+ <div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
336
+ <svg class="h-4 w-4 text-zinc-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
337
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0"/>
338
+ </svg>
339
+ </div>
340
+ <input
341
+ id="searchInput"
342
+ type="text"
343
+ placeholder="Search all columns including JSON data..."
344
+ value="${escapeHtml2(data.search || "")}"
345
+ class="block w-full rounded-lg border-0 py-2.5 pl-10 pr-10 text-sm text-zinc-900 dark:text-white bg-white dark:bg-zinc-800 ring-1 ring-inset ring-zinc-300 dark:ring-zinc-700 placeholder:text-zinc-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600"
346
+ onkeydown="if(event.key==='Enter') doSearch()"
347
+ />
348
+ ${data.search ? `
349
+ <button
350
+ onclick="clearSearch()"
351
+ class="absolute inset-y-0 right-0 flex items-center pr-3 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-200"
352
+ title="Clear search"
353
+ >
354
+ <svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
355
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
356
+ </svg>
357
+ </button>
358
+ ` : ""}
359
+ </div>
360
+ <button
361
+ onclick="doSearch()"
362
+ class="inline-flex items-center justify-center rounded-lg bg-indigo-600 px-4 py-2.5 text-sm font-semibold text-white hover:bg-indigo-500 transition-colors shadow-sm"
363
+ >
364
+ Search
365
+ </button>
366
+ ${data.search ? `<span class="text-sm text-zinc-500 dark:text-zinc-400">Filtered: <strong class="text-zinc-900 dark:text-white">${escapeHtml2(data.search)}</strong></span>` : ""}
367
+ </div>
368
+
317
369
  <!-- Table Card -->
318
370
  <div class="rounded-xl bg-white dark:bg-zinc-900 shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 overflow-hidden">
319
371
  <!-- Table -->
@@ -348,7 +400,7 @@ function renderDatabaseTablePage(data) {
348
400
  <tr class="${idx % 2 === 0 ? "bg-white dark:bg-zinc-900" : "bg-zinc-50 dark:bg-zinc-900/50"}">
349
401
  ${data.columns.map((col) => `
350
402
  <td class="px-4 py-3 text-sm text-zinc-700 dark:text-zinc-300 whitespace-nowrap max-w-xs overflow-hidden text-ellipsis" title="${escapeHtml2(String(row[col] ?? ""))}">
351
- ${formatCellValue(row[col])}
403
+ ${formatCellValue(row[col], col)}
352
404
  </td>
353
405
  `).join("")}
354
406
  </tr>
@@ -424,79 +476,143 @@ function renderDatabaseTablePage(data) {
424
476
  </div>
425
477
  </div>
426
478
 
479
+ <!-- JSON Viewer Modal -->
480
+ <div id="jsonModal" class="fixed inset-0 z-50 hidden" role="dialog" aria-modal="true">
481
+ <div class="fixed inset-0 bg-black/60 backdrop-blur-sm" onclick="closeJsonModal()"></div>
482
+ <div class="fixed inset-0 flex items-center justify-center p-4 pointer-events-none">
483
+ <div class="pointer-events-auto relative w-full max-w-4xl max-h-[85vh] flex flex-col rounded-xl bg-white dark:bg-zinc-900 shadow-2xl ring-1 ring-zinc-950/10 dark:ring-white/10">
484
+ <div class="flex items-center justify-between px-6 py-4 border-b border-zinc-200 dark:border-zinc-700">
485
+ <div>
486
+ <h3 id="jsonModalTitle" class="text-base font-semibold text-zinc-900 dark:text-white">Field Value</h3>
487
+ <p id="jsonModalSubtitle" class="text-xs text-zinc-500 dark:text-zinc-400 mt-0.5"></p>
488
+ </div>
489
+ <div class="flex items-center gap-2">
490
+ <button onclick="copyJsonValue()" class="inline-flex items-center gap-1.5 rounded-lg bg-zinc-100 dark:bg-zinc-800 px-3 py-1.5 text-xs font-medium text-zinc-700 dark:text-zinc-300 hover:bg-zinc-200 dark:hover:bg-zinc-700 transition-colors">
491
+ <svg class="h-3.5 w-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
492
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/>
493
+ </svg>
494
+ Copy
495
+ </button>
496
+ <button onclick="closeJsonModal()" class="rounded-lg p-1.5 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-200 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors">
497
+ <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
498
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
499
+ </svg>
500
+ </button>
501
+ </div>
502
+ </div>
503
+ <div class="flex-1 overflow-auto p-6">
504
+ <pre id="jsonModalContent" class="text-sm font-mono text-zinc-800 dark:text-zinc-200 whitespace-pre-wrap break-words leading-relaxed"></pre>
505
+ </div>
506
+ </div>
507
+ </div>
508
+ </div>
509
+
427
510
  <script>
428
511
  const currentTableName = '${data.tableName}';
429
512
  let currentPage = ${data.currentPage};
430
513
  let currentPageSize = ${data.pageSize};
431
514
  let currentSort = '${data.sortColumn || ""}';
432
515
  let currentSortDir = '${data.sortDirection || "asc"}';
516
+ let currentSearch = ${JSON.stringify(data.search || "")};
517
+
518
+ function buildParams(overrides = {}) {
519
+ const params = new URLSearchParams();
520
+ const p = { page: currentPage, pageSize: currentPageSize, sort: currentSort, dir: currentSortDir, search: currentSearch, ...overrides };
521
+ if (p.page) params.set('page', p.page);
522
+ if (p.pageSize) params.set('pageSize', p.pageSize);
523
+ if (p.sort) { params.set('sort', p.sort); params.set('dir', p.dir || 'asc'); }
524
+ if (p.search) params.set('search', p.search);
525
+ return params;
526
+ }
433
527
 
434
528
  function goToPage(page) {
435
529
  if (page < 1 || page > ${totalPages}) return;
436
- const params = new URLSearchParams();
437
- params.set('page', page);
438
- params.set('pageSize', currentPageSize);
439
- if (currentSort) {
440
- params.set('sort', currentSort);
441
- params.set('dir', currentSortDir);
442
- }
443
- window.location.href = \`/admin/database-tools/tables/\${currentTableName}?\${params}\`;
530
+ window.location.href = \`/admin/database-tools/tables/\${currentTableName}?\${buildParams({ page })}\`;
444
531
  }
445
532
 
446
533
  function sortTable(column) {
447
- let newDir = 'asc';
448
- if (currentSort === column && currentSortDir === 'asc') {
449
- newDir = 'desc';
450
- }
451
-
452
- const params = new URLSearchParams();
453
- params.set('page', '1');
454
- params.set('pageSize', currentPageSize);
455
- params.set('sort', column);
456
- params.set('dir', newDir);
457
- window.location.href = \`/admin/database-tools/tables/\${currentTableName}?\${params}\`;
534
+ const newDir = (currentSort === column && currentSortDir === 'asc') ? 'desc' : 'asc';
535
+ window.location.href = \`/admin/database-tools/tables/\${currentTableName}?\${buildParams({ page: 1, sort: column, dir: newDir })}\`;
458
536
  }
459
537
 
460
538
  function changePageSize(newSize) {
461
- const params = new URLSearchParams();
462
- params.set('page', '1');
463
- params.set('pageSize', newSize);
464
- if (currentSort) {
465
- params.set('sort', currentSort);
466
- params.set('dir', currentSortDir);
467
- }
468
- window.location.href = \`/admin/database-tools/tables/\${currentTableName}?\${params}\`;
539
+ window.location.href = \`/admin/database-tools/tables/\${currentTableName}?\${buildParams({ page: 1, pageSize: newSize })}\`;
469
540
  }
470
541
 
471
542
  function refreshTableData() {
472
543
  window.location.reload();
473
544
  }
474
545
 
475
- function formatCellValue(value) {
476
- if (value === null || value === undefined) {
477
- return '<span class="text-zinc-400 dark:text-zinc-500 italic">null</span>';
478
- }
479
- if (typeof value === 'boolean') {
480
- return \`<span class="px-2 py-0.5 rounded text-xs font-medium \${value ? 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400' : 'bg-zinc-100 text-zinc-800 dark:bg-zinc-800 dark:text-zinc-400'}">\${value}</span>\`;
481
- }
482
- if (typeof value === 'object') {
483
- return '<span class="text-xs font-mono text-zinc-600 dark:text-zinc-400">' + JSON.stringify(value).substring(0, 50) + (JSON.stringify(value).length > 50 ? '...' : '') + '</span>';
484
- }
485
- const str = String(value);
486
- if (str.length > 100) {
487
- return escapeHtml(str.substring(0, 100)) + '...';
546
+ function doSearch() {
547
+ const val = document.getElementById('searchInput').value.trim();
548
+ window.location.href = \`/admin/database-tools/tables/\${currentTableName}?\${buildParams({ page: 1, search: val })}\`;
549
+ }
550
+
551
+ function clearSearch() {
552
+ window.location.href = \`/admin/database-tools/tables/\${currentTableName}?\${buildParams({ page: 1, search: '' })}\`;
553
+ }
554
+
555
+ // JSON viewer modal
556
+ let _jsonModalValue = '';
557
+
558
+ function openJsonModal(rawValue, colName) {
559
+ // rawValue comes from dataset \u2014 browser already decoded HTML entities, so it's the original string
560
+ _jsonModalValue = rawValue;
561
+ document.getElementById('jsonModalTitle').textContent = colName;
562
+ const el = document.getElementById('jsonModalContent');
563
+ const subtitle = document.getElementById('jsonModalSubtitle');
564
+ try {
565
+ const parsed = JSON.parse(rawValue);
566
+ const pretty = JSON.stringify(parsed, null, 2);
567
+ subtitle.textContent = 'JSON \u2022 ' + rawValue.length + ' chars';
568
+ el.innerHTML = syntaxHighlight(pretty);
569
+ } catch {
570
+ el.textContent = rawValue;
571
+ subtitle.textContent = rawValue.length + ' chars';
488
572
  }
489
- return escapeHtml(str);
573
+ document.getElementById('jsonModal').classList.remove('hidden');
574
+ document.body.style.overflow = 'hidden';
490
575
  }
491
576
 
577
+ function closeJsonModal() {
578
+ document.getElementById('jsonModal').classList.add('hidden');
579
+ document.body.style.overflow = '';
580
+ }
581
+
582
+ function copyJsonValue() {
583
+ navigator.clipboard.writeText(_jsonModalValue).then(() => {
584
+ const btn = event.currentTarget;
585
+ const orig = btn.innerHTML;
586
+ btn.textContent = 'Copied!';
587
+ setTimeout(() => { btn.innerHTML = orig; }, 1500);
588
+ });
589
+ }
590
+
591
+ function syntaxHighlight(json) {
592
+ json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
593
+ return json.replace(/("(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\"])*"(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)/g, function(match) {
594
+ let cls = 'text-purple-600 dark:text-purple-400'; // number
595
+ if (/^"/.test(match)) {
596
+ if (/:$/.test(match)) {
597
+ cls = 'text-blue-600 dark:text-blue-400 font-medium'; // key
598
+ } else {
599
+ cls = 'text-green-700 dark:text-green-400'; // string value
600
+ }
601
+ } else if (/true|false/.test(match)) {
602
+ cls = 'text-orange-600 dark:text-orange-400';
603
+ } else if (/null/.test(match)) {
604
+ cls = 'text-zinc-400 italic';
605
+ }
606
+ return \`<span class="\${cls}">\${match}</span>\`;
607
+ });
608
+ }
609
+
610
+ document.addEventListener('keydown', (e) => {
611
+ if (e.key === 'Escape') closeJsonModal();
612
+ });
613
+
492
614
  function escapeHtml(text) {
493
- const map = {
494
- '&': '&amp;',
495
- '<': '&lt;',
496
- '>': '&gt;',
497
- '"': '&quot;',
498
- "'": '&#039;'
499
- };
615
+ const map = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;' };
500
616
  return String(text).replace(/[&<>"']/g, m => map[m]);
501
617
  }
502
618
  </script>
@@ -563,7 +679,16 @@ function escapeHtml2(text) {
563
679
  };
564
680
  return String(text).replace(/[&<>"']/g, (m) => map[m] || m);
565
681
  }
566
- function formatCellValue(value) {
682
+ function isJsonString(str2) {
683
+ const s = str2.trim();
684
+ return (s.startsWith("{") || s.startsWith("[")) && (s.endsWith("}") || s.endsWith("]"));
685
+ }
686
+ function jsonButton(raw, colName) {
687
+ const preview = raw.length > 60 ? escapeHtml2(raw.substring(0, 60)) + "\u2026" : escapeHtml2(raw);
688
+ return `<button data-json-val="${escapeHtml2(raw)}" data-col-name="${escapeHtml2(colName)}" onclick="openJsonModal(this.dataset.jsonVal, this.dataset.colName)" class="text-left text-xs font-mono text-indigo-600 dark:text-indigo-400 hover:underline">${preview}</button>`;
689
+ }
690
+ function formatCellValue(value, colName) {
691
+ const col = colName || "value";
567
692
  if (value === null || value === void 0) {
568
693
  return '<span class="text-zinc-400 dark:text-zinc-500 italic">null</span>';
569
694
  }
@@ -571,11 +696,15 @@ function formatCellValue(value) {
571
696
  return `<span class="px-2 py-0.5 rounded text-xs font-medium ${value ? "bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400" : "bg-zinc-100 text-zinc-800 dark:bg-zinc-800 dark:text-zinc-400"}">${value}</span>`;
572
697
  }
573
698
  if (typeof value === "object") {
574
- return '<span class="text-xs font-mono text-zinc-600 dark:text-zinc-400">' + JSON.stringify(value).substring(0, 50) + (JSON.stringify(value).length > 50 ? "..." : "") + "</span>";
699
+ return jsonButton(JSON.stringify(value), col);
575
700
  }
576
701
  const str2 = String(value);
577
- if (str2.length > 100) {
578
- return escapeHtml2(str2.substring(0, 100)) + "...";
702
+ if (isJsonString(str2)) {
703
+ return jsonButton(str2, col);
704
+ }
705
+ if (str2.length > 120) {
706
+ const preview = escapeHtml2(str2.substring(0, 120)) + "\u2026";
707
+ return `<button data-json-val="${escapeHtml2(str2)}" data-col-name="${escapeHtml2(col)}" onclick="openJsonModal(this.dataset.jsonVal, this.dataset.colName)" class="text-left text-sm text-zinc-700 dark:text-zinc-300 hover:underline">${preview}</button>`;
579
708
  }
580
709
  return escapeHtml2(str2);
581
710
  }
@@ -710,9 +839,10 @@ function createDatabaseToolsAdminRoutes() {
710
839
  const offset = parseInt(c.req.query("offset") || "0");
711
840
  const sortColumn = c.req.query("sort");
712
841
  const sortDirection = c.req.query("dir") || "asc";
842
+ const search = c.req.query("search");
713
843
  const db = c.env.DB;
714
844
  const service = new DatabaseToolsService(db);
715
- const tableData = await service.getTableData(tableName, limit, offset, sortColumn, sortDirection);
845
+ const tableData = await service.getTableData(tableName, limit, offset, sortColumn, sortDirection, search);
716
846
  return c.json({
717
847
  success: true,
718
848
  data: tableData
@@ -736,10 +866,11 @@ function createDatabaseToolsAdminRoutes() {
736
866
  const pageSize = parseInt(c.req.query("pageSize") || "20");
737
867
  const sortColumn = c.req.query("sort");
738
868
  const sortDirection = c.req.query("dir") || "asc";
869
+ const search = c.req.query("search");
739
870
  const offset = (page - 1) * pageSize;
740
871
  const db = c.env.DB;
741
872
  const service = new DatabaseToolsService(db);
742
- const tableData = await service.getTableData(tableName, pageSize, offset, sortColumn, sortDirection);
873
+ const tableData = await service.getTableData(tableName, pageSize, offset, sortColumn, sortDirection, search);
743
874
  const pageData = {
744
875
  user: {
745
876
  name: user.email.split("@")[0] || "Unknown",
@@ -753,7 +884,8 @@ function createDatabaseToolsAdminRoutes() {
753
884
  currentPage: page,
754
885
  pageSize,
755
886
  sortColumn,
756
- sortDirection
887
+ sortDirection,
888
+ search
757
889
  };
758
890
  return c.html(renderDatabaseTablePage(pageData));
759
891
  } catch (error) {
@@ -907,6 +1039,7 @@ var EmailSettingsService = class {
907
1039
  constructor(db) {
908
1040
  this.db = db;
909
1041
  }
1042
+ db;
910
1043
  async load() {
911
1044
  let row = null;
912
1045
  try {
@@ -935,6 +1068,8 @@ var SiteConfigService = class {
935
1068
  this.db = db;
936
1069
  this.env = env;
937
1070
  }
1071
+ db;
1072
+ env;
938
1073
  async load() {
939
1074
  const settings = new SettingsService(this.db);
940
1075
  const general = await settings.getGeneralSettings();
@@ -1272,6 +1407,8 @@ var EmailValidationError = class extends Error {
1272
1407
  this.reason = reason;
1273
1408
  this.name = "EmailValidationError";
1274
1409
  }
1410
+ field;
1411
+ reason;
1275
1412
  code = "EMAIL_VALIDATION_FAILED";
1276
1413
  };
1277
1414
 
@@ -1307,6 +1444,9 @@ var CfGraphqlClient = class {
1307
1444
  throw new EmailValidationError("apiToken", "EMAIL_API_TOKEN is required for GraphQL queries");
1308
1445
  }
1309
1446
  }
1447
+ zoneId;
1448
+ apiToken;
1449
+ fetchImpl;
1310
1450
  async queryEmailSends(input) {
1311
1451
  const startIso = new Date(input.startSeconds * 1e3).toISOString();
1312
1452
  const endIso = new Date(input.endSeconds * 1e3).toISOString();
@@ -1369,6 +1509,7 @@ var EmailLogService = class {
1369
1509
  constructor(db) {
1370
1510
  this.db = db;
1371
1511
  }
1512
+ db;
1372
1513
  /**
1373
1514
  * Records a successful submission to CF Email Service. `status` =
1374
1515
  * `'submitted'`, `delivery_state` remains NULL until reconciliation runs.
@@ -1542,6 +1683,8 @@ var ReconciliationService = class {
1542
1683
  this.graphql = graphql;
1543
1684
  this.emailLog = emailLog;
1544
1685
  }
1686
+ graphql;
1687
+ emailLog;
1545
1688
  async run(input = {}) {
1546
1689
  const windowHours = input.windowHours ?? 24;
1547
1690
  const nowMs = input.now ?? Date.now();
@@ -1950,6 +2093,7 @@ var CloudflareEmailProvider = class {
1950
2093
  constructor(binding) {
1951
2094
  this.binding = binding;
1952
2095
  }
2096
+ binding;
1953
2097
  name = "cloudflare";
1954
2098
  isConfigured() {
1955
2099
  return !!this.binding;
@@ -2132,6 +2276,7 @@ var OTPService = class {
2132
2276
  constructor(db) {
2133
2277
  this.db = db;
2134
2278
  }
2279
+ db;
2135
2280
  /**
2136
2281
  * Generate a secure random OTP code
2137
2282
  */
@@ -2766,6 +2911,7 @@ var OAuthService = class {
2766
2911
  constructor(db) {
2767
2912
  this.db = db;
2768
2913
  }
2914
+ db;
2769
2915
  /**
2770
2916
  * Build the authorization redirect URL for a provider.
2771
2917
  */
@@ -3282,6 +3428,7 @@ var EmbeddingService = class {
3282
3428
  constructor(ai) {
3283
3429
  this.ai = ai;
3284
3430
  }
3431
+ ai;
3285
3432
  /**
3286
3433
  * Generate embedding for a single text
3287
3434
  */
@@ -3486,6 +3633,9 @@ var CustomRAGService = class {
3486
3633
  this.embeddingService = new EmbeddingService(ai);
3487
3634
  this.chunkingService = new ChunkingService();
3488
3635
  }
3636
+ db;
3637
+ ai;
3638
+ vectorize;
3489
3639
  embeddingService;
3490
3640
  chunkingService;
3491
3641
  /**
@@ -3776,6 +3926,9 @@ var AISearchService = class {
3776
3926
  console.log("[AISearchService] Custom RAG not available, using keyword search only");
3777
3927
  }
3778
3928
  }
3929
+ db;
3930
+ ai;
3931
+ vectorize;
3779
3932
  customRAG;
3780
3933
  /**
3781
3934
  * Get plugin settings
@@ -4216,6 +4369,9 @@ var IndexManager = class {
4216
4369
  console.log("[IndexManager] Custom RAG initialized");
4217
4370
  }
4218
4371
  }
4372
+ db;
4373
+ ai;
4374
+ vectorize;
4219
4375
  customRAG;
4220
4376
  /**
4221
4377
  * Index all content items within a collection using Custom RAG
@@ -5149,6 +5305,8 @@ var SecurityAuditService = class {
5149
5305
  tenantId: TENANT
5150
5306
  });
5151
5307
  }
5308
+ db;
5309
+ settings;
5152
5310
  docsService;
5153
5311
  async logEvent(event) {
5154
5312
  const doc = await this.docsService.create({
@@ -6092,6 +6250,7 @@ var BruteForceDetector = class {
6092
6250
  this.kv = kv;
6093
6251
  this.settings = settings || DEFAULT_SETTINGS2.bruteForce;
6094
6252
  }
6253
+ kv;
6095
6254
  settings;
6096
6255
  async recordFailedAttempt(ip, email) {
6097
6256
  if (!this.settings.enabled || !this.kv) {
@@ -6644,11 +6803,465 @@ var securityAuditPlugin = definePlugin({
6644
6803
  deactivate: async () => console.log("[SecurityAudit] Plugin deactivated")
6645
6804
  });
6646
6805
 
6806
+ // src/plugins/core-plugins/api-keys-plugin/services/api-key-service.ts
6807
+ var API_KEY_QUERYABLE = [
6808
+ { name: "keyHash", kind: "scalar", type: "text", column: "q_apikey_hash" },
6809
+ { name: "userId", kind: "scalar", type: "text", column: "q_apikey_user_id" },
6810
+ { name: "revoked", kind: "scalar", type: "integer", column: "q_apikey_revoked" }
6811
+ ];
6812
+ var KEY_PREFIX = "sk_";
6813
+ var KEY_BYTES = 24;
6814
+ var DISPLAY_PREFIX_LEN = KEY_PREFIX.length + 8;
6815
+ function generateApiKeySecret() {
6816
+ const bytes = new Uint8Array(KEY_BYTES);
6817
+ crypto.getRandomValues(bytes);
6818
+ let hex = "";
6819
+ for (const b of bytes) hex += b.toString(16).padStart(2, "0");
6820
+ return KEY_PREFIX + hex;
6821
+ }
6822
+ async function hashApiKey(secret) {
6823
+ const data = new TextEncoder().encode(secret);
6824
+ const digest = await crypto.subtle.digest("SHA-256", data);
6825
+ const view = new Uint8Array(digest);
6826
+ let hex = "";
6827
+ for (const b of view) hex += b.toString(16).padStart(2, "0");
6828
+ return hex;
6829
+ }
6830
+ var ApiKeyService = class {
6831
+ constructor(db, tenantId = "default") {
6832
+ this.db = db;
6833
+ this.tenantId = tenantId;
6834
+ }
6835
+ db;
6836
+ tenantId;
6837
+ docService() {
6838
+ return new DocumentsService(this.db, {
6839
+ queryableFields: API_KEY_QUERYABLE,
6840
+ tenantId: this.tenantId,
6841
+ maxVersionsPerRoot: 1
6842
+ });
6843
+ }
6844
+ /** Count a user's active (non-revoked) keys — used to enforce the per-user cap. */
6845
+ async countForUser(userId) {
6846
+ const row = await this.db.prepare(
6847
+ `SELECT COUNT(*) AS c FROM documents
6848
+ WHERE type_id = 'api_key' AND tenant_id = ? AND is_current_draft = 1
6849
+ AND deleted_at IS NULL AND q_apikey_user_id = ? AND q_apikey_revoked = 0`
6850
+ ).bind(this.tenantId, userId).first();
6851
+ return row?.c ?? 0;
6852
+ }
6853
+ /**
6854
+ * Mint a new key for a user. The returned `key` is the only time the plaintext
6855
+ * exists outside the caller — it is hashed before storage.
6856
+ */
6857
+ async create(input) {
6858
+ const name = input.name.trim() || "API Key";
6859
+ const secret = generateApiKeySecret();
6860
+ const keyHash = await hashApiKey(secret);
6861
+ const prefix = secret.slice(0, DISPLAY_PREFIX_LEN);
6862
+ const expiresAt = input.expiresAt ?? null;
6863
+ const doc = await this.docService().create(
6864
+ createDocumentSchema.parse({
6865
+ typeId: "api_key",
6866
+ tenantId: this.tenantId,
6867
+ title: name,
6868
+ ownerId: input.userId,
6869
+ publishOnCreate: true,
6870
+ data: {
6871
+ name,
6872
+ userId: input.userId,
6873
+ keyHash,
6874
+ keyPrefix: prefix,
6875
+ revoked: 0,
6876
+ lastUsedAt: null,
6877
+ expiresAt
6878
+ }
6879
+ }),
6880
+ input.userId
6881
+ );
6882
+ return { id: doc.rootId, key: secret, name, prefix, expiresAt };
6883
+ }
6884
+ /** List a user's non-revoked keys (metadata only — never the secret or hash). */
6885
+ async list(userId) {
6886
+ const res = await this.db.prepare(
6887
+ `SELECT root_id, data, created_at FROM documents
6888
+ WHERE type_id = 'api_key' AND tenant_id = ? AND is_current_draft = 1
6889
+ AND deleted_at IS NULL AND q_apikey_user_id = ? AND q_apikey_revoked = 0
6890
+ ORDER BY created_at DESC`
6891
+ ).bind(this.tenantId, userId).all();
6892
+ return (res.results ?? []).map((row) => {
6893
+ const d = JSON.parse(row.data);
6894
+ return {
6895
+ id: row.root_id,
6896
+ name: d.name ?? "API Key",
6897
+ prefix: d.keyPrefix ?? "",
6898
+ createdAt: row.created_at,
6899
+ lastUsedAt: d.lastUsedAt ?? null,
6900
+ expiresAt: d.expiresAt ?? null
6901
+ };
6902
+ });
6903
+ }
6904
+ /**
6905
+ * Revoke (hard-delete) a key owned by `userId`. Credentials are erased, not
6906
+ * soft-flagged. Returns false if the key does not exist or is not owned by
6907
+ * the caller (no information leak about other users' keys).
6908
+ */
6909
+ async revoke(rootId, userId) {
6910
+ const row = await this.db.prepare(
6911
+ `SELECT q_apikey_user_id AS owner FROM documents
6912
+ WHERE root_id = ? AND tenant_id = ? AND type_id = 'api_key' AND is_current_draft = 1 AND deleted_at IS NULL`
6913
+ ).bind(rootId, this.tenantId).first();
6914
+ if (!row || row.owner !== userId) return false;
6915
+ await this.docService().erase(rootId, this.tenantId);
6916
+ return true;
6917
+ }
6918
+ /**
6919
+ * Resolve a presented secret to its owning user, or null. Rejects unknown,
6920
+ * revoked, expired keys and inactive users. Best-effort updates lastUsedAt.
6921
+ */
6922
+ async resolve(secret) {
6923
+ if (!secret || !secret.startsWith(KEY_PREFIX)) return null;
6924
+ const keyHash = await hashApiKey(secret);
6925
+ const keyRow2 = await this.db.prepare(
6926
+ `SELECT root_id, data FROM documents
6927
+ WHERE type_id = 'api_key' AND tenant_id = ? AND is_current_draft = 1 AND deleted_at IS NULL
6928
+ AND q_apikey_hash = ? AND q_apikey_revoked = 0
6929
+ LIMIT 1`
6930
+ ).bind(this.tenantId, keyHash).first();
6931
+ if (!keyRow2) return null;
6932
+ const d = JSON.parse(keyRow2.data);
6933
+ if (d.expiresAt != null && Date.now() > Number(d.expiresAt)) return null;
6934
+ const user = await this.db.prepare(`SELECT id, email, role, is_active, is_super_admin FROM auth_user WHERE id = ?`).bind(d.userId).first();
6935
+ if (!user || user.is_active !== 1) return null;
6936
+ try {
6937
+ await this.touchLastUsed(keyRow2.root_id, d);
6938
+ } catch {
6939
+ }
6940
+ return {
6941
+ userId: user.id,
6942
+ email: user.email,
6943
+ role: user.role ?? "viewer",
6944
+ isSuperAdmin: user.is_super_admin === 1
6945
+ };
6946
+ }
6947
+ /** Update lastUsedAt in-place (single-version type, no history churn). */
6948
+ async touchLastUsed(rootId, data) {
6949
+ const next = JSON.stringify({ ...data, lastUsedAt: Date.now() });
6950
+ await this.db.prepare(
6951
+ `UPDATE documents SET data = ? WHERE root_id = ? AND tenant_id = ? AND type_id = 'api_key' AND is_current_draft = 1`
6952
+ ).bind(next, rootId, this.tenantId).run();
6953
+ }
6954
+ };
6955
+
6956
+ // src/plugins/core-plugins/api-keys-plugin/components/keys-page.ts
6957
+ init_admin_layout_catalyst_template();
6958
+ function fmtDate(ms) {
6959
+ if (!ms) return "\u2014";
6960
+ try {
6961
+ return new Date(ms).toISOString().slice(0, 10);
6962
+ } catch {
6963
+ return "\u2014";
6964
+ }
6965
+ }
6966
+ function keyRow(k) {
6967
+ const name = escapeHtml(k.name);
6968
+ const prefix = escapeHtml(k.prefix);
6969
+ const id = escapeHtml(k.id);
6970
+ return `
6971
+ <tr class="border-t border-zinc-950/5 dark:border-white/10" data-key-id="${id}">
6972
+ <td class="py-3 pr-4 text-sm font-medium text-zinc-950 dark:text-white">${name}</td>
6973
+ <td class="py-3 pr-4 text-sm font-mono text-zinc-500 dark:text-zinc-400">${prefix}\u2026</td>
6974
+ <td class="py-3 pr-4 text-sm text-zinc-500 dark:text-zinc-400">${fmtDate(k.createdAt ? k.createdAt * 1e3 : null)}</td>
6975
+ <td class="py-3 pr-4 text-sm text-zinc-500 dark:text-zinc-400">${k.lastUsedAt ? fmtDate(k.lastUsedAt) : "Never"}</td>
6976
+ <td class="py-3 pr-4 text-sm text-zinc-500 dark:text-zinc-400">${k.expiresAt ? fmtDate(k.expiresAt) : "Never"}</td>
6977
+ <td class="py-3 text-right">
6978
+ <button type="button" onclick="revokeApiKey('${id}', '${name.replace(/'/g, "\\'")}')"
6979
+ class="rounded-lg bg-red-50 dark:bg-red-900/20 px-3 py-1.5 text-sm font-medium text-red-700 dark:text-red-400 hover:bg-red-100 dark:hover:bg-red-900/40 ring-1 ring-red-200 dark:ring-red-800 transition-colors">
6980
+ Revoke
6981
+ </button>
6982
+ </td>
6983
+ </tr>`;
6984
+ }
6985
+ function renderApiKeysPage(data) {
6986
+ const { keys, user, version, dynamicMenuItems } = data;
6987
+ const rows = keys.length ? keys.map(keyRow).join("") : `<tr><td colspan="6" class="py-8 text-center text-sm text-zinc-500 dark:text-zinc-400">No API keys yet. Create one to enable programmatic access.</td></tr>`;
6988
+ const content2 = `
6989
+ <div>
6990
+ <div class="sm:flex sm:items-center sm:justify-between mb-6">
6991
+ <div class="sm:flex-auto">
6992
+ <h1 class="text-2xl/8 font-semibold text-zinc-950 dark:text-white sm:text-xl/8">API Keys</h1>
6993
+ <p class="mt-2 text-sm/6 text-zinc-500 dark:text-zinc-400">
6994
+ Long-lived secrets for headless / server-to-server REST access. Send a key as the
6995
+ <code class="font-mono">x-api-key</code> header or <code class="font-mono">Authorization: Bearer &lt;key&gt;</code>.
6996
+ The secret is shown once at creation \u2014 store it now.
6997
+ </p>
6998
+ </div>
6999
+ <div class="mt-4 sm:mt-0 sm:ml-16">
7000
+ <button type="button" onclick="document.getElementById('create-key-modal').classList.remove('hidden')"
7001
+ class="inline-flex items-center justify-center rounded-lg bg-cyan-600 px-4 py-2.5 text-sm font-semibold text-white hover:bg-cyan-500 transition-colors shadow-sm">
7002
+ Create API key
7003
+ </button>
7004
+ </div>
7005
+ </div>
7006
+
7007
+ <!-- Newly-created secret banner (populated by JS, shown once) -->
7008
+ <div id="new-key-banner" class="hidden mb-6 rounded-xl bg-emerald-50 dark:bg-emerald-900/20 p-4 ring-1 ring-emerald-200 dark:ring-emerald-800">
7009
+ <p class="text-sm font-medium text-emerald-800 dark:text-emerald-300 mb-2">
7010
+ Copy your new key now \u2014 it will not be shown again.
7011
+ </p>
7012
+ <div class="flex items-center gap-2">
7013
+ <code id="new-key-value" class="flex-1 rounded-lg bg-white dark:bg-zinc-900 px-3 py-2 text-sm font-mono text-zinc-950 dark:text-white ring-1 ring-zinc-950/10 dark:ring-white/10 break-all"></code>
7014
+ <button type="button" onclick="copyNewKey()"
7015
+ class="rounded-lg bg-emerald-600 px-3 py-2 text-sm font-semibold text-white hover:bg-emerald-500 transition-colors">
7016
+ Copy
7017
+ </button>
7018
+ </div>
7019
+ </div>
7020
+
7021
+ <div class="rounded-xl bg-white/80 dark:bg-zinc-900/80 backdrop-blur-xl p-6 ring-1 ring-zinc-950/5 dark:ring-white/10 shadow-sm overflow-x-auto">
7022
+ <table class="w-full text-left">
7023
+ <thead>
7024
+ <tr class="text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400">
7025
+ <th class="pb-2 pr-4">Name</th>
7026
+ <th class="pb-2 pr-4">Prefix</th>
7027
+ <th class="pb-2 pr-4">Created</th>
7028
+ <th class="pb-2 pr-4">Last used</th>
7029
+ <th class="pb-2 pr-4">Expires</th>
7030
+ <th class="pb-2 text-right">Actions</th>
7031
+ </tr>
7032
+ </thead>
7033
+ <tbody id="keys-tbody">${rows}</tbody>
7034
+ </table>
7035
+ </div>
7036
+ </div>
7037
+
7038
+ <!-- Create modal -->
7039
+ <div id="create-key-modal" class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4">
7040
+ <div class="w-full max-w-md rounded-xl bg-white dark:bg-zinc-900 p-6 ring-1 ring-zinc-950/10 dark:ring-white/10 shadow-xl">
7041
+ <h2 class="text-base font-semibold text-zinc-950 dark:text-white mb-4">Create API key</h2>
7042
+ <label class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1">Name</label>
7043
+ <input id="new-key-name" type="text" placeholder="e.g. CI deploy token"
7044
+ class="w-full rounded-lg border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white mb-4 ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10">
7045
+ <label class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1">Expires in (days, optional)</label>
7046
+ <input id="new-key-expiry" type="number" min="1" max="3650" placeholder="Never"
7047
+ class="w-full rounded-lg border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white mb-4 ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10">
7048
+ <div class="flex justify-end gap-2">
7049
+ <button type="button" onclick="document.getElementById('create-key-modal').classList.add('hidden')"
7050
+ class="rounded-lg bg-white dark:bg-zinc-800 px-4 py-2 text-sm font-semibold text-zinc-950 dark:text-white ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10">
7051
+ Cancel
7052
+ </button>
7053
+ <button type="button" onclick="createApiKey()"
7054
+ class="rounded-lg bg-cyan-600 px-4 py-2 text-sm font-semibold text-white hover:bg-cyan-500 transition-colors">
7055
+ Create
7056
+ </button>
7057
+ </div>
7058
+ </div>
7059
+ </div>
7060
+
7061
+ <script>
7062
+ // CSRF: the csrf_token cookie is JS-readable (httpOnly:false); echo it back
7063
+ // as X-CSRF-Token on state-changing requests (signed double-submit).
7064
+ function csrfHeader() {
7065
+ const m = document.cookie.match(/(?:^|; )csrf_token=([^;]+)/)
7066
+ return m ? { 'X-CSRF-Token': decodeURIComponent(m[1]) } : {}
7067
+ }
7068
+ async function createApiKey() {
7069
+ const name = document.getElementById('new-key-name').value.trim()
7070
+ if (!name) { window.showNotification && window.showNotification('Name is required', 'error'); return }
7071
+ const expiry = parseInt(document.getElementById('new-key-expiry').value, 10)
7072
+ const body = { name }
7073
+ if (Number.isFinite(expiry) && expiry > 0) body.expiresInDays = expiry
7074
+ const res = await fetch('/admin/plugins/api-keys/api/keys', {
7075
+ method: 'POST',
7076
+ headers: { 'Content-Type': 'application/json', ...csrfHeader() },
7077
+ body: JSON.stringify(body),
7078
+ })
7079
+ if (!res.ok) {
7080
+ const e = await res.json().catch(() => ({}))
7081
+ window.showNotification && window.showNotification(e.error || 'Failed to create key', 'error')
7082
+ return
7083
+ }
7084
+ const { apiKey } = await res.json()
7085
+ document.getElementById('create-key-modal').classList.add('hidden')
7086
+ document.getElementById('new-key-value').textContent = apiKey.key
7087
+ document.getElementById('new-key-banner').classList.remove('hidden')
7088
+ setTimeout(() => location.reload(), 1500)
7089
+ }
7090
+ function copyNewKey() {
7091
+ const v = document.getElementById('new-key-value').textContent
7092
+ navigator.clipboard.writeText(v).then(() => window.showNotification && window.showNotification('Copied', 'success'))
7093
+ }
7094
+ async function revokeApiKey(id, name) {
7095
+ if (!confirm('Revoke "' + name + '"? This cannot be undone and will break any client using it.')) return
7096
+ const res = await fetch('/admin/plugins/api-keys/api/keys/' + id, { method: 'DELETE', headers: { ...csrfHeader() } })
7097
+ if (res.ok) {
7098
+ const row = document.querySelector('[data-key-id="' + id + '"]')
7099
+ if (row) row.remove()
7100
+ window.showNotification && window.showNotification('Key revoked', 'success')
7101
+ } else {
7102
+ window.showNotification && window.showNotification('Failed to revoke', 'error')
7103
+ }
7104
+ }
7105
+ </script>`;
7106
+ const layoutData = {
7107
+ title: "API Keys",
7108
+ pageTitle: "API Keys",
7109
+ currentPath: "/admin/plugins/api-keys",
7110
+ user,
7111
+ content: content2,
7112
+ version,
7113
+ dynamicMenuItems
7114
+ };
7115
+ return renderAdminLayoutCatalyst(layoutData);
7116
+ }
7117
+
7118
+ // src/plugins/core-plugins/api-keys-plugin/types.ts
7119
+ var DEFAULT_SETTINGS3 = {
7120
+ maxKeysPerUser: 50,
7121
+ defaultExpiryDays: 0
7122
+ };
7123
+
7124
+ // src/plugins/core-plugins/api-keys-plugin/routes/admin.ts
7125
+ var adminRoutes4 = new Hono();
7126
+ var DAY_MS = 24 * 60 * 60 * 1e3;
7127
+ var createSchema = z.object({
7128
+ name: z.string().min(1).max(100),
7129
+ // Optional lifetime in days; omitted/null → plugin default (which may be "never").
7130
+ expiresInDays: z.number().int().positive().max(3650).nullish()
7131
+ });
7132
+ async function getSettings3(db) {
7133
+ try {
7134
+ const plugin2 = await new PluginService(db).getPlugin("api-keys");
7135
+ if (plugin2?.settings) {
7136
+ const s = typeof plugin2.settings === "string" ? JSON.parse(plugin2.settings) : plugin2.settings;
7137
+ return { ...DEFAULT_SETTINGS3, ...s };
7138
+ }
7139
+ } catch {
7140
+ }
7141
+ return DEFAULT_SETTINGS3;
7142
+ }
7143
+ adminRoutes4.use("*", requireAuth());
7144
+ adminRoutes4.use("*", async (c, next) => {
7145
+ const user = c.get("user");
7146
+ if (user?.role !== "admin") {
7147
+ return c.json({ error: "Access denied" }, 403);
7148
+ }
7149
+ return next();
7150
+ });
7151
+ adminRoutes4.get("/", async (c) => {
7152
+ const user = c.get("user");
7153
+ const tenantId = c.get("tenantId") || "default";
7154
+ const keys = await new ApiKeyService(c.env.DB, tenantId).list(user.userId);
7155
+ return c.html(
7156
+ renderApiKeysPage({
7157
+ keys,
7158
+ user: { name: user.email, email: user.email, role: user.role },
7159
+ version: c.get("appVersion"),
7160
+ dynamicMenuItems: c.get("pluginMenuItems")
7161
+ })
7162
+ );
7163
+ });
7164
+ adminRoutes4.get("/api/keys", async (c) => {
7165
+ const user = c.get("user");
7166
+ const tenantId = c.get("tenantId") || "default";
7167
+ const keys = await new ApiKeyService(c.env.DB, tenantId).list(user.userId);
7168
+ return c.json({ keys });
7169
+ });
7170
+ adminRoutes4.post("/api/keys", async (c) => {
7171
+ const user = c.get("user");
7172
+ const tenantId = c.get("tenantId") || "default";
7173
+ const body = await c.req.json().catch(() => ({}));
7174
+ const parsed = createSchema.safeParse(body);
7175
+ if (!parsed.success) {
7176
+ return c.json({ error: "Invalid request", details: parsed.error.flatten() }, 400);
7177
+ }
7178
+ const settings = await getSettings3(c.env.DB);
7179
+ const svc = new ApiKeyService(c.env.DB, tenantId);
7180
+ if (await svc.countForUser(user.userId) >= settings.maxKeysPerUser) {
7181
+ return c.json({ error: `API key limit reached (max ${settings.maxKeysPerUser})` }, 409);
7182
+ }
7183
+ const days = parsed.data.expiresInDays ?? (settings.defaultExpiryDays > 0 ? settings.defaultExpiryDays : null);
7184
+ const expiresAt = days ? Date.now() + days * DAY_MS : null;
7185
+ const created = await svc.create({ userId: user.userId, name: parsed.data.name, expiresAt });
7186
+ return c.json({ apiKey: created }, 201);
7187
+ });
7188
+ adminRoutes4.delete("/api/keys/:id", async (c) => {
7189
+ const user = c.get("user");
7190
+ const tenantId = c.get("tenantId") || "default";
7191
+ const ok = await new ApiKeyService(c.env.DB, tenantId).revoke(c.req.param("id") ?? "", user.userId);
7192
+ if (!ok) return c.json({ error: "API key not found" }, 404);
7193
+ return c.json({ success: true });
7194
+ });
7195
+
7196
+ // src/plugins/core-plugins/api-keys-plugin/middleware/api-key-auth.ts
7197
+ function apiKeyAuthMiddleware() {
7198
+ return async (c, next) => {
7199
+ if (!c.get("user")) {
7200
+ const authz = c.req.header("authorization") || "";
7201
+ const bearer = authz.toLowerCase().startsWith("bearer ") ? authz.slice(7).trim() : "";
7202
+ const presented = c.req.header("x-api-key") || (bearer.startsWith("sk_") ? bearer : "");
7203
+ if (presented) {
7204
+ try {
7205
+ const tenantId = c.get("tenantId") || "default";
7206
+ const resolved = await new ApiKeyService(c.env.DB, tenantId).resolve(presented);
7207
+ if (resolved) {
7208
+ c.set("user", {
7209
+ userId: resolved.userId,
7210
+ email: resolved.email,
7211
+ role: resolved.role,
7212
+ isSuperAdmin: resolved.isSuperAdmin,
7213
+ // API keys are stateless bearer creds — no session window.
7214
+ exp: 0,
7215
+ iat: 0
7216
+ });
7217
+ }
7218
+ } catch {
7219
+ }
7220
+ }
7221
+ }
7222
+ await next();
7223
+ };
7224
+ }
7225
+
7226
+ // src/plugins/core-plugins/api-keys-plugin/index.ts
7227
+ var KEY_ICON = `<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"/></svg>`;
7228
+ var apiKeysPlugin = definePlugin({
7229
+ id: "api-keys",
7230
+ version: "1.0.0-beta.1",
7231
+ name: "API Keys",
7232
+ description: "Programmatic API access keys for headless / server-to-server REST access.",
7233
+ sonicjsVersionRange: "^3.0.0",
7234
+ author: { name: "SonicJS Team" },
7235
+ register(app2) {
7236
+ app2.route("/admin/plugins/api-keys", adminRoutes4);
7237
+ },
7238
+ menu: [
7239
+ { label: "API Keys", path: "/admin/plugins/api-keys", icon: KEY_ICON, order: 86 }
7240
+ ],
7241
+ configSchema: {
7242
+ maxKeysPerUser: {
7243
+ type: "number",
7244
+ label: "Max keys per user",
7245
+ description: "Hard cap on active keys a single user may hold.",
7246
+ default: 50
7247
+ },
7248
+ defaultExpiryDays: {
7249
+ type: "number",
7250
+ label: "Default expiry (days)",
7251
+ description: "Lifetime applied to new keys when no explicit expiry is given. 0 = never expires.",
7252
+ default: 0
7253
+ }
7254
+ },
7255
+ activate: async () => console.log("[ApiKeys] Plugin activated"),
7256
+ deactivate: async () => console.log("[ApiKeys] Plugin deactivated")
7257
+ });
7258
+
6647
7259
  // src/plugins/core-plugins/stripe-plugin/services/subscription-service.ts
6648
7260
  var SubscriptionService = class {
6649
7261
  constructor(db) {
6650
7262
  this.db = db;
6651
7263
  }
7264
+ db;
6652
7265
  /**
6653
7266
  * Ensure the subscriptions table exists
6654
7267
  */
@@ -6887,6 +7500,7 @@ var StripeEventService = class {
6887
7500
  constructor(db) {
6888
7501
  this.db = db;
6889
7502
  }
7503
+ db;
6890
7504
  async ensureTable() {
6891
7505
  await this.db.prepare(`
6892
7506
  CREATE TABLE IF NOT EXISTS stripe_events (
@@ -7371,7 +7985,7 @@ function renderEventPagination(page, totalPages, type, status) {
7371
7985
  }
7372
7986
 
7373
7987
  // src/plugins/core-plugins/stripe-plugin/types.ts
7374
- var DEFAULT_SETTINGS3 = {
7988
+ var DEFAULT_SETTINGS4 = {
7375
7989
  stripePublishableKey: "",
7376
7990
  stripeSecretKey: "",
7377
7991
  stripeWebhookSecret: "",
@@ -7381,28 +7995,28 @@ var DEFAULT_SETTINGS3 = {
7381
7995
  };
7382
7996
 
7383
7997
  // src/plugins/core-plugins/stripe-plugin/routes/admin.ts
7384
- var adminRoutes4 = new Hono();
7385
- adminRoutes4.use("*", requireAuth());
7386
- adminRoutes4.use("*", async (c, next) => {
7998
+ var adminRoutes5 = new Hono();
7999
+ adminRoutes5.use("*", requireAuth());
8000
+ adminRoutes5.use("*", async (c, next) => {
7387
8001
  const user = c.get("user");
7388
8002
  if (user?.role !== "admin") {
7389
8003
  return c.text("Access denied", 403);
7390
8004
  }
7391
8005
  return next();
7392
8006
  });
7393
- async function getSettings3(db) {
8007
+ async function getSettings4(db) {
7394
8008
  try {
7395
8009
  const pluginService = new PluginService(db);
7396
8010
  const plugin2 = await pluginService.getPlugin("stripe");
7397
8011
  if (plugin2?.settings) {
7398
8012
  const settings = typeof plugin2.settings === "string" ? JSON.parse(plugin2.settings) : plugin2.settings;
7399
- return { ...DEFAULT_SETTINGS3, ...settings };
8013
+ return { ...DEFAULT_SETTINGS4, ...settings };
7400
8014
  }
7401
8015
  } catch {
7402
8016
  }
7403
- return DEFAULT_SETTINGS3;
8017
+ return DEFAULT_SETTINGS4;
7404
8018
  }
7405
- adminRoutes4.get("/", async (c) => {
8019
+ adminRoutes5.get("/", async (c) => {
7406
8020
  const db = c.env.DB;
7407
8021
  const user = c.get("user");
7408
8022
  const subscriptionService = new SubscriptionService(db);
@@ -7425,7 +8039,7 @@ adminRoutes4.get("/", async (c) => {
7425
8039
  });
7426
8040
  return c.html(html4);
7427
8041
  });
7428
- adminRoutes4.get("/events", async (c) => {
8042
+ adminRoutes5.get("/events", async (c) => {
7429
8043
  const db = c.env.DB;
7430
8044
  const user = c.get("user");
7431
8045
  const eventService = new StripeEventService(db);
@@ -7451,11 +8065,11 @@ adminRoutes4.get("/events", async (c) => {
7451
8065
  });
7452
8066
  return c.html(html4);
7453
8067
  });
7454
- adminRoutes4.get("/settings", async (c) => {
8068
+ adminRoutes5.get("/settings", async (c) => {
7455
8069
  const db = c.env.DB;
7456
8070
  const user = c.get("user");
7457
- const settings = await getSettings3(db);
7458
- const { renderAdminLayoutCatalyst: renderAdminLayoutCatalyst2 } = await import('./admin-layout-catalyst.template-WHJGSWWD.js');
8071
+ const settings = await getSettings4(db);
8072
+ const { renderAdminLayoutCatalyst: renderAdminLayoutCatalyst2 } = await import('./admin-layout-catalyst.template-YQ4EMF2J.js');
7459
8073
  const content2 = `
7460
8074
  <div>
7461
8075
  <div class="mb-6">
@@ -7561,7 +8175,7 @@ adminRoutes4.get("/settings", async (c) => {
7561
8175
  dynamicMenuItems: c.get("pluginMenuItems")
7562
8176
  }));
7563
8177
  });
7564
- adminRoutes4.post("/settings", async (c) => {
8178
+ adminRoutes5.post("/settings", async (c) => {
7565
8179
  try {
7566
8180
  const body = await c.req.json();
7567
8181
  const db = c.env.DB;
@@ -7583,6 +8197,7 @@ var StripeAPI = class {
7583
8197
  constructor(secretKey) {
7584
8198
  this.secretKey = secretKey;
7585
8199
  }
8200
+ secretKey;
7586
8201
  baseUrl = "https://api.stripe.com/v1";
7587
8202
  /**
7588
8203
  * Verify a webhook signature
@@ -7712,17 +8327,17 @@ function timingSafeEqual(a, b) {
7712
8327
 
7713
8328
  // src/plugins/core-plugins/stripe-plugin/routes/api.ts
7714
8329
  var apiRoutes3 = new Hono();
7715
- async function getSettings4(db) {
8330
+ async function getSettings5(db) {
7716
8331
  try {
7717
8332
  const pluginService = new PluginService(db);
7718
8333
  const plugin2 = await pluginService.getPlugin("stripe");
7719
8334
  if (plugin2?.settings) {
7720
8335
  const settings = typeof plugin2.settings === "string" ? JSON.parse(plugin2.settings) : plugin2.settings;
7721
- return { ...DEFAULT_SETTINGS3, ...settings };
8336
+ return { ...DEFAULT_SETTINGS4, ...settings };
7722
8337
  }
7723
8338
  } catch {
7724
8339
  }
7725
- return DEFAULT_SETTINGS3;
8340
+ return DEFAULT_SETTINGS4;
7726
8341
  }
7727
8342
  function mapStripeStatus(status) {
7728
8343
  const map = {
@@ -7739,7 +8354,7 @@ function mapStripeStatus(status) {
7739
8354
  }
7740
8355
  apiRoutes3.post("/webhook", async (c) => {
7741
8356
  const db = c.env.DB;
7742
- const settings = await getSettings4(db);
8357
+ const settings = await getSettings5(db);
7743
8358
  if (!settings.stripeWebhookSecret) {
7744
8359
  return c.json({ error: "Webhook secret not configured" }, 500);
7745
8360
  }
@@ -7869,7 +8484,7 @@ apiRoutes3.post("/create-checkout-session", requireAuth(), async (c) => {
7869
8484
  const db = c.env.DB;
7870
8485
  const user = c.get("user");
7871
8486
  if (!user) return c.json({ error: "Unauthorized" }, 401);
7872
- const settings = await getSettings4(db);
8487
+ const settings = await getSettings5(db);
7873
8488
  if (!settings.stripeSecretKey) {
7874
8489
  return c.json({ error: "Stripe not configured" }, 500);
7875
8490
  }
@@ -7946,7 +8561,7 @@ apiRoutes3.post("/sync-subscriptions", requireAuth(), async (c) => {
7946
8561
  const user = c.get("user");
7947
8562
  if (user?.role !== "admin") return c.json({ error: "Access denied" }, 403);
7948
8563
  const db = c.env.DB;
7949
- const settings = await getSettings4(db);
8564
+ const settings = await getSettings5(db);
7950
8565
  if (!settings.stripeSecretKey) {
7951
8566
  return c.json({ error: "Stripe secret key not configured" }, 400);
7952
8567
  }
@@ -8021,7 +8636,7 @@ var stripePlugin = definePlugin({
8021
8636
  sonicjsVersionRange: "^3.0.0",
8022
8637
  author: { name: "SonicJS Team" },
8023
8638
  register(app2) {
8024
- app2.route("/admin/plugins/stripe", adminRoutes4);
8639
+ app2.route("/admin/plugins/stripe", adminRoutes5);
8025
8640
  app2.route("/api/stripe", apiRoutes3);
8026
8641
  },
8027
8642
  menu: [
@@ -9714,6 +10329,7 @@ function renderFormCreatePage(data) {
9714
10329
  }
9715
10330
 
9716
10331
  // src/routes/admin-forms.ts
10332
+ var isTableMissing = (err) => String(err?.message ?? err?.cause?.message ?? "").includes("no such table: forms");
9717
10333
  var adminFormsRoutes = new Hono();
9718
10334
  adminFormsRoutes.use("*", requireAuth());
9719
10335
  adminFormsRoutes.get("/", async (c) => {
@@ -9755,6 +10371,14 @@ adminFormsRoutes.get("/", async (c) => {
9755
10371
  };
9756
10372
  return c.html(renderFormsListPage(pageData));
9757
10373
  } catch (error) {
10374
+ if (isTableMissing(error)) {
10375
+ const user = c.get("user");
10376
+ return c.html(renderFormsListPage({
10377
+ forms: [],
10378
+ user: user ? { name: user.email, email: user.email, role: user.role } : void 0,
10379
+ version: c.get("appVersion")
10380
+ }));
10381
+ }
9758
10382
  console.error("Error listing forms:", error);
9759
10383
  return c.html("<p>Error loading forms</p>", 500);
9760
10384
  }
@@ -9865,6 +10489,7 @@ adminFormsRoutes.post("/", async (c) => {
9865
10489
  ).run();
9866
10490
  return c.redirect(`/admin/forms/${formId}/builder`);
9867
10491
  } catch (error) {
10492
+ if (isTableMissing(error)) return c.json({ error: "Forms storage not configured \u2014 run database migrations" }, 503);
9868
10493
  console.error("Error creating form:", error);
9869
10494
  return c.json({ error: "Failed to create form" }, 500);
9870
10495
  }
@@ -9902,6 +10527,7 @@ adminFormsRoutes.get("/:id/builder", async (c) => {
9902
10527
  };
9903
10528
  return c.html(renderFormBuilderPage(pageData));
9904
10529
  } catch (error) {
10530
+ if (isTableMissing(error)) return c.html("<p>Forms storage not configured \u2014 run database migrations</p>", 503);
9905
10531
  console.error("Error showing form builder:", error);
9906
10532
  return c.html("<p>Error loading form builder</p>", 500);
9907
10533
  }
@@ -9931,6 +10557,7 @@ adminFormsRoutes.put("/:id", async (c) => {
9931
10557
  ).run();
9932
10558
  return c.json({ success: true, message: "Form saved successfully" });
9933
10559
  } catch (error) {
10560
+ if (isTableMissing(error)) return c.json({ error: "Forms storage not configured \u2014 run database migrations" }, 503);
9934
10561
  console.error("Error updating form:", error);
9935
10562
  return c.json({ error: "Failed to save form" }, 500);
9936
10563
  }
@@ -9952,6 +10579,7 @@ adminFormsRoutes.delete("/:id", async (c) => {
9952
10579
  await db.prepare("DELETE FROM forms WHERE id = ?").bind(formId).run();
9953
10580
  return c.json({ success: true, message: "Form deleted successfully" });
9954
10581
  } catch (error) {
10582
+ if (isTableMissing(error)) return c.json({ error: "Forms storage not configured \u2014 run database migrations" }, 503);
9955
10583
  console.error("Error deleting form:", error);
9956
10584
  return c.json({ error: "Failed to delete form" }, 500);
9957
10585
  }
@@ -10012,6 +10640,7 @@ adminFormsRoutes.get("/:id/submissions", async (c) => {
10012
10640
  `;
10013
10641
  return c.html(html4);
10014
10642
  } catch (error) {
10643
+ if (isTableMissing(error)) return c.html("<p>Forms storage not configured \u2014 run database migrations</p>", 503);
10015
10644
  console.error("Error loading submissions:", error);
10016
10645
  return c.html("<p>Error loading submissions</p>", 500);
10017
10646
  }
@@ -10032,6 +10661,7 @@ function sanitizeDeep(value) {
10032
10661
  }
10033
10662
  return value;
10034
10663
  }
10664
+ var isTableMissing2 = (err) => String(err?.message ?? err?.cause?.message ?? "").includes("no such table: form");
10035
10665
  var publicFormsRoutes = new Hono();
10036
10666
  publicFormsRoutes.get("/:identifier/turnstile-config", async (c) => {
10037
10667
  try {
@@ -10059,6 +10689,7 @@ publicFormsRoutes.get("/:identifier/turnstile-config", async (c) => {
10059
10689
  appearance: formSettings.appearance || globalSettings.appearance || "always"
10060
10690
  });
10061
10691
  } catch (error) {
10692
+ if (isTableMissing2(error)) return c.json({ enabled: false });
10062
10693
  console.error("Error fetching Turnstile config:", error);
10063
10694
  return c.json({ error: "Failed to fetch config" }, 500);
10064
10695
  }
@@ -10086,6 +10717,7 @@ publicFormsRoutes.get("/:identifier/schema", async (c) => {
10086
10717
  submitUrl: `/api/forms/${form.id}/submit`
10087
10718
  });
10088
10719
  } catch (error) {
10720
+ if (isTableMissing2(error)) return c.json({ error: "Form not found" }, 404);
10089
10721
  console.error("Error fetching form schema:", error);
10090
10722
  return c.json({ error: "Failed to fetch form schema" }, 500);
10091
10723
  }
@@ -10452,6 +11084,7 @@ publicFormsRoutes.get("/:name", async (c) => {
10452
11084
  `;
10453
11085
  return c.html(html4);
10454
11086
  } catch (error) {
11087
+ if (isTableMissing2(error)) return c.html("<h1>Form not found</h1><p>This form does not exist or is not publicly available.</p>", 404);
10455
11088
  console.error("Error rendering form:", error);
10456
11089
  return c.html("<p>Error loading form</p>", 500);
10457
11090
  }
@@ -10525,6 +11158,7 @@ publicFormsRoutes.post("/:identifier/submit", async (c) => {
10525
11158
  message: "Form submitted successfully"
10526
11159
  });
10527
11160
  } catch (error) {
11161
+ if (isTableMissing2(error)) return c.json({ error: "Form not found" }, 404);
10528
11162
  console.error("Error submitting form:", error);
10529
11163
  return c.json({ error: "Failed to submit form" }, 500);
10530
11164
  }
@@ -10672,7 +11306,7 @@ adminRbacRoutes.get("/", async (c) => {
10672
11306
  ${r.is_system ? `<code class="text-xs text-zinc-500 dark:text-zinc-400">${esc(r.name)}</code><span class="rounded-full bg-zinc-100 dark:bg-white/10 px-2 py-0.5 text-[11px] text-zinc-500 dark:text-zinc-400">system</span>` : `<input form="roles-bulk-form" class="${inpSm} w-28" name="name_${esc(r.id)}" value="${esc(r.name)}" required>`}
10673
11307
  <label class="ml-auto inline-flex items-center gap-2 rounded-md bg-white/5 px-2.5 py-1 text-xs font-medium text-zinc-700 dark:text-zinc-300 ring-1 ring-inset ring-zinc-500/20">
10674
11308
  <input form="roles-bulk-form" type="checkbox" name="portal_${esc(r.id)}" value="1" ${portalChecked ? "checked" : ""} ${portalDisabled ? "disabled" : ""} class="h-3.5 w-3.5 rounded border-zinc-400 text-cyan-600 focus:ring-cyan-500">
10675
- Access portal
11309
+ Admin Panel
10676
11310
  </label>
10677
11311
  <a href="/admin/tenants/roles/${esc(r.name)}" data-role-tenants="${esc(r.name)}" class="text-xs text-cyan-600 dark:text-cyan-400 hover:underline" title="Per-tenant assignments of this role">tenants</a>
10678
11312
  ${r.is_system ? "" : `<button form="delete-role-${esc(r.id)}" type="submit" onclick="return confirm('Delete role ${esc(r.name)}?')" class="text-xs text-red-600 dark:text-red-400 hover:underline">delete</button>`}
@@ -10776,7 +11410,7 @@ adminRbacRoutes.get("/", async (c) => {
10776
11410
  <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
10777
11411
  <div class="${card}">
10778
11412
  <h3 class="text-base font-semibold text-zinc-950 dark:text-white mb-3">Roles</h3>
10779
- <p class="mb-3 text-xs text-zinc-500 dark:text-zinc-400">Use <strong>Access portal</strong> to allow a role into the admin backend. Resource permissions are configured in the <strong>Matrix</strong> tab.</p>
11413
+ <p class="mb-3 text-xs text-zinc-500 dark:text-zinc-400">Use <strong>Admin Panel</strong> to allow a role into the admin backend. Resource permissions are configured in the <strong>Matrix</strong> tab.</p>
10780
11414
  ${roleList}
10781
11415
  </div>
10782
11416
  <div class="${card}">
@@ -11074,14 +11708,24 @@ function pluginMenuMiddleware() {
11074
11708
  }
11075
11709
  } catch {
11076
11710
  }
11077
- c.set("pluginMenuItems", activeMenuItems.map((m) => ({ label: m.label, path: m.path, icon: resolveIcon(m.icon) || "" })));
11711
+ const user = c.get("user");
11712
+ const singletonItems = resolvePluginMenuItems(user ? { role: user.role } : void 0);
11713
+ const existingPaths = new Set(activeMenuItems.map((i) => i.path));
11714
+ for (const item of singletonItems) {
11715
+ if (!existingPaths.has(item.path)) {
11716
+ activeMenuItems.push({ label: item.label, path: item.path, icon: item.icon, order: item.order });
11717
+ }
11718
+ }
11719
+ activeMenuItems.sort((a, b) => a.order - b.order);
11720
+ c.set("pluginMenuItems", activeMenuItems.map((m) => ({ label: m.label, path: m.path, icon: resolveIcon(m.icon) || m.icon || "" })));
11078
11721
  await next();
11079
- if (activeMenuItems.length > 0 && c.res.headers.get("content-type")?.includes("text/html")) {
11722
+ const finalItems = c.get("pluginMenuItems") ?? [];
11723
+ if (finalItems.length > 0 && c.res.headers.get("content-type")?.includes("text/html")) {
11080
11724
  const status = c.res.status;
11081
11725
  const headers = new Headers(c.res.headers);
11082
11726
  const html4 = await c.res.text();
11083
11727
  if (html4.includes(MARKER)) {
11084
- const renderedItems = activeMenuItems.map((item) => renderMenuItem(item, path)).join("");
11728
+ const renderedItems = finalItems.map((item) => renderMenuItem(item, path)).join("");
11085
11729
  const newHtml = html4.split(MARKER).join(renderedItems);
11086
11730
  c.res = new Response(newHtml, { status, headers });
11087
11731
  } else {
@@ -11091,16 +11735,16 @@ function pluginMenuMiddleware() {
11091
11735
  };
11092
11736
  }
11093
11737
  init_admin_layout_catalyst_template();
11094
- var adminRoutes5 = new Hono();
11095
- adminRoutes5.use("*", requireAuth());
11096
- adminRoutes5.use("*", async (c, next) => {
11738
+ var adminRoutes6 = new Hono();
11739
+ adminRoutes6.use("*", requireAuth());
11740
+ adminRoutes6.use("*", async (c, next) => {
11097
11741
  const user = c.get("user");
11098
11742
  if (user?.role !== "admin") {
11099
11743
  return c.text("Access denied", 403);
11100
11744
  }
11101
11745
  return next();
11102
11746
  });
11103
- adminRoutes5.get("/", async (c) => {
11747
+ adminRoutes6.get("/", async (c) => {
11104
11748
  const user = c.get("user");
11105
11749
  const db = c.env.DB;
11106
11750
  let totalRequests = 0;
@@ -11245,6 +11889,7 @@ var EventTrackingService = class {
11245
11889
  tenantId: TENANT2
11246
11890
  });
11247
11891
  }
11892
+ db;
11248
11893
  docsService;
11249
11894
  async trackEvent(input) {
11250
11895
  const doc = await this.docsService.create({
@@ -11456,7 +12101,7 @@ var analyticsPlugin = definePlugin({
11456
12101
  author: { name: "SonicJS Team", email: "team@sonicjs.com" },
11457
12102
  register(app2) {
11458
12103
  app2.route("/api/events", apiRoutes4);
11459
- app2.route("/admin/analytics", adminRoutes5);
12104
+ app2.route("/admin/analytics", adminRoutes6);
11460
12105
  },
11461
12106
  menu: [
11462
12107
  {
@@ -11738,8 +12383,8 @@ apiRoutes5.delete("/:id", async (c) => {
11738
12383
  return c.json({ success: false, error: "Failed to delete variable" }, 500);
11739
12384
  }
11740
12385
  });
11741
- var adminRoutes6 = new Hono();
11742
- adminRoutes6.use("*", async (c, next) => {
12386
+ var adminRoutes7 = new Hono();
12387
+ adminRoutes7.use("*", async (c, next) => {
11743
12388
  try {
11744
12389
  const db = c.env?.DB;
11745
12390
  if (db) {
@@ -11750,7 +12395,7 @@ adminRoutes6.use("*", async (c, next) => {
11750
12395
  }
11751
12396
  await next();
11752
12397
  });
11753
- adminRoutes6.get("/", async (c) => {
12398
+ adminRoutes7.get("/", async (c) => {
11754
12399
  const db = c.env.DB;
11755
12400
  let variables = [];
11756
12401
  try {
@@ -11780,7 +12425,7 @@ adminRoutes6.get("/", async (c) => {
11780
12425
  }
11781
12426
  return c.html(renderAdminPage(variables, { editorActive, activeEditorName, enableEditorIntegration }));
11782
12427
  });
11783
- adminRoutes6.put("/:id", async (c) => {
12428
+ adminRoutes7.put("/:id", async (c) => {
11784
12429
  const db = c.env.DB;
11785
12430
  const id = c.req.param("id");
11786
12431
  let body;
@@ -11796,7 +12441,7 @@ adminRoutes6.put("/:id", async (c) => {
11796
12441
  }
11797
12442
  return c.html('<span class="text-green-400 text-xs">Saved</span>');
11798
12443
  });
11799
- adminRoutes6.post("/:id/toggle", async (c) => {
12444
+ adminRoutes7.post("/:id/toggle", async (c) => {
11800
12445
  const db = c.env.DB;
11801
12446
  const id = c.req.param("id");
11802
12447
  await db.prepare("UPDATE global_variables SET is_active = CASE WHEN is_active = 1 THEN 0 ELSE 1 END, updated_at = strftime('%s', 'now') WHERE id = ?").bind(id).run();
@@ -11804,7 +12449,7 @@ adminRoutes6.post("/:id/toggle", async (c) => {
11804
12449
  c.header("HX-Redirect", "/admin/global-variables");
11805
12450
  return c.body(null, 204);
11806
12451
  });
11807
- adminRoutes6.post("/", async (c) => {
12452
+ adminRoutes7.post("/", async (c) => {
11808
12453
  const db = c.env.DB;
11809
12454
  const form = await c.req.parseBody();
11810
12455
  const key = (form.key || "").trim();
@@ -11825,7 +12470,7 @@ adminRoutes6.post("/", async (c) => {
11825
12470
  c.header("HX-Redirect", "/admin/global-variables");
11826
12471
  return c.body(null, 204);
11827
12472
  });
11828
- adminRoutes6.post("/settings/editor-integration", async (c) => {
12473
+ adminRoutes7.post("/settings/editor-integration", async (c) => {
11829
12474
  const db = c.env.DB;
11830
12475
  try {
11831
12476
  const row = await db.prepare("SELECT settings FROM plugins WHERE id = 'global-variables'").first();
@@ -11837,7 +12482,7 @@ adminRoutes6.post("/settings/editor-integration", async (c) => {
11837
12482
  return c.json({ success: false, error: "Failed to update setting" }, 500);
11838
12483
  }
11839
12484
  });
11840
- adminRoutes6.delete("/:id", async (c) => {
12485
+ adminRoutes7.delete("/:id", async (c) => {
11841
12486
  const db = c.env.DB;
11842
12487
  await db.prepare("DELETE FROM global_variables WHERE id = ?").bind(c.req.param("id")).run();
11843
12488
  invalidateVariablesCache();
@@ -12168,7 +12813,7 @@ var globalVariablesPlugin = definePlugin({
12168
12813
  capabilities: ["hooks.content:subscribe"],
12169
12814
  register(app2) {
12170
12815
  app2.route("/api/global-variables", apiRoutes5);
12171
- app2.route("/admin/global-variables", adminRoutes6);
12816
+ app2.route("/admin/global-variables", adminRoutes7);
12172
12817
  },
12173
12818
  menu: [
12174
12819
  { label: "Global Variables", path: "/admin/global-variables", icon: "bolt", order: 45, permissions: ["global-variables:view"] }
@@ -12489,8 +13134,8 @@ apiRoutes6.delete("/:id", async (c) => {
12489
13134
  return c.json({ success: false, error: "Failed to delete shortcode" }, 500);
12490
13135
  }
12491
13136
  });
12492
- var adminRoutes7 = new Hono();
12493
- adminRoutes7.use("*", async (c, next) => {
13137
+ var adminRoutes8 = new Hono();
13138
+ adminRoutes8.use("*", async (c, next) => {
12494
13139
  try {
12495
13140
  const db = c.env?.DB;
12496
13141
  if (db) {
@@ -12501,7 +13146,7 @@ adminRoutes7.use("*", async (c, next) => {
12501
13146
  }
12502
13147
  await next();
12503
13148
  });
12504
- adminRoutes7.get("/", async (c) => {
13149
+ adminRoutes8.get("/", async (c) => {
12505
13150
  const db = c.env.DB;
12506
13151
  let shortcodes = [];
12507
13152
  try {
@@ -12531,7 +13176,7 @@ adminRoutes7.get("/", async (c) => {
12531
13176
  }
12532
13177
  return c.html(renderAdminPage2(shortcodes, { editorActive, activeEditorName, enableEditorIntegration }));
12533
13178
  });
12534
- adminRoutes7.post("/", async (c) => {
13179
+ adminRoutes8.post("/", async (c) => {
12535
13180
  const db = c.env.DB;
12536
13181
  const form = await c.req.parseBody();
12537
13182
  const name = (form.name || "").trim();
@@ -12552,7 +13197,7 @@ adminRoutes7.post("/", async (c) => {
12552
13197
  c.header("HX-Redirect", "/admin/shortcodes");
12553
13198
  return c.body(null, 204);
12554
13199
  });
12555
- adminRoutes7.post("/settings/editor-integration", async (c) => {
13200
+ adminRoutes8.post("/settings/editor-integration", async (c) => {
12556
13201
  const db = c.env.DB;
12557
13202
  try {
12558
13203
  const row = await db.prepare("SELECT settings FROM plugins WHERE id = 'shortcodes'").first();
@@ -12564,7 +13209,7 @@ adminRoutes7.post("/settings/editor-integration", async (c) => {
12564
13209
  return c.json({ success: false, error: "Failed to update setting" }, 500);
12565
13210
  }
12566
13211
  });
12567
- adminRoutes7.delete("/:id", async (c) => {
13212
+ adminRoutes8.delete("/:id", async (c) => {
12568
13213
  await c.env.DB.prepare("DELETE FROM shortcodes WHERE id = ?").bind(c.req.param("id")).run();
12569
13214
  return c.html("");
12570
13215
  });
@@ -12775,7 +13420,7 @@ var shortcodesPlugin = definePlugin({
12775
13420
  capabilities: ["hooks.content:subscribe"],
12776
13421
  register(app2) {
12777
13422
  app2.route("/api/shortcodes", apiRoutes6);
12778
- app2.route("/admin/shortcodes", adminRoutes7);
13423
+ app2.route("/admin/shortcodes", adminRoutes8);
12779
13424
  },
12780
13425
  menu: [
12781
13426
  { label: "Shortcodes", path: "/admin/shortcodes", icon: "bolt", order: 46, permissions: ["shortcodes:view"] }
@@ -12884,6 +13529,9 @@ var helloWorldPlugin = definePlugin({
12884
13529
  console.info("\u274C Hello World plugin deactivated");
12885
13530
  }
12886
13531
  });
13532
+ function createHelloWorldPlugin() {
13533
+ return helloWorldPlugin;
13534
+ }
12887
13535
 
12888
13536
  // src/plugins/core-plugins/multi-tenant-plugin/manifest.json
12889
13537
  var manifest_default2 = {
@@ -16057,6 +16705,7 @@ function createSonicJSApp(config = {}) {
16057
16705
  const magicLinkPlugin = createMagicLinkAuthPlugin();
16058
16706
  const corePluginsBeforeCatchAll = [
16059
16707
  securityAuditPlugin,
16708
+ apiKeysPlugin,
16060
16709
  aiSearchPlugin,
16061
16710
  oauthProvidersPlugin,
16062
16711
  userProfilesPlugin,
@@ -16154,6 +16803,17 @@ function createSonicJSApp(config = {}) {
16154
16803
  app2.use("*", async (_c, next) => {
16155
16804
  await next();
16156
16805
  });
16806
+ app2.use("*", cors({
16807
+ origin: (origin, c) => {
16808
+ const allowed = c.env?.CORS_ORIGINS;
16809
+ if (!allowed) return null;
16810
+ const list = allowed.split(",").map((s) => s.trim());
16811
+ return list.includes(origin) ? origin : null;
16812
+ },
16813
+ allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
16814
+ allowHeaders: ["Content-Type", "Authorization", "X-API-Key"],
16815
+ credentials: true
16816
+ }));
16157
16817
  app2.use("*", securityHeadersMiddleware());
16158
16818
  app2.use("*", csrfProtection());
16159
16819
  app2.use("*", async (c, next) => {
@@ -16187,6 +16847,7 @@ function createSonicJSApp(config = {}) {
16187
16847
  }
16188
16848
  await next();
16189
16849
  });
16850
+ app2.use("*", apiKeyAuthMiddleware());
16190
16851
  if (config.middleware?.afterAuth) {
16191
16852
  for (const middleware of config.middleware.afterAuth) {
16192
16853
  app2.use("*", middleware);
@@ -16211,7 +16872,7 @@ function createSonicJSApp(config = {}) {
16211
16872
  if (!user?.userId) return next();
16212
16873
  let perms = [];
16213
16874
  try {
16214
- const { RbacService: RbacService2 } = await import('./rbac-O73MFKDA.js');
16875
+ const { RbacService: RbacService2 } = await import('./rbac-Y43IEQOP.js');
16215
16876
  perms = await new RbacService2(c.env.DB, c.env.CACHE_KV).permissionsForUser(user.userId);
16216
16877
  } catch {
16217
16878
  return next();
@@ -16285,7 +16946,10 @@ function createSonicJSApp(config = {}) {
16285
16946
  ...corePluginsAfterCatchAll,
16286
16947
  ...config.plugins?.register ?? []
16287
16948
  ];
16288
- setPluginMenu(allMountedPlugins.flatMap((p) => p.menu ?? []));
16949
+ const userPlugins = (config.plugins?.register ?? []).filter(
16950
+ (p) => !PLUGIN_REGISTRY[p.id]
16951
+ );
16952
+ setPluginMenu(userPlugins.flatMap((p) => p.menu ?? []));
16289
16953
  setPluginDefinitions(allMountedPlugins);
16290
16954
  }
16291
16955
  app2.get("/favicon.svg", (c) => {
@@ -17150,6 +17814,8 @@ var RedirectService = class {
17150
17814
  this.cloudflareService = new CloudflareBulkService(env);
17151
17815
  }
17152
17816
  }
17817
+ db;
17818
+ env;
17153
17819
  cloudflareService = null;
17154
17820
  getDocService() {
17155
17821
  return new DocumentsService(this.db, {
@@ -19914,6 +20580,6 @@ function createDb(d1) {
19914
20580
  // src/index.ts
19915
20581
  var VERSION = package_default.version;
19916
20582
 
19917
- export { BUILT_IN_PROVIDERS, CloudflareEmailProvider2 as CloudflareEmailProvider, OAuthService, VERSION, createDb, createOAuthProvidersPlugin, createRedirectPlugin, createSonicJSApp, emailReconciliationPlugin, generateTriggersComment, oauthProvidersPlugin, parseCronTriggers, redirectPlugin, setupCoreMiddleware, setupCoreRoutes, site_settings_collection_default as siteSettingsCollection, updateWranglerTriggers };
20583
+ export { BUILT_IN_PROVIDERS, CloudflareEmailProvider2 as CloudflareEmailProvider, OAuthService, VERSION, createDb, createHelloWorldPlugin, createOAuthProvidersPlugin, createRedirectPlugin, createSonicJSApp, emailReconciliationPlugin, generateTriggersComment, helloWorldPlugin, oauthProvidersPlugin, parseCronTriggers, redirectPlugin, setupCoreMiddleware, setupCoreRoutes, site_settings_collection_default as siteSettingsCollection, updateWranglerTriggers };
19918
20584
  //# sourceMappingURL=index.js.map
19919
20585
  //# sourceMappingURL=index.js.map