@beechcms/api 0.6.0-preview.1 → 0.6.0-preview.3

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 (373) hide show
  1. package/README.md +4 -4
  2. package/assets/dashboard/BeechLogo.svg +18 -18
  3. package/assets/dashboard/BeechLogoLIght.svg +48 -48
  4. package/assets/dashboard/assets/chart-Cws4Yj6K.js +39 -0
  5. package/assets/dashboard/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  6. package/assets/dashboard/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  7. package/assets/dashboard/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  8. package/assets/dashboard/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  9. package/assets/dashboard/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  10. package/assets/dashboard/assets/index-BjPpppHe.css +1 -0
  11. package/assets/dashboard/assets/index-CrW-zhkA.js +642 -0
  12. package/assets/dashboard/assets/lora-cyrillic-ext-wght-normal-YcVxMP-u.woff2 +0 -0
  13. package/assets/dashboard/assets/lora-cyrillic-wght-normal-UWPuZLjM.woff2 +0 -0
  14. package/assets/dashboard/assets/lora-latin-ext-wght-normal-C2Wlntb9.woff2 +0 -0
  15. package/assets/dashboard/assets/lora-latin-wght-normal-BiLcIKcI.woff2 +0 -0
  16. package/assets/dashboard/assets/lora-math-wght-normal-sOy7rEfW.woff2 +0 -0
  17. package/assets/dashboard/assets/lora-symbols-wght-normal-DQ5VrUkH.woff2 +0 -0
  18. package/assets/dashboard/assets/lora-vietnamese-wght-normal-CfJ7gtf3.woff2 +0 -0
  19. package/assets/dashboard/assets/pie-chart-recharts-CAxoDQp2.js +1 -0
  20. package/assets/dashboard/assets/timeseries-chart-recharts-CixDW-LM.js +1 -0
  21. package/assets/dashboard/beechLogoDark.svg +48 -48
  22. package/assets/dashboard/index.html +18 -18
  23. package/assets/dashboard/noResult.svg +42 -42
  24. package/assets/dashboard/sol.svg +3 -3
  25. package/assets/dashboard/undraw_enter_nwx3.svg +36 -36
  26. package/dist/auth/__fixtures__/in-memory-hash-provider.d.ts +5 -0
  27. package/dist/auth/__fixtures__/static-token-service.d.ts +6 -0
  28. package/dist/auth/bcrypt-hash-provider.d.ts +8 -0
  29. package/dist/auth/constants.d.ts +10 -0
  30. package/dist/auth/jose-token-service.d.ts +14 -0
  31. package/dist/auth/login.d.ts +32 -0
  32. package/dist/auth/refresh.d.ts +1 -0
  33. package/dist/factory.d.ts +39 -0
  34. package/dist/features/automations/action-executors/create-entry.executor.d.ts +7 -0
  35. package/dist/features/automations/action-executors/edit-field.executor.d.ts +7 -0
  36. package/dist/features/automations/action-executors/index.d.ts +13 -0
  37. package/dist/features/automations/action-executors/send-mail.executor.d.ts +7 -0
  38. package/dist/features/automations/action-executors/set-variable.executor.d.ts +15 -0
  39. package/dist/features/automations/action-executors/webhook.executor.d.ts +7 -0
  40. package/dist/features/automations/automation-runner.d.ts +13 -0
  41. package/dist/features/automations/automation-runner.utils.d.ts +3 -0
  42. package/dist/features/automations/automations.handler.d.ts +7 -0
  43. package/dist/features/automations/automations.schema.d.ts +191 -0
  44. package/dist/features/automations/context-resolver.d.ts +8 -0
  45. package/dist/features/automations/cron-runner.d.ts +9 -0
  46. package/dist/features/automations/cron-runner.utils.d.ts +1 -0
  47. package/dist/features/automations/filter-translation.d.ts +9 -0
  48. package/dist/features/automations/index.d.ts +7 -0
  49. package/dist/features/automations/template-grammar.d.ts +73 -0
  50. package/dist/features/automations/var-access-resolver.d.ts +6 -0
  51. package/dist/features/automations/when-evaluator.d.ts +3 -0
  52. package/dist/features/automations/when-pushdown.d.ts +2 -0
  53. package/dist/features/backrefs/backrefs.handler.d.ts +3 -0
  54. package/dist/features/backrefs/d1-backref.repository.d.ts +17 -0
  55. package/dist/features/backrefs/index.d.ts +1 -0
  56. package/dist/features/content/constants.d.ts +12 -0
  57. package/dist/features/content/handlers/bulk.handler.d.ts +3 -0
  58. package/dist/features/content/handlers/create.d.ts +3 -0
  59. package/dist/features/content/handlers/delete.d.ts +3 -0
  60. package/dist/features/content/handlers/facets.d.ts +3 -0
  61. package/dist/features/content/handlers/get.d.ts +4 -0
  62. package/dist/features/content/handlers/helpers.d.ts +12 -0
  63. package/dist/features/content/handlers/list.d.ts +3 -0
  64. package/dist/features/content/handlers/update.d.ts +3 -0
  65. package/dist/features/content/index.d.ts +4 -0
  66. package/dist/features/dashboard-layout/dashboard-layout.handler.d.ts +7 -0
  67. package/dist/features/dashboard-layout/index.d.ts +1 -0
  68. package/dist/features/draft/draft.handler.d.ts +4 -0
  69. package/dist/features/draft/draft.middleware.d.ts +9 -0
  70. package/dist/features/draft/index.d.ts +1 -0
  71. package/{src/features/email/email.provider.ts → dist/features/email/email.provider.d.ts} +37 -42
  72. package/dist/features/email/email.service.d.ts +13 -0
  73. package/dist/features/email/email.types.d.ts +94 -0
  74. package/{src/features/email/index.ts → dist/features/email/index.d.ts} +20 -33
  75. package/dist/features/email/providers/resend.d.ts +36 -0
  76. package/dist/features/email/providers/smtp.d.ts +11 -0
  77. package/dist/features/email/templates/automation-mail.d.ts +8 -0
  78. package/dist/features/email/templates/password-changed.d.ts +15 -0
  79. package/dist/features/email/templates/password-reset.d.ts +18 -0
  80. package/dist/features/email/templates/shell.d.ts +58 -0
  81. package/dist/features/notifications/index.d.ts +1 -0
  82. package/dist/features/notifications/notifications.handler.d.ts +15 -0
  83. package/dist/features/password-reset/index.d.ts +6 -0
  84. package/dist/features/password-reset/request.d.ts +10 -0
  85. package/dist/features/password-reset/reset.d.ts +9 -0
  86. package/dist/features/rotate-field/index.d.ts +1 -0
  87. package/dist/features/rotate-field/rotate-field.handler.d.ts +7 -0
  88. package/dist/features/rotate-field/rotate-field.schema.d.ts +11 -0
  89. package/dist/features/schema/schema.handler.d.ts +7 -0
  90. package/dist/features/seeds/index.d.ts +1 -0
  91. package/dist/features/seeds/seeds.handler.d.ts +6 -0
  92. package/dist/features/settings/settings.handler.d.ts +7 -0
  93. package/dist/features/setup/index.d.ts +7 -0
  94. package/dist/features/stats/index.d.ts +1 -0
  95. package/dist/features/stats/stats.handler.d.ts +7 -0
  96. package/dist/features/webhooks/index.d.ts +3 -0
  97. package/dist/index.d.ts +6 -0
  98. package/dist/index.js +9666 -0
  99. package/dist/media-utils.d.ts +25 -0
  100. package/dist/middleware/auth-providers.middleware.d.ts +8 -0
  101. package/dist/middleware/observability.middleware.d.ts +20 -0
  102. package/dist/middleware/rate-limit.middleware.d.ts +9 -0
  103. package/dist/middleware/repository.middleware.d.ts +34 -0
  104. package/dist/middleware/seed-registry.middleware.d.ts +10 -0
  105. package/dist/middleware/storage.middleware.d.ts +7 -0
  106. package/dist/middleware.d.ts +11 -0
  107. package/dist/public/access-policy.d.ts +12 -0
  108. package/dist/public/api-key-middleware.d.ts +6 -0
  109. package/dist/public/cache-utils.d.ts +10 -0
  110. package/dist/public/entry-projection.d.ts +2 -0
  111. package/dist/public/idempotency.d.ts +9 -0
  112. package/dist/public/index.d.ts +11 -0
  113. package/dist/public/problem-details.d.ts +31 -0
  114. package/dist/public/public-add.d.ts +3 -0
  115. package/dist/public/public-edit.d.ts +5 -0
  116. package/dist/public/public-errors.d.ts +13 -0
  117. package/dist/public/public-read.d.ts +3 -0
  118. package/dist/public/public-routes.d.ts +3 -0
  119. package/dist/public/query-builder.d.ts +28 -0
  120. package/dist/public/rate-limit-middleware.d.ts +3 -0
  121. package/dist/public/read-list.d.ts +26 -0
  122. package/dist/public/read-single.d.ts +22 -0
  123. package/dist/public/response-builder.d.ts +22 -0
  124. package/dist/public/sanitize.d.ts +23 -0
  125. package/dist/public/slug-utils.d.ts +11 -0
  126. package/dist/rate-limit/cloudflare-rate-limiter.d.ts +6 -0
  127. package/dist/rate-limit/in-memory-rate-limiter.d.ts +7 -0
  128. package/dist/rate-limit/no-op-rate-limiter.d.ts +4 -0
  129. package/dist/search-utils.d.ts +54 -0
  130. package/dist/search.d.ts +6 -0
  131. package/dist/shared/apply-policies.d.ts +10 -0
  132. package/dist/shared/automations.repository.d1.d.ts +12 -0
  133. package/dist/shared/background-notification-service.d.ts +18 -0
  134. package/dist/shared/base.repository.d1.d.ts +18 -0
  135. package/dist/shared/content-utils.d.ts +21 -0
  136. package/dist/shared/content.repository.d1.d.ts +68 -0
  137. package/dist/shared/d1-activity-log.repository.d.ts +13 -0
  138. package/dist/shared/d1-activity-logger.d.ts +20 -0
  139. package/dist/shared/d1-analytics.repository.d.ts +20 -0
  140. package/dist/shared/d1-content-scan.repository.d.ts +7 -0
  141. package/dist/shared/d1-notification.repository.d.ts +17 -0
  142. package/dist/shared/d1-password-reset-token.repository.d.ts +10 -0
  143. package/dist/shared/d1-search.repository.d.ts +14 -0
  144. package/dist/shared/d1-session.repository.d.ts +12 -0
  145. package/dist/shared/d1-setup-checklist.repository.d.ts +12 -0
  146. package/dist/shared/d1-user.repository.d.ts +18 -0
  147. package/dist/shared/d1-widget.repository.d.ts +31 -0
  148. package/dist/shared/dashboard-layout.repository.d1.d.ts +9 -0
  149. package/dist/shared/demo-data-sql.d.ts +1 -0
  150. package/dist/shared/demo-data.repository.d1.d.ts +6 -0
  151. package/dist/shared/execution-context-scheduler.d.ts +6 -0
  152. package/dist/shared/fixed-clock.d.ts +13 -0
  153. package/dist/shared/fts-sync.d.ts +1 -0
  154. package/dist/shared/idempotency.repository.d1.d.ts +16 -0
  155. package/dist/shared/in-memory-activity-logger.d.ts +11 -0
  156. package/dist/shared/in-memory-notification-service.d.ts +11 -0
  157. package/dist/shared/in-memory-seed.repository.d.ts +18 -0
  158. package/dist/shared/media.repository.d1.d.ts +35 -0
  159. package/dist/shared/qstash-notification-service.d.ts +18 -0
  160. package/dist/shared/query-utils.d.ts +36 -0
  161. package/dist/shared/request-utils.d.ts +13 -0
  162. package/dist/shared/schema-mutator.d1.d.ts +12 -0
  163. package/dist/shared/seed-layout.repository.d1.d.ts +9 -0
  164. package/dist/shared/seed-registry-cache.d.ts +12 -0
  165. package/dist/shared/seed.repository.d1.d.ts +13 -0
  166. package/dist/shared/sequential-id-generator.d.ts +13 -0
  167. package/dist/shared/site-settings.repository.d1.d.ts +7 -0
  168. package/dist/shared/storage/factory.d.ts +8 -0
  169. package/dist/shared/storage/s3-bucket.d.ts +43 -0
  170. package/dist/shared/storage-utils.d.ts +7 -0
  171. package/dist/shared/system-stats.repository.d1.d.ts +25 -0
  172. package/dist/types.d.ts +85 -0
  173. package/dist/upload.d.ts +16 -0
  174. package/dist/widget.d.ts +7 -0
  175. package/migrations/0000_v040_base.sql +265 -265
  176. package/migrations/0029_automations.sql +14 -14
  177. package/migrations/0030_test_seeds.sql +125 -125
  178. package/package.json +27 -18
  179. package/assets/dashboard/assets/index-BSjk0Sx9.js +0 -634
  180. package/assets/dashboard/assets/index-kRCYVN2B.css +0 -1
  181. package/src/auth/__fixtures__/in-memory-hash-provider.ts +0 -17
  182. package/src/auth/__fixtures__/static-token-service.ts +0 -22
  183. package/src/auth/bcrypt-hash-provider.ts +0 -24
  184. package/src/auth/constants.ts +0 -14
  185. package/src/auth/generate-refresh-token.test.ts +0 -23
  186. package/src/auth/hash-provider.test.ts +0 -55
  187. package/src/auth/jose-token-service.ts +0 -62
  188. package/src/auth/jwt-claims-passthrough.test.ts +0 -87
  189. package/src/auth/login.test.ts +0 -100
  190. package/src/auth/login.ts +0 -78
  191. package/src/auth/refresh.ts +0 -11
  192. package/src/auth/token-service.test.ts +0 -90
  193. package/src/factory.ts +0 -368
  194. package/src/features/automations/__tests__/action-executors.test.ts +0 -255
  195. package/src/features/automations/__tests__/automation-runner.test.ts +0 -196
  196. package/src/features/automations/__tests__/automation-runner.utils.test.ts +0 -60
  197. package/src/features/automations/__tests__/automations.handler.test.ts +0 -264
  198. package/src/features/automations/__tests__/automations.repository.test.ts +0 -163
  199. package/src/features/automations/__tests__/automations.schema.test.ts +0 -224
  200. package/src/features/automations/__tests__/context-resolver.test.ts +0 -126
  201. package/src/features/automations/__tests__/cron-runner.test.ts +0 -267
  202. package/src/features/automations/__tests__/cron-runner.utils.test.ts +0 -144
  203. package/src/features/automations/__tests__/set-variable.executor.test.ts +0 -383
  204. package/src/features/automations/__tests__/template-grammar.test.ts +0 -308
  205. package/src/features/automations/__tests__/webhook.executor.test.ts +0 -142
  206. package/src/features/automations/__tests__/when-evaluator.test.ts +0 -274
  207. package/src/features/automations/__tests__/when-pushdown.test.ts +0 -281
  208. package/src/features/automations/action-executors/create-entry.executor.ts +0 -27
  209. package/src/features/automations/action-executors/edit-field.executor.ts +0 -26
  210. package/src/features/automations/action-executors/index.ts +0 -37
  211. package/src/features/automations/action-executors/send-mail.executor.ts +0 -53
  212. package/src/features/automations/action-executors/set-variable.executor.ts +0 -159
  213. package/src/features/automations/action-executors/webhook.executor.ts +0 -67
  214. package/src/features/automations/automation-runner.ts +0 -85
  215. package/src/features/automations/automation-runner.utils.ts +0 -47
  216. package/src/features/automations/automations.handler.ts +0 -197
  217. package/src/features/automations/automations.schema.ts +0 -177
  218. package/src/features/automations/context-resolver.ts +0 -152
  219. package/src/features/automations/cron-runner.ts +0 -140
  220. package/src/features/automations/cron-runner.utils.ts +0 -44
  221. package/src/features/automations/filter-translation.ts +0 -46
  222. package/src/features/automations/index.ts +0 -16
  223. package/src/features/automations/template-grammar.ts +0 -245
  224. package/src/features/automations/var-access-resolver.ts +0 -140
  225. package/src/features/automations/when-evaluator.ts +0 -87
  226. package/src/features/automations/when-pushdown.ts +0 -57
  227. package/src/features/backrefs/__tests__/backrefs.handler.test.ts +0 -359
  228. package/src/features/backrefs/backrefs.handler.ts +0 -168
  229. package/src/features/backrefs/d1-backref.repository.ts +0 -84
  230. package/src/features/backrefs/index.ts +0 -5
  231. package/src/features/content/constants.ts +0 -16
  232. package/src/features/content/handlers/bulk.handler.ts +0 -185
  233. package/src/features/content/handlers/create.ts +0 -129
  234. package/src/features/content/handlers/delete.ts +0 -62
  235. package/src/features/content/handlers/facets.ts +0 -49
  236. package/src/features/content/handlers/get.ts +0 -120
  237. package/src/features/content/handlers/helpers.test.ts +0 -180
  238. package/src/features/content/handlers/helpers.ts +0 -93
  239. package/src/features/content/handlers/list.ts +0 -174
  240. package/src/features/content/handlers/update.ts +0 -174
  241. package/src/features/content/index.ts +0 -26
  242. package/src/features/draft/draft.handler.ts +0 -195
  243. package/src/features/draft/draft.middleware.ts +0 -66
  244. package/src/features/draft/index.ts +0 -5
  245. package/src/features/email/email.service.ts +0 -98
  246. package/src/features/email/email.types.ts +0 -119
  247. package/src/features/email/providers/resend.ts +0 -67
  248. package/src/features/email/providers/smtp.ts +0 -55
  249. package/src/features/email/templates/automation-mail.ts +0 -19
  250. package/src/features/email/templates/password-changed.ts +0 -63
  251. package/src/features/email/templates/password-reset.ts +0 -68
  252. package/src/features/email/templates/shell.ts +0 -96
  253. package/src/features/notifications/index.ts +0 -5
  254. package/src/features/notifications/notifications.handler.ts +0 -105
  255. package/src/features/password-reset/index.ts +0 -19
  256. package/src/features/password-reset/request.ts +0 -92
  257. package/src/features/password-reset/reset.ts +0 -102
  258. package/src/features/rotate-field/index.ts +0 -5
  259. package/src/features/rotate-field/rotate-field.handler.ts +0 -132
  260. package/src/features/rotate-field/rotate-field.schema.ts +0 -17
  261. package/src/features/schema/schema.handler.ts +0 -131
  262. package/src/features/seeds/index.ts +0 -5
  263. package/src/features/seeds/seeds.handler.test.ts +0 -632
  264. package/src/features/seeds/seeds.handler.ts +0 -693
  265. package/src/features/settings/__tests__/settings.handler.test.ts +0 -555
  266. package/src/features/settings/settings.handler.ts +0 -392
  267. package/src/features/setup/index.ts +0 -204
  268. package/src/features/stats/index.ts +0 -5
  269. package/src/features/stats/stats.handler.ts +0 -420
  270. package/src/features/webhooks/index.ts +0 -63
  271. package/src/index.ts +0 -65
  272. package/src/media-utils.ts +0 -82
  273. package/src/middleware/auth-providers.middleware.ts +0 -45
  274. package/src/middleware/observability.middleware.ts +0 -70
  275. package/src/middleware/rate-limit.middleware.ts +0 -45
  276. package/src/middleware/repository.middleware.ts +0 -114
  277. package/src/middleware/seed-registry.middleware.test.ts +0 -97
  278. package/src/middleware/seed-registry.middleware.ts +0 -21
  279. package/src/middleware/storage.middleware.ts +0 -25
  280. package/src/middleware.ts +0 -45
  281. package/src/public/access-policy.ts +0 -27
  282. package/src/public/api-key-middleware.ts +0 -57
  283. package/src/public/cache-utils.ts +0 -38
  284. package/src/public/entry-projection.ts +0 -46
  285. package/src/public/idempotency.ts +0 -23
  286. package/src/public/index.ts +0 -16
  287. package/src/public/problem-details.ts +0 -124
  288. package/src/public/public-add.ts +0 -114
  289. package/src/public/public-edit.ts +0 -163
  290. package/src/public/public-errors.ts +0 -19
  291. package/src/public/public-read.ts +0 -63
  292. package/src/public/public-routes.ts +0 -88
  293. package/src/public/query-builder.test.ts +0 -224
  294. package/src/public/query-builder.ts +0 -156
  295. package/src/public/rate-limit-middleware.ts +0 -34
  296. package/src/public/read-list.ts +0 -54
  297. package/src/public/read-single.ts +0 -48
  298. package/src/public/response-builder.ts +0 -30
  299. package/src/public/sanitize.ts +0 -69
  300. package/src/public/slug-utils.ts +0 -18
  301. package/src/rate-limit/cloudflare-rate-limiter.test.ts +0 -30
  302. package/src/rate-limit/cloudflare-rate-limiter.ts +0 -15
  303. package/src/rate-limit/in-memory-rate-limiter.test.ts +0 -37
  304. package/src/rate-limit/in-memory-rate-limiter.ts +0 -17
  305. package/src/rate-limit/no-op-rate-limiter.test.ts +0 -22
  306. package/src/rate-limit/no-op-rate-limiter.ts +0 -11
  307. package/src/search-utils.test.ts +0 -211
  308. package/src/search-utils.ts +0 -213
  309. package/src/search.ts +0 -65
  310. package/src/shared/apply-policies.test.ts +0 -81
  311. package/src/shared/apply-policies.ts +0 -67
  312. package/src/shared/automations.repository.d1.ts +0 -150
  313. package/src/shared/background-notification-service.test.ts +0 -62
  314. package/src/shared/background-notification-service.ts +0 -52
  315. package/src/shared/base.repository.d1.ts +0 -32
  316. package/src/shared/content-utils.test.ts +0 -165
  317. package/src/shared/content-utils.ts +0 -86
  318. package/src/shared/content.repository.d1.test.ts +0 -458
  319. package/src/shared/content.repository.d1.ts +0 -851
  320. package/src/shared/d1-activity-log.repository.test.ts +0 -140
  321. package/src/shared/d1-activity-log.repository.ts +0 -105
  322. package/src/shared/d1-activity-logger.test.ts +0 -86
  323. package/src/shared/d1-activity-logger.ts +0 -67
  324. package/src/shared/d1-analytics.repository.test.ts +0 -78
  325. package/src/shared/d1-analytics.repository.ts +0 -85
  326. package/src/shared/d1-content-scan.repository.test.ts +0 -80
  327. package/src/shared/d1-content-scan.repository.ts +0 -37
  328. package/src/shared/d1-notification.repository.test.ts +0 -128
  329. package/src/shared/d1-notification.repository.ts +0 -118
  330. package/src/shared/d1-password-reset-token.repository.test.ts +0 -81
  331. package/src/shared/d1-password-reset-token.repository.ts +0 -56
  332. package/src/shared/d1-search.repository.test.ts +0 -87
  333. package/src/shared/d1-search.repository.ts +0 -88
  334. package/src/shared/d1-session.repository.test.ts +0 -125
  335. package/src/shared/d1-session.repository.ts +0 -102
  336. package/src/shared/d1-setup-checklist.repository.ts +0 -36
  337. package/src/shared/d1-user.repository.test.ts +0 -151
  338. package/src/shared/d1-user.repository.ts +0 -119
  339. package/src/shared/d1-widget.repository.test.ts +0 -221
  340. package/src/shared/d1-widget.repository.ts +0 -341
  341. package/src/shared/demo-data-sql.ts +0 -54
  342. package/src/shared/demo-data.repository.d1.ts +0 -20
  343. package/src/shared/execution-context-scheduler.ts +0 -13
  344. package/src/shared/fixed-clock.ts +0 -25
  345. package/src/shared/fts-sync.ts +0 -8
  346. package/src/shared/idempotency.repository.d1.test.ts +0 -83
  347. package/src/shared/idempotency.repository.d1.ts +0 -60
  348. package/src/shared/in-memory-activity-logger.ts +0 -19
  349. package/src/shared/in-memory-notification-service.ts +0 -19
  350. package/src/shared/in-memory-seed.repository.ts +0 -51
  351. package/src/shared/media.repository.d1.test.ts +0 -107
  352. package/src/shared/media.repository.d1.ts +0 -68
  353. package/src/shared/qstash-notification-service.test.ts +0 -67
  354. package/src/shared/qstash-notification-service.ts +0 -55
  355. package/src/shared/query-utils.ts +0 -141
  356. package/src/shared/request-utils.ts +0 -26
  357. package/src/shared/schema-mutator.d1.ts +0 -64
  358. package/src/shared/seed-layout.repository.d1.test.ts +0 -114
  359. package/src/shared/seed-layout.repository.d1.ts +0 -62
  360. package/src/shared/seed-registry-cache.test.ts +0 -68
  361. package/src/shared/seed-registry-cache.ts +0 -49
  362. package/src/shared/seed.repository.d1.test.ts +0 -143
  363. package/src/shared/seed.repository.d1.ts +0 -98
  364. package/src/shared/sequential-id-generator.ts +0 -33
  365. package/src/shared/site-settings.repository.d1.ts +0 -53
  366. package/src/shared/storage/factory.ts +0 -41
  367. package/src/shared/storage/s3-bucket.ts +0 -195
  368. package/src/shared/storage-utils.ts +0 -40
  369. package/src/shared/system-stats.repository.d1.test.ts +0 -58
  370. package/src/shared/system-stats.repository.d1.ts +0 -48
  371. package/src/types.ts +0 -87
  372. package/src/upload.ts +0 -198
  373. package/src/widget.ts +0 -212
@@ -1,420 +0,0 @@
1
- // SPDX-License-Identifier: BUSL-1.1
2
- // Copyright (c) 2024–2026 Flavio De Musso. All rights reserved.
3
- // See LICENSE in the repository root for license terms.
4
-
5
- /// <reference types="@cloudflare/workers-types" />
6
- import { Hono } from 'hono'
7
- import { SystemClock } from '@beechcms/core'
8
- import type { Env, Variables } from '../../types'
9
- import { publicProblem } from '../../public/problem-details'
10
- import { cleanStr } from '../../shared/query-utils'
11
-
12
- const DATABASE_ERROR = 'Database error'
13
- const INTERNAL_SERVER_ERROR = 'Internal Server Error'
14
-
15
- const SECONDS_PER_MINUTE = 60
16
- const SECONDS_PER_HOUR = 60 * SECONDS_PER_MINUTE
17
- const SECONDS_PER_DAY = 24 * SECONDS_PER_HOUR
18
- const HOURS_24_IN_SECONDS = 24 * SECONDS_PER_HOUR
19
- const DAYS_7_IN_SECONDS = 7 * SECONDS_PER_DAY
20
- const DAYS_30_IN_SECONDS = 30 * SECONDS_PER_DAY
21
- const MAX_MEDIA_SCAN = 1000
22
- const DEFAULT_LIMIT = 12
23
- const MAX_LIMIT = 100
24
- const R2_STORAGE_LIMIT = 10 * 1024 * 1024 * 1024 // 10GB
25
- const D1_MONTHLY_REQUESTS_LIMIT = 1000000 // 1M requests/month
26
-
27
- function getMimeType(extension: string): string {
28
- if (extension === 'pdf') {
29
- return 'application/pdf'
30
- }
31
- const type = extension === 'jpg' ? 'jpeg' : (extension || 'jpeg')
32
- return `image/${type}`
33
- }
34
-
35
- const statsApp = new Hono<{ Bindings: Env; Variables: Variables }>()
36
-
37
- /**
38
- * GET /stats/media-library - Lists all files present in R2:
39
- * combines media_objects (tracked uploads) + /api/media/ URLs found in file columns of each seed
40
- */
41
- statsApp.get('/stats/media-library', async (context) => {
42
- try {
43
- const mediaRepository = context.get('mediaRepository')
44
- const limit = Math.min(Number.parseInt(context.req.query('limit') ?? String(DEFAULT_LIMIT), 10), MAX_LIMIT)
45
- const offset = Number.parseInt(context.req.query('offset') ?? '0', 10)
46
- const mediaBaseUrl = (context.env.MEDIA_BASE_URL?.trim().replace(/\/$/, '')) ?? new URL(context.req.url).origin
47
-
48
- // 1. Files tracked in the media library
49
- // Take the first MAX_MEDIA_SCAN for cross-scanning
50
- const { items: mediaRows } = await mediaRepository.list({ limit: MAX_MEDIA_SCAN, offset: 0 })
51
-
52
- const trackedKeys = new Set<string>()
53
- const allItems: Array<{ key: string; filename: string; mime_type: string; size_bytes: number; created_at: number; url: string }> = []
54
-
55
- for (const mediaRow of mediaRows) {
56
- trackedKeys.add(mediaRow.key)
57
- allItems.push({ ...mediaRow, url: `${mediaBaseUrl}/api/media/${encodeURIComponent(mediaRow.key)}` })
58
- }
59
-
60
- // 2. /api/media/ URLs in the file columns of each seed (v0.4.0 — real columns)
61
- const seeds = context.get('seedRegistry').all()
62
- const contentScanRepository = context.get('contentScanRepository')
63
- const referencedKeysFromContent = await contentScanRepository.getReferencedMediaKeys(seeds)
64
-
65
- for (const key of referencedKeysFromContent) {
66
- if (trackedKeys.has(key)) continue
67
- trackedKeys.add(key)
68
- const filename = key.replace(/^\d+-/, '')
69
- const extension = filename.split('.').pop()?.toLowerCase() ?? ''
70
- const mimeType = getMimeType(extension)
71
- const createdAt = Number.parseInt(key.split('-')[0], 10) || 0
72
- allItems.push({
73
- key,
74
- filename,
75
- mime_type: mimeType,
76
- size_bytes: 0,
77
- created_at: createdAt,
78
- url: `${mediaBaseUrl}/api/media/${encodeURIComponent(key)}`
79
- })
80
- }
81
-
82
- allItems.sort((a, b) => b.created_at - a.created_at)
83
- const total = allItems.length
84
- const paginatedItems = allItems.slice(offset, offset + limit)
85
-
86
- return context.json({ items: paginatedItems, total })
87
- } catch (error) {
88
- console.error('Media library error:', error)
89
- return context.json({ error: INTERNAL_SERVER_ERROR }, 500)
90
- }
91
- })
92
-
93
- /**
94
- * GET /stats/unused-media - Finds media in media_objects not referenced in any file column
95
- */
96
- statsApp.get('/stats/unused-media', async (context) => {
97
- try {
98
- const mediaRepository = context.get('mediaRepository')
99
- const seeds = context.get('seedRegistry').all()
100
-
101
- // All tracked media keys
102
- const { items: mediaRows } = await mediaRepository.list({ limit: MAX_MEDIA_SCAN, offset: 0 })
103
-
104
- if (mediaRows.length === 0) {
105
- return context.json({ items: [] })
106
- }
107
-
108
- // Collect all referenced keys in the file columns of each seed
109
- const contentScanRepository = context.get('contentScanRepository')
110
- const referencedKeys = await contentScanRepository.getReferencedMediaKeys(seeds)
111
-
112
- const unusedMedia = mediaRows.filter(mediaItem => !referencedKeys.has(mediaItem.key))
113
- return context.json({ items: unusedMedia })
114
- } catch (error) {
115
- console.error('Unused media error:', error)
116
- return context.json({ error: INTERNAL_SERVER_ERROR }, 500)
117
- }
118
- })
119
-
120
- /**
121
- * GET /stats/setup-checklist - Project setup status for "Project Health" widget
122
- */
123
- statsApp.get('/stats/setup-checklist', async (context) => {
124
- try {
125
- const setupChecklistRepository = context.get('setupChecklistRepository')
126
- const seeds = context.get('seedRegistry').all()
127
-
128
- const existingTables = await setupChecklistRepository.getExistingTableNames()
129
-
130
- const systemTableNames = [
131
- 'users', 'refresh_tokens', 'media_objects',
132
- 'analytics', 'system_stats', 'activity_logs',
133
- ]
134
- const systemTablesOk = systemTableNames.every(name => existingTables.has(name))
135
-
136
- const seedsCount = seeds.length
137
- const contentTablesOk = seedsCount > 0 && seeds.every(seed => existingTables.has(`content_${seed.slug}`))
138
-
139
- let adminExists = false
140
- try {
141
- adminExists = (await setupChecklistRepository.countAdmins()) > 0
142
- } catch {
143
- // users table may not exist yet
144
- }
145
-
146
- let hasContent = false
147
- const firstSeedSlug = seeds[0]?.slug ?? null
148
- if (firstSeedSlug && existingTables.has(`content_${firstSeedSlug}`)) {
149
- try {
150
- hasContent = (await setupChecklistRepository.countEntriesInSeed(firstSeedSlug)) > 0
151
- } catch {
152
- // ignore
153
- }
154
- }
155
-
156
- return context.json({
157
- systemTablesOk,
158
- seedsCount,
159
- contentTablesOk,
160
- adminExists,
161
- hasContent,
162
- firstSeedSlug,
163
- })
164
- } catch (error) {
165
- console.error('Setup checklist error:', error)
166
- return context.json({ error: 'Failed to compute setup checklist' }, 500)
167
- }
168
- })
169
-
170
- /**
171
- * GET /stats/total - Global content statistics for dashboard
172
- */
173
- statsApp.get('/stats/total', async (context) => {
174
- try {
175
- const now = SystemClock.nowSeconds()
176
- const twentyFourHoursAgo = now - HOURS_24_IN_SECONDS
177
- const sevenDaysAgo = now - DAYS_7_IN_SECONDS
178
- const thirtyDaysAgo = now - DAYS_30_IN_SECONDS
179
-
180
- // Total: SUM of per-seed counts (current live entries)
181
- const seeds = context.get('seedRegistry').all()
182
- const widgetRepository = context.get('widgetRepository')
183
- const countResults = await Promise.all(
184
- seeds.map(seed => widgetRepository.aggregate(seed, { op: 'count' }, 'all'))
185
- )
186
- const totalEntriesCount = countResults.reduce((accumulator, count) => accumulator + count, 0)
187
-
188
- // today/week/month: create events in activity_logs (entity_type = 'content')
189
- const activityLogRepository = context.get('activityLogRepository')
190
- const [todayCount, weekCount, monthCount] = await Promise.all([
191
- activityLogRepository.countSince({ action: 'create', entityType: 'content', sinceTimestamp: twentyFourHoursAgo }),
192
- activityLogRepository.countSince({ action: 'create', entityType: 'content', sinceTimestamp: sevenDaysAgo }),
193
- activityLogRepository.countSince({ action: 'create', entityType: 'content', sinceTimestamp: thirtyDaysAgo }),
194
- ])
195
-
196
- return context.json({
197
- total: totalEntriesCount,
198
- today: todayCount,
199
- week: weekCount,
200
- month: monthCount,
201
- })
202
- } catch (error) {
203
- console.error('Content stats error:', error)
204
- return publicProblem(context, {
205
- type: 'content-database-error',
206
- title: INTERNAL_SERVER_ERROR,
207
- status: 500,
208
- detail: DATABASE_ERROR,
209
- })
210
- }
211
- })
212
-
213
- /**
214
- * GET /stats/recent-activity - Latest activities registered in the system
215
- * No ETag on this endpoint: the activity feed must always be fresh after
216
- * every mutation. Cache-Control: no-store prevents the browser from keeping a
217
- * response that could be returned as a 304 stale.
218
- */
219
- statsApp.get('/stats/recent-activity', async (context) => {
220
- try {
221
- const slug = cleanStr(context.req.query('slug'))
222
-
223
- const entries = await context.get('activityLogRepository').list({
224
- entitySlug: slug ?? undefined,
225
- limit: 15,
226
- })
227
-
228
- // Preserve the legacy snake_case wire format consumed by the dashboard
229
- // recent-activity widget. The repository returns camelCase records,
230
- // so we reshape only on the way out.
231
- const activities = entries.map((entry) => ({
232
- id: entry.id,
233
- user_id: entry.userId,
234
- user_email: entry.userEmail,
235
- user_name: entry.userName,
236
- action: entry.action,
237
- entity_type: entry.entityType,
238
- entity_id: entry.entityId,
239
- entity_slug: entry.entitySlug,
240
- details: entry.details,
241
- created_at: entry.createdAt,
242
- }))
243
-
244
- context.header('Cache-Control', 'no-store')
245
-
246
- return context.json(activities)
247
- } catch (error) {
248
- console.error('Recent activity error:', error)
249
- return publicProblem(context, {
250
- type: 'content-database-error',
251
- title: INTERNAL_SERVER_ERROR,
252
- status: 500,
253
- detail: DATABASE_ERROR,
254
- })
255
- }
256
- })
257
-
258
- /**
259
- * GET /stats/health - System health status and Cloudflare quotas
260
- */
261
- statsApp.get('/stats/health', async (context) => {
262
- try {
263
- const systemStatsRepository = context.get('systemStatsRepository')
264
-
265
- // 1. Retrieve storage usage from repository (system_stats)
266
- let storageUsedBytes = 0
267
- try {
268
- storageUsedBytes = await systemStatsRepository.getStorageUsage()
269
- } catch (error) {
270
- console.warn('Health: Could not fetch storage stats from repo:', error)
271
- }
272
-
273
- // 2. Aggregate D1 requests (proxy for database health) - last 30 days
274
- const thirtyDaysAgo = SystemClock.nowSeconds() - DAYS_30_IN_SECONDS
275
- const totalRequests = await context
276
- .get('analyticsRepository')
277
- .sumByMetric('requests', '', thirtyDaysAgo)
278
-
279
- const storagePercentage = Math.min(Math.round((storageUsedBytes / R2_STORAGE_LIMIT) * 1000) / 10, 100)
280
- const d1Percentage = Math.min(Math.round((totalRequests / D1_MONTHLY_REQUESTS_LIMIT) * 1000) / 10, 100)
281
-
282
- return context.json({
283
- storage: {
284
- used: storageUsedBytes,
285
- limit: R2_STORAGE_LIMIT,
286
- percentage: storagePercentage
287
- },
288
- database: {
289
- requests30d: totalRequests,
290
- limit: D1_MONTHLY_REQUESTS_LIMIT,
291
- percentage: d1Percentage
292
- },
293
- status: (storagePercentage < 90 && d1Percentage < 90) ? 'healthy' : 'warning',
294
- lastUpdate: SystemClock.nowSeconds()
295
- })
296
- } catch (error) {
297
- console.error('System health stats error:', error)
298
- return context.json({ error: 'Failed to calculate system health' }, 500)
299
- }
300
- })
301
-
302
- /**
303
- * GET /stats/cloudflare - Cloudflare-like metrics (Visitors, Requests, Bandwidth)
304
- */
305
- statsApp.get('/stats/cloudflare', async (context) => {
306
- try {
307
- const thirtyDaysAgo = SystemClock.nowSeconds() - DAYS_30_IN_SECONDS
308
- const analyticsRepository = context.get('analyticsRepository')
309
-
310
- const [recordedRequests, recordedVisitors] = await Promise.all([
311
- analyticsRepository.sumByMetric('requests', '', thirtyDaysAgo),
312
- analyticsRepository.sumByMetric('visitors', '', thirtyDaysAgo),
313
- ])
314
-
315
- // Simulate some Cloudflare metrics not directly tracked for a premium feel
316
- const requestsCount = recordedRequests > 0 ? recordedRequests : Math.floor(Math.random() * 5000) + 1000
317
- const visitorsCount = recordedVisitors > 0 ? recordedVisitors : Math.floor(requestsCount / 12) + 1
318
- const bandwidthMB = Math.round((requestsCount * 0.15) * 10) / 10
319
-
320
- const systemStatsRepository = context.get('systemStatsRepository')
321
- let storageUsedBytes = 0
322
- try {
323
- storageUsedBytes = await systemStatsRepository.getStorageUsage()
324
- } catch (error) {
325
- console.warn('Could not fetch storage stats from repo:', error)
326
- }
327
-
328
- const storageUsedMB = Math.round((storageUsedBytes / (1024 * 1024)) * 10) / 10
329
- const storageLimitMB = 10 * 1024 // 10 GB Free Tier
330
-
331
- return context.json({
332
- visitors: {
333
- value: visitorsCount,
334
- trend: 12, // simulated growth %
335
- isPositive: true
336
- },
337
- requests: {
338
- value: requestsCount,
339
- trend: 8,
340
- isPositive: true
341
- },
342
- bandwidth: {
343
- value: bandwidthMB,
344
- unit: 'MB',
345
- trend: 5,
346
- isPositive: false
347
- },
348
- cacheRate: {
349
- value: 94.2,
350
- unit: '%',
351
- trend: 0.5,
352
- isPositive: true
353
- },
354
- storage: {
355
- used: storageUsedMB,
356
- limit: storageLimitMB,
357
- unit: 'MB',
358
- percentage: Math.round((storageUsedMB / storageLimitMB) * 1000) / 10
359
- }
360
- })
361
- } catch (error) {
362
- console.error('Cloudflare stats error:', error)
363
- return publicProblem(context, {
364
- type: 'content-database-error',
365
- title: INTERNAL_SERVER_ERROR,
366
- status: 500,
367
- detail: DATABASE_ERROR,
368
- })
369
- }
370
- })
371
-
372
- /**
373
- * GET /stats/breakdown - Content distribution for "Content Pulse" widget
374
- */
375
- statsApp.get('/stats/breakdown', async (context) => {
376
- try {
377
- const seeds = context.get('seedRegistry').all()
378
-
379
- const widgetRepository = context.get('widgetRepository')
380
- const counts = await Promise.all(
381
- seeds.map(seed => widgetRepository.aggregate(seed, { op: 'count' }, 'all'))
382
- )
383
-
384
- const breakdown = seeds.map((seed, index) => ({
385
- slug: seed.slug,
386
- label: seed.labelPlural || seed.label,
387
- count: counts[index] ?? 0,
388
- }))
389
-
390
- return context.json(breakdown)
391
- } catch (error) {
392
- console.error('Breakdown stats error:', error)
393
- return publicProblem(context, {
394
- type: 'content-database-error',
395
- title: INTERNAL_SERVER_ERROR,
396
- status: 500,
397
- detail: DATABASE_ERROR,
398
- })
399
- }
400
- })
401
-
402
- /**
403
- * POST /stats/storage/sync - Recalculates space occupied on R2 (expensive operation, use with caution)
404
- */
405
- statsApp.post('/stats/storage/sync', async (context) => {
406
- try {
407
- const bucket = context.get('bucket')
408
- const systemStatsRepository = context.get('systemStatsRepository')
409
-
410
- const realSize = await bucket.getTotalSize()
411
- await systemStatsRepository.setStorage(realSize)
412
-
413
- return context.json({ success: true, size: realSize })
414
- } catch (error) {
415
- console.error('Storage sync error:', error)
416
- return context.json({ error: INTERNAL_SERVER_ERROR }, 500)
417
- }
418
- })
419
-
420
- export { statsApp }
@@ -1,63 +0,0 @@
1
- // SPDX-License-Identifier: BUSL-1.1
2
- // Copyright (c) 2024–2026 Flavio De Musso. All rights reserved.
3
- // See LICENSE in the repository root for license terms.
4
-
5
- import { Hono } from 'hono'
6
- import { Receiver } from '@upstash/qstash'
7
- import type { AppEnv } from '../../types'
8
- import type { CreateNotificationInput } from '@beechcms/core'
9
-
10
- export const webhooksApp = new Hono<AppEnv>()
11
-
12
- webhooksApp.post('/qstash', async (context) => {
13
- const currentSigningKey = context.env.QSTASH_CURRENT_SIGNING_KEY
14
- const nextSigningKey = context.env.QSTASH_NEXT_SIGNING_KEY
15
-
16
- if (!currentSigningKey || !nextSigningKey) {
17
- console.error('QStash webhook called but signing keys are not configured')
18
- return context.text('Configuration error', 500)
19
- }
20
-
21
- const receiver = new Receiver({
22
- currentSigningKey,
23
- nextSigningKey,
24
- })
25
-
26
- const body = await context.req.text()
27
- const signature = context.req.header('Upstash-Signature')
28
-
29
- if (!signature) {
30
- return context.text('Missing signature', 401)
31
- }
32
-
33
- try {
34
- const isValid = await receiver.verify({
35
- signature,
36
- body,
37
- })
38
-
39
- if (!isValid) {
40
- return context.text('Invalid signature', 401)
41
- }
42
- } catch (err) {
43
- console.error('QStash signature verification failed', err)
44
- return context.text('Invalid signature', 401)
45
- }
46
-
47
- try {
48
- const input = JSON.parse(body) as CreateNotificationInput
49
-
50
- const notificationRepository = context.get('notificationRepository')
51
-
52
- await notificationRepository.create({
53
- title: input.title,
54
- message: input.message,
55
- type: input.type ?? 'info',
56
- })
57
-
58
- return context.text('OK', 200)
59
- } catch (error) {
60
- console.error('Failed to process QStash webhook payload', error)
61
- return context.text('Internal server error', 500)
62
- }
63
- })
package/src/index.ts DELETED
@@ -1,65 +0,0 @@
1
- // SPDX-License-Identifier: BUSL-1.1
2
- // Copyright (c) 2024–2026 Flavio De Musso. All rights reserved.
3
- // See LICENSE in the repository root for license terms.
4
-
5
- import { createBeechApp } from './factory'
6
- import { SeedRegistry, SystemIdGenerator } from '@beechcms/core'
7
- import { runCronAutomations } from './features/automations'
8
- import { D1AutomationRepository } from './shared/automations.repository.d1'
9
- import { D1ContentRepository } from './shared/content.repository.d1'
10
- import { D1SeedRepository } from './shared/seed.repository.d1'
11
- import type { Env } from './types'
12
-
13
- const app = createBeechApp({ seeds: [] })
14
-
15
- app.get('/', (c) => c.text('Beech API is running'))
16
-
17
- export default {
18
- fetch(request: Request, env: Env, ctx: ExecutionContext) {
19
- if (env.ENV === 'development') {
20
- const checks: Array<{ name: string; url: string }> = [
21
- { name: 'MinIO', url: (env.R2_ENDPOINT ?? 'http://localhost:9000') + '/minio/health/live' },
22
- { name: 'Mailpit', url: `http://${env.SMTP_HOST ?? 'localhost'}:${env.SMTP_PORT ?? '8025'}/livez` },
23
- ]
24
- for (const c of checks) {
25
- fetch(c.url).catch(() => {
26
- console.warn(
27
- `\n⚠️ ${c.name} non raggiungibile su ${c.url}\n` +
28
- ` Beech in dev richiede lo stack Docker completo.\n` +
29
- ` Avvialo con: npm run dev:full\n`
30
- )
31
- })
32
- }
33
- }
34
- return app.fetch(request, env, ctx)
35
- },
36
-
37
- async scheduled(controller: ScheduledController, env: Env, ctx: ExecutionContext) {
38
- const scheduledTime = controller?.scheduledTime ?? Date.now()
39
-
40
- if (!env.DB) {
41
- console.warn('[cron] D1 binding missing. Skipping cron automations.')
42
- return
43
- }
44
-
45
- const automationRepository = new D1AutomationRepository(env.DB)
46
- const contentRepository = new D1ContentRepository(env.DB)
47
- const seedRepository = new D1SeedRepository(env.DB)
48
- const seeds = await seedRepository.listActive()
49
- const registry = new SeedRegistry(seeds)
50
- const getSeed = (slug: string) => registry.get(slug) ?? null
51
-
52
- ctx.waitUntil(
53
- runCronAutomations(
54
- {
55
- automationRepository,
56
- contentRepository,
57
- getSeed,
58
- env: env as unknown as Record<string, string | undefined>,
59
- idGenerator: SystemIdGenerator,
60
- },
61
- scheduledTime,
62
- ),
63
- )
64
- },
65
- } satisfies ExportedHandler<Env>
@@ -1,82 +0,0 @@
1
- // SPDX-License-Identifier: BUSL-1.1
2
- // Copyright (c) 2024–2026 Flavio De Musso. All rights reserved.
3
- // See LICENSE in the repository root for license terms.
4
-
5
- /**
6
- * Media utils: estrazione chiavi R2 dal data di un'entry.
7
- * Usato alla cancellazione entry per eliminare i file associati da R2.
8
- *
9
- * @see docs/media-engine.md
10
- */
11
- import type { Seed } from '@beechcms/core'
12
-
13
- /** Pattern per estrarre la chiave R2 da URL in formato /api/media/KEY */
14
- const MEDIA_URL_PATTERN = /\/api\/media\/([^/?#]+)/
15
-
16
- /**
17
- * Estrae la chiave R2 da un URL di media.
18
- * Es: "https://x.com/api/media/1739123456-avatar.png" → "1739123456-avatar.png"
19
- *
20
- * @param mediaUrl - URL completo o path (es. /api/media/123-foto.png)
21
- * @returns Chiave R2 o null se l'URL non è valido
22
- */
23
- export function extractMediaKey(mediaUrl: string): string | null {
24
- const match = MEDIA_URL_PATTERN.exec(String(mediaUrl))
25
- return match ? decodeURIComponent(match[1]) : null
26
- }
27
-
28
- /**
29
- * Attraversa ricorsivamente un valore (stringa, array, oggetto) e raccoglie
30
- * tutte le chiavi R2 trovate in stringhe che matchano /api/media/KEY.
31
- */
32
- function collectMediaKeysRecursive(value: unknown, collectedKeys: Set<string>): void {
33
- if (typeof value === 'string') {
34
- const r2Key = extractMediaKey(value)
35
- if (r2Key) {
36
- collectedKeys.add(r2Key)
37
- return
38
- }
39
- // Legacy compat: campi json/file possono contenere JSON serializzato.
40
- try {
41
- const parsed = JSON.parse(value) as unknown
42
- if (parsed !== value) {
43
- collectMediaKeysRecursive(parsed, collectedKeys)
44
- }
45
- } catch {
46
- // ignore
47
- }
48
- return
49
- }
50
- if (Array.isArray(value)) {
51
- for (const item of value) collectMediaKeysRecursive(item, collectedKeys)
52
- return
53
- }
54
- if (value != null && typeof value === 'object') {
55
- for (const nestedValue of Object.values(value)) {
56
- collectMediaKeysRecursive(nestedValue, collectedKeys)
57
- }
58
- }
59
- }
60
-
61
- /**
62
- * Estrae tutte le chiavi R2 dal data di un'entry.
63
- * Cerca nei campi `file` (stringa URL) e `json` (array/oggetto con URL).
64
- * Il data è in formato DB: chiavi = branch.id (es. art_03, prd_05).
65
- *
66
- * @param seed - Schema del tipo di contenuto
67
- * @param entryData - Payload in formato DB (chiavi = branch ID)
68
- * @returns Array di chiavi R2 uniche da eliminare
69
- */
70
- export function extractMediaKeysFromData(
71
- seed: Seed,
72
- entryData: Record<string, unknown>
73
- ): string[] {
74
- const r2Keys = new Set<string>()
75
- for (const branch of seed.branches) {
76
- if (branch.type !== 'file' && branch.type !== 'json') continue
77
- const fieldValue = entryData[branch.alias]
78
- if (fieldValue == null) continue
79
- collectMediaKeysRecursive(fieldValue, r2Keys)
80
- }
81
- return [...r2Keys]
82
- }
@@ -1,45 +0,0 @@
1
- // SPDX-License-Identifier: BUSL-1.1
2
- // Copyright (c) 2024–2026 Flavio De Musso. All rights reserved.
3
- // See LICENSE in the repository root for license terms.
4
-
5
- import { createMiddleware } from 'hono/factory'
6
- import type { IHashProvider, ITokenService, IClock } from '@beechcms/core'
7
- import { SystemClock } from '@beechcms/core'
8
- import type { AppEnv } from '../types'
9
- import { BcryptHashProvider } from '../auth/bcrypt-hash-provider'
10
- import { JoseTokenService } from '../auth/jose-token-service'
11
-
12
- export interface AuthProviderOverrides {
13
- hashProvider?: IHashProvider
14
- tokenService?: ITokenService
15
- clock?: IClock
16
- }
17
-
18
- // Valore di default presente in .dev.vars.example: mai accettabile in produzione.
19
- const DEV_JWT_SECRET = 'sviluppo-secret-cambiami-almeno-32-byte-per-sicurezza-hono'
20
-
21
- export const authProvidersMiddleware = (overrides?: AuthProviderOverrides) => {
22
- return createMiddleware<AppEnv>(async (context, next) => {
23
- if (context.env.ENV === 'production' && context.env.JWT_SECRET === DEV_JWT_SECRET) {
24
- throw new Error(
25
- 'JWT_SECRET non configurato: in produzione impostare un segreto univoco con `wrangler secret put JWT_SECRET`',
26
- )
27
- }
28
-
29
- const resolvedClock = overrides?.clock ?? SystemClock
30
- const hashProvider = overrides?.hashProvider ?? new BcryptHashProvider()
31
- const tokenService = overrides?.tokenService ?? new JoseTokenService(
32
- context.env.JWT_SECRET,
33
- {
34
- issuer: context.env.JWT_ISSUER,
35
- audience: context.env.JWT_AUDIENCE,
36
- },
37
- resolvedClock,
38
- )
39
-
40
- context.set('hashProvider', hashProvider)
41
- context.set('tokenService', tokenService)
42
-
43
- await next()
44
- })
45
- }