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

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 (225) hide show
  1. package/README.md +4 -3
  2. package/dist/admin-documents-form.template-KN7JF66Q.cjs +19 -0
  3. package/dist/{admin-layout-catalyst.template-UMTIN66R.js.map → admin-documents-form.template-KN7JF66Q.cjs.map} +1 -1
  4. package/dist/admin-documents-form.template-NLSI6Z42.js +6 -0
  5. package/dist/{admin-layout-catalyst.template-HFD37TY5.cjs.map → admin-documents-form.template-NLSI6Z42.js.map} +1 -1
  6. package/dist/admin-layout-catalyst.template-WHJGSWWD.js +7 -0
  7. package/dist/admin-layout-catalyst.template-WHJGSWWD.js.map +1 -0
  8. package/dist/admin-layout-catalyst.template-ZK5HD545.cjs +17 -0
  9. package/dist/admin-layout-catalyst.template-ZK5HD545.cjs.map +1 -0
  10. package/dist/app-Bo0X1OWX.d.ts +1268 -0
  11. package/dist/app-Do66yCcV.d.cts +1268 -0
  12. package/dist/cache-DDARE4QE.js +4 -0
  13. package/dist/cache-DDARE4QE.js.map +1 -0
  14. package/dist/cache-LVYS4BPL.cjs +33 -0
  15. package/dist/cache-LVYS4BPL.cjs.map +1 -0
  16. package/dist/chunk-2CB4KY7I.cjs +771 -0
  17. package/dist/chunk-2CB4KY7I.cjs.map +1 -0
  18. package/dist/{chunk-55RDMDOP.js → chunk-3TB6AT6X.js} +148 -55
  19. package/dist/chunk-3TB6AT6X.js.map +1 -0
  20. package/dist/{chunk-ON5ZMSU4.js → chunk-6JQOUUOB.js} +3 -3
  21. package/dist/chunk-6JQOUUOB.js.map +1 -0
  22. package/dist/chunk-6OUHGKFD.js +387 -0
  23. package/dist/chunk-6OUHGKFD.js.map +1 -0
  24. package/dist/{chunk-DSUJ5YQH.cjs → chunk-AAWNRBRB.cjs} +537 -92
  25. package/dist/chunk-AAWNRBRB.cjs.map +1 -0
  26. package/dist/chunk-AI663NBO.js +821 -0
  27. package/dist/chunk-AI663NBO.js.map +1 -0
  28. package/dist/chunk-BDDABDAB.cjs +1149 -0
  29. package/dist/chunk-BDDABDAB.cjs.map +1 -0
  30. package/dist/chunk-BLMTL57B.js +767 -0
  31. package/dist/chunk-BLMTL57B.js.map +1 -0
  32. package/dist/chunk-DNQCEKUK.cjs +327 -0
  33. package/dist/chunk-DNQCEKUK.cjs.map +1 -0
  34. package/dist/chunk-DSA4UX5B.cjs +276 -0
  35. package/dist/chunk-DSA4UX5B.cjs.map +1 -0
  36. package/dist/chunk-EF2NQUIQ.js +323 -0
  37. package/dist/chunk-EF2NQUIQ.js.map +1 -0
  38. package/dist/chunk-GCDZZNIN.js +192 -0
  39. package/dist/chunk-GCDZZNIN.js.map +1 -0
  40. package/dist/{chunk-ABB34XUS.cjs → chunk-H2AXVCLS.cjs} +667 -19
  41. package/dist/chunk-H2AXVCLS.cjs.map +1 -0
  42. package/dist/{chunk-XWIA3HVX.js → chunk-HDWE5FRJ.js} +6 -1249
  43. package/dist/chunk-HDWE5FRJ.js.map +1 -0
  44. package/dist/chunk-HIKBY7MS.cjs +70 -0
  45. package/dist/chunk-HIKBY7MS.cjs.map +1 -0
  46. package/dist/chunk-IESEVHXL.js +66 -0
  47. package/dist/chunk-IESEVHXL.js.map +1 -0
  48. package/dist/chunk-IVPRUGTY.js +242 -0
  49. package/dist/chunk-IVPRUGTY.js.map +1 -0
  50. package/dist/{chunk-SQ6FNXU2.cjs → chunk-IXUHXTHW.cjs} +2 -151
  51. package/dist/chunk-IXUHXTHW.cjs.map +1 -0
  52. package/dist/chunk-J6JTWD2A.cjs +100 -0
  53. package/dist/chunk-J6JTWD2A.cjs.map +1 -0
  54. package/dist/chunk-JEQ7FLOD.cjs +199 -0
  55. package/dist/chunk-JEQ7FLOD.cjs.map +1 -0
  56. package/dist/chunk-K25XHMM3.js +566 -0
  57. package/dist/chunk-K25XHMM3.js.map +1 -0
  58. package/dist/chunk-LRZIAW7U.cjs +158 -0
  59. package/dist/chunk-LRZIAW7U.cjs.map +1 -0
  60. package/dist/{chunk-OHYBNCVL.cjs → chunk-MVIZJOO5.cjs} +10 -1256
  61. package/dist/chunk-MVIZJOO5.cjs.map +1 -0
  62. package/dist/{chunk-UYJ6TJHX.cjs → chunk-NAVPFIG5.cjs} +148 -55
  63. package/dist/chunk-NAVPFIG5.cjs.map +1 -0
  64. package/dist/chunk-NLJVSER2.js +273 -0
  65. package/dist/chunk-NLJVSER2.js.map +1 -0
  66. package/dist/chunk-NMPEMSU4.js +154 -0
  67. package/dist/chunk-NMPEMSU4.js.map +1 -0
  68. package/dist/chunk-NUKJ54GA.cjs +245 -0
  69. package/dist/chunk-NUKJ54GA.cjs.map +1 -0
  70. package/dist/{chunk-T3Q5V33G.cjs → chunk-QAYFOER6.cjs} +621 -829
  71. package/dist/chunk-QAYFOER6.cjs.map +1 -0
  72. package/dist/{chunk-MGFRZO24.js → chunk-QZGABF2M.js} +3 -149
  73. package/dist/chunk-QZGABF2M.js.map +1 -0
  74. package/dist/chunk-RNZFGN4R.js +88 -0
  75. package/dist/chunk-RNZFGN4R.js.map +1 -0
  76. package/dist/chunk-RZ6H7OZK.js +1134 -0
  77. package/dist/chunk-RZ6H7OZK.js.map +1 -0
  78. package/dist/{chunk-XXDFQERJ.js → chunk-VD2EA3WT.js} +7192 -9806
  79. package/dist/chunk-VD2EA3WT.js.map +1 -0
  80. package/dist/{chunk-SXXTQETM.cjs → chunk-VXE42MYF.cjs} +8722 -11323
  81. package/dist/chunk-VXE42MYF.cjs.map +1 -0
  82. package/dist/{chunk-4ZSNJDLS.cjs → chunk-WULONYGB.cjs} +9 -9
  83. package/dist/chunk-WULONYGB.cjs.map +1 -0
  84. package/dist/chunk-XW56B23A.cjs +408 -0
  85. package/dist/chunk-XW56B23A.cjs.map +1 -0
  86. package/dist/chunk-YA3TJ65D.cjs +575 -0
  87. package/dist/chunk-YA3TJ65D.cjs.map +1 -0
  88. package/dist/{chunk-TFNTM3OA.js → chunk-YHSQVQXX.js} +645 -15
  89. package/dist/chunk-YHSQVQXX.js.map +1 -0
  90. package/dist/chunk-YP7GW2G5.cjs +866 -0
  91. package/dist/chunk-YP7GW2G5.cjs.map +1 -0
  92. package/dist/{chunk-QFWHAFEO.js → chunk-ZEZ245PW.js} +148 -858
  93. package/dist/chunk-ZEZ245PW.js.map +1 -0
  94. package/dist/{chunk-EW5NOBVU.js → chunk-ZGGXCFR6.js} +611 -817
  95. package/dist/chunk-ZGGXCFR6.js.map +1 -0
  96. package/dist/{collection-config-B4PG-AaF.d.cts → collection-config-JgHOpFCG.d.cts} +30 -2
  97. package/dist/{collection-config-B4PG-AaF.d.ts → collection-config-JgHOpFCG.d.ts} +30 -2
  98. package/dist/config-HFXANXCC.js +6 -0
  99. package/dist/config-HFXANXCC.js.map +1 -0
  100. package/dist/config-ON6FNMYX.cjs +19 -0
  101. package/dist/config-ON6FNMYX.cjs.map +1 -0
  102. package/dist/define-plugin-BzNHc1ZI.d.ts +1321 -0
  103. package/dist/define-plugin-IWDKYaVm.d.cts +1321 -0
  104. package/dist/document-projection-TDWRJX3Z.cjs +13 -0
  105. package/dist/document-projection-TDWRJX3Z.cjs.map +1 -0
  106. package/dist/document-projection-YYMC6I4U.js +4 -0
  107. package/dist/document-projection-YYMC6I4U.js.map +1 -0
  108. package/dist/index.cjs +13735 -4329
  109. package/dist/index.cjs.map +1 -1
  110. package/dist/index.d.cts +329 -492
  111. package/dist/index.d.ts +329 -492
  112. package/dist/index.js +13386 -3999
  113. package/dist/index.js.map +1 -1
  114. package/dist/middleware.cjs +36 -32
  115. package/dist/middleware.d.cts +69 -7
  116. package/dist/middleware.d.ts +69 -7
  117. package/dist/middleware.js +7 -3
  118. package/dist/migrations-NJJWQUKK.cjs +13 -0
  119. package/dist/{migrations-IYNTWDC6.cjs.map → migrations-NJJWQUKK.cjs.map} +1 -1
  120. package/dist/migrations-WCAVBD7C.js +4 -0
  121. package/dist/{migrations-R337UD46.js.map → migrations-WCAVBD7C.js.map} +1 -1
  122. package/dist/{plugin-bootstrap-DfVerYV4.d.cts → plugin-bootstrap-B8ThJU21.d.cts} +4315 -1661
  123. package/dist/{plugin-bootstrap-P_ciLp_C.d.ts → plugin-bootstrap-qu8hJgUt.d.ts} +4315 -1661
  124. package/dist/plugins.cjs +171 -12
  125. package/dist/plugins.d.cts +36 -2
  126. package/dist/plugins.d.ts +36 -2
  127. package/dist/plugins.js +5 -2
  128. package/dist/rbac-O73MFKDA.js +5 -0
  129. package/dist/rbac-O73MFKDA.js.map +1 -0
  130. package/dist/rbac-VONLJJKB.cjs +14 -0
  131. package/dist/rbac-VONLJJKB.cjs.map +1 -0
  132. package/dist/routes.cjs +41 -45
  133. package/dist/routes.d.cts +56 -146
  134. package/dist/routes.d.ts +56 -146
  135. package/dist/routes.js +17 -9
  136. package/dist/services.cjs +39 -72
  137. package/dist/services.d.cts +79 -54
  138. package/dist/services.d.ts +79 -54
  139. package/dist/services.js +6 -3
  140. package/dist/templates.cjs +17 -29
  141. package/dist/templates.d.cts +1 -66
  142. package/dist/templates.d.ts +1 -66
  143. package/dist/templates.js +3 -3
  144. package/dist/types-Dea1eNxU.d.cts +286 -0
  145. package/dist/types-Dea1eNxU.d.ts +286 -0
  146. package/dist/types.d.cts +1 -1
  147. package/dist/types.d.ts +1 -1
  148. package/dist/utils.cjs +18 -17
  149. package/dist/utils.d.cts +1 -1
  150. package/dist/utils.d.ts +1 -1
  151. package/dist/utils.js +2 -1
  152. package/migrations/0001_core.sql +184 -0
  153. package/migrations/0002_documents.sql +163 -0
  154. package/package.json +12 -7
  155. package/dist/admin-layout-catalyst.template-HFD37TY5.cjs +0 -17
  156. package/dist/admin-layout-catalyst.template-UMTIN66R.js +0 -7
  157. package/dist/app-C9esKLmh.d.cts +0 -112
  158. package/dist/app-C9esKLmh.d.ts +0 -112
  159. package/dist/chunk-4R3NOOL3.js +0 -2217
  160. package/dist/chunk-4R3NOOL3.js.map +0 -1
  161. package/dist/chunk-4ZSNJDLS.cjs.map +0 -1
  162. package/dist/chunk-55RDMDOP.js.map +0 -1
  163. package/dist/chunk-635JAMSE.cjs +0 -653
  164. package/dist/chunk-635JAMSE.cjs.map +0 -1
  165. package/dist/chunk-ABB34XUS.cjs.map +0 -1
  166. package/dist/chunk-C54YUA23.cjs +0 -2219
  167. package/dist/chunk-C54YUA23.cjs.map +0 -1
  168. package/dist/chunk-DSUJ5YQH.cjs.map +0 -1
  169. package/dist/chunk-EW5NOBVU.js.map +0 -1
  170. package/dist/chunk-EXNEW5US.js +0 -648
  171. package/dist/chunk-EXNEW5US.js.map +0 -1
  172. package/dist/chunk-I2H5NGJQ.js +0 -692
  173. package/dist/chunk-I2H5NGJQ.js.map +0 -1
  174. package/dist/chunk-MGFRZO24.js.map +0 -1
  175. package/dist/chunk-OHYBNCVL.cjs.map +0 -1
  176. package/dist/chunk-ON5ZMSU4.js.map +0 -1
  177. package/dist/chunk-QFWHAFEO.js.map +0 -1
  178. package/dist/chunk-SQ6FNXU2.cjs.map +0 -1
  179. package/dist/chunk-SXXTQETM.cjs.map +0 -1
  180. package/dist/chunk-T3Q5V33G.cjs.map +0 -1
  181. package/dist/chunk-TFNTM3OA.js.map +0 -1
  182. package/dist/chunk-UYJ6TJHX.cjs.map +0 -1
  183. package/dist/chunk-WAEQXGCX.cjs +0 -1898
  184. package/dist/chunk-WAEQXGCX.cjs.map +0 -1
  185. package/dist/chunk-XWIA3HVX.js.map +0 -1
  186. package/dist/chunk-XXDFQERJ.js.map +0 -1
  187. package/dist/migrations-IYNTWDC6.cjs +0 -13
  188. package/dist/migrations-R337UD46.js +0 -4
  189. package/dist/plugin-manager-BoM3Q7o7.d.cts +0 -328
  190. package/dist/plugin-manager-Efx9RyDX.d.ts +0 -328
  191. package/migrations/001_initial_schema.sql +0 -170
  192. package/migrations/002_faq_plugin.sql +0 -86
  193. package/migrations/003_stage5_enhancements.sql +0 -121
  194. package/migrations/004_stage6_user_management.sql +0 -183
  195. package/migrations/005_stage7_workflow_automation.sql +0 -294
  196. package/migrations/006_plugin_system.sql +0 -155
  197. package/migrations/007_demo_login_plugin.sql +0 -23
  198. package/migrations/008_fix_slug_validation.sql +0 -22
  199. package/migrations/009_system_logging.sql +0 -57
  200. package/migrations/011_config_managed_collections.sql +0 -15
  201. package/migrations/012_testimonials_plugin.sql +0 -80
  202. package/migrations/013_code_examples_plugin.sql +0 -177
  203. package/migrations/014_fix_plugin_registry.sql +0 -88
  204. package/migrations/015_add_remaining_plugins.sql +0 -89
  205. package/migrations/016_remove_duplicate_cache_plugin.sql +0 -17
  206. package/migrations/017_auth_configurable_fields.sql +0 -49
  207. package/migrations/018_settings_table.sql +0 -23
  208. package/migrations/019_remove_blog_posts_collection.sql +0 -15
  209. package/migrations/020_add_email_plugin.sql +0 -22
  210. package/migrations/021_add_magic_link_auth_plugin.sql +0 -42
  211. package/migrations/022_add_tinymce_plugin.sql +0 -25
  212. package/migrations/023_add_easy_mdx_plugin.sql +0 -25
  213. package/migrations/024_add_quill_editor_plugin.sql +0 -25
  214. package/migrations/025_add_easymde_plugin.sql +0 -25
  215. package/migrations/026_add_otp_login.sql +0 -42
  216. package/migrations/027_fix_slug_field_type.sql +0 -18
  217. package/migrations/028_fix_slug_field_type_in_schemas.sql +0 -30
  218. package/migrations/029_add_forms_system.sql +0 -184
  219. package/migrations/030_add_turnstile_to_forms.sql +0 -14
  220. package/migrations/031_ai_search_plugin.sql +0 -45
  221. package/migrations/032_user_profiles.sql +0 -37
  222. package/migrations/033_form_content_integration.sql +0 -19
  223. package/migrations/034_security_audit_plugin.sql +0 -27
  224. package/migrations/035_user_profiles_data_column.sql +0 -16
  225. package/migrations/036_analytics_events.sql +0 -22
@@ -1,692 +0,0 @@
1
- import { syncCollections, syncAllFormCollections, PluginBootstrapService } from './chunk-EW5NOBVU.js';
2
- import { MigrationService } from './chunk-4R3NOOL3.js';
3
- import { metricsTracker } from './chunk-FICTAGD4.js';
4
- import { sign, verify } from 'hono/jwt';
5
- import { setCookie, getCookie } from 'hono/cookie';
6
-
7
- // src/middleware/bootstrap.ts
8
- var bootstrapComplete = false;
9
- function verifySecurityConfig(env) {
10
- const warnings = [];
11
- if (!env.JWT_SECRET) {
12
- warnings.push(
13
- "JWT_SECRET is not set \u2014 using hardcoded fallback. Set via `wrangler secret put JWT_SECRET`"
14
- );
15
- } else if (env.JWT_SECRET.includes("change-in-production")) {
16
- warnings.push(
17
- "JWT_SECRET contains the default value \u2014 tokens are forgeable. Generate a strong random secret"
18
- );
19
- }
20
- if (!env.CORS_ORIGINS) {
21
- warnings.push(
22
- "CORS_ORIGINS is not set \u2014 all cross-origin API requests will be rejected"
23
- );
24
- }
25
- if (!env.ENVIRONMENT) {
26
- warnings.push(
27
- 'ENVIRONMENT is not set \u2014 HSTS header will not be applied. Set to "production" or "development"'
28
- );
29
- }
30
- if (warnings.length === 0) {
31
- return;
32
- }
33
- const isProduction = env.ENVIRONMENT === "production";
34
- for (const warning of warnings) {
35
- console.warn(`[SonicJS Security] ${warning}`);
36
- }
37
- if (isProduction) {
38
- const hasCritical = !env.JWT_SECRET || env.JWT_SECRET.includes("change-in-production");
39
- if (hasCritical) {
40
- throw new Error(
41
- "[SonicJS Security] CRITICAL: Production deployment is missing a secure JWT_SECRET. Set it via `wrangler secret put JWT_SECRET` before deploying."
42
- );
43
- }
44
- }
45
- }
46
- function bootstrapMiddleware(config = {}) {
47
- return async (c, next) => {
48
- if (bootstrapComplete) {
49
- return next();
50
- }
51
- const path = c.req.path;
52
- if (path.startsWith("/images/") || path.startsWith("/assets/") || path === "/health" || path.endsWith(".js") || path.endsWith(".css") || path.endsWith(".png") || path.endsWith(".jpg") || path.endsWith(".ico")) {
53
- return next();
54
- }
55
- try {
56
- console.log("[Bootstrap] Starting system initialization...");
57
- console.log("[Bootstrap] Running database migrations...");
58
- const migrationService = new MigrationService(c.env.DB);
59
- await migrationService.runPendingMigrations();
60
- console.log("[Bootstrap] Syncing collection configurations...");
61
- try {
62
- await syncCollections(c.env.DB);
63
- } catch (error) {
64
- console.error("[Bootstrap] Error syncing collections:", error);
65
- }
66
- console.log("[Bootstrap] Syncing form collections...");
67
- try {
68
- await syncAllFormCollections(c.env.DB);
69
- } catch (error) {
70
- console.error("[Bootstrap] Error syncing form collections:", error);
71
- }
72
- if (!config.plugins?.disableAll) {
73
- console.log("[Bootstrap] Bootstrapping core plugins...");
74
- const bootstrapService = new PluginBootstrapService(c.env.DB);
75
- const needsBootstrap = await bootstrapService.isBootstrapNeeded();
76
- if (needsBootstrap) {
77
- await bootstrapService.bootstrapCorePlugins();
78
- }
79
- } else {
80
- console.log("[Bootstrap] Plugin bootstrap skipped (disableAll is true)");
81
- }
82
- bootstrapComplete = true;
83
- console.log("[Bootstrap] System initialization completed");
84
- } catch (error) {
85
- console.error("[Bootstrap] Error during system initialization:", error);
86
- }
87
- verifySecurityConfig(c.env);
88
- return next();
89
- };
90
- }
91
- var JWT_SECRET_FALLBACK = "your-super-secret-jwt-key-change-in-production";
92
- var DEFAULT_JWT_EXPIRES_IN_SECONDS = 60 * 60 * 24 * 30;
93
- function parseDuration(input) {
94
- if (input === void 0 || input === null || input === "") return null;
95
- if (typeof input === "number" && Number.isFinite(input) && input > 0) {
96
- return Math.floor(input);
97
- }
98
- const raw = String(input).trim();
99
- if (/^\d+$/.test(raw)) {
100
- const n = parseInt(raw, 10);
101
- return n > 0 ? n : null;
102
- }
103
- const match = raw.match(/^(\d+)\s*(s|sec|secs|seconds|m|min|mins|minutes|h|hr|hrs|hours|d|day|days)$/i);
104
- if (!match) return null;
105
- const value = parseInt(match[1], 10);
106
- const unit = match[2].toLowerCase();
107
- if (unit.startsWith("s")) return value;
108
- if (unit.startsWith("m")) return value * 60;
109
- if (unit.startsWith("h")) return value * 60 * 60;
110
- if (unit.startsWith("d")) return value * 60 * 60 * 24;
111
- return null;
112
- }
113
- function getJwtExpirySeconds(env) {
114
- const configured = parseDuration(env?.JWT_EXPIRES_IN);
115
- return configured ?? DEFAULT_JWT_EXPIRES_IN_SECONDS;
116
- }
117
- async function getJwtExpirySecondsFromDb(db, env) {
118
- const envParsed = parseDuration(env?.JWT_EXPIRES_IN);
119
- if (envParsed) return envParsed;
120
- if (db) {
121
- try {
122
- const row = await db.prepare("SELECT value FROM settings WHERE category = 'security' AND key = 'jwtExpiresIn'").first();
123
- if (row?.value) {
124
- let stored = row.value;
125
- try {
126
- stored = JSON.parse(row.value);
127
- } catch {
128
- }
129
- const parsed = parseDuration(stored);
130
- if (parsed) return parsed;
131
- }
132
- } catch (err) {
133
- console.warn("Failed to read jwtExpiresIn from settings, falling back to default:", err);
134
- }
135
- }
136
- return DEFAULT_JWT_EXPIRES_IN_SECONDS;
137
- }
138
- async function getJwtRefreshGraceSecondsFromDb(db, env) {
139
- const DEFAULT_GRACE = 60 * 60 * 24 * 7;
140
- const envParsed = parseDuration(env?.JWT_REFRESH_GRACE_SECONDS);
141
- if (envParsed) return envParsed;
142
- if (db) {
143
- try {
144
- const row = await db.prepare("SELECT value FROM settings WHERE category = 'security' AND key = 'jwtRefreshGraceSeconds'").first();
145
- if (row?.value) {
146
- let stored = row.value;
147
- try {
148
- stored = JSON.parse(row.value);
149
- } catch {
150
- }
151
- const parsed = parseDuration(stored);
152
- if (parsed) return parsed;
153
- }
154
- } catch (err) {
155
- console.warn("Failed to read jwtRefreshGraceSeconds from settings:", err);
156
- }
157
- }
158
- return DEFAULT_GRACE;
159
- }
160
- function decodeJwtPayload(token) {
161
- try {
162
- const parts = token.split(".");
163
- if (parts.length !== 3) return null;
164
- const b64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
165
- const padded = b64 + "=".repeat((4 - b64.length % 4) % 4);
166
- const json = atob(padded);
167
- const obj = JSON.parse(json);
168
- if (!obj || typeof obj.exp !== "number") return null;
169
- return obj;
170
- } catch {
171
- return null;
172
- }
173
- }
174
- function base64UrlToBytes(b64url) {
175
- const b64 = b64url.replace(/-/g, "+").replace(/_/g, "/");
176
- const padded = b64 + "=".repeat((4 - b64.length % 4) % 4);
177
- const bin = atob(padded);
178
- const bytes = new Uint8Array(bin.length);
179
- for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
180
- return bytes;
181
- }
182
- async function verifyHs256Signature(token, secret) {
183
- try {
184
- const parts = token.split(".");
185
- if (parts.length !== 3) return false;
186
- const encoder = new TextEncoder();
187
- const key = await crypto.subtle.importKey(
188
- "raw",
189
- encoder.encode(secret),
190
- { name: "HMAC", hash: "SHA-256" },
191
- false,
192
- ["verify"]
193
- );
194
- const signature = base64UrlToBytes(parts[2]);
195
- const message = encoder.encode(`${parts[0]}.${parts[1]}`);
196
- return await crypto.subtle.verify("HMAC", key, signature, message);
197
- } catch {
198
- return false;
199
- }
200
- }
201
- var AuthManager = class {
202
- static async generateToken(userId, email, role, secret, expiresInSeconds) {
203
- const ttl = expiresInSeconds && expiresInSeconds > 0 ? Math.floor(expiresInSeconds) : DEFAULT_JWT_EXPIRES_IN_SECONDS;
204
- const now = Math.floor(Date.now() / 1e3);
205
- const payload = {
206
- userId,
207
- email,
208
- role,
209
- exp: now + ttl,
210
- iat: now
211
- };
212
- return await sign(payload, secret || JWT_SECRET_FALLBACK, "HS256");
213
- }
214
- /**
215
- * Verify a token's signature and expiration.
216
- *
217
- * If `graceSeconds` > 0, tokens whose `exp` is within the grace window
218
- * (i.e. expired by no more than `graceSeconds`) are still returned. This
219
- * supports a sliding-session refresh endpoint that accepts recently-expired
220
- * tokens. Signature failures always return null.
221
- */
222
- static async verifyToken(token, secret, graceSeconds = 0) {
223
- const effectiveSecret = secret || JWT_SECRET_FALLBACK;
224
- try {
225
- let payload = null;
226
- try {
227
- payload = await verify(token, effectiveSecret, "HS256");
228
- } catch (verifyError) {
229
- const name = verifyError?.name || "";
230
- const message = String(verifyError?.message || "");
231
- const isExpired = name === "JwtTokenExpired" || message.includes("expired");
232
- if (!isExpired || graceSeconds <= 0) {
233
- throw verifyError;
234
- }
235
- const signatureValid = await verifyHs256Signature(token, effectiveSecret);
236
- if (!signatureValid) return null;
237
- const decoded = decodeJwtPayload(token);
238
- if (!decoded) return null;
239
- payload = decoded;
240
- }
241
- if (!payload) return null;
242
- const now = Math.floor(Date.now() / 1e3);
243
- if (payload.exp < now - Math.max(0, Math.floor(graceSeconds))) {
244
- return null;
245
- }
246
- return payload;
247
- } catch (error) {
248
- console.error("Token verification failed:", error);
249
- return null;
250
- }
251
- }
252
- static async hashPassword(password) {
253
- const iterations = 1e5;
254
- const salt = new Uint8Array(16);
255
- crypto.getRandomValues(salt);
256
- const encoder = new TextEncoder();
257
- const keyMaterial = await crypto.subtle.importKey(
258
- "raw",
259
- encoder.encode(password),
260
- "PBKDF2",
261
- false,
262
- ["deriveBits"]
263
- );
264
- const hashBuffer = await crypto.subtle.deriveBits(
265
- {
266
- name: "PBKDF2",
267
- salt,
268
- iterations,
269
- hash: "SHA-256"
270
- },
271
- keyMaterial,
272
- 256
273
- );
274
- const saltHex = Array.from(salt).map((b) => b.toString(16).padStart(2, "0")).join("");
275
- const hashHex = Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
276
- return `pbkdf2:${iterations}:${saltHex}:${hashHex}`;
277
- }
278
- static async hashPasswordLegacy(password) {
279
- const encoder = new TextEncoder();
280
- const data = encoder.encode(password + "salt-change-in-production");
281
- const hashBuffer = await crypto.subtle.digest("SHA-256", data);
282
- const hashArray = Array.from(new Uint8Array(hashBuffer));
283
- return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
284
- }
285
- static async verifyPassword(password, storedHash) {
286
- if (storedHash.startsWith("pbkdf2:")) {
287
- const parts = storedHash.split(":");
288
- if (parts.length !== 4) return false;
289
- const iterationsStr = parts[1];
290
- const saltHex = parts[2];
291
- const expectedHashHex = parts[3];
292
- const iterations = parseInt(iterationsStr, 10);
293
- const saltBytes = saltHex.match(/.{2}/g);
294
- if (!saltBytes) return false;
295
- const salt = new Uint8Array(saltBytes.map((byte) => parseInt(byte, 16)));
296
- const encoder = new TextEncoder();
297
- const keyMaterial = await crypto.subtle.importKey(
298
- "raw",
299
- encoder.encode(password),
300
- "PBKDF2",
301
- false,
302
- ["deriveBits"]
303
- );
304
- const hashBuffer = await crypto.subtle.deriveBits(
305
- {
306
- name: "PBKDF2",
307
- salt,
308
- iterations,
309
- hash: "SHA-256"
310
- },
311
- keyMaterial,
312
- 256
313
- );
314
- const actualHashHex = Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
315
- if (actualHashHex.length !== expectedHashHex.length) return false;
316
- let result2 = 0;
317
- for (let i = 0; i < actualHashHex.length; i++) {
318
- result2 |= actualHashHex.charCodeAt(i) ^ expectedHashHex.charCodeAt(i);
319
- }
320
- return result2 === 0;
321
- }
322
- const legacyHash = await this.hashPasswordLegacy(password);
323
- if (legacyHash.length !== storedHash.length) return false;
324
- let result = 0;
325
- for (let i = 0; i < legacyHash.length; i++) {
326
- result |= legacyHash.charCodeAt(i) ^ storedHash.charCodeAt(i);
327
- }
328
- return result === 0;
329
- }
330
- static isLegacyHash(storedHash) {
331
- return !storedHash.startsWith("pbkdf2:");
332
- }
333
- /**
334
- * Set authentication cookie - useful for plugins implementing alternative auth methods
335
- * @param c - Hono context
336
- * @param token - JWT token to set in cookie
337
- * @param options - Optional cookie configuration
338
- */
339
- static setAuthCookie(c, token, options) {
340
- setCookie(c, "auth_token", token, {
341
- httpOnly: options?.httpOnly ?? true,
342
- secure: options?.secure ?? true,
343
- sameSite: options?.sameSite ?? "Strict",
344
- maxAge: options?.maxAge ?? getJwtExpirySeconds(c?.env)
345
- });
346
- }
347
- };
348
- var requireAuth = () => {
349
- return async (c, next) => {
350
- try {
351
- let token = c.req.header("Authorization")?.replace("Bearer ", "");
352
- if (!token) {
353
- token = getCookie(c, "auth_token");
354
- }
355
- if (!token) {
356
- const acceptHeader = c.req.header("Accept") || "";
357
- if (acceptHeader.includes("text/html")) {
358
- return c.redirect("/auth/login?error=Please login to access the admin area");
359
- }
360
- return c.json({ error: "Authentication required" }, 401);
361
- }
362
- const kv = c.env?.KV;
363
- let payload = null;
364
- if (kv) {
365
- const cacheKey = `auth:${token.substring(0, 20)}`;
366
- const cached = await kv.get(cacheKey, "json");
367
- if (cached) {
368
- payload = cached;
369
- }
370
- }
371
- if (!payload) {
372
- const jwtSecret = c.env?.JWT_SECRET;
373
- payload = await AuthManager.verifyToken(token, jwtSecret);
374
- if (payload && kv) {
375
- const cacheKey = `auth:${token.substring(0, 20)}`;
376
- await kv.put(cacheKey, JSON.stringify(payload), { expirationTtl: 300 });
377
- }
378
- }
379
- if (!payload) {
380
- const acceptHeader = c.req.header("Accept") || "";
381
- if (acceptHeader.includes("text/html")) {
382
- return c.redirect("/auth/login?error=Your session has expired, please login again");
383
- }
384
- return c.json({ error: "Invalid or expired token" }, 401);
385
- }
386
- c.set("user", payload);
387
- return await next();
388
- } catch (error) {
389
- console.error("Auth middleware error:", error);
390
- const acceptHeader = c.req.header("Accept") || "";
391
- if (acceptHeader.includes("text/html")) {
392
- return c.redirect("/auth/login?error=Authentication failed, please login again");
393
- }
394
- return c.json({ error: "Authentication failed" }, 401);
395
- }
396
- };
397
- };
398
- var requireRole = (requiredRole) => {
399
- return async (c, next) => {
400
- const user = c.get("user");
401
- if (!user) {
402
- const acceptHeader = c.req.header("Accept") || "";
403
- if (acceptHeader.includes("text/html")) {
404
- return c.redirect("/auth/login?error=Please login to access the admin area");
405
- }
406
- return c.json({ error: "Authentication required" }, 401);
407
- }
408
- const roles = Array.isArray(requiredRole) ? requiredRole : [requiredRole];
409
- if (!roles.includes(user.role)) {
410
- const acceptHeader = c.req.header("Accept") || "";
411
- if (acceptHeader.includes("text/html")) {
412
- return c.redirect("/auth/login?error=You do not have permission to access this area");
413
- }
414
- return c.json({ error: "Insufficient permissions" }, 403);
415
- }
416
- return await next();
417
- };
418
- };
419
- var optionalAuth = () => {
420
- return async (c, next) => {
421
- try {
422
- let token = c.req.header("Authorization")?.replace("Bearer ", "");
423
- if (!token) {
424
- token = getCookie(c, "auth_token");
425
- }
426
- if (token) {
427
- const jwtSecret = c.env?.JWT_SECRET;
428
- const payload = await AuthManager.verifyToken(token, jwtSecret);
429
- if (payload) {
430
- c.set("user", payload);
431
- }
432
- }
433
- return await next();
434
- } catch (error) {
435
- console.error("Optional auth error:", error);
436
- return await next();
437
- }
438
- };
439
- };
440
-
441
- // src/middleware/metrics.ts
442
- var metricsMiddleware = () => {
443
- return async (c, next) => {
444
- const path = new URL(c.req.url).pathname;
445
- if (path !== "/admin/dashboard/api/metrics") {
446
- metricsTracker.recordRequest();
447
- }
448
- await next();
449
- };
450
- };
451
- var JWT_SECRET_FALLBACK2 = "your-super-secret-jwt-key-change-in-production";
452
- function arrayBufferToBase64Url(buffer) {
453
- const bytes = new Uint8Array(buffer);
454
- let binary = "";
455
- for (let i = 0; i < bytes.length; i++) {
456
- binary += String.fromCharCode(bytes[i]);
457
- }
458
- return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
459
- }
460
- async function getHmacKey(secret) {
461
- const encoder = new TextEncoder();
462
- return crypto.subtle.importKey(
463
- "raw",
464
- encoder.encode(secret),
465
- { name: "HMAC", hash: "SHA-256" },
466
- false,
467
- ["sign", "verify"]
468
- );
469
- }
470
- async function generateCsrfToken(secret) {
471
- const nonceBytes = new Uint8Array(32);
472
- crypto.getRandomValues(nonceBytes);
473
- const nonce = arrayBufferToBase64Url(nonceBytes.buffer);
474
- const key = await getHmacKey(secret);
475
- const encoder = new TextEncoder();
476
- const signatureBuffer = await crypto.subtle.sign("HMAC", key, encoder.encode(nonce));
477
- const signature = arrayBufferToBase64Url(signatureBuffer);
478
- return `${nonce}.${signature}`;
479
- }
480
- async function validateCsrfToken(token, secret) {
481
- if (!token || typeof token !== "string") return false;
482
- const dotIndex = token.indexOf(".");
483
- if (dotIndex === -1) return false;
484
- const nonce = token.substring(0, dotIndex);
485
- const signature = token.substring(dotIndex + 1);
486
- if (!nonce || !signature) return false;
487
- try {
488
- const key = await getHmacKey(secret);
489
- const encoder = new TextEncoder();
490
- const sigPadded = signature.replace(/-/g, "+").replace(/_/g, "/");
491
- const sigBinary = atob(sigPadded);
492
- const sigBytes = new Uint8Array(sigBinary.length);
493
- for (let i = 0; i < sigBinary.length; i++) {
494
- sigBytes[i] = sigBinary.charCodeAt(i);
495
- }
496
- return await crypto.subtle.verify("HMAC", key, sigBytes.buffer, encoder.encode(nonce));
497
- } catch {
498
- return false;
499
- }
500
- }
501
- var DEFAULT_EXEMPT_PATHS = [
502
- "/auth/login",
503
- "/auth/register",
504
- "/auth/seed-admin",
505
- "/auth/accept-invitation",
506
- "/auth/reset-password",
507
- "/auth/request-password-reset",
508
- "/auth/otp",
509
- "/auth/magic-link",
510
- "/auth/verify",
511
- "/api/stripe/webhook",
512
- "/api/events"
513
- ];
514
- function isExemptPath(path, extraExemptPaths = []) {
515
- if (path.startsWith("/forms/") || path.startsWith("/api/forms/") || path === "/forms" || path === "/api/forms") {
516
- return true;
517
- }
518
- if (path.startsWith("/api/search")) {
519
- return true;
520
- }
521
- const allExempt = [...DEFAULT_EXEMPT_PATHS, ...extraExemptPaths];
522
- for (const exempt of allExempt) {
523
- if (path === exempt || path.startsWith(exempt + "/")) {
524
- return true;
525
- }
526
- }
527
- return false;
528
- }
529
- function csrfProtection(options = {}) {
530
- return async (c, next) => {
531
- const method = c.req.method.toUpperCase();
532
- const path = new URL(c.req.url).pathname;
533
- const secret = c.env?.JWT_SECRET || JWT_SECRET_FALLBACK2;
534
- if (c.env?.ENVIRONMENT === "production" && !c.env?.JWT_SECRET) {
535
- console.warn(
536
- "[CSRF] WARNING: JWT_SECRET is not set in production. CSRF tokens are signed with the fallback key, which is insecure."
537
- );
538
- }
539
- if (method === "GET" || method === "HEAD" || method === "OPTIONS") {
540
- await ensureCsrfCookie(c, secret);
541
- await next();
542
- return;
543
- }
544
- if (isExemptPath(path, options.exemptPaths)) {
545
- await next();
546
- return;
547
- }
548
- const authCookie = getCookie(c, "auth_token");
549
- if (!authCookie) {
550
- await next();
551
- return;
552
- }
553
- const authHeader = c.req.header("Authorization");
554
- if (authHeader) {
555
- await next();
556
- return;
557
- }
558
- const cookieToken = getCookie(c, "csrf_token");
559
- let headerToken = c.req.header("X-CSRF-Token");
560
- if (!headerToken) {
561
- const contentType = c.req.header("Content-Type") || "";
562
- if (contentType.includes("application/x-www-form-urlencoded") || contentType.includes("multipart/form-data")) {
563
- try {
564
- const body = await c.req.parseBody();
565
- headerToken = body["_csrf"];
566
- } catch {
567
- }
568
- }
569
- }
570
- if (!cookieToken || !headerToken) {
571
- return csrfError(c, "CSRF token missing");
572
- }
573
- if (cookieToken !== headerToken) {
574
- return csrfError(c, "CSRF token mismatch");
575
- }
576
- const isValid = await validateCsrfToken(cookieToken, secret);
577
- if (!isValid) {
578
- return csrfError(c, "CSRF token invalid");
579
- }
580
- await next();
581
- };
582
- }
583
- async function ensureCsrfCookie(c, secret) {
584
- const existing = getCookie(c, "csrf_token");
585
- if (existing) {
586
- const isValid = await validateCsrfToken(existing, secret);
587
- if (isValid) {
588
- c.set("csrfToken", existing);
589
- return;
590
- }
591
- }
592
- const token = await generateCsrfToken(secret);
593
- c.set("csrfToken", token);
594
- const isDev = c.env?.ENVIRONMENT === "development" || !c.env?.ENVIRONMENT;
595
- setCookie(c, "csrf_token", token, {
596
- httpOnly: false,
597
- // JS must read this cookie
598
- secure: !isDev,
599
- sameSite: "Strict",
600
- path: "/",
601
- maxAge: 86400
602
- // 24 hours — browser-side expiry
603
- });
604
- }
605
- function csrfError(c, message) {
606
- const accept = c.req.header("Accept") || "";
607
- if (accept.includes("text/html")) {
608
- return c.html(
609
- `<!DOCTYPE html><html><head><title>403 Forbidden</title></head><body><h1>403 Forbidden</h1><p>${message}</p></body></html>`,
610
- 403
611
- );
612
- }
613
- return c.json({ error: message, status: 403 }, 403);
614
- }
615
-
616
- // src/middleware/rate-limit.ts
617
- function rateLimit(options) {
618
- const { max, windowMs, keyPrefix } = options;
619
- return async (c, next) => {
620
- const kv = c.env?.CACHE_KV;
621
- if (!kv) {
622
- return await next();
623
- }
624
- const ip = c.req.header("cf-connecting-ip") || c.req.header("x-forwarded-for") || "unknown";
625
- const key = `ratelimit:${keyPrefix}:${ip}`;
626
- try {
627
- const now = Date.now();
628
- const stored = await kv.get(key, "json");
629
- let entry;
630
- if (stored && stored.resetAt > now) {
631
- entry = stored;
632
- } else {
633
- entry = { count: 0, resetAt: now + windowMs };
634
- }
635
- entry.count++;
636
- const ttlSeconds = Math.ceil((entry.resetAt - now) / 1e3);
637
- if (entry.count > max) {
638
- await kv.put(key, JSON.stringify(entry), { expirationTtl: Math.max(ttlSeconds, 60) });
639
- const retryAfter = Math.ceil((entry.resetAt - now) / 1e3);
640
- c.header("Retry-After", String(retryAfter));
641
- c.header("X-RateLimit-Limit", String(max));
642
- c.header("X-RateLimit-Remaining", "0");
643
- c.header("X-RateLimit-Reset", String(Math.ceil(entry.resetAt / 1e3)));
644
- return c.json({ error: "Too many requests. Please try again later." }, 429);
645
- }
646
- await kv.put(key, JSON.stringify(entry), { expirationTtl: Math.max(ttlSeconds, 60) });
647
- c.header("X-RateLimit-Limit", String(max));
648
- c.header("X-RateLimit-Remaining", String(max - entry.count));
649
- c.header("X-RateLimit-Reset", String(Math.ceil(entry.resetAt / 1e3)));
650
- return await next();
651
- } catch (error) {
652
- console.error("Rate limiter error (non-fatal):", error);
653
- return await next();
654
- }
655
- };
656
- }
657
-
658
- // src/middleware/security-headers.ts
659
- var securityHeadersMiddleware = () => {
660
- return async (c, next) => {
661
- await next();
662
- c.header("X-Content-Type-Options", "nosniff");
663
- c.header("X-Frame-Options", "SAMEORIGIN");
664
- c.header("Referrer-Policy", "strict-origin-when-cross-origin");
665
- c.header("Permissions-Policy", "camera=(), microphone=(), geolocation=()");
666
- const environment = c.env?.ENVIRONMENT;
667
- if (environment !== "development") {
668
- c.header("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
669
- }
670
- };
671
- };
672
-
673
- // src/middleware/index.ts
674
- var loggingMiddleware = () => async (_c, next) => await next();
675
- var detailedLoggingMiddleware = () => async (_c, next) => await next();
676
- var securityLoggingMiddleware = () => async (_c, next) => await next();
677
- var performanceLoggingMiddleware = () => async (_c, next) => await next();
678
- var cacheHeaders = () => async (_c, next) => await next();
679
- var compressionMiddleware = async (_c, next) => await next();
680
- var PermissionManager = {};
681
- var requirePermission = () => async (_c, next) => await next();
682
- var requireAnyPermission = () => async (_c, next) => await next();
683
- var logActivity = () => {
684
- };
685
- var requireActivePlugin = () => async (_c, next) => await next();
686
- var requireActivePlugins = () => async (_c, next) => await next();
687
- var getActivePlugins = () => [];
688
- var isPluginActive = () => false;
689
-
690
- export { AuthManager, PermissionManager, bootstrapMiddleware, cacheHeaders, compressionMiddleware, csrfProtection, detailedLoggingMiddleware, generateCsrfToken, getActivePlugins, getJwtExpirySeconds, getJwtExpirySecondsFromDb, getJwtRefreshGraceSecondsFromDb, isPluginActive, logActivity, loggingMiddleware, metricsMiddleware, optionalAuth, performanceLoggingMiddleware, rateLimit, requireActivePlugin, requireActivePlugins, requireAnyPermission, requireAuth, requirePermission, requireRole, securityHeadersMiddleware, securityLoggingMiddleware, validateCsrfToken, verifySecurityConfig };
691
- //# sourceMappingURL=chunk-I2H5NGJQ.js.map
692
- //# sourceMappingURL=chunk-I2H5NGJQ.js.map