@kyro-cms/core 0.9.0 → 0.9.1

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 (248) hide show
  1. package/README.md +57 -589
  2. package/dist/{WebhookService-118ZTFis.d.ts → WebhookService-CUTb9XOy.d.ts} +1 -1
  3. package/dist/{WebhookService-AefJfqX0.d.cts → WebhookService-Yg2UEOB4.d.cts} +1 -1
  4. package/dist/api-handler-graphql.cjs +44 -0
  5. package/dist/api-handler-graphql.cjs.map +1 -0
  6. package/dist/api-handler-graphql.d.cts +6 -0
  7. package/dist/api-handler-graphql.d.ts +6 -0
  8. package/dist/api-handler-graphql.js +41 -0
  9. package/dist/api-handler-graphql.js.map +1 -0
  10. package/dist/api-handler-trpc.cjs +38 -0
  11. package/dist/api-handler-trpc.cjs.map +1 -0
  12. package/dist/api-handler-trpc.d.cts +5 -0
  13. package/dist/api-handler-trpc.d.ts +5 -0
  14. package/dist/api-handler-trpc.js +36 -0
  15. package/dist/api-handler-trpc.js.map +1 -0
  16. package/dist/api-handler.cjs +31 -97
  17. package/dist/api-handler.cjs.map +1 -1
  18. package/dist/api-handler.d.cts +2 -1
  19. package/dist/api-handler.d.ts +2 -1
  20. package/dist/api-handler.js +19 -95
  21. package/dist/api-handler.js.map +1 -1
  22. package/dist/{tenant-B1YB0Jy8.d.ts → base-B71y_EAF.d.cts} +6 -12
  23. package/dist/{tenant-Cpeveji6.d.cts → base-DaqY2GhA.d.ts} +6 -12
  24. package/dist/bootstrap-5NLASFOG.cjs +32 -0
  25. package/dist/{bootstrap-AKAUP6F6.cjs.map → bootstrap-5NLASFOG.cjs.map} +1 -1
  26. package/dist/bootstrap-T5BK77LD.js +7 -0
  27. package/dist/{bootstrap-JCML6NFO.js.map → bootstrap-T5BK77LD.js.map} +1 -1
  28. package/dist/{chunk-35U3FROB.js → chunk-22M4O4ZJ.js} +607 -63
  29. package/dist/chunk-22M4O4ZJ.js.map +1 -0
  30. package/dist/chunk-2HZRBATX.cjs +253 -0
  31. package/dist/chunk-2HZRBATX.cjs.map +1 -0
  32. package/dist/{chunk-VJT6P4N6.cjs → chunk-3HR772HI.cjs} +199 -32
  33. package/dist/chunk-3HR772HI.cjs.map +1 -0
  34. package/dist/chunk-3KTWGODI.cjs +178 -0
  35. package/dist/chunk-3KTWGODI.cjs.map +1 -0
  36. package/dist/{chunk-QXIQWPAP.js → chunk-3UK5XBVJ.js} +4 -134
  37. package/dist/chunk-3UK5XBVJ.js.map +1 -0
  38. package/dist/{chunk-FXYP2HA6.js → chunk-4AO3A3JM.js} +48 -4
  39. package/dist/chunk-4AO3A3JM.js.map +1 -0
  40. package/dist/chunk-4M7X5HAB.cjs +173 -0
  41. package/dist/chunk-4M7X5HAB.cjs.map +1 -0
  42. package/dist/chunk-5EPFQUQD.js +3243 -0
  43. package/dist/chunk-5EPFQUQD.js.map +1 -0
  44. package/dist/{chunk-Y3N7UUDO.js → chunk-7OGPN7MP.js} +5 -2
  45. package/dist/chunk-7OGPN7MP.js.map +1 -0
  46. package/dist/{chunk-WOWUL7ZY.js → chunk-AL5KX63J.js} +4 -3
  47. package/dist/chunk-AL5KX63J.js.map +1 -0
  48. package/dist/{chunk-2OL4O2TH.cjs → chunk-C36TMDTY.cjs} +66 -61
  49. package/dist/chunk-C36TMDTY.cjs.map +1 -0
  50. package/dist/{chunk-ES5HNFFT.js → chunk-CF7OL6HR.js} +4 -2
  51. package/dist/chunk-CF7OL6HR.js.map +1 -0
  52. package/dist/chunk-CJONKRHJ.js +162 -0
  53. package/dist/chunk-CJONKRHJ.js.map +1 -0
  54. package/dist/{chunk-2KVHZE6O.cjs → chunk-COIASRDK.cjs} +202 -46
  55. package/dist/chunk-COIASRDK.cjs.map +1 -0
  56. package/dist/chunk-DEVFAKCQ.cjs +3291 -0
  57. package/dist/chunk-DEVFAKCQ.cjs.map +1 -0
  58. package/dist/{chunk-3ZFYL34R.js → chunk-DYTZ6FQ7.js} +12 -185
  59. package/dist/chunk-DYTZ6FQ7.js.map +1 -0
  60. package/dist/{chunk-QPPDLRNR.js → chunk-EJN2PAOE.js} +197 -41
  61. package/dist/chunk-EJN2PAOE.js.map +1 -0
  62. package/dist/chunk-FAXU7BMP.js +220 -0
  63. package/dist/chunk-FAXU7BMP.js.map +1 -0
  64. package/dist/{chunk-OHVB4AJ7.js → chunk-FOPGUM27.js} +22 -17
  65. package/dist/chunk-FOPGUM27.js.map +1 -0
  66. package/dist/chunk-GAOXD3XT.js +175 -0
  67. package/dist/chunk-GAOXD3XT.js.map +1 -0
  68. package/dist/{chunk-4DA7QPLA.cjs → chunk-GXFOGU7N.cjs} +5 -2
  69. package/dist/chunk-GXFOGU7N.cjs.map +1 -0
  70. package/dist/{chunk-I7HHI6QV.cjs → chunk-IDVRRRAK.cjs} +17 -9
  71. package/dist/chunk-IDVRRRAK.cjs.map +1 -0
  72. package/dist/{chunk-WQBRWOQT.cjs → chunk-JOPVMWTM.cjs} +3 -2
  73. package/dist/chunk-JOPVMWTM.cjs.map +1 -0
  74. package/dist/chunk-KC2GDBLS.cjs +84 -0
  75. package/dist/chunk-KC2GDBLS.cjs.map +1 -0
  76. package/dist/{chunk-K7JPTH3G.cjs → chunk-KNRSROWB.cjs} +132 -74
  77. package/dist/chunk-KNRSROWB.cjs.map +1 -0
  78. package/dist/{chunk-3AJE4SEG.js → chunk-KPA4AN4R.js} +125 -67
  79. package/dist/chunk-KPA4AN4R.js.map +1 -0
  80. package/dist/{chunk-QUW2RZTM.cjs → chunk-L46ROHUS.cjs} +51 -7
  81. package/dist/chunk-L46ROHUS.cjs.map +1 -0
  82. package/dist/chunk-L4EZKIEX.js +185 -0
  83. package/dist/chunk-L4EZKIEX.js.map +1 -0
  84. package/dist/{chunk-REK7AYOC.js → chunk-L5UKKZQN.js} +199 -32
  85. package/dist/chunk-L5UKKZQN.js.map +1 -0
  86. package/dist/chunk-NKPKR5BW.cjs +188 -0
  87. package/dist/chunk-NKPKR5BW.cjs.map +1 -0
  88. package/dist/{chunk-Y3QQN7PN.js → chunk-P2HKJ7P5.js} +13 -4
  89. package/dist/chunk-P2HKJ7P5.js.map +1 -0
  90. package/dist/{chunk-SA7NSSIQ.cjs → chunk-PI73NNOK.cjs} +13 -187
  91. package/dist/chunk-PI73NNOK.cjs.map +1 -0
  92. package/dist/{chunk-HXRD4B37.js → chunk-PU2Z5VWF.js} +1279 -556
  93. package/dist/chunk-PU2Z5VWF.js.map +1 -0
  94. package/dist/{chunk-H727JIG7.js → chunk-Q72BOAPK.js} +16 -8
  95. package/dist/chunk-Q72BOAPK.js.map +1 -0
  96. package/dist/{chunk-IBG6V56E.cjs → chunk-QFLB4EIJ.cjs} +2 -139
  97. package/dist/chunk-QFLB4EIJ.cjs.map +1 -0
  98. package/dist/{chunk-YVUJBEXE.cjs → chunk-RAMGUDJN.cjs} +16 -7
  99. package/dist/chunk-RAMGUDJN.cjs.map +1 -0
  100. package/dist/{chunk-LINKCEG4.cjs → chunk-ROJHKAQ4.cjs} +617 -73
  101. package/dist/chunk-ROJHKAQ4.cjs.map +1 -0
  102. package/dist/{chunk-5KVM3WEY.cjs → chunk-RSF3UU7H.cjs} +1330 -602
  103. package/dist/chunk-RSF3UU7H.cjs.map +1 -0
  104. package/dist/{chunk-V3LKPM3O.cjs → chunk-SHTTJMLT.cjs} +4 -2
  105. package/dist/chunk-SHTTJMLT.cjs.map +1 -0
  106. package/dist/chunk-SPBTLUN6.js +92 -0
  107. package/dist/chunk-SPBTLUN6.js.map +1 -0
  108. package/dist/{chunk-57P6MJKC.js → chunk-TXSZFA4G.js} +3 -3
  109. package/dist/chunk-TXSZFA4G.js.map +1 -0
  110. package/dist/chunk-UERVXYVK.cjs +99 -0
  111. package/dist/chunk-UERVXYVK.cjs.map +1 -0
  112. package/dist/{chunk-PDYFVNUX.cjs → chunk-V2TVSCV5.cjs} +16 -23
  113. package/dist/chunk-V2TVSCV5.cjs.map +1 -0
  114. package/dist/{chunk-DXHRBMGB.js → chunk-VO35MNPH.js} +12 -19
  115. package/dist/chunk-VO35MNPH.js.map +1 -0
  116. package/dist/{chunk-IA6AU5PI.cjs → chunk-WNCYAKF3.cjs} +3 -3
  117. package/dist/chunk-WNCYAKF3.cjs.map +1 -0
  118. package/dist/chunk-XEB7PH2E.js +81 -0
  119. package/dist/chunk-XEB7PH2E.js.map +1 -0
  120. package/dist/cli/index.cjs +5 -5
  121. package/dist/cli/index.cjs.map +1 -1
  122. package/dist/cli/index.js +5 -5
  123. package/dist/cli/index.js.map +1 -1
  124. package/dist/client.cjs +3 -3
  125. package/dist/client.d.cts +3 -3
  126. package/dist/client.d.ts +3 -3
  127. package/dist/client.js +1 -1
  128. package/dist/drizzle/index.cjs +14 -13
  129. package/dist/drizzle/index.d.cts +9 -7
  130. package/dist/drizzle/index.d.ts +9 -7
  131. package/dist/drizzle/index.js +5 -4
  132. package/dist/fields/index.cjs +21 -37
  133. package/dist/fields/index.d.cts +2 -22
  134. package/dist/fields/index.d.ts +2 -22
  135. package/dist/fields/index.js +1 -1
  136. package/dist/graphql/index.cjs +5 -4
  137. package/dist/graphql/index.d.cts +5 -3
  138. package/dist/graphql/index.d.ts +5 -3
  139. package/dist/graphql/index.js +3 -2
  140. package/dist/index-CJXPB_ot.d.ts +276 -0
  141. package/dist/index-CaTNnLGd.d.cts +276 -0
  142. package/dist/index.cjs +304 -162
  143. package/dist/index.cjs.map +1 -1
  144. package/dist/index.d.cts +129 -205
  145. package/dist/index.d.ts +129 -205
  146. package/dist/index.js +172 -33
  147. package/dist/index.js.map +1 -1
  148. package/dist/integration.cjs +2 -2
  149. package/dist/integration.js +1 -1
  150. package/dist/mongo-auth-adapter-ISOM7FSS.cjs +17 -0
  151. package/dist/{mongo-auth-adapter-NHHUJHVH.cjs.map → mongo-auth-adapter-ISOM7FSS.cjs.map} +1 -1
  152. package/dist/mongo-auth-adapter-MO6STCV3.js +4 -0
  153. package/dist/{mongo-auth-adapter-NJQUUCTP.js.map → mongo-auth-adapter-MO6STCV3.js.map} +1 -1
  154. package/dist/mongodb/index.cjs +8 -7
  155. package/dist/mongodb/index.d.cts +5 -7
  156. package/dist/mongodb/index.d.ts +5 -7
  157. package/dist/mongodb/index.js +4 -3
  158. package/dist/postgres-auth-adapter-DWDR7P5G.js +5 -0
  159. package/dist/{postgres-auth-adapter-3T2NKTSE.js.map → postgres-auth-adapter-DWDR7P5G.js.map} +1 -1
  160. package/dist/postgres-auth-adapter-WRWSJD4E.cjs +14 -0
  161. package/dist/{postgres-auth-adapter-7IEENCKQ.cjs.map → postgres-auth-adapter-WRWSJD4E.cjs.map} +1 -1
  162. package/dist/redis-adapter-HGTPWIGV.js +4 -0
  163. package/dist/{redis-adapter-VQXD7ESY.js.map → redis-adapter-HGTPWIGV.js.map} +1 -1
  164. package/dist/redis-adapter-KJ3YOOT6.cjs +13 -0
  165. package/dist/{redis-adapter-D2E2S3GB.cjs.map → redis-adapter-KJ3YOOT6.cjs.map} +1 -1
  166. package/dist/rest/index.cjs +15 -14
  167. package/dist/rest/index.d.cts +4 -4
  168. package/dist/rest/index.d.ts +4 -4
  169. package/dist/rest/index.js +13 -12
  170. package/dist/{schema-5PHL5IVB.js → schema-6I5OFR4Z.js} +3 -3
  171. package/dist/{schema-5PHL5IVB.js.map → schema-6I5OFR4Z.js.map} +1 -1
  172. package/dist/{schema-37SE2F4B.cjs → schema-TTFE4467.cjs} +14 -14
  173. package/dist/{schema-37SE2F4B.cjs.map → schema-TTFE4467.cjs.map} +1 -1
  174. package/dist/sqlite-adapter-6GEUSVXQ.js +4 -0
  175. package/dist/{sqlite-adapter-TR3U3W6Q.js.map → sqlite-adapter-6GEUSVXQ.js.map} +1 -1
  176. package/dist/sqlite-adapter-CSIZE5SX.cjs +13 -0
  177. package/dist/{sqlite-adapter-LVK5PS4T.cjs.map → sqlite-adapter-CSIZE5SX.cjs.map} +1 -1
  178. package/dist/templates/index.cjs +133 -31
  179. package/dist/templates/index.d.cts +52 -9
  180. package/dist/templates/index.d.ts +52 -9
  181. package/dist/templates/index.js +3 -1
  182. package/dist/trpc/index.cjs +13 -12
  183. package/dist/trpc/index.d.cts +55 -49
  184. package/dist/trpc/index.d.ts +55 -49
  185. package/dist/trpc/index.js +4 -3
  186. package/dist/{types-D6ZLRGbH.d.cts → types-CpjuXbe7.d.cts} +2 -0
  187. package/dist/{types-D6ZLRGbH.d.ts → types-CpjuXbe7.d.ts} +2 -0
  188. package/dist/{types-Bs1up4yP.d.ts → types-CyCQ6SAI.d.ts} +28 -2
  189. package/dist/{types-J3R9nVsZ.d.cts → types-DJxD9394.d.cts} +28 -2
  190. package/dist/{types-VtjUxIMp.d.cts → types-Z6FBiqa2.d.cts} +35 -14
  191. package/dist/{types-VtjUxIMp.d.ts → types-Z6FBiqa2.d.ts} +35 -14
  192. package/package.json +22 -4
  193. package/dist/bootstrap-AKAUP6F6.cjs +0 -32
  194. package/dist/bootstrap-JCML6NFO.js +0 -7
  195. package/dist/chunk-2KVHZE6O.cjs.map +0 -1
  196. package/dist/chunk-2OL4O2TH.cjs.map +0 -1
  197. package/dist/chunk-35U3FROB.js.map +0 -1
  198. package/dist/chunk-3AJE4SEG.js.map +0 -1
  199. package/dist/chunk-3J4MFTI3.js +0 -3872
  200. package/dist/chunk-3J4MFTI3.js.map +0 -1
  201. package/dist/chunk-3ZFYL34R.js.map +0 -1
  202. package/dist/chunk-4DA7QPLA.cjs.map +0 -1
  203. package/dist/chunk-57P6MJKC.js.map +0 -1
  204. package/dist/chunk-5KVM3WEY.cjs.map +0 -1
  205. package/dist/chunk-6IMPH6WV.cjs +0 -3897
  206. package/dist/chunk-6IMPH6WV.cjs.map +0 -1
  207. package/dist/chunk-ATBOUGQP.cjs +0 -513
  208. package/dist/chunk-ATBOUGQP.cjs.map +0 -1
  209. package/dist/chunk-DXHRBMGB.js.map +0 -1
  210. package/dist/chunk-ES5HNFFT.js.map +0 -1
  211. package/dist/chunk-FXYP2HA6.js.map +0 -1
  212. package/dist/chunk-H727JIG7.js.map +0 -1
  213. package/dist/chunk-HXRD4B37.js.map +0 -1
  214. package/dist/chunk-I7HHI6QV.cjs.map +0 -1
  215. package/dist/chunk-IA6AU5PI.cjs.map +0 -1
  216. package/dist/chunk-IBG6V56E.cjs.map +0 -1
  217. package/dist/chunk-K7JPTH3G.cjs.map +0 -1
  218. package/dist/chunk-LINKCEG4.cjs.map +0 -1
  219. package/dist/chunk-OHVB4AJ7.js.map +0 -1
  220. package/dist/chunk-PDYFVNUX.cjs.map +0 -1
  221. package/dist/chunk-Q23JB3KL.js +0 -488
  222. package/dist/chunk-Q23JB3KL.js.map +0 -1
  223. package/dist/chunk-QPPDLRNR.js.map +0 -1
  224. package/dist/chunk-QUW2RZTM.cjs.map +0 -1
  225. package/dist/chunk-QXIQWPAP.js.map +0 -1
  226. package/dist/chunk-R3XIBBAW.cjs +0 -34
  227. package/dist/chunk-R3XIBBAW.cjs.map +0 -1
  228. package/dist/chunk-REK7AYOC.js.map +0 -1
  229. package/dist/chunk-SA7NSSIQ.cjs.map +0 -1
  230. package/dist/chunk-SDMNUYVU.js +0 -30
  231. package/dist/chunk-SDMNUYVU.js.map +0 -1
  232. package/dist/chunk-V3LKPM3O.cjs.map +0 -1
  233. package/dist/chunk-VJT6P4N6.cjs.map +0 -1
  234. package/dist/chunk-WOWUL7ZY.js.map +0 -1
  235. package/dist/chunk-WQBRWOQT.cjs.map +0 -1
  236. package/dist/chunk-Y3N7UUDO.js.map +0 -1
  237. package/dist/chunk-Y3QQN7PN.js.map +0 -1
  238. package/dist/chunk-YVUJBEXE.cjs.map +0 -1
  239. package/dist/index-CLp-DRKA.d.ts +0 -64
  240. package/dist/index-DfO7G4kN.d.cts +0 -64
  241. package/dist/mongo-auth-adapter-NHHUJHVH.cjs +0 -17
  242. package/dist/mongo-auth-adapter-NJQUUCTP.js +0 -4
  243. package/dist/postgres-auth-adapter-3T2NKTSE.js +0 -5
  244. package/dist/postgres-auth-adapter-7IEENCKQ.cjs +0 -14
  245. package/dist/redis-adapter-D2E2S3GB.cjs +0 -13
  246. package/dist/redis-adapter-VQXD7ESY.js +0 -4
  247. package/dist/sqlite-adapter-LVK5PS4T.cjs +0 -13
  248. package/dist/sqlite-adapter-TR3U3W6Q.js +0 -4
@@ -1,16 +1,16 @@
1
1
  'use strict';
2
2
 
3
- var chunkIA6AU5PI_cjs = require('./chunk-IA6AU5PI.cjs');
4
- var chunkI7HHI6QV_cjs = require('./chunk-I7HHI6QV.cjs');
3
+ var chunkWNCYAKF3_cjs = require('./chunk-WNCYAKF3.cjs');
4
+ var chunkKC2GDBLS_cjs = require('./chunk-KC2GDBLS.cjs');
5
+ var chunkIDVRRRAK_cjs = require('./chunk-IDVRRRAK.cjs');
5
6
  var chunkADLJSJSN_cjs = require('./chunk-ADLJSJSN.cjs');
6
- var chunkIBG6V56E_cjs = require('./chunk-IBG6V56E.cjs');
7
- var chunkVJT6P4N6_cjs = require('./chunk-VJT6P4N6.cjs');
8
- var chunkR3XIBBAW_cjs = require('./chunk-R3XIBBAW.cjs');
9
- var chunk2KVHZE6O_cjs = require('./chunk-2KVHZE6O.cjs');
10
- var chunk2OL4O2TH_cjs = require('./chunk-2OL4O2TH.cjs');
11
- var chunkPDYFVNUX_cjs = require('./chunk-PDYFVNUX.cjs');
12
- var chunk4DA7QPLA_cjs = require('./chunk-4DA7QPLA.cjs');
13
- var chunkSA7NSSIQ_cjs = require('./chunk-SA7NSSIQ.cjs');
7
+ var chunkQFLB4EIJ_cjs = require('./chunk-QFLB4EIJ.cjs');
8
+ var chunk4M7X5HAB_cjs = require('./chunk-4M7X5HAB.cjs');
9
+ var chunkCOIASRDK_cjs = require('./chunk-COIASRDK.cjs');
10
+ var chunkC36TMDTY_cjs = require('./chunk-C36TMDTY.cjs');
11
+ var chunkV2TVSCV5_cjs = require('./chunk-V2TVSCV5.cjs');
12
+ var chunkGXFOGU7N_cjs = require('./chunk-GXFOGU7N.cjs');
13
+ var chunkNKPKR5BW_cjs = require('./chunk-NKPKR5BW.cjs');
14
14
  var chunkG7VZBCD6_cjs = require('./chunk-G7VZBCD6.cjs');
15
15
  var crypto = require('crypto');
16
16
  var unstorage = require('unstorage');
@@ -19,11 +19,12 @@ var hono = require('hono');
19
19
  var path = require('path');
20
20
  var fs = require('fs');
21
21
  var sharp = require('sharp');
22
- var graphql = require('graphql');
23
22
  var promises = require('fs/promises');
23
+ var process2 = require('process');
24
24
  var clientS3 = require('@aws-sdk/client-s3');
25
25
  var stream = require('stream');
26
26
  var basicFtp = require('basic-ftp');
27
+ var zodToJsonSchema = require('zod-to-json-schema');
27
28
 
28
29
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
29
30
 
@@ -31,6 +32,7 @@ var crypto__default = /*#__PURE__*/_interopDefault(crypto);
31
32
  var fsDriver__default = /*#__PURE__*/_interopDefault(fsDriver);
32
33
  var path__default = /*#__PURE__*/_interopDefault(path);
33
34
  var sharp__default = /*#__PURE__*/_interopDefault(sharp);
35
+ var process2__default = /*#__PURE__*/_interopDefault(process2);
34
36
 
35
37
  function setDbAdapter(adapter) {
36
38
  dbAdapter = adapter;
@@ -388,14 +390,14 @@ function createAuthMiddleware(config) {
388
390
  userLookup
389
391
  } = config;
390
392
  return async function authMiddleware(req) {
391
- const apiKeyRaw = chunkIBG6V56E_cjs.extractApiKeyFromRequest(req);
393
+ const apiKeyRaw = chunk4M7X5HAB_cjs.extractApiKeyFromRequest(req);
392
394
  if (apiKeyRaw && db) {
393
- const result = await chunkIBG6V56E_cjs.validateApiKey(apiKeyRaw, db, userLookup);
395
+ const result = await chunk4M7X5HAB_cjs.validateApiKey(apiKeyRaw, db, userLookup);
394
396
  if (result.valid && result.user) {
395
397
  return {
396
398
  user: result.user,
397
399
  tenantContext: createTenantContextFromUser(result.user),
398
- apiKeyContext: chunkIBG6V56E_cjs.createApiKeyContext(result),
400
+ apiKeyContext: chunk4M7X5HAB_cjs.createApiKeyContext(result),
399
401
  status: 200,
400
402
  authType: "apikey"
401
403
  };
@@ -782,7 +784,7 @@ var AuditLogger = class {
782
784
  serializeLog(log) {
783
785
  const result = {
784
786
  id: log.id,
785
- timestamp: log.timestamp.toISOString(),
787
+ timestamp: new Date(log.timestamp).toISOString(),
786
788
  action: log.action,
787
789
  resource: log.resource,
788
790
  success: log.success ? "1" : "0"
@@ -957,7 +959,7 @@ var AuthRoutes = class {
957
959
  constructor(config) {
958
960
  this.authAdapter = config.redis;
959
961
  this.email = config.email;
960
- this.passwordPolicy = config.passwordPolicy || new chunkIA6AU5PI_cjs.PasswordPolicy();
962
+ this.passwordPolicy = config.passwordPolicy || new chunkWNCYAKF3_cjs.PasswordPolicy();
961
963
  this.lockout = config.lockout;
962
964
  this.rateLimiter = config.rateLimiter;
963
965
  this.auditLogger = config.auditLogger;
@@ -1177,23 +1179,19 @@ var AuthRoutes = class {
1177
1179
  }
1178
1180
  }
1179
1181
  async me(req) {
1180
- try {
1181
- const session = await getCurrentUser(req);
1182
- if (!session) {
1183
- return this.errorResponse("Not authenticated", 401);
1184
- }
1185
- const user = await this.authAdapter.findUserById(session.userId);
1186
- if (!user) {
1187
- return this.errorResponse("User not found", 404);
1188
- }
1189
- return this.jsonResponse({
1190
- success: true,
1191
- user: this.sanitizeUser(user)
1192
- });
1193
- } catch (error) {
1194
- console.error("[AuthRoutes.me] Authentication failed:", error.message);
1195
- return this.errorResponse("Authentication failed", 401);
1182
+ const session = await getCurrentUser(req);
1183
+ if (!session) {
1184
+ return this.errorResponse("Not authenticated", 401);
1196
1185
  }
1186
+ return this.jsonResponse({
1187
+ success: true,
1188
+ user: {
1189
+ id: session.userId,
1190
+ email: session.email,
1191
+ role: session.role,
1192
+ tenantId: session.tenantId
1193
+ }
1194
+ });
1197
1195
  }
1198
1196
  async changePassword(req) {
1199
1197
  const session = await getCurrentUser(req);
@@ -1303,7 +1301,7 @@ var AuthRoutes = class {
1303
1301
  }
1304
1302
  if (this.auditLogger) {
1305
1303
  await this.auditLogger.log({
1306
- action: "password_reset_request",
1304
+ action: "password_reset",
1307
1305
  userId: user.id,
1308
1306
  userEmail: user.email,
1309
1307
  resource: "auth",
@@ -1491,7 +1489,7 @@ var AuthRoutes = class {
1491
1489
  }
1492
1490
  };
1493
1491
  function createLocalStorage(config) {
1494
- const { uploadDir, baseUrl = "/uploads" } = config;
1492
+ const { uploadDir = path.join(process2__default.default.cwd(), "public", "uploads"), baseUrl = "/uploads" } = config;
1495
1493
  async function ensureDir(dir) {
1496
1494
  if (!fs.existsSync(dir)) {
1497
1495
  await promises.mkdir(dir, { recursive: true });
@@ -1679,6 +1677,467 @@ function createLocalStorage(config) {
1679
1677
  }
1680
1678
  };
1681
1679
  }
1680
+
1681
+ // src/storage/imgix.ts
1682
+ function createImgixStorage(config) {
1683
+ const signUrl = (path3, params) => {
1684
+ if (!config.signKey) return path3;
1685
+ const signer = new TextEncoder();
1686
+ const key = signer.encode(config.signKey);
1687
+ const data = signer.encode(path3 + params.toString());
1688
+ let hash = 0;
1689
+ const combined = new Uint8Array(key.length + data.length);
1690
+ combined.set(key);
1691
+ combined.set(data, key.length);
1692
+ for (let i = 0; i < combined.length; i++) {
1693
+ hash = (hash << 5) - hash + combined[i] | 0;
1694
+ }
1695
+ params.set("s", Math.abs(hash).toString(16));
1696
+ return path3;
1697
+ };
1698
+ return {
1699
+ name: "imgix",
1700
+ displayName: "Imgix",
1701
+ supportsDynamicResize: true,
1702
+ async upload(_file, _options) {
1703
+ throw new Error(
1704
+ "Imgix is a transformation service. Use another provider for uploads."
1705
+ );
1706
+ },
1707
+ async uploadFromUrl(url, options) {
1708
+ const filename = options?.filename || url.split("/").pop() || "file";
1709
+ const response = await fetch(url);
1710
+ if (!response.ok) {
1711
+ throw new Error(`Failed to fetch: ${response.statusText}`);
1712
+ }
1713
+ const blob = await response.blob();
1714
+ new File([blob], filename, { type: blob.type });
1715
+ return {
1716
+ id: Buffer.from(url).toString("base64").slice(0, 20),
1717
+ filename,
1718
+ originalName: filename,
1719
+ mimeType: blob.type,
1720
+ size: blob.size,
1721
+ url: this.getImageUrl(url),
1722
+ thumbnailUrl: this.getImageUrl(url, {
1723
+ width: 200,
1724
+ height: 200,
1725
+ fit: "crop"
1726
+ }),
1727
+ provider: "imgix",
1728
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
1729
+ };
1730
+ },
1731
+ async delete(_url) {
1732
+ },
1733
+ async rename(_oldUrl, newKey) {
1734
+ return `https://${config.domain}/${newKey}`;
1735
+ },
1736
+ getImageUrl(url, transforms) {
1737
+ const parsed = new URL(url);
1738
+ const params = new URLSearchParams(parsed.search);
1739
+ if (config.defaultParameters) {
1740
+ Object.entries(config.defaultParameters).forEach(([key, value]) => {
1741
+ if (!params.has(key)) {
1742
+ params.set(key, value);
1743
+ }
1744
+ });
1745
+ }
1746
+ if (transforms) {
1747
+ if (transforms.width) params.set("w", String(transforms.width));
1748
+ if (transforms.height) params.set("h", String(transforms.height));
1749
+ if (transforms.quality) params.set("q", String(transforms.quality));
1750
+ if (transforms.format) params.set("fm", transforms.format);
1751
+ if (transforms.fit) params.set("fit", transforms.fit);
1752
+ if (transforms.blur) params.set("blur", String(transforms.blur));
1753
+ if (transforms.sharpen) params.set("sharp", String(transforms.sharpen));
1754
+ }
1755
+ params.set("auto", "compress,format");
1756
+ parsed.pathname + "?" + params.toString();
1757
+ if (config.signKey) {
1758
+ signUrl(parsed.pathname + params.toString(), params);
1759
+ }
1760
+ return `https://${config.domain}${parsed.pathname}${params.toString() ? "?" + params.toString() : ""}`;
1761
+ },
1762
+ async generateThumbnail(file) {
1763
+ return this.getImageUrl(file.url, {
1764
+ width: 200,
1765
+ height: 200,
1766
+ fit: "crop"
1767
+ });
1768
+ },
1769
+ async list() {
1770
+ return [];
1771
+ },
1772
+ async exists(url) {
1773
+ try {
1774
+ const response = await fetch(url, { method: "HEAD" });
1775
+ return response.ok;
1776
+ } catch {
1777
+ return false;
1778
+ }
1779
+ },
1780
+ async createFolder() {
1781
+ },
1782
+ async deleteFolder() {
1783
+ }
1784
+ };
1785
+ }
1786
+
1787
+ // src/storage/bunny.ts
1788
+ function getUrl(key, config) {
1789
+ if (config.cdnUrl) {
1790
+ return `${config.cdnUrl.replace(/\/$/, "")}/${key}`;
1791
+ }
1792
+ return `https://${config.storageZone}.b-cdn.net/${key}`;
1793
+ }
1794
+ function getUrlPrefix(config) {
1795
+ if (config.cdnUrl) {
1796
+ return config.cdnUrl.replace(/\/$/, "") + "/";
1797
+ }
1798
+ return `https://${config.storageZone}.b-cdn.net/`;
1799
+ }
1800
+ function createBunnyStorage(config) {
1801
+ const baseUrl = `https://storage.bunnycdn.com/${config.storageZone}`;
1802
+ const getKey = (path3) => {
1803
+ const prefix = config.prefix ? `${config.prefix}/` : "";
1804
+ return `${prefix}${path3}`.replace(/\/+/g, "/");
1805
+ };
1806
+ return {
1807
+ name: "bunny",
1808
+ displayName: "Bunny.net Storage",
1809
+ supportsDynamicResize: true,
1810
+ async upload(file, options) {
1811
+ const key = getKey(
1812
+ `${options?.folder ? `${options.folder}/` : ""}${options?.filename || file.name}`
1813
+ );
1814
+ const buffer = Buffer.from(await file.arrayBuffer());
1815
+ const response = await fetch(`${baseUrl}/${key}`, {
1816
+ method: "PUT",
1817
+ headers: {
1818
+ AccessKey: config.apiKey,
1819
+ "Content-Type": file.type
1820
+ },
1821
+ body: buffer
1822
+ });
1823
+ if (!response.ok) {
1824
+ const errorText = await response.text();
1825
+ throw new Error(
1826
+ `Bunny.net upload failed: ${response.status} ${errorText}`
1827
+ );
1828
+ }
1829
+ return {
1830
+ id: Buffer.from(key).toString("base64url"),
1831
+ filename: options?.filename || file.name,
1832
+ originalName: file.name,
1833
+ mimeType: file.type,
1834
+ size: buffer.length,
1835
+ url: getUrl(key, config),
1836
+ thumbnailUrl: file.type.startsWith("image/") ? getUrl(key, config) : void 0,
1837
+ folder: options?.folder,
1838
+ provider: "bunny",
1839
+ metadata: options?.metadata,
1840
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
1841
+ };
1842
+ },
1843
+ async uploadFromUrl(url) {
1844
+ const response = await fetch(url);
1845
+ if (!response.ok) {
1846
+ throw new Error(`Failed to fetch: ${response.statusText}`);
1847
+ }
1848
+ const blob = await response.blob();
1849
+ const filename = url.split("/").pop() || "file";
1850
+ const file = new File([blob], filename, { type: blob.type });
1851
+ return this.upload(file);
1852
+ },
1853
+ async delete(url) {
1854
+ const key = url.replace(getUrlPrefix(config), "");
1855
+ const response = await fetch(`${baseUrl}/${key}`, {
1856
+ method: "DELETE",
1857
+ headers: {
1858
+ AccessKey: config.apiKey
1859
+ }
1860
+ });
1861
+ if (!response.ok && response.status !== 404) {
1862
+ const errorText = await response.text();
1863
+ throw new Error(
1864
+ `Bunny.net delete failed: ${response.status} ${errorText}`
1865
+ );
1866
+ }
1867
+ },
1868
+ async rename(oldUrl, newKey) {
1869
+ const oldKey = oldUrl.replace(getUrlPrefix(config), "");
1870
+ const fullPath = config.prefix ? `${config.prefix}/${newKey}` : newKey;
1871
+ const response = await fetch(`${baseUrl}/${oldKey}`, {
1872
+ method: "GET",
1873
+ headers: {
1874
+ AccessKey: config.apiKey
1875
+ }
1876
+ });
1877
+ if (!response.ok) {
1878
+ throw new Error(`Bunny.net rename failed: could not read old file`);
1879
+ }
1880
+ const content = await response.arrayBuffer();
1881
+ await fetch(`${baseUrl}/${fullPath}`, {
1882
+ method: "PUT",
1883
+ headers: {
1884
+ AccessKey: config.apiKey,
1885
+ "Content-Type": response.headers.get("Content-Type") || "application/octet-stream"
1886
+ },
1887
+ body: content
1888
+ });
1889
+ await this.delete(oldUrl);
1890
+ return getUrl(fullPath, config);
1891
+ },
1892
+ getImageUrl(url, transforms) {
1893
+ if (!transforms || Object.keys(transforms).length === 0) return url;
1894
+ const params = new URLSearchParams({ url });
1895
+ if (transforms.width) params.set("w", String(transforms.width));
1896
+ if (transforms.height) params.set("h", String(transforms.height));
1897
+ if (transforms.quality) params.set("q", String(transforms.quality));
1898
+ if (transforms.format) params.set("f", transforms.format);
1899
+ return `/api/media/resize?${params.toString()}`;
1900
+ },
1901
+ async generateThumbnail(file) {
1902
+ return this.getImageUrl(file.url, { width: 400, height: 400 });
1903
+ },
1904
+ async list(prefix) {
1905
+ const key = getKey(prefix || "");
1906
+ const response = await fetch(`${baseUrl}/${key}`, {
1907
+ method: "GET",
1908
+ headers: {
1909
+ AccessKey: config.apiKey
1910
+ }
1911
+ });
1912
+ if (!response.ok) {
1913
+ const errorText = await response.text();
1914
+ throw new Error(
1915
+ `Bunny.net list failed: ${response.status} ${errorText}`
1916
+ );
1917
+ }
1918
+ const items = await response.json();
1919
+ return items.map((item) => ({
1920
+ id: Buffer.from(item.ObjectName || "").toString("base64url"),
1921
+ filename: item.ObjectName?.split("/").pop() || "",
1922
+ originalName: item.ObjectName?.split("/").pop() || "",
1923
+ mimeType: "application/octet-stream",
1924
+ size: item.Length || 0,
1925
+ url: getUrl(item.ObjectName || "", config),
1926
+ provider: "bunny",
1927
+ createdAt: item.LastChanged || (/* @__PURE__ */ new Date()).toISOString()
1928
+ }));
1929
+ },
1930
+ async exists(url) {
1931
+ const key = url.replace(getUrlPrefix(config), "");
1932
+ const response = await fetch(`${baseUrl}/${key}`, {
1933
+ method: "HEAD",
1934
+ headers: {
1935
+ AccessKey: config.apiKey
1936
+ }
1937
+ });
1938
+ return response.ok;
1939
+ },
1940
+ async createFolder(folder) {
1941
+ const key = getKey(`${folder}/`);
1942
+ await fetch(`${baseUrl}/${key}`, {
1943
+ method: "PUT",
1944
+ headers: {
1945
+ AccessKey: config.apiKey,
1946
+ "Content-Length": "0"
1947
+ },
1948
+ body: ""
1949
+ });
1950
+ },
1951
+ async deleteFolder(folder) {
1952
+ const key = getKey(`${folder}/`);
1953
+ await fetch(`${baseUrl}/${key}`, {
1954
+ method: "DELETE",
1955
+ headers: {
1956
+ AccessKey: config.apiKey
1957
+ }
1958
+ });
1959
+ }
1960
+ };
1961
+ }
1962
+ var StorageProviderRegistry = class {
1963
+ providers = /* @__PURE__ */ new Map();
1964
+ providerToPlugin = /* @__PURE__ */ new Map();
1965
+ disabledPlugins = /* @__PURE__ */ new Set();
1966
+ constructor() {
1967
+ this.registerLocal();
1968
+ this.registerImgix();
1969
+ this.registerBunny();
1970
+ }
1971
+ registerLocal() {
1972
+ this.register({
1973
+ type: "local",
1974
+ displayName: "Local Server",
1975
+ configKey: "local",
1976
+ configFields: [
1977
+ {
1978
+ name: "uploadDir",
1979
+ type: "text",
1980
+ label: "Upload Directory",
1981
+ defaultValue: "./public/uploads"
1982
+ },
1983
+ {
1984
+ name: "baseUrl",
1985
+ type: "text",
1986
+ label: "Base URL",
1987
+ defaultValue: "/uploads"
1988
+ }
1989
+ ],
1990
+ extractConfig: (sc, key) => sc[key] || {},
1991
+ extractRawConfig: (c) => {
1992
+ const localConfig = c?.local || c;
1993
+ const savedUploadDir = (localConfig?.uploadDir || "").trim();
1994
+ let uploadDir;
1995
+ if (savedUploadDir) {
1996
+ if (path__default.default.isAbsolute(savedUploadDir)) {
1997
+ uploadDir = savedUploadDir;
1998
+ } else if (savedUploadDir.includes("/") || savedUploadDir.includes("\\")) {
1999
+ uploadDir = path__default.default.resolve(process.cwd(), savedUploadDir);
2000
+ } else {
2001
+ uploadDir = path__default.default.join(process.cwd(), "public", savedUploadDir);
2002
+ }
2003
+ } else {
2004
+ uploadDir = path__default.default.join(process.cwd(), "public", "uploads");
2005
+ }
2006
+ const savedBaseUrl = (localConfig?.baseUrl || "").trim();
2007
+ let baseUrl;
2008
+ if (savedBaseUrl) {
2009
+ baseUrl = savedBaseUrl.startsWith("/") ? savedBaseUrl : `/${savedBaseUrl}`;
2010
+ } else {
2011
+ baseUrl = "/uploads";
2012
+ }
2013
+ return { uploadDir, baseUrl };
2014
+ },
2015
+ factory: (c) => createLocalStorage(c)
2016
+ });
2017
+ }
2018
+ registerImgix() {
2019
+ this.register({
2020
+ type: "imgix",
2021
+ displayName: "Imgix",
2022
+ configKey: "imgix",
2023
+ configFields: [
2024
+ { name: "domain", type: "text", label: "Domain", required: true },
2025
+ { name: "signKey", type: "password", label: "Sign Key" }
2026
+ ],
2027
+ extractConfig: (sc, key) => sc[key] || {},
2028
+ extractRawConfig: (c) => c?.imgix || c,
2029
+ factory: (c) => createImgixStorage(c)
2030
+ });
2031
+ }
2032
+ registerBunny() {
2033
+ this.register({
2034
+ type: "bunny",
2035
+ displayName: "Bunny.net",
2036
+ configKey: "bunny",
2037
+ configFields: [
2038
+ {
2039
+ name: "storageZone",
2040
+ type: "text",
2041
+ label: "Storage Zone",
2042
+ required: true
2043
+ },
2044
+ { name: "apiKey", type: "password", label: "API Key", required: true },
2045
+ { name: "cdnUrl", type: "text", label: "CDN URL" },
2046
+ { name: "prefix", type: "text", label: "Path Prefix" }
2047
+ ],
2048
+ extractConfig: (sc, key) => sc[key] || {},
2049
+ extractRawConfig: (c) => c?.bunny || c,
2050
+ factory: (c) => createBunnyStorage(c)
2051
+ });
2052
+ }
2053
+ register(registration) {
2054
+ if (this.providers.has(registration.type)) {
2055
+ console.warn(
2056
+ `[StorageRegistry] Provider "${registration.type}" already registered, skipping`
2057
+ );
2058
+ return;
2059
+ }
2060
+ if (registration.pluginName) {
2061
+ this.providerToPlugin.set(registration.type, registration.pluginName);
2062
+ }
2063
+ this.providers.set(registration.type, registration);
2064
+ }
2065
+ unregister(type) {
2066
+ this.providers.delete(type);
2067
+ this.providerToPlugin.delete(type);
2068
+ }
2069
+ get(type) {
2070
+ return this.providers.get(type);
2071
+ }
2072
+ getAll() {
2073
+ return Array.from(this.providers.values());
2074
+ }
2075
+ getAllAvailable(isPluginEnabled) {
2076
+ const all = this.getAll();
2077
+ if (!isPluginEnabled) return all;
2078
+ return all.filter((p) => {
2079
+ if (!p.pluginName) return true;
2080
+ return isPluginEnabled(p.pluginName);
2081
+ });
2082
+ }
2083
+ has(type) {
2084
+ return this.providers.has(type);
2085
+ }
2086
+ async resolve(type, storageConfig) {
2087
+ const reg = this.providers.get(type);
2088
+ if (!reg) {
2089
+ throw new Error(
2090
+ `Unknown storage provider type: "${type}". Is the plugin that provides it enabled?`
2091
+ );
2092
+ }
2093
+ if (reg.pluginName && this.disabledPlugins.has(reg.pluginName)) {
2094
+ throw new Error(
2095
+ `Storage provider "${type}" is not available (plugin "${reg.pluginName}" is disabled)`
2096
+ );
2097
+ }
2098
+ const configKey = reg.configKey || type;
2099
+ const config = reg.extractConfig(storageConfig, configKey);
2100
+ return reg.factory(config);
2101
+ }
2102
+ async resolveWithConfig(type, config) {
2103
+ const reg = this.providers.get(type);
2104
+ if (!reg) {
2105
+ throw new Error(
2106
+ `Unknown storage provider type: "${type}". Is the plugin that provides it enabled?`
2107
+ );
2108
+ }
2109
+ if (reg.pluginName && this.disabledPlugins.has(reg.pluginName)) {
2110
+ throw new Error(
2111
+ `Storage provider "${type}" is not available (plugin "${reg.pluginName}" is disabled)`
2112
+ );
2113
+ }
2114
+ const providerConfig = reg.extractRawConfig(config);
2115
+ return reg.factory(providerConfig);
2116
+ }
2117
+ getProviderPluginName(type) {
2118
+ return this.providerToPlugin.get(type);
2119
+ }
2120
+ getAllPluginNames() {
2121
+ return Array.from(new Set(this.providerToPlugin.values()));
2122
+ }
2123
+ setPluginEnabled(name, enabled) {
2124
+ if (enabled) {
2125
+ this.disabledPlugins.delete(name);
2126
+ } else {
2127
+ this.disabledPlugins.add(name);
2128
+ }
2129
+ }
2130
+ isPluginEnabled(name) {
2131
+ return !this.disabledPlugins.has(name);
2132
+ }
2133
+ };
2134
+ var instance = null;
2135
+ function getDefaultRegistry() {
2136
+ if (!instance) {
2137
+ instance = new StorageProviderRegistry();
2138
+ }
2139
+ return instance;
2140
+ }
1682
2141
  function extractPublicDevUrlId(url) {
1683
2142
  if (!url) return "";
1684
2143
  if (url.startsWith("pub-")) return url;
@@ -1712,7 +2171,7 @@ function getPublicUrl(key, config) {
1712
2171
  return `https://${config.bucket}.s3.${config.region}.amazonaws.com/${normalizedKey}`;
1713
2172
  }
1714
2173
  }
1715
- function getUrlPrefix(config) {
2174
+ function getUrlPrefix2(config) {
1716
2175
  if (config.cdnUrl) {
1717
2176
  return config.cdnUrl.replace(/\/$/, "") + "/";
1718
2177
  }
@@ -1780,11 +2239,11 @@ function createS3Storage(config) {
1780
2239
  })
1781
2240
  }
1782
2241
  });
1783
- const getKey = (path2) => {
2242
+ const getKey = (path3) => {
1784
2243
  const prefix = config.prefix ? `${config.prefix}/` : "";
1785
- return `${prefix}${path2}`.replace(/\/+/g, "/");
2244
+ return `${prefix}${path3}`.replace(/\/+/g, "/");
1786
2245
  };
1787
- const getUrl = (key) => getPublicUrl(key, config);
2246
+ const getUrl2 = (key) => getPublicUrl(key, config);
1788
2247
  return {
1789
2248
  name: config.provider,
1790
2249
  displayName: getDisplayName(config.provider),
@@ -1815,8 +2274,8 @@ function createS3Storage(config) {
1815
2274
  originalName: file.name,
1816
2275
  mimeType: file.type,
1817
2276
  size: buffer.length,
1818
- url: getUrl(key),
1819
- thumbnailUrl: file.type.startsWith("image/") ? getUrl(key) : void 0,
2277
+ url: getUrl2(key),
2278
+ thumbnailUrl: file.type.startsWith("image/") ? getUrl2(key) : void 0,
1820
2279
  folder: options?.folder,
1821
2280
  provider: config.provider,
1822
2281
  metadata: {
@@ -1837,7 +2296,7 @@ function createS3Storage(config) {
1837
2296
  return this.upload(file);
1838
2297
  },
1839
2298
  async delete(url) {
1840
- const key = url.replace(getUrlPrefix(config), "");
2299
+ const key = url.replace(getUrlPrefix2(config), "");
1841
2300
  await client.send(
1842
2301
  new clientS3.DeleteObjectCommand({
1843
2302
  Bucket: config.bucket,
@@ -1846,7 +2305,7 @@ function createS3Storage(config) {
1846
2305
  );
1847
2306
  },
1848
2307
  async rename(oldUrl, newKey) {
1849
- const oldKey = oldUrl.replace(getUrlPrefix(config), "");
2308
+ const oldKey = oldUrl.replace(getUrlPrefix2(config), "");
1850
2309
  const newKeyWithPrefix = config.prefix ? `${config.prefix}/${newKey}` : newKey;
1851
2310
  await client.send(
1852
2311
  new clientS3.CopyObjectCommand({
@@ -1861,7 +2320,7 @@ function createS3Storage(config) {
1861
2320
  Key: oldKey
1862
2321
  })
1863
2322
  );
1864
- return getUrl(newKeyWithPrefix);
2323
+ return getUrl2(newKeyWithPrefix);
1865
2324
  },
1866
2325
  getImageUrl(url, transforms) {
1867
2326
  if (!transforms || Object.keys(transforms).length === 0) return url;
@@ -1889,14 +2348,14 @@ function createS3Storage(config) {
1889
2348
  originalName: item.Key?.split("/").pop() || "",
1890
2349
  mimeType: "application/octet-stream",
1891
2350
  size: item.Size || 0,
1892
- url: getUrl(item.Key || ""),
2351
+ url: getUrl2(item.Key || ""),
1893
2352
  provider: config.provider,
1894
2353
  createdAt: item.LastModified?.toISOString() || (/* @__PURE__ */ new Date()).toISOString()
1895
2354
  }));
1896
2355
  },
1897
2356
  async exists(url) {
1898
2357
  try {
1899
- const key = url.replace(getUrlPrefix(config), "");
2358
+ const key = url.replace(getUrlPrefix2(config), "");
1900
2359
  await client.send(
1901
2360
  new clientS3.HeadObjectCommand({
1902
2361
  Bucket: config.bucket,
@@ -1956,9 +2415,9 @@ function createS3Storage(config) {
1956
2415
  function createCloudinaryStorage(config) {
1957
2416
  const getBaseUrl = () => `https://api.cloudinary.com/v1_1/${config.cloudName}/upload`;
1958
2417
  const generateSignature = async (params) => {
1959
- const crypto3 = await import('crypto');
2418
+ const crypto4 = await import('crypto');
1960
2419
  const sortedParams = Object.keys(params).sort().map((key) => `${key}=${params[key]}`).join("&");
1961
- return crypto3.createHash("sha256").update(sortedParams + config.apiSecret).digest("hex");
2420
+ return crypto4.createHash("sha256").update(sortedParams + config.apiSecret).digest("hex");
1962
2421
  };
1963
2422
  return {
1964
2423
  name: "cloudinary",
@@ -2060,8 +2519,8 @@ function createCloudinaryStorage(config) {
2060
2519
  public_id: publicId
2061
2520
  };
2062
2521
  const sortedParams = Object.keys(signatureParams).sort().map((key) => `${key}=${signatureParams[key]}`).join("&");
2063
- const crypto3 = await import('crypto');
2064
- const signature = crypto3.createHash("sha256").update(sortedParams + config.apiSecret).digest("hex");
2522
+ const crypto4 = await import('crypto');
2523
+ const signature = crypto4.createHash("sha256").update(sortedParams + config.apiSecret).digest("hex");
2065
2524
  const deleteUrl = `https://api.cloudinary.com/v1_1/${config.cloudName}/image/destroy`;
2066
2525
  const formData = new FormData();
2067
2526
  formData.append("public_id", publicId);
@@ -2112,8 +2571,8 @@ function createCloudinaryStorage(config) {
2112
2571
  timestamp: String(timestamp)
2113
2572
  };
2114
2573
  const sortedParams = Object.keys(signatureParams).sort().map((key) => `${key}=${signatureParams[key]}`).join("&");
2115
- const crypto3 = await import('crypto');
2116
- const signature = crypto3.createHash("sha256").update(sortedParams + config.apiSecret).digest("hex");
2574
+ const crypto4 = await import('crypto');
2575
+ const signature = crypto4.createHash("sha256").update(sortedParams + config.apiSecret).digest("hex");
2117
2576
  const formData = new FormData();
2118
2577
  formData.append("from_public_id", publicIdWithoutVersion);
2119
2578
  formData.append("to_public_id", newPublicId);
@@ -2184,112 +2643,6 @@ function createCloudinaryStorage(config) {
2184
2643
  }
2185
2644
  };
2186
2645
  }
2187
-
2188
- // src/storage/imgix.ts
2189
- function createImgixStorage(config) {
2190
- const signUrl = (path2, params) => {
2191
- if (!config.signKey) return path2;
2192
- const signer = new TextEncoder();
2193
- const key = signer.encode(config.signKey);
2194
- const data = signer.encode(path2 + params.toString());
2195
- let hash = 0;
2196
- const combined = new Uint8Array(key.length + data.length);
2197
- combined.set(key);
2198
- combined.set(data, key.length);
2199
- for (let i = 0; i < combined.length; i++) {
2200
- hash = (hash << 5) - hash + combined[i] | 0;
2201
- }
2202
- params.set("s", Math.abs(hash).toString(16));
2203
- return path2;
2204
- };
2205
- return {
2206
- name: "imgix",
2207
- displayName: "Imgix",
2208
- supportsDynamicResize: true,
2209
- async upload(_file, _options) {
2210
- throw new Error(
2211
- "Imgix is a transformation service. Use another provider for uploads."
2212
- );
2213
- },
2214
- async uploadFromUrl(url, options) {
2215
- const filename = options?.filename || url.split("/").pop() || "file";
2216
- const response = await fetch(url);
2217
- if (!response.ok) {
2218
- throw new Error(`Failed to fetch: ${response.statusText}`);
2219
- }
2220
- const blob = await response.blob();
2221
- new File([blob], filename, { type: blob.type });
2222
- return {
2223
- id: Buffer.from(url).toString("base64").slice(0, 20),
2224
- filename,
2225
- originalName: filename,
2226
- mimeType: blob.type,
2227
- size: blob.size,
2228
- url: this.getImageUrl(url),
2229
- thumbnailUrl: this.getImageUrl(url, {
2230
- width: 200,
2231
- height: 200,
2232
- fit: "crop"
2233
- }),
2234
- provider: "imgix",
2235
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
2236
- };
2237
- },
2238
- async delete(_url) {
2239
- },
2240
- async rename(_oldUrl, newKey) {
2241
- return `https://${config.domain}/${newKey}`;
2242
- },
2243
- getImageUrl(url, transforms) {
2244
- const parsed = new URL(url);
2245
- const params = new URLSearchParams(parsed.search);
2246
- if (config.defaultParameters) {
2247
- Object.entries(config.defaultParameters).forEach(([key, value]) => {
2248
- if (!params.has(key)) {
2249
- params.set(key, value);
2250
- }
2251
- });
2252
- }
2253
- if (transforms) {
2254
- if (transforms.width) params.set("w", String(transforms.width));
2255
- if (transforms.height) params.set("h", String(transforms.height));
2256
- if (transforms.quality) params.set("q", String(transforms.quality));
2257
- if (transforms.format) params.set("fm", transforms.format);
2258
- if (transforms.fit) params.set("fit", transforms.fit);
2259
- if (transforms.blur) params.set("blur", String(transforms.blur));
2260
- if (transforms.sharpen) params.set("sharp", String(transforms.sharpen));
2261
- }
2262
- params.set("auto", "compress,format");
2263
- parsed.pathname + "?" + params.toString();
2264
- if (config.signKey) {
2265
- signUrl(parsed.pathname + params.toString(), params);
2266
- }
2267
- return `https://${config.domain}${parsed.pathname}${params.toString() ? "?" + params.toString() : ""}`;
2268
- },
2269
- async generateThumbnail(file) {
2270
- return this.getImageUrl(file.url, {
2271
- width: 200,
2272
- height: 200,
2273
- fit: "crop"
2274
- });
2275
- },
2276
- async list() {
2277
- return [];
2278
- },
2279
- async exists(url) {
2280
- try {
2281
- const response = await fetch(url, { method: "HEAD" });
2282
- return response.ok;
2283
- } catch {
2284
- return false;
2285
- }
2286
- },
2287
- async createFolder() {
2288
- },
2289
- async deleteFolder() {
2290
- }
2291
- };
2292
- }
2293
2646
  function createFtpStorage(config) {
2294
2647
  let client = null;
2295
2648
  async function getClient() {
@@ -2307,15 +2660,15 @@ function createFtpStorage(config) {
2307
2660
  }
2308
2661
  return client;
2309
2662
  }
2310
- const getKey = (path2) => {
2663
+ const getKey = (path3) => {
2311
2664
  const prefix = config.prefix ? `${config.prefix}/` : "";
2312
- return `${prefix}${path2}`.replace(/\/+/g, "/");
2665
+ return `${prefix}${path3}`.replace(/\/+/g, "/");
2313
2666
  };
2314
- const getUrl = (key) => {
2667
+ const getUrl2 = (key) => {
2315
2668
  const base = config.baseUrl.replace(/\/$/, "");
2316
2669
  return `${base}/${key}`;
2317
2670
  };
2318
- const getUrlPrefix2 = () => {
2671
+ const getUrlPrefix3 = () => {
2319
2672
  const base = config.baseUrl.replace(/\/$/, "");
2320
2673
  return base + "/";
2321
2674
  };
@@ -2346,8 +2699,8 @@ function createFtpStorage(config) {
2346
2699
  originalName: file.name,
2347
2700
  mimeType: file.type,
2348
2701
  size: buffer.length,
2349
- url: getUrl(key),
2350
- thumbnailUrl: file.type.startsWith("image/") ? getUrl(key) : void 0,
2702
+ url: getUrl2(key),
2703
+ thumbnailUrl: file.type.startsWith("image/") ? getUrl2(key) : void 0,
2351
2704
  folder: options?.folder,
2352
2705
  provider: config.type,
2353
2706
  metadata: options?.metadata,
@@ -2366,15 +2719,15 @@ function createFtpStorage(config) {
2366
2719
  },
2367
2720
  async delete(url) {
2368
2721
  const ftp = await getClient();
2369
- const key = url.replace(getUrlPrefix2(), "");
2722
+ const key = url.replace(getUrlPrefix3(), "");
2370
2723
  await ftp.remove(key);
2371
2724
  },
2372
2725
  async rename(oldUrl, newKey) {
2373
2726
  const ftp = await getClient();
2374
- const oldKey = oldUrl.replace(getUrlPrefix2(), "");
2727
+ const oldKey = oldUrl.replace(getUrlPrefix3(), "");
2375
2728
  const fullPath = config.prefix ? `${config.prefix}/${newKey}` : newKey;
2376
2729
  await ftp.rename(oldKey, fullPath);
2377
- return getUrl(fullPath);
2730
+ return getUrl2(fullPath);
2378
2731
  },
2379
2732
  getImageUrl(url, transforms) {
2380
2733
  if (!transforms || Object.keys(transforms).length === 0) return url;
@@ -2403,14 +2756,14 @@ function createFtpStorage(config) {
2403
2756
  originalName: item.name,
2404
2757
  mimeType: "application/octet-stream",
2405
2758
  size: Number(item.size) || 0,
2406
- url: getUrl(`${key}/${item.name}`.replace(/\/+/g, "/")),
2759
+ url: getUrl2(`${key}/${item.name}`.replace(/\/+/g, "/")),
2407
2760
  provider: config.type,
2408
2761
  createdAt: item.modifiedAt ? item.modifiedAt.toISOString() : (/* @__PURE__ */ new Date()).toISOString()
2409
2762
  }));
2410
2763
  },
2411
2764
  async exists(url) {
2412
2765
  const ftp = await getClient();
2413
- const key = url.replace(getUrlPrefix2(), "");
2766
+ const key = url.replace(getUrlPrefix3(), "");
2414
2767
  try {
2415
2768
  await ftp.size(key);
2416
2769
  return true;
@@ -2434,105 +2787,17 @@ function createFtpStorage(config) {
2434
2787
  // src/storage/index.ts
2435
2788
  async function resolveProvider(configService) {
2436
2789
  const config = configService.getStorageConfig();
2437
- switch (config.type) {
2438
- case "aws":
2439
- return createS3Storage({
2440
- provider: "aws",
2441
- bucket: config.s3.bucket || "",
2442
- region: config.s3.region || "us-east-1",
2443
- accessKeyId: config.s3.accessKeyId || "",
2444
- secretAccessKey: config.s3.secretAccessKey || "",
2445
- endpoint: config.s3.endpoint,
2446
- cdnUrl: config.s3.cdnUrl,
2447
- prefix: config.s3.prefix
2448
- });
2449
- case "r2":
2450
- return createS3Storage({
2451
- provider: "r2",
2452
- bucket: config.r2.bucket || "",
2453
- region: "auto",
2454
- accessKeyId: config.r2.accessKeyId || "",
2455
- secretAccessKey: config.r2.secretAccessKey || "",
2456
- accountId: config.r2.accountId || "",
2457
- publicDevUrl: config.r2.publicDevUrl,
2458
- endpoint: `https://${config.r2.accountId || ""}.r2.cloudflarestorage.com`,
2459
- cdnUrl: config.r2.cdnUrl,
2460
- prefix: config.r2.prefix
2461
- });
2462
- case "gcs":
2463
- return createS3Storage({
2464
- provider: "gcs",
2465
- bucket: config.gcs.bucket || "",
2466
- region: config.gcs.projectId || "auto",
2467
- accessKeyId: config.gcs.clientEmail || "",
2468
- secretAccessKey: config.gcs.privateKey || "",
2469
- cdnUrl: config.gcs.cdnUrl,
2470
- prefix: config.gcs.prefix
2471
- });
2472
- case "digitalocean":
2473
- return createS3Storage({
2474
- provider: "digitalocean",
2475
- bucket: config.digitalocean.bucket || "",
2476
- region: config.digitalocean.region || "nyc3",
2477
- accessKeyId: config.digitalocean.accessKeyId || "",
2478
- secretAccessKey: config.digitalocean.secretAccessKey || "",
2479
- endpoint: `https://${config.digitalocean.region || "nyc3"}.digitaloceanspaces.com`,
2480
- cdnUrl: config.digitalocean.cdnUrl,
2481
- prefix: config.digitalocean.prefix
2482
- });
2483
- case "backblaze":
2484
- return createS3Storage({
2485
- provider: "backblaze",
2486
- bucket: config.backblaze.bucket || "",
2487
- region: "auto",
2488
- accessKeyId: config.backblaze.applicationKeyId || "",
2489
- secretAccessKey: config.backblaze.applicationKey || "",
2490
- accountId: config.backblaze.accountId || "",
2491
- endpoint: `https://s3.backblazeb2.com`,
2492
- cdnUrl: config.backblaze.cdnUrl,
2493
- prefix: config.backblaze.prefix
2494
- });
2495
- case "wasabi":
2496
- return createS3Storage({
2497
- provider: "wasabi",
2498
- bucket: config.wasabi.bucket || "",
2499
- region: config.wasabi.region || "us-east-1",
2500
- accessKeyId: config.wasabi.accessKeyId || "",
2501
- secretAccessKey: config.wasabi.secretAccessKey || "",
2502
- endpoint: `https://s3.${config.wasabi.region || "us-east-1"}.wasabisys.com`,
2503
- cdnUrl: config.wasabi.cdnUrl,
2504
- prefix: config.wasabi.prefix
2505
- });
2506
- case "ftp":
2507
- case "sftp":
2508
- return createFtpStorage({
2509
- host: config.ftp?.host || "",
2510
- port: config.ftp?.port || 21,
2511
- user: config.ftp?.user || "",
2512
- password: config.ftp?.password || "",
2513
- secure: config.ftp?.secure || false,
2514
- baseUrl: config.ftp?.baseUrl || "",
2515
- prefix: config.ftp?.prefix,
2516
- type: "ftp"
2517
- });
2518
- case "cloudinary":
2519
- return createCloudinaryStorage({
2520
- cloudName: config.cloudinary.cloudName || "",
2521
- apiKey: config.cloudinary.apiKey || "",
2522
- apiSecret: config.cloudinary.apiSecret || "",
2523
- folder: config.cloudinary.folder
2524
- });
2525
- case "imgix":
2526
- return createImgixStorage({
2527
- domain: config.imgix.domain || "",
2528
- signKey: config.imgix.signKey
2529
- });
2530
- case "local":
2531
- default:
2532
- return createLocalStorage({
2533
- uploadDir: config.local.uploadDir || path__default.default.join(process.cwd(), "public", "uploads"),
2534
- baseUrl: config.local.baseUrl || "/uploads"
2535
- });
2790
+ const registry = getDefaultRegistry();
2791
+ try {
2792
+ return await registry.resolve(config.type, config);
2793
+ } catch (err) {
2794
+ console.warn(
2795
+ `[resolveProvider] ${err.message} \u2014 falling back to local storage`
2796
+ );
2797
+ return createLocalStorage({
2798
+ uploadDir: path__default.default.join(process.cwd(), "public", "uploads"),
2799
+ baseUrl: "/uploads"
2800
+ });
2536
2801
  }
2537
2802
  }
2538
2803
  async function resolveProviderWithConfig(config) {
@@ -2543,121 +2808,18 @@ async function resolveProviderWithConfig(config) {
2543
2808
  baseUrl: "/uploads"
2544
2809
  });
2545
2810
  }
2546
- console.log("[resolveProviderWithConfig] Creating provider:", config.type);
2547
- switch (config.type) {
2548
- case "aws":
2549
- return createS3Storage({
2550
- provider: "aws",
2551
- bucket: config.s3?.bucket || "",
2552
- region: config.s3?.region || "us-east-1",
2553
- accessKeyId: config.s3?.accessKeyId || "",
2554
- secretAccessKey: config.s3?.secretAccessKey || "",
2555
- endpoint: config.s3?.endpoint,
2556
- cdnUrl: config.s3?.cdnUrl,
2557
- prefix: config.s3?.prefix
2558
- });
2559
- case "r2":
2560
- return createS3Storage({
2561
- provider: "r2",
2562
- bucket: config.r2?.bucket || "",
2563
- region: "auto",
2564
- accessKeyId: config.r2?.accessKeyId || "",
2565
- secretAccessKey: config.r2?.secretAccessKey || "",
2566
- accountId: config.r2?.accountId || "",
2567
- publicDevUrl: config.r2?.publicDevUrl,
2568
- endpoint: `https://${config.r2?.accountId || ""}.r2.cloudflarestorage.com`,
2569
- cdnUrl: config.r2?.cdnUrl,
2570
- prefix: config.r2?.prefix
2571
- });
2572
- case "gcs":
2573
- return createS3Storage({
2574
- provider: "gcs",
2575
- bucket: config.gcs?.bucket || "",
2576
- region: config.gcs?.projectId || "auto",
2577
- accessKeyId: config.gcs?.clientEmail || "",
2578
- secretAccessKey: config.gcs?.privateKey || "",
2579
- cdnUrl: config.gcs?.cdnUrl,
2580
- prefix: config.gcs?.prefix
2581
- });
2582
- case "digitalocean":
2583
- return createS3Storage({
2584
- provider: "digitalocean",
2585
- bucket: config.digitalocean?.bucket || "",
2586
- region: config.digitalocean?.region || "nyc3",
2587
- accessKeyId: config.digitalocean?.accessKeyId || "",
2588
- secretAccessKey: config.digitalocean?.secretAccessKey || "",
2589
- cdnUrl: config.digitalocean?.cdnUrl,
2590
- prefix: config.digitalocean?.prefix
2591
- });
2592
- case "backblaze":
2593
- return createS3Storage({
2594
- provider: "backblaze",
2595
- bucket: config.backblaze?.bucket || "",
2596
- region: "auto",
2597
- accessKeyId: config.backblaze?.applicationKeyId || "",
2598
- secretAccessKey: config.backblaze?.applicationKey || "",
2599
- cdnUrl: config.backblaze?.cdnUrl,
2600
- prefix: config.backblaze?.prefix
2601
- });
2602
- case "wasabi":
2603
- return createS3Storage({
2604
- provider: "wasabi",
2605
- bucket: config.wasabi?.bucket || "",
2606
- region: config.wasabi?.region || "us-east-1",
2607
- accessKeyId: config.wasabi?.accessKeyId || "",
2608
- secretAccessKey: config.wasabi?.secretAccessKey || "",
2609
- cdnUrl: config.wasabi?.cdnUrl,
2610
- prefix: config.wasabi?.prefix
2611
- });
2612
- case "cloudinary":
2613
- return createCloudinaryStorage({
2614
- cloudName: config.cloudinary?.cloudName || "",
2615
- apiKey: config.cloudinary?.apiKey || "",
2616
- apiSecret: config.cloudinary?.apiSecret || "",
2617
- folder: config.cloudinary?.folder
2618
- });
2619
- case "ftp":
2620
- case "sftp": {
2621
- const ftpConf = config.ftp || config;
2622
- return createFtpStorage({
2623
- type: "ftp",
2624
- host: ftpConf.host || "",
2625
- port: ftpConf.port || 21,
2626
- user: ftpConf.user || "",
2627
- password: ftpConf.password || "",
2628
- secure: ftpConf.secure || false,
2629
- baseUrl: ftpConf.baseUrl || "",
2630
- prefix: ftpConf.prefix
2631
- });
2632
- }
2633
- case "local":
2634
- default: {
2635
- const localConfig = config.local || {
2636
- uploadDir: config["local.uploadDir"],
2637
- baseUrl: config["local.baseUrl"]
2638
- };
2639
- const savedUploadDir = (localConfig?.uploadDir || "").trim();
2640
- let uploadDir;
2641
- if (savedUploadDir) {
2642
- if (path__default.default.isAbsolute(savedUploadDir)) {
2643
- uploadDir = savedUploadDir;
2644
- } else if (savedUploadDir.includes("/") || savedUploadDir.includes("\\")) {
2645
- uploadDir = path__default.default.resolve(process.cwd(), savedUploadDir);
2646
- } else {
2647
- uploadDir = path__default.default.join(process.cwd(), "public", savedUploadDir);
2648
- }
2649
- } else {
2650
- uploadDir = path__default.default.join(process.cwd(), "public", "uploads");
2651
- }
2652
- const savedBaseUrl = (localConfig?.baseUrl || "").trim();
2653
- let baseUrl;
2654
- if (savedBaseUrl) {
2655
- baseUrl = savedBaseUrl.startsWith("/") ? savedBaseUrl : `/${savedBaseUrl}`;
2656
- } else {
2657
- baseUrl = "/uploads";
2658
- }
2659
- return createLocalStorage({ uploadDir, baseUrl });
2660
- }
2811
+ const type = config.type || "local";
2812
+ const registry = getDefaultRegistry();
2813
+ try {
2814
+ return await registry.resolveWithConfig(type, config);
2815
+ } catch (err) {
2816
+ console.warn(
2817
+ `[resolveProviderWithConfig] ${err.message} \u2014 falling back to local storage`
2818
+ );
2819
+ return createLocalStorage({
2820
+ uploadDir: path__default.default.join(process.cwd(), "public", "uploads"),
2821
+ baseUrl: "/uploads"
2822
+ });
2661
2823
  }
2662
2824
  }
2663
2825
  async function processImage(buffer) {
@@ -2686,14 +2848,14 @@ var MediaService = class _MediaService {
2686
2848
  this.db = db;
2687
2849
  this.storage = storage2;
2688
2850
  this.dialect = options?.dialect || "sqlite";
2689
- this.genId = options?.genId || chunk2KVHZE6O_cjs.genId;
2851
+ this.genId = options?.genId || chunkCOIASRDK_cjs.genId;
2690
2852
  }
2691
2853
  static async init(db, options) {
2692
2854
  let storage2;
2693
2855
  if (options?.storageConfig) {
2694
2856
  storage2 = await resolveProviderWithConfig(options.storageConfig);
2695
2857
  } else {
2696
- const configService = new chunkIA6AU5PI_cjs.ConfigService(db);
2858
+ const configService = new chunkWNCYAKF3_cjs.ConfigService(db);
2697
2859
  if (typeof db?.select === "function") {
2698
2860
  await configService.load();
2699
2861
  }
@@ -3261,13 +3423,38 @@ var MediaService = class _MediaService {
3261
3423
  }
3262
3424
  }
3263
3425
  };
3264
-
3265
- // src/api/rest/hono-app.ts
3426
+ function formatZodErrors(errors) {
3427
+ return errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ");
3428
+ }
3429
+ function convertRichtextFields(fields, data) {
3430
+ if (!data || typeof data !== "object") return;
3431
+ for (const field of fields) {
3432
+ if (field.type === "richtext" && field.name) {
3433
+ const val = data[field.name];
3434
+ if (typeof val === "string") {
3435
+ data[field.name] = [{ type: "paragraph", children: [{ text: val }] }];
3436
+ } else if (val && typeof val === "object" && !Array.isArray(val) && val.type === "doc" && Array.isArray(val.content)) {
3437
+ data[field.name] = val.content;
3438
+ }
3439
+ }
3440
+ if (field.type === "tabs" && field.name && Array.isArray(field.tabs) && data[field.name] && typeof data[field.name] === "object") {
3441
+ for (const tab of field.tabs) {
3442
+ if (Array.isArray(tab.fields)) convertRichtextFields(tab.fields, data[field.name]);
3443
+ }
3444
+ } else if ((field.type === "group" || field.type === "collapsible") && field.name && Array.isArray(field.fields) && data[field.name] && typeof data[field.name] === "object") {
3445
+ convertRichtextFields(field.fields, data[field.name]);
3446
+ } else if (field.type === "array" && field.name && Array.isArray(field.fields) && Array.isArray(data[field.name])) {
3447
+ for (const item of data[field.name]) {
3448
+ if (item && typeof item === "object") convertRichtextFields(field.fields, item);
3449
+ }
3450
+ }
3451
+ }
3452
+ }
3266
3453
  var COLLECTION_EVENT_MAP = {
3267
3454
  _media: {
3268
- create: chunkIBG6V56E_cjs.WEBHOOK_EVENTS.MEDIA_UPLOAD,
3269
- update: chunkIBG6V56E_cjs.WEBHOOK_EVENTS.MEDIA_UPLOAD,
3270
- delete: chunkIBG6V56E_cjs.WEBHOOK_EVENTS.MEDIA_DELETE
3455
+ create: chunkQFLB4EIJ_cjs.WEBHOOK_EVENTS.MEDIA_UPLOAD,
3456
+ update: chunkQFLB4EIJ_cjs.WEBHOOK_EVENTS.MEDIA_UPLOAD,
3457
+ delete: chunkQFLB4EIJ_cjs.WEBHOOK_EVENTS.MEDIA_DELETE
3271
3458
  }
3272
3459
  };
3273
3460
  function getWebhookEvent(collection, operation) {
@@ -3326,7 +3513,7 @@ async function checkCollectionAccess(collection, operation, req, ctxUser, ctxTen
3326
3513
  };
3327
3514
  const isDefaultAllowed = accessLevels[defaultCollectionAccess] || false;
3328
3515
  if (accessRule) {
3329
- const allowed = await chunkR3XIBBAW_cjs.evaluateAccess(accessRule, {
3516
+ const allowed = await chunk4M7X5HAB_cjs.evaluateAccess(accessRule, {
3330
3517
  req,
3331
3518
  user: ctxUser,
3332
3519
  tenantID: ctxTenantID
@@ -3348,7 +3535,7 @@ async function checkCollectionAccess(collection, operation, req, ctxUser, ctxTen
3348
3535
  const resource = collection.slug;
3349
3536
  const action = operation === "read" ? "read" : operation === "create" ? "create" : "update";
3350
3537
  const permission = `${resource}:${action}`;
3351
- if (!chunkIBG6V56E_cjs.hasApiKeyPermission(apiKeyContext.permissions, permission) && !chunkIBG6V56E_cjs.hasApiKeyPermission(apiKeyContext.permissions, `${resource}:admin`)) {
3538
+ if (!chunk4M7X5HAB_cjs.hasApiKeyPermission(apiKeyContext.permissions, permission) && !chunk4M7X5HAB_cjs.hasApiKeyPermission(apiKeyContext.permissions, `${resource}:admin`)) {
3352
3539
  return {
3353
3540
  allowed: false,
3354
3541
  error: `Missing permission: ${permission}`,
@@ -3362,14 +3549,14 @@ async function checkCollectionAccess(collection, operation, req, ctxUser, ctxTen
3362
3549
  const permission = `${resource}:${action}`;
3363
3550
  let rbacAllowed = false;
3364
3551
  if (ctxUser.role) {
3365
- const userHasPermission = chunkSA7NSSIQ_cjs.hasPermission(
3552
+ const userHasPermission = chunkNKPKR5BW_cjs.hasPermission(
3366
3553
  { id: ctxUser.id, email: ctxUser.email, role: ctxUser.role },
3367
3554
  permission
3368
3555
  );
3369
3556
  if (userHasPermission) {
3370
3557
  rbacAllowed = true;
3371
3558
  } else {
3372
- const adminPermission = chunkSA7NSSIQ_cjs.hasPermission(
3559
+ const adminPermission = chunkNKPKR5BW_cjs.hasPermission(
3373
3560
  { id: ctxUser.id, email: ctxUser.email, role: ctxUser.role },
3374
3561
  `${resource}:admin`
3375
3562
  );
@@ -3389,7 +3576,7 @@ async function checkCollectionAccess(collection, operation, req, ctxUser, ctxTen
3389
3576
  async function checkGlobalAccess(global, operation, req, ctxUser, ctxTenantID, enablePublicAccess = true) {
3390
3577
  const accessRule = global.access?.[operation];
3391
3578
  if (accessRule) {
3392
- const allowed = await chunkR3XIBBAW_cjs.evaluateAccess(accessRule, {
3579
+ const allowed = await chunk4M7X5HAB_cjs.evaluateAccess(accessRule, {
3393
3580
  req,
3394
3581
  user: ctxUser,
3395
3582
  tenantID: ctxTenantID
@@ -3415,42 +3602,41 @@ async function checkGlobalAccess(global, operation, req, ctxUser, ctxTenantID, e
3415
3602
  return { allowed: true };
3416
3603
  }
3417
3604
  async function resolveAuthContext(req, authMw, staticUser, staticTenantID) {
3418
- if (!authMw) {
3605
+ if (staticUser) {
3419
3606
  return {
3420
3607
  user: staticUser,
3421
3608
  tenantID: staticTenantID,
3422
- apiKeyContext: void 0
3609
+ apiKeyContext: void 0,
3610
+ authType: "static"
3423
3611
  };
3424
3612
  }
3425
- let result;
3426
- try {
3427
- result = await authMw(req);
3428
- } catch (err) {
3429
- return {
3430
- user: staticUser,
3431
- tenantID: staticTenantID,
3432
- apiKeyContext: void 0
3433
- };
3434
- }
3435
- if (result.status === 401) {
3436
- return { user: void 0, tenantID: void 0, apiKeyContext: void 0 };
3613
+ if (authMw) {
3614
+ const res = await authMw(req);
3615
+ if (res.status === 200 && res.user) {
3616
+ return {
3617
+ user: res.user,
3618
+ tenantID: res.tenantContext?.tenantId,
3619
+ apiKeyContext: res.apiKeyContext,
3620
+ authType: res.authType
3621
+ };
3622
+ }
3437
3623
  }
3438
3624
  return {
3439
- user: result.user || staticUser,
3440
- tenantID: result.tenantContext?.tenantId || staticTenantID,
3441
- apiKeyContext: result.apiKeyContext,
3442
- authType: result.authType
3625
+ user: void 0,
3626
+ tenantID: void 0,
3627
+ apiKeyContext: void 0,
3628
+ authType: void 0
3443
3629
  };
3444
3630
  }
3445
3631
  function createDefaultAuthAdapter(db, rootDir) {
3446
- if (db instanceof chunk2KVHZE6O_cjs.DrizzleAdapter && db.dialect === "postgres") {
3447
- return new chunk2OL4O2TH_cjs.PostgresAuthAdapter({ db: db.client });
3632
+ if (db instanceof chunkCOIASRDK_cjs.DrizzleAdapter && db.dialect === "postgres") {
3633
+ return new chunkC36TMDTY_cjs.PostgresAuthAdapter({ db: db.client });
3448
3634
  }
3449
- if (db instanceof chunkPDYFVNUX_cjs.MongoDBAdapter) {
3450
- return new chunk4DA7QPLA_cjs.MongoDBAuthAdapter({ db: db.db });
3635
+ if (db instanceof chunkV2TVSCV5_cjs.MongoDBAdapter) {
3636
+ return new chunkGXFOGU7N_cjs.MongoDBAuthAdapter({ db: db.db });
3451
3637
  }
3452
3638
  const defaultAuthDbPath = path.resolve(rootDir, "data", "kyro.db");
3453
- return new chunkI7HHI6QV_cjs.SQLiteAuthAdapter({
3639
+ return new chunkIDVRRRAK_cjs.SQLiteAuthAdapter({
3454
3640
  path: process.env.KYRO_AUTH_DB_PATH || defaultAuthDbPath
3455
3641
  });
3456
3642
  }
@@ -3554,78 +3740,15 @@ function createHonoApp(options) {
3554
3740
  rateLimiter
3555
3741
  });
3556
3742
  app.post("/api/auth/login", async (c) => authRoutes.login(c.req.raw));
3557
- app.post("/api/auth/register", async (c) => authRoutes.register(c.req.raw));
3558
- app.post("/api/auth/logout", async (c) => authRoutes.logout(c.req.raw));
3559
- app.post("/api/auth/refresh", async (c) => authRoutes.refresh(c.req.raw));
3560
- app.get("/api/auth/me", async (c) => authRoutes.me(c.req.raw));
3561
- app.get("/api/auth/sessions", async (c) => authRoutes.listSessions(c.req.raw));
3562
- app.post("/api/auth/sessions/refresh", async (c) => authRoutes.refreshSession(c.req.raw));
3563
- app.delete("/api/auth/sessions", async (c) => authRoutes.revokeOtherSessions(c.req.raw));
3564
- app.delete("/api/auth/sessions/:id", async (c) => authRoutes.revokeSession(c.req.raw, c.req.param("id")));
3565
- app.put("/api/auth/sessions/:id/name", async (c) => authRoutes.renameSession(c.req.raw, c.req.param("id")));
3566
- app.post("/api/graphql", async (c) => {
3567
- try {
3568
- const req = c.req.raw;
3569
- const apiKeyRaw = chunkIBG6V56E_cjs.extractApiKeyFromRequest(req);
3570
- if (apiKeyRaw && db) {
3571
- const apiKeyResult = await chunkIBG6V56E_cjs.validateApiKey(apiKeyRaw, db);
3572
- if (!apiKeyResult.valid) {
3573
- return c.json({ errors: [{ message: apiKeyResult.error || "Invalid API key" }] }, 401);
3574
- }
3575
- const apiKeyId = apiKeyResult.apiKeyId || "";
3576
- await sessionAuthAdapter?.createAuditLog({
3577
- action: "api_key_request",
3578
- userId: apiKeyResult.userId || "",
3579
- resource: "api_key",
3580
- resourceId: apiKeyId,
3581
- success: true,
3582
- metadata: {
3583
- endpoint: "/api/graphql",
3584
- method: "POST",
3585
- ip: extractIp(req)
3586
- }
3587
- });
3588
- }
3589
- const body = await req.json().catch(() => ({}));
3590
- const { query, variables } = body;
3591
- if (!query) {
3592
- return c.json({ errors: [{ message: "No query provided" }] }, 400);
3593
- }
3594
- let gqlUser;
3595
- let apiKeyCtx;
3596
- if (apiKeyRaw && db) {
3597
- const apiKeyResult = await chunkIBG6V56E_cjs.validateApiKey(apiKeyRaw, db);
3598
- if (apiKeyResult.valid && apiKeyResult.user) {
3599
- gqlUser = apiKeyResult.user;
3600
- apiKeyCtx = chunkIBG6V56E_cjs.createApiKeyContext(apiKeyResult);
3601
- }
3602
- }
3603
- const schema = chunkVJT6P4N6_cjs.buildGraphQLSchema({
3604
- registry,
3605
- db,
3606
- user: gqlUser,
3607
- req,
3608
- settings
3609
- });
3610
- const document = graphql.parse(query);
3611
- const result = await graphql.execute({
3612
- schema,
3613
- document,
3614
- variableValues: variables,
3615
- contextValue: { user: gqlUser, apiKeyContext: apiKeyCtx, req, db }
3616
- });
3617
- return c.json(result);
3618
- } catch (error) {
3619
- if (error.message?.includes("GraphQL is disabled")) {
3620
- return c.json({ errors: [{ message: "GraphQL API is disabled" }] }, 403);
3621
- }
3622
- if (error instanceof SyntaxError) {
3623
- return c.json({ errors: [{ message: "Invalid request body" }] }, 400);
3624
- }
3625
- console.error("[GraphQL] execution error:", error);
3626
- return c.json({ errors: [{ message: error.message || "GraphQL execution failed" }] }, 500);
3627
- }
3628
- });
3743
+ app.post("/api/auth/register", async (c) => authRoutes.register(c.req.raw));
3744
+ app.post("/api/auth/logout", async (c) => authRoutes.logout(c.req.raw));
3745
+ app.post("/api/auth/refresh", async (c) => authRoutes.refresh(c.req.raw));
3746
+ app.get("/api/auth/me", async (c) => authRoutes.me(c.req.raw));
3747
+ app.get("/api/auth/sessions", async (c) => authRoutes.listSessions(c.req.raw));
3748
+ app.post("/api/auth/sessions/refresh", async (c) => authRoutes.refreshSession(c.req.raw));
3749
+ app.delete("/api/auth/sessions", async (c) => authRoutes.revokeOtherSessions(c.req.raw));
3750
+ app.delete("/api/auth/sessions/:id", async (c) => authRoutes.revokeSession(c.req.raw, c.req.param("id")));
3751
+ app.put("/api/auth/sessions/:id/name", async (c) => authRoutes.renameSession(c.req.raw, c.req.param("id")));
3629
3752
  app.get("/api/auth/access", async (c) => {
3630
3753
  try {
3631
3754
  const { user: ctxUser, tenantID: ctxTenantID } = await resolveAuthContext(
@@ -3710,7 +3833,13 @@ function createHonoApp(options) {
3710
3833
  return c.json({ error: error.message }, 500);
3711
3834
  }
3712
3835
  });
3713
- const usersCollection = registry.getCollection("users");
3836
+ const usersCollection2 = typeof registry.hasCollection === "function" && registry.hasCollection("users") ? registry.getCollection("users") : (() => {
3837
+ try {
3838
+ return registry.getCollection("users");
3839
+ } catch {
3840
+ return chunkKC2GDBLS_cjs.usersCollection;
3841
+ }
3842
+ })();
3714
3843
  app.get("/api/users", async (c) => {
3715
3844
  try {
3716
3845
  const { user: ctxUser, tenantID: ctxTenantID } = await resolveAuthContext(
@@ -3720,7 +3849,7 @@ function createHonoApp(options) {
3720
3849
  tenantID
3721
3850
  );
3722
3851
  const access = await checkCollectionAccess(
3723
- usersCollection,
3852
+ usersCollection2,
3724
3853
  "read",
3725
3854
  c.req.raw,
3726
3855
  ctxUser,
@@ -3765,19 +3894,6 @@ function createHonoApp(options) {
3765
3894
  user,
3766
3895
  tenantID
3767
3896
  );
3768
- const access = await checkCollectionAccess(
3769
- usersCollection,
3770
- "read",
3771
- c.req.raw,
3772
- ctxUser,
3773
- ctxTenantID,
3774
- void 0,
3775
- enablePublicAccess,
3776
- defaultCollectionAccess
3777
- );
3778
- if (!access.allowed) {
3779
- return c.json({ error: access.error }, access.status || 403);
3780
- }
3781
3897
  const id = c.req.param("id");
3782
3898
  const found = await sessionAuthAdapter.findUserById(id);
3783
3899
  if (!found) {
@@ -3797,7 +3913,7 @@ function createHonoApp(options) {
3797
3913
  tenantID
3798
3914
  );
3799
3915
  const access = await checkCollectionAccess(
3800
- usersCollection,
3916
+ usersCollection2,
3801
3917
  "create",
3802
3918
  c.req.raw,
3803
3919
  ctxUser,
@@ -3822,8 +3938,18 @@ function createHonoApp(options) {
3822
3938
  password: body.password,
3823
3939
  name: body.name,
3824
3940
  role: body.role || "customer",
3941
+ avatar: body.avatar,
3825
3942
  tenantId: body.tenantId
3826
3943
  });
3944
+ if (ctxUser) {
3945
+ sessionAuthAdapter?.createAuditLog({
3946
+ action: "user_create",
3947
+ userId: ctxUser.id,
3948
+ resource: "users",
3949
+ resourceId: created.id,
3950
+ success: true
3951
+ });
3952
+ }
3827
3953
  return c.json(
3828
3954
  { data: created, message: "User created successfully" },
3829
3955
  201
@@ -3841,7 +3967,7 @@ function createHonoApp(options) {
3841
3967
  tenantID
3842
3968
  );
3843
3969
  const access = await checkCollectionAccess(
3844
- usersCollection,
3970
+ usersCollection2,
3845
3971
  "update",
3846
3972
  c.req.raw,
3847
3973
  ctxUser,
@@ -3863,6 +3989,9 @@ function createHonoApp(options) {
3863
3989
  if (body.name !== void 0) updateData.name = body.name;
3864
3990
  if (body.email !== void 0) updateData.email = body.email;
3865
3991
  if (body.role !== void 0) updateData.role = body.role;
3992
+ if (body.avatar !== void 0) {
3993
+ updateData.avatar = typeof body.avatar === "object" && body.avatar !== null ? body.avatar.id || String(body.avatar) : body.avatar;
3994
+ }
3866
3995
  if (body.tenantId !== void 0) updateData.tenantId = body.tenantId;
3867
3996
  if (body.emailVerified !== void 0)
3868
3997
  updateData.emailVerified = body.emailVerified;
@@ -3874,6 +4003,25 @@ function createHonoApp(options) {
3874
4003
  if (!updated) {
3875
4004
  return c.json({ error: "User update failed" }, 500);
3876
4005
  }
4006
+ if (ctxUser) {
4007
+ sessionAuthAdapter?.createAuditLog({
4008
+ action: "user_update",
4009
+ userId: ctxUser.id,
4010
+ resource: "users",
4011
+ resourceId: id,
4012
+ success: true
4013
+ });
4014
+ if (body.role && existing.role !== body.role) {
4015
+ sessionAuthAdapter?.createAuditLog({
4016
+ action: "role_change",
4017
+ userId: ctxUser.id,
4018
+ resource: "users",
4019
+ resourceId: id,
4020
+ success: true,
4021
+ metadata: { oldRole: existing.role, newRole: body.role }
4022
+ });
4023
+ }
4024
+ }
3877
4025
  return c.json({ data: updated, message: "User updated successfully" });
3878
4026
  } catch (error) {
3879
4027
  return c.json({ error: error.message }, 500);
@@ -3888,7 +4036,7 @@ function createHonoApp(options) {
3888
4036
  tenantID
3889
4037
  );
3890
4038
  const access = await checkCollectionAccess(
3891
- usersCollection,
4039
+ usersCollection2,
3892
4040
  "delete",
3893
4041
  c.req.raw,
3894
4042
  ctxUser,
@@ -3912,6 +4060,15 @@ function createHonoApp(options) {
3912
4060
  if (!deleted) {
3913
4061
  return c.json({ error: "User deletion failed" }, 500);
3914
4062
  }
4063
+ if (ctxUser) {
4064
+ sessionAuthAdapter?.createAuditLog({
4065
+ action: "user_delete",
4066
+ userId: ctxUser.id,
4067
+ resource: "users",
4068
+ resourceId: id,
4069
+ success: true
4070
+ });
4071
+ }
3915
4072
  return c.json({ data: existing, message: "User deleted successfully" });
3916
4073
  } catch (error) {
3917
4074
  return c.json({ error: error.message }, 500);
@@ -3980,8 +4137,8 @@ function createHonoApp(options) {
3980
4137
  }
3981
4138
  if (!mediaService) {
3982
4139
  try {
3983
- const dialect = db instanceof chunk2KVHZE6O_cjs.DrizzleAdapter ? db.dialect : "sqlite";
3984
- const mediaDb = dialect === "postgres" && db instanceof chunk2KVHZE6O_cjs.DrizzleAdapter ? db.client : db;
4140
+ const dialect = db instanceof chunkCOIASRDK_cjs.DrizzleAdapter ? db.dialect : "sqlite";
4141
+ const mediaDb = dialect === "postgres" && db instanceof chunkCOIASRDK_cjs.DrizzleAdapter ? db.client : db;
3985
4142
  mediaService = await MediaService.init(mediaDb, { dialect });
3986
4143
  } catch (error) {
3987
4144
  console.error("[getMedia] Init error:", error);
@@ -4068,11 +4225,11 @@ function createHonoApp(options) {
4068
4225
  const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4069
4226
  if (!ctxUser) return c.json({ error: "Authentication required" }, 401);
4070
4227
  const service = await getMedia();
4071
- const path2 = c.req.query("path");
4072
- if (!path2) {
4228
+ const path3 = c.req.query("path");
4229
+ if (!path3) {
4073
4230
  return c.json({ error: "Path is required" }, 400);
4074
4231
  }
4075
- await service.deleteFolder(path2);
4232
+ await service.deleteFolder(path3);
4076
4233
  return c.json({ message: "Folder deleted" });
4077
4234
  } catch (error) {
4078
4235
  console.error("[Media] delete folder error:", error);
@@ -4225,6 +4382,119 @@ function createHonoApp(options) {
4225
4382
  return c.json({ error: error.message }, 500);
4226
4383
  }
4227
4384
  });
4385
+ app.get("/api/plugins", async (c) => {
4386
+ try {
4387
+ const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4388
+ if (!ctxUser) return c.json({ error: "Authentication required" }, 401);
4389
+ const plugins = registry.getPlugins();
4390
+ const storageRegistry = registry.storageProviders;
4391
+ const pluginList = await Promise.all(
4392
+ plugins.map(async (p) => {
4393
+ let enabled = true;
4394
+ const pluginName = p.name;
4395
+ let states = {};
4396
+ try {
4397
+ const doc = await db.findOne({
4398
+ collection: "_globals_plugin-settings",
4399
+ where: {}
4400
+ });
4401
+ if (doc && doc.states) states = doc.states;
4402
+ } catch {
4403
+ }
4404
+ if (states[pluginName] !== void 0) {
4405
+ enabled = states[pluginName];
4406
+ }
4407
+ storageRegistry.setPluginEnabled(pluginName, enabled);
4408
+ return {
4409
+ id: pluginName,
4410
+ name: pluginName,
4411
+ version: p.version || "1.0.0",
4412
+ description: p.description || "",
4413
+ enabled,
4414
+ status: enabled ? "active" : "disabled"
4415
+ };
4416
+ })
4417
+ );
4418
+ return c.json(pluginList);
4419
+ } catch (error) {
4420
+ console.error("[Plugins] list error:", error);
4421
+ return c.json({ error: error.message }, 500);
4422
+ }
4423
+ });
4424
+ app.put("/api/plugins/:name/toggle", async (c) => {
4425
+ try {
4426
+ const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4427
+ if (!ctxUser) return c.json({ error: "Authentication required" }, 401);
4428
+ const pluginName = c.req.param("name");
4429
+ const storageRegistry = registry.storageProviders;
4430
+ const currentEnabled = storageRegistry.isPluginEnabled(pluginName);
4431
+ const newEnabled = !currentEnabled;
4432
+ const affectedProviders = [];
4433
+ if (!newEnabled) {
4434
+ for (const p of storageRegistry.getAll()) {
4435
+ if (p.pluginName === pluginName) {
4436
+ affectedProviders.push(p.type);
4437
+ }
4438
+ }
4439
+ }
4440
+ const force = c.req.query("force") === "1";
4441
+ if (!force && !newEnabled && affectedProviders.length > 0) {
4442
+ let activeProvider = "local";
4443
+ try {
4444
+ const row = db?.prepare?.(`SELECT provider FROM "_globals_storage-settings" LIMIT 1`)?.get();
4445
+ if (row?.provider) activeProvider = row.provider;
4446
+ } catch {
4447
+ try {
4448
+ const result = await db?.findOne?.({
4449
+ collection: "_globals_storage-settings",
4450
+ where: {}
4451
+ });
4452
+ if (result?.provider) activeProvider = result.provider;
4453
+ } catch {
4454
+ }
4455
+ }
4456
+ if (affectedProviders.includes(activeProvider)) {
4457
+ return c.json({
4458
+ error: `Cannot disable "${pluginName}" \u2014 storage provider "${activeProvider}" is currently active. Switch to Local storage first.`,
4459
+ requiresAction: true,
4460
+ activeProvider
4461
+ }, 409);
4462
+ }
4463
+ }
4464
+ try {
4465
+ let states = {};
4466
+ let docId = "global";
4467
+ const doc = await db.findOne({
4468
+ collection: "_globals_plugin-settings",
4469
+ where: {}
4470
+ });
4471
+ if (doc) {
4472
+ states = doc.states || {};
4473
+ docId = doc.id;
4474
+ }
4475
+ states[pluginName] = newEnabled;
4476
+ if (doc) {
4477
+ await db.update({
4478
+ collection: "_globals_plugin-settings",
4479
+ id: docId,
4480
+ data: { states }
4481
+ });
4482
+ } else {
4483
+ await db.create({
4484
+ collection: "_globals_plugin-settings",
4485
+ data: { states, id: "global" }
4486
+ });
4487
+ }
4488
+ } catch (e) {
4489
+ console.warn(`[Plugins] Could not persist state for "${pluginName}":`, e);
4490
+ }
4491
+ storageRegistry.setPluginEnabled(pluginName, newEnabled);
4492
+ return c.json({ name: pluginName, enabled: newEnabled });
4493
+ } catch (error) {
4494
+ console.error("[Plugins] toggle error:", error);
4495
+ return c.json({ error: error.message }, 500);
4496
+ }
4497
+ });
4228
4498
  app.get("/api/health", (c) => {
4229
4499
  return c.json({
4230
4500
  status: "ok",
@@ -4233,6 +4503,102 @@ function createHonoApp(options) {
4233
4503
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
4234
4504
  });
4235
4505
  });
4506
+ app.get("/api/kyro/schema", async (c) => {
4507
+ const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4508
+ if (!ctxUser) return c.json({ error: "Authentication required" }, 401);
4509
+ const extractFields = (fields) => fields.map((f) => {
4510
+ const meta = {
4511
+ name: f.name,
4512
+ type: f.type,
4513
+ label: f.label,
4514
+ required: f.required ?? false,
4515
+ unique: f.unique ?? false,
4516
+ indexed: f.indexed ?? false,
4517
+ defaultValue: f.defaultValue,
4518
+ admin: f.admin ? { ...f.admin } : void 0
4519
+ };
4520
+ if (f.minLength !== void 0) meta.minLength = f.minLength;
4521
+ if (f.maxLength !== void 0) meta.maxLength = f.maxLength;
4522
+ if (f.pattern !== void 0) meta.pattern = f.pattern;
4523
+ if (f.variant !== void 0) meta.variant = f.variant;
4524
+ if (f.hasMany !== void 0) meta.hasMany = f.hasMany;
4525
+ if (f.min !== void 0) meta.min = f.min;
4526
+ if (f.max !== void 0) meta.max = f.max;
4527
+ if (f.step !== void 0) meta.step = f.step;
4528
+ if (f.integer !== void 0) meta.integer = f.integer;
4529
+ if (f.options) meta.options = f.options;
4530
+ if (f.relationTo) meta.relationTo = f.relationTo;
4531
+ if (f.maxDepth !== void 0) meta.maxDepth = f.maxDepth;
4532
+ if (f.minRows !== void 0) meta.minRows = f.minRows;
4533
+ if (f.maxRows !== void 0) meta.maxRows = f.maxRows;
4534
+ if (f.localized !== void 0) meta.localized = f.localized;
4535
+ if (f.language) meta.language = f.language;
4536
+ if (f.format) meta.format = f.format;
4537
+ if (f.allowedTypes) meta.allowedTypes = f.allowedTypes;
4538
+ if (f.maxSize) meta.maxSize = f.maxSize;
4539
+ if (f.type === "group" || f.type === "row" || f.type === "collapsible" && f.fields) {
4540
+ meta.fields = extractFields(f.fields);
4541
+ }
4542
+ if (f.type === "array" && f.fields) {
4543
+ meta.fields = extractFields(f.fields);
4544
+ }
4545
+ if (f.type === "blocks" && f.blocks) {
4546
+ meta.blocks = f.blocks.map((b) => ({
4547
+ slug: b.slug,
4548
+ label: b.label,
4549
+ fields: extractFields(b.fields)
4550
+ }));
4551
+ }
4552
+ if (f.type === "tabs" && f.tabs) {
4553
+ meta.tabs = f.tabs.map((t) => ({
4554
+ label: t.label,
4555
+ name: t.name,
4556
+ fields: extractFields(t.fields)
4557
+ }));
4558
+ }
4559
+ return meta;
4560
+ });
4561
+ const data = { collections: {}, globals: {} };
4562
+ for (const col of registry.getCollections()) {
4563
+ const slug = col.slug;
4564
+ try {
4565
+ data.collections[slug] = {
4566
+ slug,
4567
+ label: col.label || slug,
4568
+ fields: extractFields(col.fields),
4569
+ jsonSchema: zodToJsonSchema.zodToJsonSchema(registry.getZodSchema(slug), { target: "openApi3" }),
4570
+ createSchema: zodToJsonSchema.zodToJsonSchema(registry.getCreateZodSchema(slug), { target: "openApi3" }),
4571
+ updateSchema: zodToJsonSchema.zodToJsonSchema(registry.getUpdateZodSchema(slug), { target: "openApi3" }),
4572
+ procedures: {
4573
+ find: { collection: slug, where: "Record<string,any>", sort: "string", limit: "number", page: "number", depth: "number", select: "string[]", draft: "boolean" },
4574
+ findByID: { collection: slug, id: "string", depth: "number", select: "string[]", draft: "boolean" },
4575
+ create: { collection: slug, data: "Record<string,any>", depth: "number", select: "string[]" },
4576
+ update: { collection: slug, id: "string", data: "Record<string,any>", depth: "number", select: "string[]", baseUpdatedAt: "string" },
4577
+ delete: { collection: slug, id: "string" },
4578
+ count: { collection: slug, where: "Record<string,any>" }
4579
+ }
4580
+ };
4581
+ } catch {
4582
+ }
4583
+ }
4584
+ for (const global of registry.getGlobals()) {
4585
+ const slug = global.slug;
4586
+ try {
4587
+ data.globals[slug] = {
4588
+ slug,
4589
+ label: global.label || slug,
4590
+ fields: extractFields(global.fields),
4591
+ jsonSchema: zodToJsonSchema.zodToJsonSchema(registry.getZodSchema(slug), { target: "openApi3" }),
4592
+ procedures: {
4593
+ get: {},
4594
+ update: { data: "Record<string,any>" }
4595
+ }
4596
+ };
4597
+ } catch {
4598
+ }
4599
+ }
4600
+ return c.json(data);
4601
+ });
4236
4602
  app.get("/api/collections", async (c) => {
4237
4603
  const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4238
4604
  if (!ctxUser) return c.json({ error: "Authentication required" }, 401);
@@ -4248,6 +4614,129 @@ function createHonoApp(options) {
4248
4614
  }));
4249
4615
  return c.json(collections2);
4250
4616
  });
4617
+ function resolveDocField(fields, doc, fieldName) {
4618
+ if (fieldName in doc) return doc[fieldName];
4619
+ for (const field of fields) {
4620
+ if (!field.name) continue;
4621
+ if (field.type === "tabs" && field.tabs) {
4622
+ const data = doc[field.name];
4623
+ if (data && typeof data === "object" && fieldName in data) return data[fieldName];
4624
+ }
4625
+ if ((field.type === "group" || field.type === "collapsible") && field.fields) {
4626
+ const data = doc[field.name];
4627
+ if (data && typeof data === "object") {
4628
+ if (fieldName in data) return data[fieldName];
4629
+ const nested = resolveDocField(field.fields, data, fieldName);
4630
+ if (nested !== void 0) return nested;
4631
+ }
4632
+ }
4633
+ }
4634
+ return void 0;
4635
+ }
4636
+ function flattenRelationshipFields(fields) {
4637
+ const relFields = [];
4638
+ for (const field of fields) {
4639
+ if (field.type === "relationship") {
4640
+ relFields.push(field);
4641
+ } else if (field.type === "tabs" && field.tabs) {
4642
+ for (const tab of field.tabs) {
4643
+ relFields.push(...flattenRelationshipFields(tab.fields || []));
4644
+ }
4645
+ } else if ((field.type === "group" || field.type === "collapsible") && field.fields) {
4646
+ relFields.push(...flattenRelationshipFields(field.fields || []));
4647
+ }
4648
+ }
4649
+ return relFields;
4650
+ }
4651
+ function extractRelValue(value) {
4652
+ if (!value) return [];
4653
+ if (typeof value === "string") return [value];
4654
+ if (Array.isArray(value)) return value.map((v) => typeof v === "object" ? v.value ?? v : v);
4655
+ if (typeof value === "object") {
4656
+ const v = value.value ?? value;
4657
+ return typeof v === "string" ? [v] : Array.isArray(v) ? v : [];
4658
+ }
4659
+ return [];
4660
+ }
4661
+ async function populateRelationships(docs, collection, db2, registry2) {
4662
+ if (docs.length === 0) return;
4663
+ const relFields = flattenRelationshipFields(collection.fields);
4664
+ if (relFields.length === 0) return;
4665
+ for (const relField of relFields) {
4666
+ const targetSlugs = Array.isArray(relField.relationTo) ? relField.relationTo : [relField.relationTo];
4667
+ const idsBySlug = {};
4668
+ for (const slug of targetSlugs) {
4669
+ idsBySlug[slug] = /* @__PURE__ */ new Set();
4670
+ }
4671
+ for (const doc of docs) {
4672
+ const raw = resolveDocField(collection.fields, doc, relField.name);
4673
+ const ids = extractRelValue(raw);
4674
+ for (const id of ids) {
4675
+ if (!id) continue;
4676
+ if (targetSlugs.length === 1) {
4677
+ idsBySlug[targetSlugs[0]].add(id);
4678
+ } else {
4679
+ for (const slug of targetSlugs) {
4680
+ idsBySlug[slug].add(id);
4681
+ }
4682
+ }
4683
+ }
4684
+ }
4685
+ for (const [targetSlug, idSet] of Object.entries(idsBySlug)) {
4686
+ if (idSet.size === 0) continue;
4687
+ const targetCollection = registry2.getCollection(targetSlug);
4688
+ if (!targetCollection) continue;
4689
+ const titleField = targetCollection.admin?.useAsTitle || "title";
4690
+ const idArr = Array.from(idSet);
4691
+ const relatedDocs = [];
4692
+ for (const id of idArr) {
4693
+ try {
4694
+ const relDoc = await db2.findByID({ collection: targetSlug, id, draft: true });
4695
+ if (relDoc) {
4696
+ const title = resolveDocField(targetCollection.fields, relDoc, titleField) ?? id;
4697
+ relatedDocs.push({ id, title: String(title) });
4698
+ }
4699
+ } catch {
4700
+ relatedDocs.push({ id, title: id });
4701
+ }
4702
+ }
4703
+ const titleMap = new Map(relatedDocs.map((d) => [d.id, d.title]));
4704
+ for (const doc of docs) {
4705
+ const raw = resolveDocField(collection.fields, doc, relField.name);
4706
+ if (!raw) continue;
4707
+ const setValue = (val) => {
4708
+ if (relField.name in doc) {
4709
+ doc[relField.name] = val;
4710
+ } else {
4711
+ for (const f of collection.fields) {
4712
+ if (f.type === "tabs" && f.tabs) {
4713
+ for (const tab of f.tabs) {
4714
+ if (tab.fields?.some((tf) => tf.name === relField.name)) {
4715
+ const tabData = doc[f.name];
4716
+ if (tabData && typeof tabData === "object") {
4717
+ tabData[relField.name] = val;
4718
+ }
4719
+ }
4720
+ }
4721
+ }
4722
+ }
4723
+ }
4724
+ };
4725
+ if (typeof raw === "string") {
4726
+ setValue({ id: raw, title: titleMap.get(raw) || raw });
4727
+ } else if (Array.isArray(raw)) {
4728
+ setValue(raw.map((v) => {
4729
+ const id = typeof v === "object" ? v.value ?? v.id : v;
4730
+ return { id, title: titleMap.get(id) || id };
4731
+ }));
4732
+ } else if (typeof raw === "object") {
4733
+ const id = raw.value ?? raw.id;
4734
+ setValue({ id, title: titleMap.get(id) || id });
4735
+ }
4736
+ }
4737
+ }
4738
+ }
4739
+ }
4251
4740
  app.get("/api/search", async (c) => {
4252
4741
  try {
4253
4742
  const query = c.req.query("q") || "";
@@ -4299,11 +4788,12 @@ function createHonoApp(options) {
4299
4788
  limit,
4300
4789
  tenantID: ctxTenantID
4301
4790
  });
4791
+ await populateRelationships(searchResult.docs, collection, db, registry);
4302
4792
  for (const doc of searchResult.docs) {
4303
4793
  const titleField = collection.admin?.useAsTitle || searchableFields.find(
4304
4794
  (f) => f === "title" || f === "name" || f === "heading" || f === "slug"
4305
4795
  );
4306
- const title = titleField ? doc[titleField] : doc.id;
4796
+ const title = titleField ? resolveDocField(collection.fields, doc, titleField) ?? doc.id : doc.id;
4307
4797
  results.push({
4308
4798
  collection: collection.slug,
4309
4799
  label: collection.label || collection.slug,
@@ -4325,13 +4815,13 @@ function createHonoApp(options) {
4325
4815
  app.get("/api/keys", async (c) => {
4326
4816
  try {
4327
4817
  const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4328
- if (!ctxUser || !chunkSA7NSSIQ_cjs.hasPermission(ctxUser, "users:read")) {
4818
+ if (!ctxUser || !chunkNKPKR5BW_cjs.hasPermission(ctxUser, "users:read")) {
4329
4819
  return c.json({ error: "Forbidden" }, 403);
4330
4820
  }
4331
4821
  const page = parseInt(c.req.query("page") || "1");
4332
4822
  const limit = Math.min(parseInt(c.req.query("limit") || "50"), 100);
4333
- console.log("[ApiKeys] Querying", chunkIBG6V56E_cjs.API_KEY_COLLECTION, { page, limit });
4334
- const result = await db.find({ collection: chunkIBG6V56E_cjs.API_KEY_COLLECTION, where: {}, page, limit });
4823
+ console.log("[ApiKeys] Querying", chunk4M7X5HAB_cjs.API_KEY_COLLECTION, { page, limit });
4824
+ const result = await db.find({ collection: chunk4M7X5HAB_cjs.API_KEY_COLLECTION, where: {}, page, limit });
4335
4825
  console.log("[ApiKeys] Result:", result);
4336
4826
  const docs = (result.docs || []).map((doc) => ({
4337
4827
  id: doc.id,
@@ -4350,21 +4840,21 @@ function createHonoApp(options) {
4350
4840
  app.post("/api/keys", async (c) => {
4351
4841
  try {
4352
4842
  const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4353
- if (!ctxUser || !chunkSA7NSSIQ_cjs.hasPermission(ctxUser, "users:admin")) {
4843
+ if (!ctxUser || !chunkNKPKR5BW_cjs.hasPermission(ctxUser, "users:admin")) {
4354
4844
  return c.json({ error: "Forbidden" }, 403);
4355
4845
  }
4356
4846
  const body = await c.req.json();
4357
4847
  if (!body.name || typeof body.name !== "string") {
4358
4848
  return c.json({ error: "name is required" }, 400);
4359
4849
  }
4360
- const rawKey = chunkIBG6V56E_cjs.generateApiKey();
4850
+ const rawKey = chunk4M7X5HAB_cjs.generateApiKey();
4361
4851
  const doc = await db.create({
4362
- collection: chunkIBG6V56E_cjs.API_KEY_COLLECTION,
4852
+ collection: chunk4M7X5HAB_cjs.API_KEY_COLLECTION,
4363
4853
  data: {
4364
4854
  userId: ctxUser.id,
4365
4855
  name: body.name,
4366
4856
  key: rawKey,
4367
- keyPrefix: chunkIBG6V56E_cjs.generateApiKeyPrefix(rawKey),
4857
+ keyPrefix: chunk4M7X5HAB_cjs.generateApiKeyPrefix(rawKey),
4368
4858
  permissions: Array.isArray(body.permissions) ? body.permissions : ["*"],
4369
4859
  expiresAt: body.expiresAt || null,
4370
4860
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -4391,13 +4881,13 @@ function createHonoApp(options) {
4391
4881
  app.delete("/api/keys/:id", async (c) => {
4392
4882
  try {
4393
4883
  const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4394
- if (!ctxUser || !chunkSA7NSSIQ_cjs.hasPermission(ctxUser, "users:admin")) {
4884
+ if (!ctxUser || !chunkNKPKR5BW_cjs.hasPermission(ctxUser, "users:admin")) {
4395
4885
  return c.json({ error: "Forbidden" }, 403);
4396
4886
  }
4397
4887
  const id = c.req.param("id");
4398
- const existing = await db.findByID({ collection: chunkIBG6V56E_cjs.API_KEY_COLLECTION, id });
4888
+ const existing = await db.findByID({ collection: chunk4M7X5HAB_cjs.API_KEY_COLLECTION, id });
4399
4889
  if (!existing) return c.json({ error: "API key not found" }, 404);
4400
- await db.delete({ collection: chunkIBG6V56E_cjs.API_KEY_COLLECTION, id });
4890
+ await db.delete({ collection: chunk4M7X5HAB_cjs.API_KEY_COLLECTION, id });
4401
4891
  await sessionAuthAdapter?.createAuditLog({
4402
4892
  action: "api_key_delete",
4403
4893
  userId: ctxUser.id,
@@ -4415,19 +4905,19 @@ function createHonoApp(options) {
4415
4905
  app.patch("/api/keys/:id", async (c) => {
4416
4906
  try {
4417
4907
  const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4418
- if (!ctxUser || !chunkSA7NSSIQ_cjs.hasPermission(ctxUser, "users:admin")) {
4908
+ if (!ctxUser || !chunkNKPKR5BW_cjs.hasPermission(ctxUser, "users:admin")) {
4419
4909
  return c.json({ error: "Forbidden" }, 403);
4420
4910
  }
4421
4911
  const id = c.req.param("id");
4422
4912
  const body = await c.req.json();
4423
- const existing = await db.findByID({ collection: chunkIBG6V56E_cjs.API_KEY_COLLECTION, id });
4913
+ const existing = await db.findByID({ collection: chunk4M7X5HAB_cjs.API_KEY_COLLECTION, id });
4424
4914
  if (!existing) return c.json({ error: "API key not found" }, 404);
4425
4915
  const updateData = {};
4426
4916
  if (typeof body.name === "string" && body.name.trim()) updateData.name = body.name.trim();
4427
4917
  if (Array.isArray(body.permissions)) updateData.permissions = body.permissions;
4428
4918
  if (body.expiresAt !== void 0) updateData.expiresAt = body.expiresAt || null;
4429
4919
  if (Object.keys(updateData).length === 0) return c.json({ error: "Nothing to update" }, 400);
4430
- const updated = await db.update({ collection: chunkIBG6V56E_cjs.API_KEY_COLLECTION, id, data: updateData });
4920
+ const updated = await db.update({ collection: chunk4M7X5HAB_cjs.API_KEY_COLLECTION, id, data: updateData });
4431
4921
  return c.json({ ...updated, keyPrefix: existing.keyPrefix });
4432
4922
  } catch (error) {
4433
4923
  console.error("[ApiKeys] PATCH error:", error);
@@ -4437,19 +4927,19 @@ function createHonoApp(options) {
4437
4927
  app.post("/api/keys/:id/rotate", async (c) => {
4438
4928
  try {
4439
4929
  const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4440
- if (!ctxUser || !chunkSA7NSSIQ_cjs.hasPermission(ctxUser, "users:admin")) {
4930
+ if (!ctxUser || !chunkNKPKR5BW_cjs.hasPermission(ctxUser, "users:admin")) {
4441
4931
  return c.json({ error: "Forbidden" }, 403);
4442
4932
  }
4443
4933
  const id = c.req.param("id");
4444
- const existing = await db.findByID({ collection: chunkIBG6V56E_cjs.API_KEY_COLLECTION, id });
4934
+ const existing = await db.findByID({ collection: chunk4M7X5HAB_cjs.API_KEY_COLLECTION, id });
4445
4935
  if (!existing) return c.json({ error: "API key not found" }, 404);
4446
- const rawKey = chunkIBG6V56E_cjs.generateApiKey();
4936
+ const rawKey = chunk4M7X5HAB_cjs.generateApiKey();
4447
4937
  const updated = await db.update({
4448
- collection: chunkIBG6V56E_cjs.API_KEY_COLLECTION,
4938
+ collection: chunk4M7X5HAB_cjs.API_KEY_COLLECTION,
4449
4939
  id,
4450
4940
  data: {
4451
4941
  key: rawKey,
4452
- keyPrefix: chunkIBG6V56E_cjs.generateApiKeyPrefix(rawKey),
4942
+ keyPrefix: chunk4M7X5HAB_cjs.generateApiKeyPrefix(rawKey),
4453
4943
  lastUsedAt: null
4454
4944
  }
4455
4945
  });
@@ -4475,7 +4965,7 @@ function createHonoApp(options) {
4475
4965
  app.get("/api/webhooks", async (c) => {
4476
4966
  try {
4477
4967
  const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4478
- if (!ctxUser || !chunkSA7NSSIQ_cjs.hasPermission(ctxUser, "users:read")) {
4968
+ if (!ctxUser || !chunkNKPKR5BW_cjs.hasPermission(ctxUser, "users:read")) {
4479
4969
  return c.json({ error: "Forbidden" }, 403);
4480
4970
  }
4481
4971
  if (!webhookService) return c.json({ error: "Webhook service not available" }, 503);
@@ -4489,7 +4979,7 @@ function createHonoApp(options) {
4489
4979
  app.post("/api/webhooks", async (c) => {
4490
4980
  try {
4491
4981
  const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4492
- if (!ctxUser || !chunkSA7NSSIQ_cjs.hasPermission(ctxUser, "users:admin")) {
4982
+ if (!ctxUser || !chunkNKPKR5BW_cjs.hasPermission(ctxUser, "users:admin")) {
4493
4983
  return c.json({ error: "Forbidden" }, 403);
4494
4984
  }
4495
4985
  if (!webhookService) return c.json({ error: "Webhook service not available" }, 503);
@@ -4512,7 +5002,7 @@ function createHonoApp(options) {
4512
5002
  app.get("/api/webhooks/:id", async (c) => {
4513
5003
  try {
4514
5004
  const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4515
- if (!ctxUser || !chunkSA7NSSIQ_cjs.hasPermission(ctxUser, "users:read")) {
5005
+ if (!ctxUser || !chunkNKPKR5BW_cjs.hasPermission(ctxUser, "users:read")) {
4516
5006
  return c.json({ error: "Forbidden" }, 403);
4517
5007
  }
4518
5008
  if (!webhookService) return c.json({ error: "Webhook service not available" }, 503);
@@ -4528,7 +5018,7 @@ function createHonoApp(options) {
4528
5018
  app.patch("/api/webhooks/:id", async (c) => {
4529
5019
  try {
4530
5020
  const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4531
- if (!ctxUser || !chunkSA7NSSIQ_cjs.hasPermission(ctxUser, "users:admin")) {
5021
+ if (!ctxUser || !chunkNKPKR5BW_cjs.hasPermission(ctxUser, "users:admin")) {
4532
5022
  return c.json({ error: "Forbidden" }, 403);
4533
5023
  }
4534
5024
  if (!webhookService) return c.json({ error: "Webhook service not available" }, 503);
@@ -4553,7 +5043,7 @@ function createHonoApp(options) {
4553
5043
  app.delete("/api/webhooks/:id", async (c) => {
4554
5044
  try {
4555
5045
  const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4556
- if (!ctxUser || !chunkSA7NSSIQ_cjs.hasPermission(ctxUser, "users:admin")) {
5046
+ if (!ctxUser || !chunkNKPKR5BW_cjs.hasPermission(ctxUser, "users:admin")) {
4557
5047
  return c.json({ error: "Forbidden" }, 403);
4558
5048
  }
4559
5049
  if (!webhookService) return c.json({ error: "Webhook service not available" }, 503);
@@ -4578,7 +5068,7 @@ function createHonoApp(options) {
4578
5068
  app.post("/api/webhooks/:id/test", async (c) => {
4579
5069
  try {
4580
5070
  const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4581
- if (!ctxUser || !chunkSA7NSSIQ_cjs.hasPermission(ctxUser, "users:admin")) {
5071
+ if (!ctxUser || !chunkNKPKR5BW_cjs.hasPermission(ctxUser, "users:admin")) {
4582
5072
  return c.json({ error: "Forbidden" }, 403);
4583
5073
  }
4584
5074
  if (!webhookService) return c.json({ error: "Webhook service not available" }, 503);
@@ -4594,7 +5084,7 @@ function createHonoApp(options) {
4594
5084
  app.get("/api/webhooks/:id/history", async (c) => {
4595
5085
  try {
4596
5086
  const { user: ctxUser } = await resolveAuthContext(c.req.raw, authMw, user, tenantID);
4597
- if (!ctxUser || !chunkSA7NSSIQ_cjs.hasPermission(ctxUser, "users:read")) {
5087
+ if (!ctxUser || !chunkNKPKR5BW_cjs.hasPermission(ctxUser, "users:read")) {
4598
5088
  return c.json({ error: "Forbidden" }, 403);
4599
5089
  }
4600
5090
  if (!webhookService) return c.json({ error: "Webhook service not available" }, 503);
@@ -4643,37 +5133,31 @@ function createHonoApp(options) {
4643
5133
  if (!access.allowed) {
4644
5134
  return c.json({ error: access.error }, access.status || 403);
4645
5135
  }
4646
- auditApiKeyUsage(sessionAuthAdapter, apiKeyContext, basePath, "GET", c.req.raw);
5136
+ if (ctxTenantID) {
5137
+ db.setTenantContext({ tenantId: ctxTenantID, userId: ctxUser?.id ?? "", role: ctxUser?.role, isSuperAdmin: ctxUser?.role === "super_admin" });
5138
+ }
4647
5139
  const url = new URL(c.req.url);
4648
5140
  const page = parseInt(url.searchParams.get("page") || "1");
4649
- const limit = Math.min(
4650
- parseInt(url.searchParams.get("limit") || "10"),
4651
- 100
4652
- );
5141
+ const limit = Math.min(parseInt(url.searchParams.get("limit") || "10"), 100);
4653
5142
  const sort = url.searchParams.get("sort") || void 0;
4654
5143
  const depth = parseInt(url.searchParams.get("depth") || "0");
4655
5144
  const select = url.searchParams.get("select")?.split(",") || void 0;
4656
- let where = {};
4657
- const whereParam = url.searchParams.get("where");
4658
- if (whereParam) {
4659
- try {
4660
- where = JSON.parse(whereParam);
4661
- } catch {
4662
- }
4663
- }
5145
+ const isDraftRequest = !!ctxUser;
4664
5146
  const result = await db.find({
4665
5147
  collection: slug,
4666
- where,
5148
+ where: {},
4667
5149
  sort,
4668
5150
  limit,
4669
5151
  page,
4670
5152
  depth,
4671
5153
  tenantID: ctxTenantID,
4672
- select
5154
+ select,
5155
+ draft: isDraftRequest
4673
5156
  });
5157
+ await populateRelationships(result.docs, collection, db, registry);
4674
5158
  return c.json(result);
4675
5159
  } catch (error) {
4676
- console.error(`[API] Error listing ${slug}:`, error);
5160
+ console.error("[API] list error:", error);
4677
5161
  return c.json({ error: error.message }, 500);
4678
5162
  }
4679
5163
  });
@@ -4698,6 +5182,9 @@ function createHonoApp(options) {
4698
5182
  return c.json({ error: access.error }, access.status || 403);
4699
5183
  }
4700
5184
  const id = c.req.param("id");
5185
+ if (!id) {
5186
+ return c.json({ error: "Missing document ID" }, 400);
5187
+ }
4701
5188
  const url = new URL(c.req.url);
4702
5189
  const compareA = url.searchParams.get("compareA");
4703
5190
  const compareB = url.searchParams.get("compareB");
@@ -4781,23 +5268,39 @@ function createHonoApp(options) {
4781
5268
  const id = c.req.param("id");
4782
5269
  const body = await c.req.json();
4783
5270
  const baseUpdatedAt = readBaseUpdatedAt(body);
4784
- const data = body.data ?? omitRevisionFields(body);
4785
5271
  const originalDoc = await db.findByID({
4786
5272
  collection: slug,
4787
5273
  id,
4788
- tenantID: ctxTenantID
5274
+ tenantID: ctxTenantID,
5275
+ draft: true
4789
5276
  });
4790
5277
  if (!originalDoc) {
4791
5278
  return c.json({ error: "Document not found" }, 404);
4792
5279
  }
5280
+ let finalData;
5281
+ if (body.delta) {
5282
+ finalData = { ...originalDoc, ...body.delta };
5283
+ } else {
5284
+ finalData = body.data ?? omitRevisionFields(body);
5285
+ }
4793
5286
  const draft = await db.upsertDraft({
4794
5287
  collection: slug,
4795
5288
  documentId: id,
4796
5289
  tenantID: ctxTenantID,
4797
- data,
5290
+ data: finalData,
4798
5291
  baseUpdatedAt,
4799
5292
  draftUpdatedAt: body.draftUpdatedAt
4800
5293
  });
5294
+ if (ctxUser) {
5295
+ sessionAuthAdapter?.createAuditLog({
5296
+ action: "document_update",
5297
+ userId: ctxUser.id,
5298
+ resource: slug,
5299
+ resourceId: id,
5300
+ success: true,
5301
+ metadata: { type: "draft_save" }
5302
+ });
5303
+ }
4801
5304
  return c.json({ data: draft, message: "Draft saved successfully" });
4802
5305
  } catch (error) {
4803
5306
  return c.json({ error: error.message }, 500);
@@ -4828,6 +5331,16 @@ function createHonoApp(options) {
4828
5331
  documentId: c.req.param("id"),
4829
5332
  tenantID: ctxTenantID
4830
5333
  });
5334
+ if (ctxUser) {
5335
+ sessionAuthAdapter?.createAuditLog({
5336
+ action: "document_update",
5337
+ userId: ctxUser.id,
5338
+ resource: slug,
5339
+ resourceId: c.req.param("id"),
5340
+ success: true,
5341
+ metadata: { type: "draft_discard" }
5342
+ });
5343
+ }
4831
5344
  return c.json({ message: "Draft discarded successfully" });
4832
5345
  } catch (error) {
4833
5346
  return c.json({ error: error.message }, 500);
@@ -4870,12 +5383,14 @@ function createHonoApp(options) {
4870
5383
  doc = await db.findOne({
4871
5384
  collection: slug,
4872
5385
  where: { slug: id },
4873
- tenantID: ctxTenantID
5386
+ tenantID: ctxTenantID,
5387
+ draft: isDraftRequest
4874
5388
  });
4875
5389
  }
4876
5390
  if (!doc) {
4877
5391
  return c.json({ error: "Document not found" }, 404);
4878
5392
  }
5393
+ await populateRelationships([doc], collection, db, registry);
4879
5394
  return c.json({ data: doc });
4880
5395
  } catch (error) {
4881
5396
  return c.json({ error: error.message }, 500);
@@ -4901,23 +5416,80 @@ function createHonoApp(options) {
4901
5416
  if (!access.allowed) {
4902
5417
  return c.json({ error: access.error }, access.status || 403);
4903
5418
  }
5419
+ if (ctxTenantID) {
5420
+ db.setTenantContext({ tenantId: ctxTenantID, userId: ctxUser?.id ?? "", role: ctxUser?.role, isSuperAdmin: ctxUser?.role === "super_admin" });
5421
+ }
4904
5422
  auditApiKeyUsage(sessionAuthAdapter, apiKeyContext, basePath, "POST", c.req.raw);
4905
5423
  const body = await c.req.json();
5424
+ let validated = body;
5425
+ for (const field of collection.fields) {
5426
+ if (field.name && validated[field.name] === "") {
5427
+ const isTextual = field.type === "text" || field.type === "textarea" || field.type === "code" || field.type === "markdown" || field.type === "email" || field.type === "password" || field.type === "color";
5428
+ if (!isTextual) {
5429
+ validated[field.name] = null;
5430
+ }
5431
+ }
5432
+ }
5433
+ convertRichtextFields(collection.fields, validated);
5434
+ const hookReq = c.req.raw;
5435
+ if (collection.hooks?.beforeValidate) {
5436
+ for (const hook of collection.hooks.beforeValidate) {
5437
+ const hookResult = await hook({
5438
+ collection: slug,
5439
+ data: validated,
5440
+ req: hookReq,
5441
+ user: ctxUser,
5442
+ tenantID: ctxTenantID,
5443
+ operation: "create"
5444
+ });
5445
+ if (hookResult) Object.assign(validated, hookResult);
5446
+ }
5447
+ }
4906
5448
  const schema = registry.getCreateZodSchema(slug);
4907
- let validated;
4908
5449
  try {
4909
- validated = schema.parse(body);
5450
+ validated = schema.parse(validated);
4910
5451
  } catch (zodErr) {
4911
- return c.json({ error: "Validation failed", details: zodErr.errors }, 400);
5452
+ return c.json({ error: `Validation failed: ${formatZodErrors(zodErr.errors)}`, details: zodErr.errors }, 400);
4912
5453
  }
4913
5454
  if (collection.tenantScoped && ctxTenantID) {
4914
5455
  validated.tenantID = ctxTenantID;
4915
5456
  }
5457
+ const isDraftEnabled = collection.versions?.drafts === true;
5458
+ if (isDraftEnabled) {
5459
+ validated.publishStatus = "draft";
5460
+ validated.hasDraft = false;
5461
+ }
5462
+ if (collection.hooks?.beforeChange) {
5463
+ for (const hook of collection.hooks.beforeChange) {
5464
+ const hookResult = await hook({
5465
+ collection: slug,
5466
+ data: validated,
5467
+ req: hookReq,
5468
+ user: ctxUser,
5469
+ tenantID: ctxTenantID,
5470
+ operation: "create"
5471
+ });
5472
+ if (hookResult) Object.assign(validated, hookResult);
5473
+ }
5474
+ }
4916
5475
  const doc = await db.create({
4917
5476
  collection: slug,
4918
5477
  data: validated,
4919
5478
  tenantID: ctxTenantID
4920
5479
  });
5480
+ if (collection.hooks?.afterChange) {
5481
+ for (const hook of collection.hooks.afterChange) {
5482
+ await hook({
5483
+ collection: slug,
5484
+ doc,
5485
+ data: validated,
5486
+ req: hookReq,
5487
+ user: ctxUser,
5488
+ tenantID: ctxTenantID,
5489
+ operation: "create"
5490
+ });
5491
+ }
5492
+ }
4921
5493
  if (webhookService) {
4922
5494
  webhookService.trigger(getWebhookEvent(slug, "create"), {
4923
5495
  collection: slug,
@@ -4927,11 +5499,20 @@ function createHonoApp(options) {
4927
5499
  tenantId: ctxTenantID
4928
5500
  }).catch((err) => console.error(`[Webhook] Failed to trigger:`, err));
4929
5501
  }
5502
+ if (ctxUser) {
5503
+ sessionAuthAdapter?.createAuditLog({
5504
+ action: "document_create",
5505
+ userId: ctxUser.id,
5506
+ resource: slug,
5507
+ resourceId: doc.id,
5508
+ success: true
5509
+ });
5510
+ }
4930
5511
  return c.json({ data: doc, message: "Created successfully" }, 201);
4931
5512
  } catch (error) {
4932
5513
  if (error.name === "ZodError") {
4933
5514
  return c.json(
4934
- { error: "Validation failed", details: error.errors },
5515
+ { error: `Validation failed: ${formatZodErrors(error.errors)}`, details: error.errors },
4935
5516
  400
4936
5517
  );
4937
5518
  }
@@ -4966,32 +5547,66 @@ function createHonoApp(options) {
4966
5547
  bodyKeys: Object.keys(body),
4967
5548
  tenantID: ctxTenantID
4968
5549
  });
4969
- const cleaned = Object.fromEntries(
4970
- Object.entries(omitRevisionFields(body)).filter(
4971
- ([_, v]) => v !== "null" && v !== void 0
4972
- )
4973
- );
4974
- const schema = registry.getUpdateZodSchema(slug);
4975
- const validated = schema.parse(cleaned);
4976
- console.log(`[PATCH] Validated data:`, Object.keys(validated));
4977
5550
  const originalDoc = await db.findByID({
4978
5551
  collection: slug,
4979
5552
  id,
4980
5553
  tenantID: ctxTenantID,
4981
5554
  draft: true
4982
- // Always fetch current doc regardless of status
4983
5555
  });
4984
- if (originalDoc) {
4985
- console.log(`[PATCH] Original doc updatedAt:`, originalDoc.updatedAt);
4986
- }
4987
5556
  if (!originalDoc) {
4988
5557
  return c.json({ error: "Document not found" }, 404);
4989
5558
  }
4990
5559
  if (baseUpdatedAt && originalDoc.updatedAt && baseUpdatedAt !== originalDoc.updatedAt) {
4991
5560
  return c.json(buildConflictResponse(baseUpdatedAt, originalDoc), 409);
4992
5561
  }
5562
+ let validated = Object.fromEntries(
5563
+ Object.entries(omitRevisionFields(body)).filter(
5564
+ ([_, v]) => v !== "null" && v !== void 0
5565
+ )
5566
+ );
5567
+ for (const field of collection.fields) {
5568
+ if (field.name && validated[field.name] === "") {
5569
+ const isTextual = field.type === "text" || field.type === "textarea" || field.type === "code" || field.type === "markdown" || field.type === "email" || field.type === "password" || field.type === "color";
5570
+ if (!isTextual) {
5571
+ validated[field.name] = null;
5572
+ }
5573
+ }
5574
+ }
5575
+ convertRichtextFields(collection.fields, validated);
5576
+ const hookReq = c.req.raw;
5577
+ if (collection.hooks?.beforeValidate) {
5578
+ for (const hook of collection.hooks.beforeValidate) {
5579
+ const hookResult = await hook({
5580
+ collection: slug,
5581
+ data: validated,
5582
+ originalDoc,
5583
+ req: hookReq,
5584
+ user: ctxUser,
5585
+ tenantID: ctxTenantID,
5586
+ operation: "update"
5587
+ });
5588
+ if (hookResult) Object.assign(validated, hookResult);
5589
+ }
5590
+ }
5591
+ const schema = registry.getUpdateZodSchema(slug);
5592
+ validated = schema.parse(validated);
5593
+ if (collection.hooks?.beforeChange) {
5594
+ for (const hook of collection.hooks.beforeChange) {
5595
+ const hookResult = await hook({
5596
+ collection: slug,
5597
+ data: validated,
5598
+ originalDoc,
5599
+ req: hookReq,
5600
+ user: ctxUser,
5601
+ tenantID: ctxTenantID,
5602
+ operation: "update"
5603
+ });
5604
+ if (hookResult) Object.assign(validated, hookResult);
5605
+ }
5606
+ }
5607
+ console.log(`[PATCH] Validated data:`, Object.keys(validated));
4993
5608
  const isDraftEnabled = collection.versions?.drafts === true;
4994
- const isAlreadyPublished = originalDoc._status === "published";
5609
+ const isAlreadyPublished = originalDoc.publishStatus === "published";
4995
5610
  let doc;
4996
5611
  if (isDraftEnabled && isAlreadyPublished) {
4997
5612
  await db.createVersion({
@@ -5006,18 +5621,20 @@ function createHonoApp(options) {
5006
5621
  await db.update({
5007
5622
  collection: slug,
5008
5623
  id,
5009
- data: { _has_draft: true },
5624
+ data: { hasDraft: true },
5010
5625
  // Keep old data, just set flag
5011
5626
  tenantID: ctxTenantID
5012
5627
  });
5013
5628
  } else {
5014
- const saveData = isDraftEnabled ? { ...validated, _status: "draft", _has_draft: false } : validated;
5629
+ const saveData = isDraftEnabled ? { ...validated, publishStatus: "draft", hasDraft: false } : validated;
5630
+ console.log(`[PATCH] About to call db.update for ${slug}/${id} with keys:`, Object.keys(saveData));
5015
5631
  await db.update({
5016
5632
  collection: slug,
5017
5633
  id,
5018
5634
  data: saveData,
5019
5635
  tenantID: ctxTenantID
5020
5636
  });
5637
+ console.log(`[PATCH] db.update SUCCEEDED for ${slug}/${id}`);
5021
5638
  }
5022
5639
  doc = await db.findByID({
5023
5640
  collection: slug,
@@ -5044,6 +5661,20 @@ function createHonoApp(options) {
5044
5661
  tenantID: ctxTenantID
5045
5662
  });
5046
5663
  }
5664
+ if (collection.hooks?.afterChange) {
5665
+ for (const hook of collection.hooks.afterChange) {
5666
+ await hook({
5667
+ collection: slug,
5668
+ doc,
5669
+ data: validated,
5670
+ originalDoc,
5671
+ req: hookReq,
5672
+ user: ctxUser,
5673
+ tenantID: ctxTenantID,
5674
+ operation: "update"
5675
+ });
5676
+ }
5677
+ }
5047
5678
  if (webhookService) {
5048
5679
  webhookService.trigger(getWebhookEvent(slug, "update"), {
5049
5680
  collection: slug,
@@ -5054,16 +5685,26 @@ function createHonoApp(options) {
5054
5685
  tenantId: ctxTenantID
5055
5686
  }).catch((err) => console.error(`[Webhook] Failed to trigger:`, err));
5056
5687
  }
5057
- console.log(`[PATCH] Result doc updatedAt:`, doc?.updatedAt);
5688
+ auditApiKeyUsage(sessionAuthAdapter, apiKeyContext, `${basePath}/${id}`, "PATCH", c.req.raw);
5689
+ if (ctxUser) {
5690
+ sessionAuthAdapter?.createAuditLog({
5691
+ action: "document_update",
5692
+ userId: ctxUser.id,
5693
+ resource: slug,
5694
+ resourceId: id,
5695
+ success: true
5696
+ });
5697
+ }
5058
5698
  return c.json({ data: doc, message: isDraftEnabled ? "Draft saved" : "Updated successfully" });
5059
5699
  } catch (error) {
5060
5700
  if (error.name === "ZodError") {
5061
5701
  console.error(`[PATCH ${basePath}/:id] Validation failed:`, error.errors);
5062
5702
  return c.json(
5063
- { error: "Validation failed", details: error.errors },
5703
+ { error: `Validation failed: ${formatZodErrors(error.errors)}`, details: error.errors },
5064
5704
  400
5065
5705
  );
5066
5706
  }
5707
+ console.error(`[PATCH ${basePath}/:id] ERROR:`, error.message, `CAUSE:`, error.cause?.message || error.cause, `QUERY:`, error.query);
5067
5708
  return c.json({ error: error.message }, 500);
5068
5709
  }
5069
5710
  });
@@ -5087,13 +5728,33 @@ function createHonoApp(options) {
5087
5728
  if (!access.allowed) {
5088
5729
  return c.json({ error: access.error }, access.status || 403);
5089
5730
  }
5731
+ if (ctxTenantID) {
5732
+ db.setTenantContext({ tenantId: ctxTenantID, userId: ctxUser?.id ?? "", role: ctxUser?.role, isSuperAdmin: ctxUser?.role === "super_admin" });
5733
+ }
5090
5734
  const id = c.req.param("id");
5091
5735
  console.log(`[DELETE] Deleting ${slug}/${id}`);
5736
+ const hookReq = c.req.raw;
5092
5737
  const originalDoc = await db.findByID({
5093
5738
  collection: slug,
5094
5739
  id,
5095
- tenantID: ctxTenantID
5740
+ tenantID: ctxTenantID,
5741
+ draft: true
5096
5742
  });
5743
+ if (!originalDoc) {
5744
+ return c.json({ error: "Document not found" }, 404);
5745
+ }
5746
+ if (collection.hooks?.beforeDelete) {
5747
+ for (const hook of collection.hooks.beforeDelete) {
5748
+ await hook({
5749
+ collection: slug,
5750
+ doc: originalDoc,
5751
+ req: hookReq,
5752
+ user: ctxUser,
5753
+ tenantID: ctxTenantID,
5754
+ operation: "delete"
5755
+ });
5756
+ }
5757
+ }
5097
5758
  const doc = await db.delete({
5098
5759
  collection: slug,
5099
5760
  id,
@@ -5104,6 +5765,19 @@ function createHonoApp(options) {
5104
5765
  documentId: id,
5105
5766
  tenantID: ctxTenantID
5106
5767
  });
5768
+ if (collection.hooks?.afterDelete) {
5769
+ for (const hook of collection.hooks.afterDelete) {
5770
+ await hook({
5771
+ collection: slug,
5772
+ doc,
5773
+ originalDoc,
5774
+ req: hookReq,
5775
+ user: ctxUser,
5776
+ tenantID: ctxTenantID,
5777
+ operation: "delete"
5778
+ });
5779
+ }
5780
+ }
5107
5781
  if (webhookService) {
5108
5782
  webhookService.trigger(getWebhookEvent(slug, "delete"), {
5109
5783
  collection: slug,
@@ -5114,6 +5788,16 @@ function createHonoApp(options) {
5114
5788
  tenantId: ctxTenantID
5115
5789
  }).catch((err) => console.error(`[Webhook] Failed to trigger:`, err));
5116
5790
  }
5791
+ auditApiKeyUsage(sessionAuthAdapter, apiKeyContext, `${basePath}/${id}`, "DELETE", c.req.raw);
5792
+ if (ctxUser) {
5793
+ sessionAuthAdapter?.createAuditLog({
5794
+ action: "document_delete",
5795
+ userId: ctxUser.id,
5796
+ resource: slug,
5797
+ resourceId: id,
5798
+ success: true
5799
+ });
5800
+ }
5117
5801
  return c.json({ data: doc, message: "Deleted successfully" });
5118
5802
  } catch (error) {
5119
5803
  console.error(`[DELETE] Error deleting ${slug}:`, error);
@@ -5147,7 +5831,8 @@ function createHonoApp(options) {
5147
5831
  const originalDoc = await db.findByID({
5148
5832
  collection: slug,
5149
5833
  id,
5150
- tenantID: ctxTenantID
5834
+ tenantID: ctxTenantID,
5835
+ draft: true
5151
5836
  });
5152
5837
  if (!originalDoc) {
5153
5838
  console.log("[Duplicate] Document not found");
@@ -5207,7 +5892,7 @@ function createHonoApp(options) {
5207
5892
  const doc = await db.update({
5208
5893
  collection: slug,
5209
5894
  id,
5210
- data: { ...version.data, _status: "draft", _has_draft: false },
5895
+ data: { ...version.data, publishStatus: "draft", hasDraft: false },
5211
5896
  tenantID: ctxTenantID
5212
5897
  });
5213
5898
  return c.json({
@@ -5252,7 +5937,7 @@ function createHonoApp(options) {
5252
5937
  const doc = await db.update({
5253
5938
  collection: slug,
5254
5939
  id: c.req.param("id"),
5255
- data: { ...version.data, _status: "draft", _has_draft: false },
5940
+ data: { ...version.data, publishStatus: "draft", hasDraft: false },
5256
5941
  tenantID: ctxTenantID
5257
5942
  });
5258
5943
  return c.json({
@@ -5285,6 +5970,9 @@ function createHonoApp(options) {
5285
5970
  if (!access.allowed) {
5286
5971
  return c.json({ error: access.error }, access.status || 403);
5287
5972
  }
5973
+ if (ctxTenantID) {
5974
+ db.setTenantContext({ tenantId: ctxTenantID, userId: ctxUser?.id ?? "", role: ctxUser?.role, isSuperAdmin: ctxUser?.role === "super_admin" });
5975
+ }
5288
5976
  const id = c.req.param("id");
5289
5977
  const body = await c.req.json().catch(() => ({}));
5290
5978
  const baseUpdatedAt = readBaseUpdatedAt(body);
@@ -5300,9 +5988,9 @@ function createHonoApp(options) {
5300
5988
  if (baseUpdatedAt && originalDoc.updatedAt && baseUpdatedAt !== originalDoc.updatedAt) {
5301
5989
  return c.json(buildConflictResponse(baseUpdatedAt, originalDoc), 409);
5302
5990
  }
5303
- let publishData = { _status: "published", _has_draft: false };
5991
+ let publishData = { publishStatus: "published", hasDraft: false };
5304
5992
  let finalContent = originalDoc;
5305
- if (originalDoc._has_draft) {
5993
+ if (originalDoc.hasDraft) {
5306
5994
  const versions = await db.findVersions({
5307
5995
  collection: slug,
5308
5996
  documentId: id,
@@ -5325,7 +6013,7 @@ function createHonoApp(options) {
5325
6013
  await db.createVersion({
5326
6014
  collection: slug,
5327
6015
  documentId: id,
5328
- data: { ...finalContent, _status: "published" },
6016
+ data: { ...finalContent, publishStatus: "published" },
5329
6017
  status: "published",
5330
6018
  createdBy: ctxUser?.id,
5331
6019
  changeDescription: "Published",
@@ -5384,7 +6072,7 @@ function createHonoApp(options) {
5384
6072
  const doc = await db.update({
5385
6073
  collection: slug,
5386
6074
  id,
5387
- data: { _status: "draft" },
6075
+ data: { publishStatus: "draft" },
5388
6076
  tenantID: ctxTenantID
5389
6077
  });
5390
6078
  if (webhookService) {
@@ -5420,12 +6108,30 @@ function createHonoApp(options) {
5420
6108
  return c.json({ error: access.error }, access.status || 403);
5421
6109
  }
5422
6110
  const isDraftRequest = c.req.query("draft") === "true" && !!ctxUser;
5423
- const doc = await db.findOne({
6111
+ let doc = await db.findOne({
5424
6112
  collection: `_globals_${slug}`,
5425
6113
  where: {},
5426
6114
  tenantID: ctxTenantID,
5427
6115
  draft: isDraftRequest
5428
6116
  });
6117
+ if (slug === "system") {
6118
+ const newSecret = crypto__default.default.randomBytes(32).toString("hex");
6119
+ if (!doc) {
6120
+ doc = await db.create({
6121
+ collection: `_globals_${slug}`,
6122
+ data: { id: slug, appSecret: newSecret },
6123
+ tenantID: ctxTenantID
6124
+ });
6125
+ } else if (!doc.appSecret) {
6126
+ await db.update({
6127
+ collection: `_globals_${slug}`,
6128
+ id: slug,
6129
+ data: { appSecret: newSecret },
6130
+ tenantID: ctxTenantID
6131
+ });
6132
+ doc.appSecret = newSecret;
6133
+ }
6134
+ }
5429
6135
  return c.json({ data: doc || {} });
5430
6136
  } catch (error) {
5431
6137
  return c.json({ error: error.message }, 500);
@@ -5445,18 +6151,27 @@ function createHonoApp(options) {
5445
6151
  if (!access.allowed) {
5446
6152
  return c.json({ error: access.error }, access.status || 403);
5447
6153
  }
5448
- const body = await c.req.json();
6154
+ const body = omitRevisionFields(await c.req.json());
5449
6155
  const cleaned = Object.fromEntries(
5450
6156
  Object.entries(body).filter(([_, v]) => v !== null && v !== "null" && v !== void 0)
5451
6157
  );
6158
+ for (const field of globalConfig.fields) {
6159
+ if (field.name && cleaned[field.name] === "") {
6160
+ const isTextual = field.type === "text" || field.type === "textarea" || field.type === "code" || field.type === "markdown" || field.type === "email" || field.type === "password" || field.type === "color";
6161
+ if (!isTextual) {
6162
+ cleaned[field.name] = null;
6163
+ }
6164
+ }
6165
+ }
6166
+ convertRichtextFields(globalConfig.fields, cleaned);
5452
6167
  const schema = registry.getZodSchema(slug);
5453
6168
  let validated;
5454
6169
  try {
5455
6170
  validated = schema.parse(cleaned);
5456
6171
  } catch (zodErr) {
5457
- return c.json({ error: "Validation failed", details: zodErr.errors }, 400);
6172
+ return c.json({ error: `Validation failed: ${formatZodErrors(zodErr.errors)}`, details: zodErr.errors }, 400);
5458
6173
  }
5459
- const SYSTEM_FIELDS = /* @__PURE__ */ new Set(["id", "createdAt", "updatedAt", "_status", "_has_draft"]);
6174
+ const SYSTEM_FIELDS = /* @__PURE__ */ new Set(["id", "createdAt", "updatedAt", "publishStatus", "hasDraft", "baseUpdatedAt", "_baseUpdatedAt"]);
5460
6175
  const userData = Object.fromEntries(
5461
6176
  Object.entries(validated).filter(([k]) => !SYSTEM_FIELDS.has(k))
5462
6177
  );
@@ -5468,7 +6183,7 @@ function createHonoApp(options) {
5468
6183
  draft: true
5469
6184
  });
5470
6185
  const isDraftEnabled = globalConfig.versions?.drafts === true;
5471
- const isAlreadyPublished = originalDoc?._status === "published";
6186
+ const isAlreadyPublished = originalDoc?.publishStatus === "published";
5472
6187
  let doc;
5473
6188
  if (isDraftEnabled && isAlreadyPublished) {
5474
6189
  await db.createVersion({
@@ -5483,11 +6198,11 @@ function createHonoApp(options) {
5483
6198
  doc = await db.update({
5484
6199
  collection: collectionSlug,
5485
6200
  id: slug,
5486
- data: { _has_draft: true },
6201
+ data: { hasDraft: true },
5487
6202
  tenantID: ctxTenantID
5488
6203
  });
5489
6204
  } else {
5490
- const saveData = isDraftEnabled ? { ...userData, _status: "draft", _has_draft: false } : { ...userData, _status: "published", _has_draft: false };
6205
+ const saveData = isDraftEnabled ? { ...userData, publishStatus: "draft", hasDraft: false } : { ...userData, publishStatus: "published", hasDraft: false };
5491
6206
  if (originalDoc) {
5492
6207
  doc = await db.update({
5493
6208
  collection: collectionSlug,
@@ -5518,6 +6233,15 @@ function createHonoApp(options) {
5518
6233
  mediaService = null;
5519
6234
  mediaServiceInitError = null;
5520
6235
  }
6236
+ if (ctxUser) {
6237
+ sessionAuthAdapter?.createAuditLog({
6238
+ action: "settings_change",
6239
+ userId: ctxUser.id,
6240
+ resource: `global:${slug}`,
6241
+ resourceId: slug,
6242
+ success: true
6243
+ });
6244
+ }
5521
6245
  return c.json({ data: doc, message: "Updated successfully" });
5522
6246
  } catch (error) {
5523
6247
  console.error(`[API] Save global "${slug}" failed:`, error);
@@ -5542,9 +6266,9 @@ function createHonoApp(options) {
5542
6266
  draft: true
5543
6267
  });
5544
6268
  if (!originalDoc) return c.json({ error: "Global not found" }, 404);
5545
- let publishData = { _status: "published", _has_draft: false };
6269
+ let publishData = { publishStatus: "published", hasDraft: false };
5546
6270
  let finalContent = originalDoc;
5547
- if (originalDoc._has_draft) {
6271
+ if (originalDoc.hasDraft) {
5548
6272
  const versions = await db.findVersions({
5549
6273
  collection: collectionSlug,
5550
6274
  documentId: slug,
@@ -5567,7 +6291,7 @@ function createHonoApp(options) {
5567
6291
  await db.createVersion({
5568
6292
  collection: collectionSlug,
5569
6293
  documentId: slug,
5570
- data: { ...finalContent, _status: "published" },
6294
+ data: { ...finalContent, publishStatus: "published" },
5571
6295
  status: "published",
5572
6296
  createdBy: ctxUser?.id,
5573
6297
  changeDescription: "Published",
@@ -5587,7 +6311,7 @@ function createHonoApp(options) {
5587
6311
  const doc = await db.update({
5588
6312
  collection: `_globals_${slug}`,
5589
6313
  id: slug,
5590
- data: { _status: "draft", _has_draft: false },
6314
+ data: { publishStatus: "draft", hasDraft: false },
5591
6315
  tenantID: ctxTenantID
5592
6316
  });
5593
6317
  return c.json({ data: doc, message: "Unpublished successfully" });
@@ -5647,7 +6371,7 @@ function createHonoApp(options) {
5647
6371
  const doc = await db.update({
5648
6372
  collection: collectionSlug,
5649
6373
  id: slug,
5650
- data: { ...version.data, _status: "draft", _has_draft: false },
6374
+ data: { ...version.data, publishStatus: "draft", hasDraft: false },
5651
6375
  tenantID: ctxTenantID
5652
6376
  });
5653
6377
  return c.json({ data: doc, message: "Restored successfully" });
@@ -5693,7 +6417,7 @@ function createHonoApp(options) {
5693
6417
  mailgun: body.mailgun,
5694
6418
  ses: body.ses
5695
6419
  };
5696
- const transport = new chunkIA6AU5PI_cjs.EmailTransport(transportConfig);
6420
+ const transport = new chunkWNCYAKF3_cjs.EmailTransport(transportConfig);
5697
6421
  const recipient = body.testEmail || body.testEmailSection && body.testEmailSection.testEmail;
5698
6422
  if (!recipient) {
5699
6423
  return c.json({ error: "No test recipient email provided" }, 400);
@@ -5757,15 +6481,19 @@ exports.InMemoryAuditLogger = InMemoryAuditLogger;
5757
6481
  exports.InMemoryRateLimiter = InMemoryRateLimiter;
5758
6482
  exports.MediaService = MediaService;
5759
6483
  exports.createAuditContext = createAuditContext2;
6484
+ exports.createCloudinaryStorage = createCloudinaryStorage;
6485
+ exports.createFtpStorage = createFtpStorage;
5760
6486
  exports.createHonoApp = createHonoApp;
5761
6487
  exports.createLocalStorage = createLocalStorage;
5762
6488
  exports.createRESTAPI = createRESTAPI;
6489
+ exports.createS3Storage = createS3Storage;
5763
6490
  exports.getAppSecret = getAppSecret;
6491
+ exports.getDefaultRegistry = getDefaultRegistry;
5764
6492
  exports.getEncryptionKey = getEncryptionKey;
5765
6493
  exports.getSessionConfig = getSessionConfig;
5766
6494
  exports.init_secret = init_secret;
5767
6495
  exports.loadSecrets = loadSecrets;
5768
6496
  exports.resolveProvider = resolveProvider;
5769
6497
  exports.setDbAdapter = setDbAdapter;
5770
- //# sourceMappingURL=chunk-5KVM3WEY.cjs.map
5771
- //# sourceMappingURL=chunk-5KVM3WEY.cjs.map
6498
+ //# sourceMappingURL=chunk-RSF3UU7H.cjs.map
6499
+ //# sourceMappingURL=chunk-RSF3UU7H.cjs.map