@sonicjs-cms/core 3.0.0-beta.14 → 3.0.0-beta.15

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 (85) hide show
  1. package/dist/{app-Bo0X1OWX.d.ts → app-01M0dIwt.d.ts} +1 -1
  2. package/dist/{app-Do66yCcV.d.cts → app-aj39TGHB.d.cts} +1 -1
  3. package/dist/{chunk-H5FB6GKR.cjs → chunk-4AB3PQHF.cjs} +2 -2
  4. package/dist/{chunk-H5FB6GKR.cjs.map → chunk-4AB3PQHF.cjs.map} +1 -1
  5. package/dist/{chunk-INE5E3SL.js → chunk-4ENXIP4M.js} +27 -2
  6. package/dist/chunk-4ENXIP4M.js.map +1 -0
  7. package/dist/{chunk-5L6EMI7A.js → chunk-75WXXLAU.js} +4 -4
  8. package/dist/{chunk-5L6EMI7A.js.map → chunk-75WXXLAU.js.map} +1 -1
  9. package/dist/{chunk-SSOP7G4R.cjs → chunk-BDQOPRA3.cjs} +220 -219
  10. package/dist/chunk-BDQOPRA3.cjs.map +1 -0
  11. package/dist/{chunk-U7T5EOHJ.js → chunk-CE6POA7A.js} +4 -4
  12. package/dist/{chunk-U7T5EOHJ.js.map → chunk-CE6POA7A.js.map} +1 -1
  13. package/dist/{chunk-G5LC7MYH.cjs → chunk-FVI3H6RG.cjs} +10 -10
  14. package/dist/{chunk-G5LC7MYH.cjs.map → chunk-FVI3H6RG.cjs.map} +1 -1
  15. package/dist/{chunk-WIV3R2MJ.cjs → chunk-FXZSZ7C7.cjs} +27 -2
  16. package/dist/chunk-FXZSZ7C7.cjs.map +1 -0
  17. package/dist/{chunk-O32IZ6JS.js → chunk-GQXVDFIG.js} +43 -5
  18. package/dist/chunk-GQXVDFIG.js.map +1 -0
  19. package/dist/{chunk-75JKPNUZ.js → chunk-IQZA2OY5.js} +3 -3
  20. package/dist/{chunk-75JKPNUZ.js.map → chunk-IQZA2OY5.js.map} +1 -1
  21. package/dist/{chunk-QNPT6BQP.js → chunk-JRLADH5B.js} +3 -3
  22. package/dist/{chunk-QNPT6BQP.js.map → chunk-JRLADH5B.js.map} +1 -1
  23. package/dist/{chunk-ZTEJHSYS.cjs → chunk-KMCLNO4K.cjs} +5 -5
  24. package/dist/{chunk-ZTEJHSYS.cjs.map → chunk-KMCLNO4K.cjs.map} +1 -1
  25. package/dist/{chunk-HNUGMALL.js → chunk-L3VIH6U4.js} +15 -15
  26. package/dist/chunk-L3VIH6U4.js.map +1 -0
  27. package/dist/{chunk-SIPBHWMG.cjs → chunk-PK3ODTAL.cjs} +4 -4
  28. package/dist/{chunk-SIPBHWMG.cjs.map → chunk-PK3ODTAL.cjs.map} +1 -1
  29. package/dist/{chunk-4YRYGDCQ.cjs → chunk-PTTFLUEX.cjs} +4 -4
  30. package/dist/{chunk-4YRYGDCQ.cjs.map → chunk-PTTFLUEX.cjs.map} +1 -1
  31. package/dist/{chunk-32SDMKZ7.js → chunk-VOAP3KFU.js} +2 -2
  32. package/dist/{chunk-32SDMKZ7.js.map → chunk-VOAP3KFU.js.map} +1 -1
  33. package/dist/{chunk-IESEVHXL.js → chunk-VVYVFCML.js} +12 -3
  34. package/dist/chunk-VVYVFCML.js.map +1 -0
  35. package/dist/{chunk-HIKBY7MS.cjs → chunk-XWVDSM2L.cjs} +12 -3
  36. package/dist/chunk-XWVDSM2L.cjs.map +1 -0
  37. package/dist/{chunk-L3KHLEHL.cjs → chunk-ZBDDZYA3.cjs} +53 -15
  38. package/dist/chunk-ZBDDZYA3.cjs.map +1 -0
  39. package/dist/config-SM2UWSDN.js +6 -0
  40. package/dist/{config-PLJTDINX.js.map → config-SM2UWSDN.js.map} +1 -1
  41. package/dist/config-YCRSU7ZP.cjs +19 -0
  42. package/dist/{config-FLOUCYHH.cjs.map → config-YCRSU7ZP.cjs.map} +1 -1
  43. package/dist/{define-plugin-BzNHc1ZI.d.ts → define-plugin-BYIey6sp.d.ts} +1 -1
  44. package/dist/{define-plugin-IWDKYaVm.d.cts → define-plugin-DsKiGu1q.d.cts} +1 -1
  45. package/dist/index.cjs +754 -296
  46. package/dist/index.cjs.map +1 -1
  47. package/dist/index.d.cts +17 -6
  48. package/dist/index.d.ts +17 -6
  49. package/dist/index.js +512 -56
  50. package/dist/index.js.map +1 -1
  51. package/dist/middleware.cjs +33 -33
  52. package/dist/middleware.d.cts +2 -2
  53. package/dist/middleware.d.ts +2 -2
  54. package/dist/middleware.js +4 -4
  55. package/dist/migrations-6R2N6G2P.js +4 -0
  56. package/dist/{migrations-DOPZXFK3.js.map → migrations-6R2N6G2P.js.map} +1 -1
  57. package/dist/migrations-X3UX24QG.cjs +13 -0
  58. package/dist/{migrations-TYYC66P4.cjs.map → migrations-X3UX24QG.cjs.map} +1 -1
  59. package/dist/plugins.cjs +40 -40
  60. package/dist/plugins.d.cts +2 -2
  61. package/dist/plugins.d.ts +2 -2
  62. package/dist/plugins.js +3 -3
  63. package/dist/routes.cjs +27 -27
  64. package/dist/routes.d.cts +2 -2
  65. package/dist/routes.d.ts +2 -2
  66. package/dist/routes.js +7 -7
  67. package/dist/services.cjs +18 -18
  68. package/dist/services.js +2 -2
  69. package/dist/{types-Dea1eNxU.d.cts → types-DT7XN5TX.d.cts} +1 -1
  70. package/dist/{types-Dea1eNxU.d.ts → types-DT7XN5TX.d.ts} +1 -1
  71. package/dist/utils.cjs +3 -3
  72. package/dist/utils.js +1 -1
  73. package/package.json +2 -2
  74. package/dist/chunk-HIKBY7MS.cjs.map +0 -1
  75. package/dist/chunk-HNUGMALL.js.map +0 -1
  76. package/dist/chunk-IESEVHXL.js.map +0 -1
  77. package/dist/chunk-INE5E3SL.js.map +0 -1
  78. package/dist/chunk-L3KHLEHL.cjs.map +0 -1
  79. package/dist/chunk-O32IZ6JS.js.map +0 -1
  80. package/dist/chunk-SSOP7G4R.cjs.map +0 -1
  81. package/dist/chunk-WIV3R2MJ.cjs.map +0 -1
  82. package/dist/config-FLOUCYHH.cjs +0 -19
  83. package/dist/config-PLJTDINX.js +0 -6
  84. package/dist/migrations-DOPZXFK3.js +0 -4
  85. package/dist/migrations-TYYC66P4.cjs +0 -13
package/dist/index.js CHANGED
@@ -1,37 +1,37 @@
1
- import { createAuth } from './chunk-75JKPNUZ.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-HNUGMALL.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-HNUGMALL.js';
4
- import './chunk-IESEVHXL.js';
1
+ import { createAuth } from './chunk-IQZA2OY5.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-L3VIH6U4.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-L3VIH6U4.js';
4
+ import './chunk-VVYVFCML.js';
5
5
  import './chunk-RMRJGMDE.js';
6
6
  import { SettingsService, setAppInstance } from './chunk-RQ6N3FTV.js';
7
7
  export { Logger, getLogger, initLogger } from './chunk-RQ6N3FTV.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-O32IZ6JS.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-O32IZ6JS.js';
11
+ import { requireAuth, DocumentTypeRegistry, requireRbac, requireRole, getJwtExpirySecondsFromDb, AuthManager, metricsMiddleware, bootstrapMiddleware, securityHeadersMiddleware, csrfProtection } from './chunk-GQXVDFIG.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-GQXVDFIG.js';
13
13
  import { RbacService, DocumentsService } from './chunk-PDC7DLOV.js';
14
14
  export { RbacService } from './chunk-PDC7DLOV.js';
15
15
  import { nanoid } from './chunk-GCDZZNIN.js';
16
- import { PluginService, PLUGIN_REGISTRY, invalidateTenantCache, getCollectionRegistry, TENANT_COOKIE, MULTI_TENANT_PLUGIN_ID, collectionRecordToRow, tenantMiddleware } from './chunk-INE5E3SL.js';
17
- export { PluginBootstrapService, PluginService as PluginServiceClass, getAvailableCollectionNames, loadCollectionConfig, loadCollectionConfigs, registerCollections, validateCollectionConfig } from './chunk-INE5E3SL.js';
18
- export { MigrationService } from './chunk-32SDMKZ7.js';
16
+ import { PluginService, PLUGIN_REGISTRY, invalidateTenantCache, getCollectionRegistry, TENANT_COOKIE, MULTI_TENANT_PLUGIN_ID, collectionRecordToRow, tenantMiddleware } from './chunk-4ENXIP4M.js';
17
+ export { PluginBootstrapService, PluginService as PluginServiceClass, getAvailableCollectionNames, loadCollectionConfig, loadCollectionConfigs, registerCollections, validateCollectionConfig } from './chunk-4ENXIP4M.js';
18
+ export { MigrationService } from './chunk-VOAP3KFU.js';
19
19
  export { renderFilterBar } from './chunk-JQISFW6U.js';
20
20
  import { renderAdminLayout } from './chunk-OBA2RYZN.js';
21
21
  export { getConfirmationDialogScript, renderAlert, renderConfirmationDialog, renderForm, renderFormField, renderPagination, renderTable } from './chunk-OBA2RYZN.js';
22
22
  import { init_admin_layout_catalyst_template, renderAdminLayoutCatalyst } from './chunk-5V62WT6M.js';
23
23
  export { renderAdminLayoutCatalyst } from './chunk-5V62WT6M.js';
24
- import { TurnstileService, HookSystemImpl, createPluginWirer, registerPluginRoutes, setPluginMenu } from './chunk-5L6EMI7A.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-5L6EMI7A.js';
26
- import { definePlugin, setPluginDefinitions } from './chunk-QNPT6BQP.js';
27
- export { applySchemaDefaults, definePlugin, isDefinedPlugin, parseConfigSchema, parseFormDataToSettings, renderSchemaFields } from './chunk-QNPT6BQP.js';
24
+ import { TurnstileService, HookSystemImpl, createPluginWirer, registerPluginRoutes, setPluginMenu } from './chunk-75WXXLAU.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-75WXXLAU.js';
26
+ import { definePlugin, setPluginDefinitions } from './chunk-JRLADH5B.js';
27
+ export { applySchemaDefaults, definePlugin, isDefinedPlugin, parseConfigSchema, parseFormDataToSettings, renderSchemaFields } from './chunk-JRLADH5B.js';
28
28
  import { setHookSystem } from './chunk-RNZFGN4R.js';
29
29
  export { getHookSystem, getTypedHooks, hasHookSystem, resetHookSystem, setHookSystem } from './chunk-RNZFGN4R.js';
30
30
  export { QueryFilterBuilder, TemplateRenderer, buildQuery, renderTemplate, templateRenderer } from './chunk-QZGABF2M.js';
31
31
  export { metricsTracker } from './chunk-FICTAGD4.js';
32
32
  import './chunk-X7ZAEI5S.js';
33
- import { getCoreVersion, package_default } from './chunk-U7T5EOHJ.js';
34
- export { SONICJS_VERSION, getCoreVersion } from './chunk-U7T5EOHJ.js';
33
+ import { getCoreVersion, package_default } from './chunk-CE6POA7A.js';
34
+ export { SONICJS_VERSION, getCoreVersion } from './chunk-CE6POA7A.js';
35
35
  import { escapeHtml, sanitizeInput } from './chunk-TQABQWOP.js';
36
36
  export { escapeHtml, sanitizeInput, sanitizeObject } from './chunk-TQABQWOP.js';
37
37
  export { HOOKS } from './chunk-QXOZI5Q2.js';
@@ -6646,6 +6646,457 @@ var securityAuditPlugin = definePlugin({
6646
6646
  deactivate: async () => console.log("[SecurityAudit] Plugin deactivated")
6647
6647
  });
6648
6648
 
6649
+ // src/plugins/core-plugins/api-keys-plugin/services/api-key-service.ts
6650
+ var API_KEY_QUERYABLE = [
6651
+ { name: "keyHash", kind: "scalar", type: "text", column: "q_apikey_hash" },
6652
+ { name: "userId", kind: "scalar", type: "text", column: "q_apikey_user_id" },
6653
+ { name: "revoked", kind: "scalar", type: "integer", column: "q_apikey_revoked" }
6654
+ ];
6655
+ var KEY_PREFIX = "sk_";
6656
+ var KEY_BYTES = 24;
6657
+ var DISPLAY_PREFIX_LEN = KEY_PREFIX.length + 8;
6658
+ function generateApiKeySecret() {
6659
+ const bytes = new Uint8Array(KEY_BYTES);
6660
+ crypto.getRandomValues(bytes);
6661
+ let hex = "";
6662
+ for (const b of bytes) hex += b.toString(16).padStart(2, "0");
6663
+ return KEY_PREFIX + hex;
6664
+ }
6665
+ async function hashApiKey(secret) {
6666
+ const data = new TextEncoder().encode(secret);
6667
+ const digest = await crypto.subtle.digest("SHA-256", data);
6668
+ const view = new Uint8Array(digest);
6669
+ let hex = "";
6670
+ for (const b of view) hex += b.toString(16).padStart(2, "0");
6671
+ return hex;
6672
+ }
6673
+ var ApiKeyService = class {
6674
+ constructor(db, tenantId = "default") {
6675
+ this.db = db;
6676
+ this.tenantId = tenantId;
6677
+ }
6678
+ docService() {
6679
+ return new DocumentsService(this.db, {
6680
+ queryableFields: API_KEY_QUERYABLE,
6681
+ tenantId: this.tenantId,
6682
+ maxVersionsPerRoot: 1
6683
+ });
6684
+ }
6685
+ /** Count a user's active (non-revoked) keys — used to enforce the per-user cap. */
6686
+ async countForUser(userId) {
6687
+ const row = await this.db.prepare(
6688
+ `SELECT COUNT(*) AS c FROM documents
6689
+ WHERE type_id = 'api_key' AND tenant_id = ? AND is_current_draft = 1
6690
+ AND deleted_at IS NULL AND q_apikey_user_id = ? AND q_apikey_revoked = 0`
6691
+ ).bind(this.tenantId, userId).first();
6692
+ return row?.c ?? 0;
6693
+ }
6694
+ /**
6695
+ * Mint a new key for a user. The returned `key` is the only time the plaintext
6696
+ * exists outside the caller — it is hashed before storage.
6697
+ */
6698
+ async create(input) {
6699
+ const name = input.name.trim() || "API Key";
6700
+ const secret = generateApiKeySecret();
6701
+ const keyHash = await hashApiKey(secret);
6702
+ const prefix = secret.slice(0, DISPLAY_PREFIX_LEN);
6703
+ const expiresAt = input.expiresAt ?? null;
6704
+ const doc = await this.docService().create(
6705
+ createDocumentSchema.parse({
6706
+ typeId: "api_key",
6707
+ tenantId: this.tenantId,
6708
+ title: name,
6709
+ ownerId: input.userId,
6710
+ publishOnCreate: true,
6711
+ data: {
6712
+ name,
6713
+ userId: input.userId,
6714
+ keyHash,
6715
+ keyPrefix: prefix,
6716
+ revoked: 0,
6717
+ lastUsedAt: null,
6718
+ expiresAt
6719
+ }
6720
+ }),
6721
+ input.userId
6722
+ );
6723
+ return { id: doc.rootId, key: secret, name, prefix, expiresAt };
6724
+ }
6725
+ /** List a user's non-revoked keys (metadata only — never the secret or hash). */
6726
+ async list(userId) {
6727
+ const res = await this.db.prepare(
6728
+ `SELECT root_id, data, created_at FROM documents
6729
+ WHERE type_id = 'api_key' AND tenant_id = ? AND is_current_draft = 1
6730
+ AND deleted_at IS NULL AND q_apikey_user_id = ? AND q_apikey_revoked = 0
6731
+ ORDER BY created_at DESC`
6732
+ ).bind(this.tenantId, userId).all();
6733
+ return (res.results ?? []).map((row) => {
6734
+ const d = JSON.parse(row.data);
6735
+ return {
6736
+ id: row.root_id,
6737
+ name: d.name ?? "API Key",
6738
+ prefix: d.keyPrefix ?? "",
6739
+ createdAt: row.created_at,
6740
+ lastUsedAt: d.lastUsedAt ?? null,
6741
+ expiresAt: d.expiresAt ?? null
6742
+ };
6743
+ });
6744
+ }
6745
+ /**
6746
+ * Revoke (hard-delete) a key owned by `userId`. Credentials are erased, not
6747
+ * soft-flagged. Returns false if the key does not exist or is not owned by
6748
+ * the caller (no information leak about other users' keys).
6749
+ */
6750
+ async revoke(rootId, userId) {
6751
+ const row = await this.db.prepare(
6752
+ `SELECT q_apikey_user_id AS owner FROM documents
6753
+ WHERE root_id = ? AND tenant_id = ? AND type_id = 'api_key' AND is_current_draft = 1 AND deleted_at IS NULL`
6754
+ ).bind(rootId, this.tenantId).first();
6755
+ if (!row || row.owner !== userId) return false;
6756
+ await this.docService().erase(rootId, this.tenantId);
6757
+ return true;
6758
+ }
6759
+ /**
6760
+ * Resolve a presented secret to its owning user, or null. Rejects unknown,
6761
+ * revoked, expired keys and inactive users. Best-effort updates lastUsedAt.
6762
+ */
6763
+ async resolve(secret) {
6764
+ if (!secret || !secret.startsWith(KEY_PREFIX)) return null;
6765
+ const keyHash = await hashApiKey(secret);
6766
+ const keyRow2 = await this.db.prepare(
6767
+ `SELECT root_id, data FROM documents
6768
+ WHERE type_id = 'api_key' AND tenant_id = ? AND is_current_draft = 1 AND deleted_at IS NULL
6769
+ AND q_apikey_hash = ? AND q_apikey_revoked = 0
6770
+ LIMIT 1`
6771
+ ).bind(this.tenantId, keyHash).first();
6772
+ if (!keyRow2) return null;
6773
+ const d = JSON.parse(keyRow2.data);
6774
+ if (d.expiresAt != null && Date.now() > Number(d.expiresAt)) return null;
6775
+ const user = await this.db.prepare(`SELECT id, email, role, is_active, is_super_admin FROM auth_user WHERE id = ?`).bind(d.userId).first();
6776
+ if (!user || user.is_active !== 1) return null;
6777
+ try {
6778
+ await this.touchLastUsed(keyRow2.root_id, d);
6779
+ } catch {
6780
+ }
6781
+ return {
6782
+ userId: user.id,
6783
+ email: user.email,
6784
+ role: user.role ?? "viewer",
6785
+ isSuperAdmin: user.is_super_admin === 1
6786
+ };
6787
+ }
6788
+ /** Update lastUsedAt in-place (single-version type, no history churn). */
6789
+ async touchLastUsed(rootId, data) {
6790
+ const next = JSON.stringify({ ...data, lastUsedAt: Date.now() });
6791
+ await this.db.prepare(
6792
+ `UPDATE documents SET data = ? WHERE root_id = ? AND tenant_id = ? AND type_id = 'api_key' AND is_current_draft = 1`
6793
+ ).bind(next, rootId, this.tenantId).run();
6794
+ }
6795
+ };
6796
+
6797
+ // src/plugins/core-plugins/api-keys-plugin/components/keys-page.ts
6798
+ init_admin_layout_catalyst_template();
6799
+ function fmtDate(ms) {
6800
+ if (!ms) return "\u2014";
6801
+ try {
6802
+ return new Date(ms).toISOString().slice(0, 10);
6803
+ } catch {
6804
+ return "\u2014";
6805
+ }
6806
+ }
6807
+ function keyRow(k) {
6808
+ const name = escapeHtml(k.name);
6809
+ const prefix = escapeHtml(k.prefix);
6810
+ const id = escapeHtml(k.id);
6811
+ return `
6812
+ <tr class="border-t border-zinc-950/5 dark:border-white/10" data-key-id="${id}">
6813
+ <td class="py-3 pr-4 text-sm font-medium text-zinc-950 dark:text-white">${name}</td>
6814
+ <td class="py-3 pr-4 text-sm font-mono text-zinc-500 dark:text-zinc-400">${prefix}\u2026</td>
6815
+ <td class="py-3 pr-4 text-sm text-zinc-500 dark:text-zinc-400">${fmtDate(k.createdAt ? k.createdAt * 1e3 : null)}</td>
6816
+ <td class="py-3 pr-4 text-sm text-zinc-500 dark:text-zinc-400">${k.lastUsedAt ? fmtDate(k.lastUsedAt) : "Never"}</td>
6817
+ <td class="py-3 pr-4 text-sm text-zinc-500 dark:text-zinc-400">${k.expiresAt ? fmtDate(k.expiresAt) : "Never"}</td>
6818
+ <td class="py-3 text-right">
6819
+ <button type="button" onclick="revokeApiKey('${id}', '${name.replace(/'/g, "\\'")}')"
6820
+ 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">
6821
+ Revoke
6822
+ </button>
6823
+ </td>
6824
+ </tr>`;
6825
+ }
6826
+ function renderApiKeysPage(data) {
6827
+ const { keys, user, version, dynamicMenuItems } = data;
6828
+ 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>`;
6829
+ const content2 = `
6830
+ <div>
6831
+ <div class="sm:flex sm:items-center sm:justify-between mb-6">
6832
+ <div class="sm:flex-auto">
6833
+ <h1 class="text-2xl/8 font-semibold text-zinc-950 dark:text-white sm:text-xl/8">API Keys</h1>
6834
+ <p class="mt-2 text-sm/6 text-zinc-500 dark:text-zinc-400">
6835
+ Long-lived secrets for headless / server-to-server REST access. Send a key as the
6836
+ <code class="font-mono">x-api-key</code> header or <code class="font-mono">Authorization: Bearer &lt;key&gt;</code>.
6837
+ The secret is shown once at creation \u2014 store it now.
6838
+ </p>
6839
+ </div>
6840
+ <div class="mt-4 sm:mt-0 sm:ml-16">
6841
+ <button type="button" onclick="document.getElementById('create-key-modal').classList.remove('hidden')"
6842
+ 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">
6843
+ Create API key
6844
+ </button>
6845
+ </div>
6846
+ </div>
6847
+
6848
+ <!-- Newly-created secret banner (populated by JS, shown once) -->
6849
+ <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">
6850
+ <p class="text-sm font-medium text-emerald-800 dark:text-emerald-300 mb-2">
6851
+ Copy your new key now \u2014 it will not be shown again.
6852
+ </p>
6853
+ <div class="flex items-center gap-2">
6854
+ <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>
6855
+ <button type="button" onclick="copyNewKey()"
6856
+ class="rounded-lg bg-emerald-600 px-3 py-2 text-sm font-semibold text-white hover:bg-emerald-500 transition-colors">
6857
+ Copy
6858
+ </button>
6859
+ </div>
6860
+ </div>
6861
+
6862
+ <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">
6863
+ <table class="w-full text-left">
6864
+ <thead>
6865
+ <tr class="text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400">
6866
+ <th class="pb-2 pr-4">Name</th>
6867
+ <th class="pb-2 pr-4">Prefix</th>
6868
+ <th class="pb-2 pr-4">Created</th>
6869
+ <th class="pb-2 pr-4">Last used</th>
6870
+ <th class="pb-2 pr-4">Expires</th>
6871
+ <th class="pb-2 text-right">Actions</th>
6872
+ </tr>
6873
+ </thead>
6874
+ <tbody id="keys-tbody">${rows}</tbody>
6875
+ </table>
6876
+ </div>
6877
+ </div>
6878
+
6879
+ <!-- Create modal -->
6880
+ <div id="create-key-modal" class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4">
6881
+ <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">
6882
+ <h2 class="text-base font-semibold text-zinc-950 dark:text-white mb-4">Create API key</h2>
6883
+ <label class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1">Name</label>
6884
+ <input id="new-key-name" type="text" placeholder="e.g. CI deploy token"
6885
+ 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">
6886
+ <label class="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1">Expires in (days, optional)</label>
6887
+ <input id="new-key-expiry" type="number" min="1" max="3650" placeholder="Never"
6888
+ 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">
6889
+ <div class="flex justify-end gap-2">
6890
+ <button type="button" onclick="document.getElementById('create-key-modal').classList.add('hidden')"
6891
+ 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">
6892
+ Cancel
6893
+ </button>
6894
+ <button type="button" onclick="createApiKey()"
6895
+ class="rounded-lg bg-cyan-600 px-4 py-2 text-sm font-semibold text-white hover:bg-cyan-500 transition-colors">
6896
+ Create
6897
+ </button>
6898
+ </div>
6899
+ </div>
6900
+ </div>
6901
+
6902
+ <script>
6903
+ // CSRF: the csrf_token cookie is JS-readable (httpOnly:false); echo it back
6904
+ // as X-CSRF-Token on state-changing requests (signed double-submit).
6905
+ function csrfHeader() {
6906
+ const m = document.cookie.match(/(?:^|; )csrf_token=([^;]+)/)
6907
+ return m ? { 'X-CSRF-Token': decodeURIComponent(m[1]) } : {}
6908
+ }
6909
+ async function createApiKey() {
6910
+ const name = document.getElementById('new-key-name').value.trim()
6911
+ if (!name) { window.showNotification && window.showNotification('Name is required', 'error'); return }
6912
+ const expiry = parseInt(document.getElementById('new-key-expiry').value, 10)
6913
+ const body = { name }
6914
+ if (Number.isFinite(expiry) && expiry > 0) body.expiresInDays = expiry
6915
+ const res = await fetch('/admin/plugins/api-keys/api/keys', {
6916
+ method: 'POST',
6917
+ headers: { 'Content-Type': 'application/json', ...csrfHeader() },
6918
+ body: JSON.stringify(body),
6919
+ })
6920
+ if (!res.ok) {
6921
+ const e = await res.json().catch(() => ({}))
6922
+ window.showNotification && window.showNotification(e.error || 'Failed to create key', 'error')
6923
+ return
6924
+ }
6925
+ const { apiKey } = await res.json()
6926
+ document.getElementById('create-key-modal').classList.add('hidden')
6927
+ document.getElementById('new-key-value').textContent = apiKey.key
6928
+ document.getElementById('new-key-banner').classList.remove('hidden')
6929
+ setTimeout(() => location.reload(), 1500)
6930
+ }
6931
+ function copyNewKey() {
6932
+ const v = document.getElementById('new-key-value').textContent
6933
+ navigator.clipboard.writeText(v).then(() => window.showNotification && window.showNotification('Copied', 'success'))
6934
+ }
6935
+ async function revokeApiKey(id, name) {
6936
+ if (!confirm('Revoke "' + name + '"? This cannot be undone and will break any client using it.')) return
6937
+ const res = await fetch('/admin/plugins/api-keys/api/keys/' + id, { method: 'DELETE', headers: { ...csrfHeader() } })
6938
+ if (res.ok) {
6939
+ const row = document.querySelector('[data-key-id="' + id + '"]')
6940
+ if (row) row.remove()
6941
+ window.showNotification && window.showNotification('Key revoked', 'success')
6942
+ } else {
6943
+ window.showNotification && window.showNotification('Failed to revoke', 'error')
6944
+ }
6945
+ }
6946
+ </script>`;
6947
+ const layoutData = {
6948
+ title: "API Keys",
6949
+ pageTitle: "API Keys",
6950
+ currentPath: "/admin/plugins/api-keys",
6951
+ user,
6952
+ content: content2,
6953
+ version,
6954
+ dynamicMenuItems
6955
+ };
6956
+ return renderAdminLayoutCatalyst(layoutData);
6957
+ }
6958
+
6959
+ // src/plugins/core-plugins/api-keys-plugin/types.ts
6960
+ var DEFAULT_SETTINGS3 = {
6961
+ maxKeysPerUser: 50,
6962
+ defaultExpiryDays: 0
6963
+ };
6964
+
6965
+ // src/plugins/core-plugins/api-keys-plugin/routes/admin.ts
6966
+ var adminRoutes4 = new Hono();
6967
+ var DAY_MS = 24 * 60 * 60 * 1e3;
6968
+ var createSchema = z.object({
6969
+ name: z.string().min(1).max(100),
6970
+ // Optional lifetime in days; omitted/null → plugin default (which may be "never").
6971
+ expiresInDays: z.number().int().positive().max(3650).nullish()
6972
+ });
6973
+ async function getSettings3(db) {
6974
+ try {
6975
+ const plugin2 = await new PluginService(db).getPlugin("api-keys");
6976
+ if (plugin2?.settings) {
6977
+ const s = typeof plugin2.settings === "string" ? JSON.parse(plugin2.settings) : plugin2.settings;
6978
+ return { ...DEFAULT_SETTINGS3, ...s };
6979
+ }
6980
+ } catch {
6981
+ }
6982
+ return DEFAULT_SETTINGS3;
6983
+ }
6984
+ adminRoutes4.use("*", requireAuth());
6985
+ adminRoutes4.use("*", async (c, next) => {
6986
+ const user = c.get("user");
6987
+ if (user?.role !== "admin") {
6988
+ return c.json({ error: "Access denied" }, 403);
6989
+ }
6990
+ return next();
6991
+ });
6992
+ adminRoutes4.get("/", async (c) => {
6993
+ const user = c.get("user");
6994
+ const tenantId = c.get("tenantId") || "default";
6995
+ const keys = await new ApiKeyService(c.env.DB, tenantId).list(user.userId);
6996
+ return c.html(
6997
+ renderApiKeysPage({
6998
+ keys,
6999
+ user: { name: user.email, email: user.email, role: user.role },
7000
+ version: c.get("appVersion"),
7001
+ dynamicMenuItems: c.get("pluginMenuItems")
7002
+ })
7003
+ );
7004
+ });
7005
+ adminRoutes4.get("/api/keys", async (c) => {
7006
+ const user = c.get("user");
7007
+ const tenantId = c.get("tenantId") || "default";
7008
+ const keys = await new ApiKeyService(c.env.DB, tenantId).list(user.userId);
7009
+ return c.json({ keys });
7010
+ });
7011
+ adminRoutes4.post("/api/keys", async (c) => {
7012
+ const user = c.get("user");
7013
+ const tenantId = c.get("tenantId") || "default";
7014
+ const body = await c.req.json().catch(() => ({}));
7015
+ const parsed = createSchema.safeParse(body);
7016
+ if (!parsed.success) {
7017
+ return c.json({ error: "Invalid request", details: parsed.error.flatten() }, 400);
7018
+ }
7019
+ const settings = await getSettings3(c.env.DB);
7020
+ const svc = new ApiKeyService(c.env.DB, tenantId);
7021
+ if (await svc.countForUser(user.userId) >= settings.maxKeysPerUser) {
7022
+ return c.json({ error: `API key limit reached (max ${settings.maxKeysPerUser})` }, 409);
7023
+ }
7024
+ const days = parsed.data.expiresInDays ?? (settings.defaultExpiryDays > 0 ? settings.defaultExpiryDays : null);
7025
+ const expiresAt = days ? Date.now() + days * DAY_MS : null;
7026
+ const created = await svc.create({ userId: user.userId, name: parsed.data.name, expiresAt });
7027
+ return c.json({ apiKey: created }, 201);
7028
+ });
7029
+ adminRoutes4.delete("/api/keys/:id", async (c) => {
7030
+ const user = c.get("user");
7031
+ const tenantId = c.get("tenantId") || "default";
7032
+ const ok = await new ApiKeyService(c.env.DB, tenantId).revoke(c.req.param("id") ?? "", user.userId);
7033
+ if (!ok) return c.json({ error: "API key not found" }, 404);
7034
+ return c.json({ success: true });
7035
+ });
7036
+
7037
+ // src/plugins/core-plugins/api-keys-plugin/middleware/api-key-auth.ts
7038
+ function apiKeyAuthMiddleware() {
7039
+ return async (c, next) => {
7040
+ if (!c.get("user")) {
7041
+ const authz = c.req.header("authorization") || "";
7042
+ const bearer = authz.toLowerCase().startsWith("bearer ") ? authz.slice(7).trim() : "";
7043
+ const presented = c.req.header("x-api-key") || (bearer.startsWith("sk_") ? bearer : "");
7044
+ if (presented) {
7045
+ try {
7046
+ const tenantId = c.get("tenantId") || "default";
7047
+ const resolved = await new ApiKeyService(c.env.DB, tenantId).resolve(presented);
7048
+ if (resolved) {
7049
+ c.set("user", {
7050
+ userId: resolved.userId,
7051
+ email: resolved.email,
7052
+ role: resolved.role,
7053
+ isSuperAdmin: resolved.isSuperAdmin,
7054
+ // API keys are stateless bearer creds — no session window.
7055
+ exp: 0,
7056
+ iat: 0
7057
+ });
7058
+ }
7059
+ } catch {
7060
+ }
7061
+ }
7062
+ }
7063
+ await next();
7064
+ };
7065
+ }
7066
+
7067
+ // src/plugins/core-plugins/api-keys-plugin/index.ts
7068
+ 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>`;
7069
+ var apiKeysPlugin = definePlugin({
7070
+ id: "api-keys",
7071
+ version: "1.0.0-beta.1",
7072
+ name: "API Keys",
7073
+ description: "Programmatic API access keys for headless / server-to-server REST access.",
7074
+ sonicjsVersionRange: "^3.0.0",
7075
+ author: { name: "SonicJS Team" },
7076
+ register(app2) {
7077
+ app2.route("/admin/plugins/api-keys", adminRoutes4);
7078
+ },
7079
+ menu: [
7080
+ { label: "API Keys", path: "/admin/plugins/api-keys", icon: KEY_ICON, order: 86 }
7081
+ ],
7082
+ configSchema: {
7083
+ maxKeysPerUser: {
7084
+ type: "number",
7085
+ label: "Max keys per user",
7086
+ description: "Hard cap on active keys a single user may hold.",
7087
+ default: 50
7088
+ },
7089
+ defaultExpiryDays: {
7090
+ type: "number",
7091
+ label: "Default expiry (days)",
7092
+ description: "Lifetime applied to new keys when no explicit expiry is given. 0 = never expires.",
7093
+ default: 0
7094
+ }
7095
+ },
7096
+ activate: async () => console.log("[ApiKeys] Plugin activated"),
7097
+ deactivate: async () => console.log("[ApiKeys] Plugin deactivated")
7098
+ });
7099
+
6649
7100
  // src/plugins/core-plugins/stripe-plugin/services/subscription-service.ts
6650
7101
  var SubscriptionService = class {
6651
7102
  constructor(db) {
@@ -7373,7 +7824,7 @@ function renderEventPagination(page, totalPages, type, status) {
7373
7824
  }
7374
7825
 
7375
7826
  // src/plugins/core-plugins/stripe-plugin/types.ts
7376
- var DEFAULT_SETTINGS3 = {
7827
+ var DEFAULT_SETTINGS4 = {
7377
7828
  stripePublishableKey: "",
7378
7829
  stripeSecretKey: "",
7379
7830
  stripeWebhookSecret: "",
@@ -7383,28 +7834,28 @@ var DEFAULT_SETTINGS3 = {
7383
7834
  };
7384
7835
 
7385
7836
  // src/plugins/core-plugins/stripe-plugin/routes/admin.ts
7386
- var adminRoutes4 = new Hono();
7387
- adminRoutes4.use("*", requireAuth());
7388
- adminRoutes4.use("*", async (c, next) => {
7837
+ var adminRoutes5 = new Hono();
7838
+ adminRoutes5.use("*", requireAuth());
7839
+ adminRoutes5.use("*", async (c, next) => {
7389
7840
  const user = c.get("user");
7390
7841
  if (user?.role !== "admin") {
7391
7842
  return c.text("Access denied", 403);
7392
7843
  }
7393
7844
  return next();
7394
7845
  });
7395
- async function getSettings3(db) {
7846
+ async function getSettings4(db) {
7396
7847
  try {
7397
7848
  const pluginService = new PluginService(db);
7398
7849
  const plugin2 = await pluginService.getPlugin("stripe");
7399
7850
  if (plugin2?.settings) {
7400
7851
  const settings = typeof plugin2.settings === "string" ? JSON.parse(plugin2.settings) : plugin2.settings;
7401
- return { ...DEFAULT_SETTINGS3, ...settings };
7852
+ return { ...DEFAULT_SETTINGS4, ...settings };
7402
7853
  }
7403
7854
  } catch {
7404
7855
  }
7405
- return DEFAULT_SETTINGS3;
7856
+ return DEFAULT_SETTINGS4;
7406
7857
  }
7407
- adminRoutes4.get("/", async (c) => {
7858
+ adminRoutes5.get("/", async (c) => {
7408
7859
  const db = c.env.DB;
7409
7860
  const user = c.get("user");
7410
7861
  const subscriptionService = new SubscriptionService(db);
@@ -7427,7 +7878,7 @@ adminRoutes4.get("/", async (c) => {
7427
7878
  });
7428
7879
  return c.html(html4);
7429
7880
  });
7430
- adminRoutes4.get("/events", async (c) => {
7881
+ adminRoutes5.get("/events", async (c) => {
7431
7882
  const db = c.env.DB;
7432
7883
  const user = c.get("user");
7433
7884
  const eventService = new StripeEventService(db);
@@ -7453,10 +7904,10 @@ adminRoutes4.get("/events", async (c) => {
7453
7904
  });
7454
7905
  return c.html(html4);
7455
7906
  });
7456
- adminRoutes4.get("/settings", async (c) => {
7907
+ adminRoutes5.get("/settings", async (c) => {
7457
7908
  const db = c.env.DB;
7458
7909
  const user = c.get("user");
7459
- const settings = await getSettings3(db);
7910
+ const settings = await getSettings4(db);
7460
7911
  const { renderAdminLayoutCatalyst: renderAdminLayoutCatalyst2 } = await import('./admin-layout-catalyst.template-YQ4EMF2J.js');
7461
7912
  const content2 = `
7462
7913
  <div>
@@ -7563,7 +8014,7 @@ adminRoutes4.get("/settings", async (c) => {
7563
8014
  dynamicMenuItems: c.get("pluginMenuItems")
7564
8015
  }));
7565
8016
  });
7566
- adminRoutes4.post("/settings", async (c) => {
8017
+ adminRoutes5.post("/settings", async (c) => {
7567
8018
  try {
7568
8019
  const body = await c.req.json();
7569
8020
  const db = c.env.DB;
@@ -7714,17 +8165,17 @@ function timingSafeEqual(a, b) {
7714
8165
 
7715
8166
  // src/plugins/core-plugins/stripe-plugin/routes/api.ts
7716
8167
  var apiRoutes3 = new Hono();
7717
- async function getSettings4(db) {
8168
+ async function getSettings5(db) {
7718
8169
  try {
7719
8170
  const pluginService = new PluginService(db);
7720
8171
  const plugin2 = await pluginService.getPlugin("stripe");
7721
8172
  if (plugin2?.settings) {
7722
8173
  const settings = typeof plugin2.settings === "string" ? JSON.parse(plugin2.settings) : plugin2.settings;
7723
- return { ...DEFAULT_SETTINGS3, ...settings };
8174
+ return { ...DEFAULT_SETTINGS4, ...settings };
7724
8175
  }
7725
8176
  } catch {
7726
8177
  }
7727
- return DEFAULT_SETTINGS3;
8178
+ return DEFAULT_SETTINGS4;
7728
8179
  }
7729
8180
  function mapStripeStatus(status) {
7730
8181
  const map = {
@@ -7741,7 +8192,7 @@ function mapStripeStatus(status) {
7741
8192
  }
7742
8193
  apiRoutes3.post("/webhook", async (c) => {
7743
8194
  const db = c.env.DB;
7744
- const settings = await getSettings4(db);
8195
+ const settings = await getSettings5(db);
7745
8196
  if (!settings.stripeWebhookSecret) {
7746
8197
  return c.json({ error: "Webhook secret not configured" }, 500);
7747
8198
  }
@@ -7871,7 +8322,7 @@ apiRoutes3.post("/create-checkout-session", requireAuth(), async (c) => {
7871
8322
  const db = c.env.DB;
7872
8323
  const user = c.get("user");
7873
8324
  if (!user) return c.json({ error: "Unauthorized" }, 401);
7874
- const settings = await getSettings4(db);
8325
+ const settings = await getSettings5(db);
7875
8326
  if (!settings.stripeSecretKey) {
7876
8327
  return c.json({ error: "Stripe not configured" }, 500);
7877
8328
  }
@@ -7948,7 +8399,7 @@ apiRoutes3.post("/sync-subscriptions", requireAuth(), async (c) => {
7948
8399
  const user = c.get("user");
7949
8400
  if (user?.role !== "admin") return c.json({ error: "Access denied" }, 403);
7950
8401
  const db = c.env.DB;
7951
- const settings = await getSettings4(db);
8402
+ const settings = await getSettings5(db);
7952
8403
  if (!settings.stripeSecretKey) {
7953
8404
  return c.json({ error: "Stripe secret key not configured" }, 400);
7954
8405
  }
@@ -8023,7 +8474,7 @@ var stripePlugin = definePlugin({
8023
8474
  sonicjsVersionRange: "^3.0.0",
8024
8475
  author: { name: "SonicJS Team" },
8025
8476
  register(app2) {
8026
- app2.route("/admin/plugins/stripe", adminRoutes4);
8477
+ app2.route("/admin/plugins/stripe", adminRoutes5);
8027
8478
  app2.route("/api/stripe", apiRoutes3);
8028
8479
  },
8029
8480
  menu: [
@@ -11113,16 +11564,16 @@ function pluginMenuMiddleware() {
11113
11564
  };
11114
11565
  }
11115
11566
  init_admin_layout_catalyst_template();
11116
- var adminRoutes5 = new Hono();
11117
- adminRoutes5.use("*", requireAuth());
11118
- adminRoutes5.use("*", async (c, next) => {
11567
+ var adminRoutes6 = new Hono();
11568
+ adminRoutes6.use("*", requireAuth());
11569
+ adminRoutes6.use("*", async (c, next) => {
11119
11570
  const user = c.get("user");
11120
11571
  if (user?.role !== "admin") {
11121
11572
  return c.text("Access denied", 403);
11122
11573
  }
11123
11574
  return next();
11124
11575
  });
11125
- adminRoutes5.get("/", async (c) => {
11576
+ adminRoutes6.get("/", async (c) => {
11126
11577
  const user = c.get("user");
11127
11578
  const db = c.env.DB;
11128
11579
  let totalRequests = 0;
@@ -11478,7 +11929,7 @@ var analyticsPlugin = definePlugin({
11478
11929
  author: { name: "SonicJS Team", email: "team@sonicjs.com" },
11479
11930
  register(app2) {
11480
11931
  app2.route("/api/events", apiRoutes4);
11481
- app2.route("/admin/analytics", adminRoutes5);
11932
+ app2.route("/admin/analytics", adminRoutes6);
11482
11933
  },
11483
11934
  menu: [
11484
11935
  {
@@ -11760,8 +12211,8 @@ apiRoutes5.delete("/:id", async (c) => {
11760
12211
  return c.json({ success: false, error: "Failed to delete variable" }, 500);
11761
12212
  }
11762
12213
  });
11763
- var adminRoutes6 = new Hono();
11764
- adminRoutes6.use("*", async (c, next) => {
12214
+ var adminRoutes7 = new Hono();
12215
+ adminRoutes7.use("*", async (c, next) => {
11765
12216
  try {
11766
12217
  const db = c.env?.DB;
11767
12218
  if (db) {
@@ -11772,7 +12223,7 @@ adminRoutes6.use("*", async (c, next) => {
11772
12223
  }
11773
12224
  await next();
11774
12225
  });
11775
- adminRoutes6.get("/", async (c) => {
12226
+ adminRoutes7.get("/", async (c) => {
11776
12227
  const db = c.env.DB;
11777
12228
  let variables = [];
11778
12229
  try {
@@ -11802,7 +12253,7 @@ adminRoutes6.get("/", async (c) => {
11802
12253
  }
11803
12254
  return c.html(renderAdminPage(variables, { editorActive, activeEditorName, enableEditorIntegration }));
11804
12255
  });
11805
- adminRoutes6.put("/:id", async (c) => {
12256
+ adminRoutes7.put("/:id", async (c) => {
11806
12257
  const db = c.env.DB;
11807
12258
  const id = c.req.param("id");
11808
12259
  let body;
@@ -11818,7 +12269,7 @@ adminRoutes6.put("/:id", async (c) => {
11818
12269
  }
11819
12270
  return c.html('<span class="text-green-400 text-xs">Saved</span>');
11820
12271
  });
11821
- adminRoutes6.post("/:id/toggle", async (c) => {
12272
+ adminRoutes7.post("/:id/toggle", async (c) => {
11822
12273
  const db = c.env.DB;
11823
12274
  const id = c.req.param("id");
11824
12275
  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();
@@ -11826,7 +12277,7 @@ adminRoutes6.post("/:id/toggle", async (c) => {
11826
12277
  c.header("HX-Redirect", "/admin/global-variables");
11827
12278
  return c.body(null, 204);
11828
12279
  });
11829
- adminRoutes6.post("/", async (c) => {
12280
+ adminRoutes7.post("/", async (c) => {
11830
12281
  const db = c.env.DB;
11831
12282
  const form = await c.req.parseBody();
11832
12283
  const key = (form.key || "").trim();
@@ -11847,7 +12298,7 @@ adminRoutes6.post("/", async (c) => {
11847
12298
  c.header("HX-Redirect", "/admin/global-variables");
11848
12299
  return c.body(null, 204);
11849
12300
  });
11850
- adminRoutes6.post("/settings/editor-integration", async (c) => {
12301
+ adminRoutes7.post("/settings/editor-integration", async (c) => {
11851
12302
  const db = c.env.DB;
11852
12303
  try {
11853
12304
  const row = await db.prepare("SELECT settings FROM plugins WHERE id = 'global-variables'").first();
@@ -11859,7 +12310,7 @@ adminRoutes6.post("/settings/editor-integration", async (c) => {
11859
12310
  return c.json({ success: false, error: "Failed to update setting" }, 500);
11860
12311
  }
11861
12312
  });
11862
- adminRoutes6.delete("/:id", async (c) => {
12313
+ adminRoutes7.delete("/:id", async (c) => {
11863
12314
  const db = c.env.DB;
11864
12315
  await db.prepare("DELETE FROM global_variables WHERE id = ?").bind(c.req.param("id")).run();
11865
12316
  invalidateVariablesCache();
@@ -12190,7 +12641,7 @@ var globalVariablesPlugin = definePlugin({
12190
12641
  capabilities: ["hooks.content:subscribe"],
12191
12642
  register(app2) {
12192
12643
  app2.route("/api/global-variables", apiRoutes5);
12193
- app2.route("/admin/global-variables", adminRoutes6);
12644
+ app2.route("/admin/global-variables", adminRoutes7);
12194
12645
  },
12195
12646
  menu: [
12196
12647
  { label: "Global Variables", path: "/admin/global-variables", icon: "bolt", order: 45, permissions: ["global-variables:view"] }
@@ -12511,8 +12962,8 @@ apiRoutes6.delete("/:id", async (c) => {
12511
12962
  return c.json({ success: false, error: "Failed to delete shortcode" }, 500);
12512
12963
  }
12513
12964
  });
12514
- var adminRoutes7 = new Hono();
12515
- adminRoutes7.use("*", async (c, next) => {
12965
+ var adminRoutes8 = new Hono();
12966
+ adminRoutes8.use("*", async (c, next) => {
12516
12967
  try {
12517
12968
  const db = c.env?.DB;
12518
12969
  if (db) {
@@ -12523,7 +12974,7 @@ adminRoutes7.use("*", async (c, next) => {
12523
12974
  }
12524
12975
  await next();
12525
12976
  });
12526
- adminRoutes7.get("/", async (c) => {
12977
+ adminRoutes8.get("/", async (c) => {
12527
12978
  const db = c.env.DB;
12528
12979
  let shortcodes = [];
12529
12980
  try {
@@ -12553,7 +13004,7 @@ adminRoutes7.get("/", async (c) => {
12553
13004
  }
12554
13005
  return c.html(renderAdminPage2(shortcodes, { editorActive, activeEditorName, enableEditorIntegration }));
12555
13006
  });
12556
- adminRoutes7.post("/", async (c) => {
13007
+ adminRoutes8.post("/", async (c) => {
12557
13008
  const db = c.env.DB;
12558
13009
  const form = await c.req.parseBody();
12559
13010
  const name = (form.name || "").trim();
@@ -12574,7 +13025,7 @@ adminRoutes7.post("/", async (c) => {
12574
13025
  c.header("HX-Redirect", "/admin/shortcodes");
12575
13026
  return c.body(null, 204);
12576
13027
  });
12577
- adminRoutes7.post("/settings/editor-integration", async (c) => {
13028
+ adminRoutes8.post("/settings/editor-integration", async (c) => {
12578
13029
  const db = c.env.DB;
12579
13030
  try {
12580
13031
  const row = await db.prepare("SELECT settings FROM plugins WHERE id = 'shortcodes'").first();
@@ -12586,7 +13037,7 @@ adminRoutes7.post("/settings/editor-integration", async (c) => {
12586
13037
  return c.json({ success: false, error: "Failed to update setting" }, 500);
12587
13038
  }
12588
13039
  });
12589
- adminRoutes7.delete("/:id", async (c) => {
13040
+ adminRoutes8.delete("/:id", async (c) => {
12590
13041
  await c.env.DB.prepare("DELETE FROM shortcodes WHERE id = ?").bind(c.req.param("id")).run();
12591
13042
  return c.html("");
12592
13043
  });
@@ -12797,7 +13248,7 @@ var shortcodesPlugin = definePlugin({
12797
13248
  capabilities: ["hooks.content:subscribe"],
12798
13249
  register(app2) {
12799
13250
  app2.route("/api/shortcodes", apiRoutes6);
12800
- app2.route("/admin/shortcodes", adminRoutes7);
13251
+ app2.route("/admin/shortcodes", adminRoutes8);
12801
13252
  },
12802
13253
  menu: [
12803
13254
  { label: "Shortcodes", path: "/admin/shortcodes", icon: "bolt", order: 46, permissions: ["shortcodes:view"] }
@@ -12906,6 +13357,9 @@ var helloWorldPlugin = definePlugin({
12906
13357
  console.info("\u274C Hello World plugin deactivated");
12907
13358
  }
12908
13359
  });
13360
+ function createHelloWorldPlugin() {
13361
+ return helloWorldPlugin;
13362
+ }
12909
13363
 
12910
13364
  // src/plugins/core-plugins/multi-tenant-plugin/manifest.json
12911
13365
  var manifest_default2 = {
@@ -16079,6 +16533,7 @@ function createSonicJSApp(config = {}) {
16079
16533
  const magicLinkPlugin = createMagicLinkAuthPlugin();
16080
16534
  const corePluginsBeforeCatchAll = [
16081
16535
  securityAuditPlugin,
16536
+ apiKeysPlugin,
16082
16537
  aiSearchPlugin,
16083
16538
  oauthProvidersPlugin,
16084
16539
  userProfilesPlugin,
@@ -16220,6 +16675,7 @@ function createSonicJSApp(config = {}) {
16220
16675
  }
16221
16676
  await next();
16222
16677
  });
16678
+ app2.use("*", apiKeyAuthMiddleware());
16223
16679
  if (config.middleware?.afterAuth) {
16224
16680
  for (const middleware of config.middleware.afterAuth) {
16225
16681
  app2.use("*", middleware);
@@ -19947,6 +20403,6 @@ function createDb(d1) {
19947
20403
  // src/index.ts
19948
20404
  var VERSION = package_default.version;
19949
20405
 
19950
- 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 };
20406
+ 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 };
19951
20407
  //# sourceMappingURL=index.js.map
19952
20408
  //# sourceMappingURL=index.js.map