ghost 6.54.0 → 6.55.0

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 (327) hide show
  1. package/components/ghost-storage-base-3.0.0.tgz +0 -0
  2. package/components/tryghost-adapter-base-cache-0.2.0.tgz +0 -0
  3. package/components/tryghost-adapter-base-scheduling-0.2.0.tgz +0 -0
  4. package/components/tryghost-adapter-base-sso-0.1.1.tgz +0 -0
  5. package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
  6. package/components/tryghost-kg-default-cards-10.3.3.tgz +0 -0
  7. package/core/boot.js +3 -6
  8. package/core/built/admin/assets/{account-migration-DLvVA5yq.js → account-migration-sjHLx5dy.js} +1 -1
  9. package/core/built/admin/assets/activitypub/{account-migration-DHbZcxGM.js → account-migration-DoYXtnZ8.js} +4 -4
  10. package/core/built/admin/assets/activitypub/activitypub.js +2 -2
  11. package/core/built/admin/assets/activitypub/{bluesky-sharing-zfxUey_L.js → bluesky-sharing-Bxeh6MKW.js} +4 -4
  12. package/core/built/admin/assets/activitypub/{dist-BqkDUQxJ2.js → dist-ByFmDSNP2.js} +64 -63
  13. package/core/built/admin/assets/activitypub/{dist-Ba_QxHHl.js → dist-D8sFfdpC.js} +2 -2
  14. package/core/built/admin/assets/activitypub/{domain-CmwSZLHK.js → domain-CWueFW6q.js} +5 -5
  15. package/core/built/admin/assets/activitypub/{edit-profile-CXdwNmVG.js → edit-profile-BGxPU7N5.js} +3 -3
  16. package/core/built/admin/assets/activitypub/{explore-DfbjzGM1.js → explore-DBK-u0Ch.js} +3 -3
  17. package/core/built/admin/assets/activitypub/{feed-DSEBJOqf.js → feed-Bcir5RQZ.js} +3 -3
  18. package/core/built/admin/assets/activitypub/{field-CY7XVcx6.js → field-BV-CIT3d.js} +2 -2
  19. package/core/built/admin/assets/activitypub/{inbox-DtQ0kytT.js → inbox-yC0nfvzl.js} +4 -4
  20. package/core/built/admin/assets/activitypub/{label-Dw96GQ7u.js → label-DTRS9TDk.js} +2 -2
  21. package/core/built/admin/assets/activitypub/layout-qx-bUL1k.js +1 -0
  22. package/core/built/admin/assets/activitypub/{moderation-Dg_-I-8C.js → moderation-Dwl9t0pb.js} +4 -4
  23. package/core/built/admin/assets/activitypub/{note-Cy1-uKMY.js → note-B-zKsZ7r.js} +3 -3
  24. package/core/built/admin/assets/activitypub/{notifications-B_fvoLIM.js → notifications-DjJZD54t.js} +3 -3
  25. package/core/built/admin/assets/activitypub/{preferences-ByJseZp6.js → preferences-CoU8vs6d.js} +6 -6
  26. package/core/built/admin/assets/activitypub/{profile-BFtbnIjz.js → profile-BAsaJekx.js} +6 -6
  27. package/core/built/admin/assets/activitypub/{routes-Dh3xXfce.js → routes-CLH3OVTP.js} +28 -25
  28. package/core/built/admin/assets/activitypub/{settings-DTgqJPxM.js → settings-DA6pNJ-Q.js} +3 -3
  29. package/core/built/admin/assets/activitypub/{site-CdL9ZLqm.js → site-CQD42Pw5.js} +2 -2
  30. package/core/built/admin/assets/activitypub/{tabs-ogqwoBlb.js → tabs-Cg0saow1.js} +7 -7
  31. package/core/built/admin/assets/activitypub/{use-reply-chain-data-CxonteFD.js → use-reply-chain-data-Br4aOWOh.js} +2 -2
  32. package/core/built/admin/assets/audience-DjEWNhp3.js +1 -0
  33. package/core/built/admin/assets/{automated-emails-Cn87L5Md.js → automated-emails-DRy7qY-J.js} +2 -2
  34. package/core/built/admin/assets/{automations-BP_aHamJ.js → automations-Bx3VcFIL.js} +1 -1
  35. package/core/built/admin/assets/{automations-B7-y3dXt.js → automations-DhKeS_U5.js} +1 -1
  36. package/core/built/admin/assets/{bluesky-sharing-Bh36XPh4.js → bluesky-sharing-ClMd0-Ft.js} +1 -1
  37. package/core/built/admin/assets/{breadcrumb-D34YWnaw.js → breadcrumb-BD0U2ygg.js} +1 -1
  38. package/core/built/admin/assets/{chart-helpers-DbklaJw9.js → chart-helpers-fjQKRVyY.js} +1 -1
  39. package/core/built/admin/assets/checkbox-CWa28lgO.js +1 -0
  40. package/core/built/admin/assets/{chunk.962.b73442b07be930bb5d85.js → chunk.211.6f7328d63c41536451b3.js} +737 -559
  41. package/core/built/admin/assets/{chunk.962.b73442b07be930bb5d85.js.LICENSE.txt → chunk.211.6f7328d63c41536451b3.js.LICENSE.txt} +2 -0
  42. package/core/built/admin/assets/{chunk.589.096f4d0bb775e5229aa9.js → chunk.481.6c7395a49484bb4583aa.js} +41 -38
  43. package/core/built/admin/assets/chunk.524.cffd399783a519e404a9.js +25 -0
  44. package/core/built/admin/assets/chunk.582.eebf0d0b2d202bb746a5.js +27 -0
  45. package/core/built/admin/assets/code-editor-view-CCr0rBWS.js +1 -0
  46. package/core/built/admin/assets/comments-DyUSMUdj.js +1 -0
  47. package/core/built/admin/assets/{date-range-select-B5vSsrpx.js → date-range-select-BFp58EMk.js} +1 -1
  48. package/core/built/admin/assets/{disabled-sources-indicator-NkOIoMue.js → disabled-sources-indicator-CUxMfiPa.js} +1 -1
  49. package/core/built/admin/assets/dist-2Tnz2Yfq.js +1 -0
  50. package/core/built/admin/assets/dist-CjY-f5Z1.js +1 -0
  51. package/core/built/admin/assets/dist-DIC8yz5C.js +1 -0
  52. package/core/built/admin/assets/domain-C0bV2UV8.js +1 -0
  53. package/core/built/admin/assets/dropzone-DlK-K2DQ.js +5 -0
  54. package/core/built/admin/assets/{edit-profile-rvDGrS8J.js → edit-profile-BKT-REJw.js} +1 -1
  55. package/core/built/admin/assets/editor-Dp865O-4.js +8 -0
  56. package/core/built/admin/assets/{explore-BZAZOg5X.js → explore-ClUpJZOp.js} +1 -1
  57. package/core/built/admin/assets/{feed-CFV8xDw9.js → feed-DF92dUMP.js} +1 -1
  58. package/core/built/admin/assets/field-Do7lieBA.js +1 -0
  59. package/core/built/admin/assets/{filter-sources-Fx8wiYYw.js → filter-sources-BV2rnpfA.js} +1 -1
  60. package/core/built/admin/assets/filters-CUXJ2Nm9.js +1 -0
  61. package/core/built/admin/assets/{filters-CGX_pf2N.js → filters-DfbIWc_l.js} +1 -1
  62. package/core/built/admin/assets/{ghost-3063eae29e6c9cc6d6e7131928a27da0.css → ghost-a9c788f6f0b1ea6cf0f14ca4b13f4496.css} +1 -1
  63. package/core/built/admin/assets/{ghost-b1dce91ad3232cc259ee5fbbf9526243.js → ghost-c4057474943effc568c5659ccc4909d7.js} +407 -406
  64. package/core/built/admin/assets/{ghost-dark-c8d0ab7f515b2010c173e23e9b32ea8a.css → ghost-dark-db36d795d21c61426837df40af270966.css} +1 -1
  65. package/core/built/admin/assets/gift-link-modal-f9VIMNDv.js +1 -0
  66. package/core/built/admin/assets/{gift-links-nBxJjZiQ.js → gift-links-CsUhefWG.js} +1 -1
  67. package/core/built/admin/assets/growth-B3I6gNLm.js +1 -0
  68. package/core/built/admin/assets/{Growth-CSaHoSVD.js → growth-CVw1My4S.js} +2 -2
  69. package/core/built/admin/assets/{hooks-BApwwBE0.js → hooks-CixO01lL.js} +1 -1
  70. package/core/built/admin/assets/{inbox-BRp1_aaC.js → inbox-CFBSu4Ed.js} +12 -12
  71. package/core/built/admin/assets/index-9Cw8z3rA.css +1 -0
  72. package/core/built/admin/assets/index-BGG7NckO.js +67 -0
  73. package/core/built/admin/assets/input-group-BSgG_lVw.js +1 -0
  74. package/core/built/admin/assets/input-surface-BpAjZXY5.js +1 -0
  75. package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +974 -937
  76. package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +28 -28
  77. package/core/built/admin/assets/koenig-lexical/style.css +1 -1
  78. package/core/built/admin/assets/{koenig-lexical-CM7UFNdL.js → koenig-lexical-f8vHRYvz.js} +28 -28
  79. package/core/built/admin/assets/{kpi-card-BrGH6FsT.js → kpi-card-DSap9rAy.js} +1 -1
  80. package/core/built/admin/assets/{kpi-card-CIGyijjx.js → kpi-card-DU0rn9Cj.js} +1 -1
  81. package/core/built/admin/assets/{kpi-helpers-_HRRsklE.js → kpi-helpers-Dz33AV6h.js} +1 -1
  82. package/core/built/admin/assets/{kpi-tabs-BSvh9eoq.js → kpi-tabs-BtTTrKvJ.js} +1 -1
  83. package/core/built/admin/assets/{label-BOR9XdKl.js → label-DS8Mi6G5.js} +1 -1
  84. package/core/built/admin/assets/label-picker-1DvSuLA1.js +1 -0
  85. package/core/built/admin/assets/labels-CDSUt4J_.js +1 -0
  86. package/core/built/admin/assets/layout-yQEi2qDM.js +1 -0
  87. package/core/built/admin/assets/{link-helpers-tOlxmuH5.js → link-helpers-7EYqvvHs.js} +1 -1
  88. package/core/built/admin/assets/member-custom-fields-yOMHyAg9.js +1 -0
  89. package/core/built/admin/assets/member-detail-BAq7gDaa.js +1 -0
  90. package/core/built/admin/assets/members-CQpVFJu4.js +10 -0
  91. package/core/built/admin/assets/modals-ReORvWnu.js +31 -0
  92. package/core/built/admin/assets/moderation-DySbgyUS.js +1 -0
  93. package/core/built/admin/assets/newsletter-CJQvtHqN.js +1 -0
  94. package/core/built/admin/assets/{Newsletters-C5cs6FGo.js → newsletters-B3QQXfGT.js} +1 -1
  95. package/core/built/admin/assets/{newsletters-D1jbtV-h.js → newsletters-BXJbJgA-.js} +1 -1
  96. package/core/built/admin/assets/note-C8kxrz6w.js +1 -0
  97. package/core/built/admin/assets/notifications-CgtSfX7t.js +1 -0
  98. package/core/built/admin/assets/{offers-CrY6nSRR.js → offers-DEniVRD5.js} +1 -1
  99. package/core/built/admin/assets/onboarding-route-4qtziA9d.js +1 -0
  100. package/core/built/admin/assets/overview-Bf2sbznq.js +1 -0
  101. package/core/built/admin/assets/overview-CBfePxcr.js +1 -0
  102. package/core/built/admin/assets/{page-header-C0jVxtS8.js → page-header-B3Wb9Jqv.js} +1 -1
  103. package/core/built/admin/assets/{pages-BbNWFcGj.js → pages-B4goRPUn.js} +1 -1
  104. package/core/built/admin/assets/{post-analytics-Iw7dk1mT.js → post-analytics-We-ko5Cb.js} +1 -1
  105. package/core/built/admin/assets/post-analytics-header-Bmc9su6q.js +1 -0
  106. package/core/built/admin/assets/post-analytics-provider-B6UynTGb.js +1 -0
  107. package/core/built/admin/assets/{post-share-modal-DBfKl2GZ.js → post-share-modal-DFfO93_0.js} +1 -1
  108. package/core/built/admin/assets/{posts-EWpZIjkr.js → posts-DhXE7b6r.js} +1 -1
  109. package/core/built/admin/assets/{preferences-D4EANGoc.js → preferences-DJPfHEGu.js} +2 -2
  110. package/core/built/admin/assets/profile-DqBQp-gT.js +1 -0
  111. package/core/built/admin/assets/{referrers-BCfFYSHc.js → referrers-pmJT4k3-.js} +1 -1
  112. package/core/built/admin/assets/select-C5_xEt5b.js +1 -0
  113. package/core/built/admin/assets/settings-BwiVIXau.js +4 -0
  114. package/core/built/admin/assets/{settings-Bz3RZRmE.js → settings-WXNeWyU1.js} +1 -1
  115. package/core/built/admin/assets/shade-app-B3heOAfz-CGXQwWeO.js +3 -0
  116. package/core/built/admin/assets/{share-modal-CkdTTMJd.js → share-modal-DsgW3wbg.js} +1 -1
  117. package/core/built/admin/assets/social-urls-BQPFEk1_.js +8 -0
  118. package/core/built/admin/assets/{sort-button-DZp9Y2r1.js → sort-button-Bb_FCl7e.js} +1 -1
  119. package/core/built/admin/assets/{source-icon-CHr9qJ2Y.js → source-icon-BeaRxUn9.js} +1 -1
  120. package/core/built/admin/assets/{stats-DGoXZxWE.js → stats-Bjj_kmJ1.js} +1 -1
  121. package/core/built/admin/assets/{stats-view-D2-1L0ms.js → stats-view-BxPbW6K1.js} +1 -1
  122. package/core/built/admin/assets/{step-1-RdahbwyG.js → step-1-B1e3-1F8.js} +1 -1
  123. package/core/built/admin/assets/{step-2-D83PeRwd.js → step-2-C5WaeBJo.js} +1 -1
  124. package/core/built/admin/assets/{step-3-p_Op4wv4.js → step-3-B46iVPwu.js} +2 -2
  125. package/core/built/admin/assets/switch-z5Y6Uzt6.js +1 -0
  126. package/core/built/admin/assets/{table-B8LgTQJZ.js → table-CB-4_HBJ.js} +1 -1
  127. package/core/built/admin/assets/tabs-CCEgKYzq.js +1 -0
  128. package/core/built/admin/assets/{tags-B4l891z2.js → tags-Cg2VJGmG.js} +1 -1
  129. package/core/built/admin/assets/textarea-Bjccvh_E.js +1 -0
  130. package/core/built/admin/assets/{tiers-BgYLWRJv.js → tiers-DYsa4DCo.js} +1 -1
  131. package/core/built/admin/assets/{toggle-group-DNXDkAwf.js → toggle-group-b3IlxvhV.js} +1 -1
  132. package/core/built/admin/assets/{topic-filter-DZGjEcz3.js → topic-filter-DAFT87tQ.js} +1 -1
  133. package/core/built/admin/assets/{use-analytics-data-CaT8RirX.js → use-analytics-data-S0FTd43s.js} +1 -1
  134. package/core/built/admin/assets/use-filter-params-Gpoq2E1L.js +1 -0
  135. package/core/built/admin/assets/{use-growth-stats-9Db2CuJe.js → use-growth-stats-CIaiHOVb.js} +1 -1
  136. package/core/built/admin/assets/{use-post-referrers-BV54YcQS.js → use-post-referrers-Cq2vBPrA.js} +1 -1
  137. package/core/built/admin/assets/{use-reply-chain-data-DuYZGDan.js → use-reply-chain-data-DBDRtGu-.js} +1 -1
  138. package/core/built/admin/assets/{use-simple-pagination-8fTua9Nt.js → use-simple-pagination-CjYNxdwe.js} +1 -1
  139. package/core/built/admin/assets/{use-tinybird-query-Dz156HvZ.js → use-tinybird-query-falHAtuK.js} +1 -1
  140. package/core/built/admin/assets/{use-tinybird-token-C9RUdL_n.js → use-tinybird-token-D7-8ND1y.js} +1 -1
  141. package/core/built/admin/assets/{users-CcSCQFcQ.js → users-Dkv8NQZe.js} +1 -1
  142. package/core/built/admin/assets/{virtual-list-BCvP0YF-.js → virtual-list-CvnC9zF2.js} +1 -1
  143. package/core/built/admin/assets/web-B_5GZRvH.js +1 -0
  144. package/core/built/admin/assets/web-jFYWCKDn.js +2 -0
  145. package/core/built/admin/index.html +14 -10
  146. package/core/frontend/services/proxy.js +10 -1
  147. package/core/frontend/services/routing/router-manager.js +14 -14
  148. package/core/frontend/services/routing/static-routes-router.js +8 -10
  149. package/core/server/adapters/storage/LocalFilesStorage.js +13 -11
  150. package/core/server/adapters/storage/LocalFilesStorage.ts +24 -0
  151. package/core/server/adapters/storage/LocalImagesStorage.js +14 -12
  152. package/core/server/adapters/storage/LocalImagesStorage.ts +24 -0
  153. package/core/server/adapters/storage/LocalMediaStorage.js +13 -11
  154. package/core/server/adapters/storage/LocalMediaStorage.ts +24 -0
  155. package/core/server/adapters/storage/LocalStorageBase.js +107 -167
  156. package/core/server/adapters/storage/LocalStorageBase.ts +342 -0
  157. package/core/server/adapters/storage/utils.js +36 -50
  158. package/core/server/adapters/storage/utils.ts +60 -0
  159. package/core/server/api/endpoints/automation-action-links.js +68 -0
  160. package/core/server/api/endpoints/automation-action-links.ts +46 -0
  161. package/core/server/api/endpoints/db.js +6 -0
  162. package/core/server/api/endpoints/index.js +4 -0
  163. package/core/server/api/endpoints/members.js +25 -28
  164. package/core/server/api/endpoints/pages.js +6 -1
  165. package/core/server/api/endpoints/posts.js +11 -2
  166. package/core/server/api/endpoints/settings.js +0 -2
  167. package/core/server/api/endpoints/utils/serializers/output/mappers/comments.js +4 -1
  168. package/core/server/api/endpoints/utils/serializers/output/members.js +18 -87
  169. package/core/server/api/endpoints/utils/serializers/output/utils/url.js +11 -1
  170. package/core/server/data/exporter/export-filename.js +2 -1
  171. package/core/server/data/importer/importers/data/revue-subscriber-importer.js +6 -10
  172. package/core/server/data/importer/importers/json-to-html.js +24 -4
  173. package/core/server/data/migrations/versions/6.55/2026-07-27-11-25-44-add-to-hash-to-redirects.js +51 -0
  174. package/core/server/data/migrations/versions/6.55/2026-07-28-12-08-00-remove-routes-hash-from-settings.js +5 -0
  175. package/core/server/data/schema/commands.js +5 -0
  176. package/core/server/data/schema/default-settings/default-settings.json +0 -4
  177. package/core/server/data/schema/schema.js +5 -1
  178. package/core/server/lib/image/image-size.js +3 -17
  179. package/core/server/lib/image/image-utils.js +15 -1
  180. package/core/server/lib/request-external.js +1 -1
  181. package/core/server/models/user.js +46 -70
  182. package/core/server/services/auth/session/session-service.js +28 -1
  183. package/core/server/services/automations/automations-api.js +21 -6
  184. package/core/server/services/automations/automations-api.ts +25 -6
  185. package/core/server/services/automations/automations-repository.ts +19 -0
  186. package/core/server/services/automations/database-automations-repository.js +116 -28
  187. package/core/server/services/automations/database-automations-repository.ts +118 -9
  188. package/core/server/services/automations/database-date.js +10 -0
  189. package/core/server/services/automations/database-date.ts +5 -0
  190. package/core/server/services/automations/poll.js +5 -2
  191. package/core/server/services/automations/poll.ts +7 -2
  192. package/core/server/services/automations/scheduler-poll-time.js +40 -0
  193. package/core/server/services/automations/scheduler-poll-time.ts +38 -0
  194. package/core/server/services/automations/service.js +15 -3
  195. package/core/server/services/automations/service.ts +16 -3
  196. package/core/server/services/comments/comments-service-emails.js +14 -8
  197. package/core/server/services/comments/comments-service.js +9 -4
  198. package/core/server/services/email-rendering/partials/email-wrapper.hbs +3 -1
  199. package/core/server/services/email-service/email-service-wrapper.js +6 -1
  200. package/core/server/services/email-service/email-templates/partials/styles.hbs +8 -0
  201. package/core/server/services/email-service/newsletter-email-event-storage.js +41 -10
  202. package/core/server/services/lib/dynamic-redirect-manager.js +12 -3
  203. package/core/server/services/link-redirection/link-redirect-repository.js +41 -4
  204. package/core/server/services/link-redirection/link-redirect.js +2 -0
  205. package/core/server/services/link-redirection/link-redirect.ts +3 -1
  206. package/core/server/services/link-redirection/link-redirects-service.js +38 -2
  207. package/core/server/services/link-tracking/click-event.js +3 -0
  208. package/core/server/services/link-tracking/index.js +38 -6
  209. package/core/server/services/link-tracking/link-click-repository.js +30 -9
  210. package/core/server/services/link-tracking/link-click-tracking-service.js +44 -3
  211. package/core/server/services/member-attribution/outbound-link-tagger.js +3 -1
  212. package/core/server/services/member-welcome-emails/member-welcome-email-renderer.js +19 -4
  213. package/core/server/services/member-welcome-emails/service.js +11 -3
  214. package/core/server/services/members/{importer → import-export}/config.js +4 -9
  215. package/core/server/services/members/{importer → import-export}/config.ts +4 -9
  216. package/core/server/services/members/import-export/csv/formula.js +16 -0
  217. package/core/server/services/members/import-export/csv/formula.ts +14 -0
  218. package/core/server/services/members/import-export/csv/index.js +12 -0
  219. package/core/server/services/members/import-export/csv/index.ts +3 -0
  220. package/core/server/services/members/import-export/csv/parse.js +64 -0
  221. package/core/server/services/members/import-export/csv/parse.ts +69 -0
  222. package/core/server/services/members/import-export/csv/serialize.js +13 -0
  223. package/core/server/services/members/import-export/csv/serialize.ts +18 -0
  224. package/core/server/services/members/import-export/export/exporter.js +202 -0
  225. package/core/server/services/members/import-export/export/exporter.ts +325 -0
  226. package/core/server/services/members/import-export/import/completion-email.js +84 -0
  227. package/core/server/services/members/import-export/import/completion-email.ts +141 -0
  228. package/core/server/services/members/import-export/import/importer.js +297 -0
  229. package/core/server/services/members/import-export/import/importer.ts +468 -0
  230. package/core/server/services/members/import-export/import/reader.js +32 -0
  231. package/core/server/services/members/import-export/import/reader.ts +33 -0
  232. package/core/server/services/members/import-export/import/row.js +33 -0
  233. package/core/server/services/members/import-export/import/row.ts +52 -0
  234. package/core/server/services/members/import-export/import/spool.js +30 -0
  235. package/core/server/services/members/import-export/import/spool.ts +38 -0
  236. package/core/server/services/members/import-export/index.js +99 -0
  237. package/core/server/services/members/import-export/index.ts +137 -0
  238. package/core/server/services/members/members-api/controllers/router-controller.js +7 -0
  239. package/core/server/services/members/service.js +25 -20
  240. package/core/server/services/members-custom-fields/values-service.js +19 -7
  241. package/core/server/services/members-custom-fields/values-service.ts +19 -7
  242. package/core/server/services/mentions/service.js +5 -1
  243. package/core/server/services/posts/posts-exporter.js +6 -4
  244. package/core/server/services/posts/posts-service.js +2 -2
  245. package/core/server/services/route-settings/activation-bridge.js +59 -49
  246. package/core/server/services/route-settings/activation-bridge.ts +91 -51
  247. package/core/server/services/route-settings/dynamic-routing-service.js +22 -51
  248. package/core/server/services/route-settings/index.js +1 -16
  249. package/core/server/services/settings/settings-service.js +0 -18
  250. package/core/server/services/themes/storage.js +9 -0
  251. package/core/server/services/themes/theme-storage.js +1 -1
  252. package/core/server/services/url/url-service-facade.js +1 -1
  253. package/core/server/services/url/url-service-facade.ts +8 -1
  254. package/core/server/web/api/endpoints/admin/routes.js +1 -0
  255. package/core/shared/config/defaults.json +2 -1
  256. package/core/shared/express.js +6 -1
  257. package/core/shared/labs.js +2 -1
  258. package/package.json +11 -11
  259. package/pnpm-lock.yaml +18 -18
  260. package/pnpm-workspace.yaml +8 -7
  261. package/components/tryghost-adapter-base-scheduling-0.1.1.tgz +0 -0
  262. package/core/built/admin/assets/Overview-j4NaeUNm.js +0 -1
  263. package/core/built/admin/assets/Web-_fD935OX.js +0 -2
  264. package/core/built/admin/assets/activitypub/layout-IdVq3lRy.js +0 -1
  265. package/core/built/admin/assets/audience-BKRdsFmy.js +0 -1
  266. package/core/built/admin/assets/checkbox-DA4-1UiI.js +0 -1
  267. package/core/built/admin/assets/chunk.524.45e0875f41b71e46e849.js +0 -25
  268. package/core/built/admin/assets/chunk.582.51defd4b009f0de8d4d5.js +0 -27
  269. package/core/built/admin/assets/code-editor-view-BKBrkffZ.js +0 -1
  270. package/core/built/admin/assets/comments-CzVNIT8b.js +0 -1
  271. package/core/built/admin/assets/constants-c-JNF9c2.js +0 -1
  272. package/core/built/admin/assets/dist-CT46_f_J.js +0 -1
  273. package/core/built/admin/assets/domain-B7_n5nfx.js +0 -1
  274. package/core/built/admin/assets/dropzone-DcuJEEUf.js +0 -5
  275. package/core/built/admin/assets/editor-BCiIyFjm.js +0 -8
  276. package/core/built/admin/assets/field-BW-M2Zwp.js +0 -1
  277. package/core/built/admin/assets/filters-DtcXMZDU.js +0 -1
  278. package/core/built/admin/assets/gift-link-modal-Bk8qpnMw.js +0 -1
  279. package/core/built/admin/assets/growth-Ck2s3JjR.js +0 -1
  280. package/core/built/admin/assets/index-BBf502Wg.js +0 -69
  281. package/core/built/admin/assets/index-BCCZKxP2.css +0 -1
  282. package/core/built/admin/assets/input-group-DjuYnNCd.js +0 -1
  283. package/core/built/admin/assets/label-picker-BdNAWuUX.js +0 -1
  284. package/core/built/admin/assets/labels-Cyayfd-e.js +0 -1
  285. package/core/built/admin/assets/layout-Bw4GnxXp.js +0 -1
  286. package/core/built/admin/assets/member-custom-fields-DvFyamLu.js +0 -1
  287. package/core/built/admin/assets/member-detail-Cg2vTqvJ.js +0 -1
  288. package/core/built/admin/assets/members-DsPxYAVm.js +0 -10
  289. package/core/built/admin/assets/modals-Bf_fyvHZ.js +0 -31
  290. package/core/built/admin/assets/moderation-Dlp7M5sa.js +0 -1
  291. package/core/built/admin/assets/multi-select-combobox-CH1o8XXQ.js +0 -1
  292. package/core/built/admin/assets/newsletter-CQ5CAYUI.js +0 -1
  293. package/core/built/admin/assets/note-BrAZ7Z0o.js +0 -1
  294. package/core/built/admin/assets/notifications-BnbHLOPC.js +0 -1
  295. package/core/built/admin/assets/onboarding-route-eU146kuA.js +0 -1
  296. package/core/built/admin/assets/overview-BpbaK-xu.js +0 -1
  297. package/core/built/admin/assets/post-analytics-header-D9pJ4iNm.js +0 -1
  298. package/core/built/admin/assets/post-analytics-provider-BDqcfBor.js +0 -1
  299. package/core/built/admin/assets/profile-dJ44Bn_W.js +0 -1
  300. package/core/built/admin/assets/select--2uVN9_-.js +0 -1
  301. package/core/built/admin/assets/settings-SD9bxIpt.js +0 -4
  302. package/core/built/admin/assets/social-urls-BvFZwYcE.js +0 -8
  303. package/core/built/admin/assets/switch-qp518gFQ.js +0 -1
  304. package/core/built/admin/assets/tabs-D3ZOYkM7.js +0 -1
  305. package/core/built/admin/assets/textarea-CTlb3Uu6.js +0 -1
  306. package/core/built/admin/assets/use-filter-params-DeQ8sX4N.js +0 -1
  307. package/core/built/admin/assets/web-DTZnnUGr.js +0 -1
  308. package/core/server/services/mail/templates/newsletter.html +0 -876
  309. package/core/server/services/members/csv/index.js +0 -10
  310. package/core/server/services/members/csv/index.ts +0 -3
  311. package/core/server/services/members/csv/parse.js +0 -94
  312. package/core/server/services/members/csv/parse.ts +0 -111
  313. package/core/server/services/members/csv/types.js +0 -2
  314. package/core/server/services/members/csv/types.ts +0 -39
  315. package/core/server/services/members/csv/unparse.js +0 -69
  316. package/core/server/services/members/csv/unparse.ts +0 -79
  317. package/core/server/services/members/exporter/query.js +0 -233
  318. package/core/server/services/members/importer/index.js +0 -30
  319. package/core/server/services/members/importer/labels.js +0 -125
  320. package/core/server/services/members/importer/members-csv-importer.js +0 -520
  321. package/core/server/services/route-settings/default-settings-manager.js +0 -63
  322. package/core/server/services/route-settings/route-settings.js +0 -170
  323. package/core/server/services/route-settings/settings-loader.js +0 -60
  324. package/core/server/services/route-settings/settings-path-manager.js +0 -47
  325. package/core/server/services/route-settings/validate.js +0 -440
  326. /package/core/server/services/members/{importer → import-export/import}/email-template.js +0 -0
  327. /package/core/server/services/members/{importer/members-csv-importer-stripe-utils.js → import-export/import/stripe-utils.js} +0 -0
@@ -0,0 +1,342 @@
1
+ // # Local File Base Storage module
2
+ // The (default) module for storing files using the local file system
3
+ import fs from 'fs-extra';
4
+ import path from 'path';
5
+ import type {NextFunction, Request, RequestHandler, Response} from 'express';
6
+ import type express from 'express';
7
+ import tpl from '@tryghost/tpl';
8
+ import errors from '@tryghost/errors';
9
+ import {StorageBase, type ReadOptions, type StorageFile} from 'ghost-storage-base';
10
+ import urlUtils from '../../../shared/url-utils';
11
+ import {errify} from '../../../shared/errify';
12
+
13
+ const serveStatic: typeof express.static = require('../../../shared/express').static;
14
+
15
+ const messages = {
16
+ notFound: 'File not found',
17
+ notFoundWithRef: 'File not found: {file}',
18
+ cannotRead: 'Could not read file: {file}',
19
+ invalidUrlParameter: `The URL "{url}" is not a valid URL for this site.`,
20
+ invalidPathParameter: 'The path "{path}" is not valid for this storage.'
21
+ };
22
+
23
+ export interface LocalStorageBaseErrorMessages {
24
+ notFound: string;
25
+ notFoundWithRef: string;
26
+ cannotRead: string;
27
+ }
28
+
29
+ export interface LocalStorageBaseOptions {
30
+ storagePath: string;
31
+ siteUrl?: string;
32
+ staticFileURLPrefix?: string;
33
+ errorMessages?: LocalStorageBaseErrorMessages;
34
+ }
35
+
36
+ /**
37
+ * Errors thrown by `serve-static` carry an HTTP status alongside the standard
38
+ * Error fields.
39
+ */
40
+ interface ServeStaticError extends Error {
41
+ statusCode?: number;
42
+ path?: string;
43
+ }
44
+
45
+ class LocalStorageBase extends StorageBase {
46
+ readonly staticFileURLPrefix: string | undefined;
47
+
48
+ readonly siteUrl: string | undefined;
49
+
50
+ readonly staticFileUrl: string;
51
+
52
+ readonly errorMessages: LocalStorageBaseErrorMessages;
53
+
54
+ constructor({storagePath, staticFileURLPrefix, siteUrl, errorMessages}: LocalStorageBaseOptions) {
55
+ super();
56
+
57
+ this.storagePath = storagePath;
58
+ this.staticFileURLPrefix = staticFileURLPrefix;
59
+ this.siteUrl = siteUrl;
60
+ this.staticFileUrl = `${siteUrl}${staticFileURLPrefix}`;
61
+ this.errorMessages = errorMessages || messages;
62
+ }
63
+
64
+ /**
65
+ * Normalizes a relative storage path and rejects traversal outside the storage root.
66
+ */
67
+ _normalizeStorageRelativePath(filePath?: string): string {
68
+ const normalized = path.posix.normalize(String(filePath || '')
69
+ .replaceAll('\\', '/')
70
+ .replace(/^\/+/, '')
71
+ .replace(/\/+$/, ''));
72
+
73
+ if (normalized === '.' || normalized === '..' || normalized.startsWith('../')) {
74
+ throw new errors.IncorrectUsageError({
75
+ message: tpl(messages.invalidPathParameter, {path: filePath})
76
+ });
77
+ }
78
+
79
+ return normalized;
80
+ }
81
+
82
+ /**
83
+ * Resolves a target directory and optional file name into a full path,
84
+ * validating the result is inside the storage root.
85
+ *
86
+ * Supports relative paths (preferred) and absolute paths (legacy).
87
+ * TODO: remove absolute path support once all callers pass relative paths
88
+ *
89
+ * @param targetDir absolute or relative directory
90
+ * @param fileName file name to normalize and append
91
+ * @returns resolved absolute path inside storagePath
92
+ */
93
+ _resolveAndValidateStoragePath(targetDir?: string, fileName?: string): string {
94
+ const resolvedRoot = path.resolve(this.storagePath);
95
+
96
+ // Resolve targetDir: if already inside storage root use as-is, otherwise treat as relative
97
+ let resolvedBase;
98
+ if (targetDir) {
99
+ const resolvedTargetDir = path.resolve(targetDir);
100
+ const relToRoot = path.relative(resolvedRoot, resolvedTargetDir);
101
+ if (relToRoot === '' || (!relToRoot.startsWith('..') && !path.isAbsolute(relToRoot))) {
102
+ resolvedBase = resolvedTargetDir;
103
+ } else {
104
+ resolvedBase = path.resolve(this.storagePath, targetDir);
105
+ }
106
+ } else {
107
+ resolvedBase = resolvedRoot;
108
+ }
109
+
110
+ // If fileName provided, normalize and resolve
111
+ let resolvedPath;
112
+ if (fileName) {
113
+ const normalizedFileName = this._normalizeStorageRelativePath(fileName);
114
+ resolvedPath = path.resolve(resolvedBase, normalizedFileName);
115
+ } else {
116
+ resolvedPath = resolvedBase;
117
+ }
118
+
119
+ // Validate the resolved path is strictly inside the storage root (not equal to it)
120
+ const relative = path.relative(resolvedRoot, resolvedPath);
121
+ if (relative === '' || relative === '..' || relative.startsWith('..' + path.sep) || path.isAbsolute(relative)) {
122
+ throw new errors.IncorrectUsageError({
123
+ message: tpl(messages.invalidPathParameter, {path: fileName || targetDir || ''})
124
+ });
125
+ }
126
+
127
+ return resolvedPath;
128
+ }
129
+
130
+ /**
131
+ * Saves the file to storage (the file system)
132
+ * - returns a promise which ultimately returns the full url to the uploaded file
133
+ */
134
+ async save(file: StorageFile, targetDir?: string): Promise<string> {
135
+ let targetFilename;
136
+
137
+ targetDir = targetDir
138
+ ? this._resolveAndValidateStoragePath(targetDir)
139
+ : this.getTargetDir(this.storagePath);
140
+
141
+ const filename = await this.getUniqueFileName(file, targetDir);
142
+
143
+ targetFilename = filename;
144
+
145
+ // Verify that we are saving directly under `targetDir` and not outside of it.
146
+ const expectedPrefix = path.join(path.resolve(targetDir), '/');
147
+ if (!path.resolve(targetFilename).startsWith(expectedPrefix)) {
148
+ throw new errors.BadRequestError({message: 'Cannot save to the given filename'});
149
+ }
150
+
151
+ await fs.mkdirs(targetDir);
152
+
153
+ try {
154
+ await fs.copy(file.path, targetFilename);
155
+ } catch (err) {
156
+ if ((err as NodeJS.ErrnoException).code === 'ENAMETOOLONG') {
157
+ throw new errors.BadRequestError({err: errify(err)});
158
+ }
159
+
160
+ throw err;
161
+ }
162
+
163
+ // The src for the image must be in URI format, not a file system path, which in Windows uses \
164
+ // For local file system storage can use relative path so add a slash
165
+ const fullUrl = (
166
+ urlUtils.urlJoin('/',
167
+ urlUtils.getSubdir(),
168
+ this.staticFileURLPrefix as string,
169
+ path.relative(this.storagePath, targetFilename))
170
+ ).replace(new RegExp(`\\${path.sep}`, 'g'), '/');
171
+
172
+ return fullUrl;
173
+ }
174
+
175
+ /**
176
+ * Saves a buffer in the targetPath
177
+ *
178
+ * @param buffer is an instance of Buffer
179
+ * @param targetPath relative path NOT including storage path to which the buffer should be written
180
+ * @returns a URL to retrieve the data
181
+ */
182
+ async saveRaw(buffer: Buffer, targetPath: string): Promise<string> {
183
+ const storagePath = path.join(this.storagePath, this._normalizeStorageRelativePath(targetPath));
184
+ const targetDir = path.dirname(storagePath);
185
+
186
+ await fs.mkdirs(targetDir);
187
+ await fs.writeFile(storagePath, buffer);
188
+
189
+ // For local file system storage can use relative path so add a slash
190
+ const fullUrl = (
191
+ urlUtils.urlJoin('/', urlUtils.getSubdir(),
192
+ this.staticFileURLPrefix as string,
193
+ targetPath)
194
+ ).replace(new RegExp(`\\${path.sep}`, 'g'), '/');
195
+
196
+ return fullUrl;
197
+ }
198
+
199
+ /**
200
+ * @param url full url under which the stored content is served, result of save method
201
+ * @returns relative path under which the content is stored
202
+ */
203
+ urlToPath(url: string): string {
204
+ let relativePath;
205
+
206
+ const prefix = urlUtils.urlJoin('/',
207
+ urlUtils.getSubdir(),
208
+ this.staticFileURLPrefix as string
209
+ );
210
+
211
+ if (url.startsWith(this.staticFileUrl)) {
212
+ // CASE: full path that includes the site url
213
+ relativePath = url.replace(this.staticFileUrl, '');
214
+ } else if (url.startsWith(prefix)) {
215
+ // CASE: The result of the save method doesn't include the site url. So we need to handle this case.
216
+ relativePath = url.replace(prefix, '');
217
+ } else {
218
+ throw new errors.IncorrectUsageError({
219
+ message: tpl(messages.invalidUrlParameter, {url})
220
+ });
221
+ }
222
+
223
+ try {
224
+ return this._normalizeStorageRelativePath(relativePath);
225
+ } catch (err) {
226
+ throw new errors.IncorrectUsageError({
227
+ message: tpl(messages.invalidUrlParameter, {url})
228
+ });
229
+ }
230
+ }
231
+
232
+ async exists(fileName: string, targetDir?: string): Promise<boolean> {
233
+ let filePath;
234
+
235
+ try {
236
+ filePath = this._resolveAndValidateStoragePath(targetDir, fileName);
237
+ } catch (err) {
238
+ if (err instanceof errors.IncorrectUsageError) {
239
+ return false;
240
+ }
241
+
242
+ throw err;
243
+ }
244
+
245
+ try {
246
+ await fs.stat(filePath);
247
+ return true;
248
+ } catch {
249
+ return false;
250
+ }
251
+ }
252
+
253
+ /**
254
+ * For some reason send divides the max age number by 1000
255
+ * Fallthrough: false ensures that if an image isn't found, it automatically 404s
256
+ * Wrap server static errors
257
+ */
258
+ serve(): RequestHandler {
259
+ const {storagePath, errorMessages} = this;
260
+
261
+ return function serveStaticContent(req: Request, res: Response, next: NextFunction) {
262
+ return serveStatic(
263
+ storagePath,
264
+ {
265
+ maxAge: (365 * 24 * 60 * 60 * 1000), // 1 year in ms
266
+ fallthrough: false
267
+ }
268
+ )(req, res, (err?: ServeStaticError) => {
269
+ if (err) {
270
+ if (err.statusCode === 404) {
271
+ return next(new errors.NotFoundError({
272
+ message: tpl(errorMessages.notFound),
273
+ code: 'STATIC_FILE_NOT_FOUND',
274
+ property: err.path
275
+ }));
276
+ }
277
+
278
+ if (err.statusCode === 400) {
279
+ return next(new errors.BadRequestError({err: err}));
280
+ }
281
+
282
+ if (err.statusCode === 403) {
283
+ return next(new errors.NoPermissionError({err: err}));
284
+ }
285
+
286
+ if (err.name === 'RangeNotSatisfiableError') {
287
+ return next(new errors.RangeNotSatisfiableError({err}));
288
+ }
289
+
290
+ return next(new errors.InternalServerError({err: err}));
291
+ }
292
+
293
+ next();
294
+ });
295
+ };
296
+ }
297
+
298
+ async delete(fileName: string, targetDir?: string): Promise<void> {
299
+ const filePath = this._resolveAndValidateStoragePath(targetDir, fileName);
300
+ return await fs.remove(filePath);
301
+ }
302
+
303
+ /**
304
+ * Reads bytes from disk for a target file
305
+ * - path of target file (without content path!)
306
+ */
307
+ async read(options?: Partial<ReadOptions>): Promise<Buffer> {
308
+ options = options || {};
309
+
310
+ const normalizedPath = this._normalizeStorageRelativePath(options.path);
311
+ const targetPath = path.join(this.storagePath, normalizedPath);
312
+
313
+ try {
314
+ return await fs.readFile(targetPath);
315
+ } catch (rawError) {
316
+ const err = errify(rawError);
317
+ const code = (rawError as NodeJS.ErrnoException).code;
318
+
319
+ if (code === 'ENOENT' || code === 'ENOTDIR') {
320
+ throw new errors.NotFoundError({
321
+ err: err,
322
+ message: tpl(this.errorMessages.notFoundWithRef, {file: options.path})
323
+ });
324
+ }
325
+
326
+ if (code === 'ENAMETOOLONG') {
327
+ throw new errors.BadRequestError({err: err});
328
+ }
329
+
330
+ if (code === 'EACCES') {
331
+ throw new errors.NoPermissionError({err: err});
332
+ }
333
+
334
+ throw new errors.InternalServerError({
335
+ err: err,
336
+ message: tpl(this.errorMessages.cannotRead, {file: options.path})
337
+ });
338
+ }
339
+ }
340
+ }
341
+
342
+ export default LocalStorageBase;
@@ -1,68 +1,54 @@
1
- const config = require('../../../shared/config');
2
- const urlUtils = require('../../../shared/url-utils').default;
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getLocalImagesStoragePath = getLocalImagesStoragePath;
7
+ exports.isLocalImage = isLocalImage;
8
+ exports.isInternalImage = isInternalImage;
9
+ const config_1 = __importDefault(require("../../../shared/config"));
10
+ const url_utils_1 = __importDefault(require("../../../shared/url-utils"));
3
11
  /**
4
12
  * @TODO: move `events.js` to here - e.g. storageUtils.getStorage
5
13
  */
6
-
7
14
  /**
8
- * Sanitizes a given URL or path for an image to be readable by the local file storage
9
- * as storage needs the path without `/content/images/` prefix
10
- * Always returns {string} url
11
- * @param {string} imagePath
12
- * @returns {string} imagePath
13
- * @description Takes a url or filepath and returns a filepath with is readable
14
- * for the local file storage.
15
+ * Sanitizes a given URL or path for an image to be readable by the local file
16
+ * storage, as storage needs the path without the `/content/images/` prefix.
17
+ *
18
+ * Takes a url or filepath and returns a filepath which is readable for the
19
+ * local file storage.
15
20
  */
16
- exports.getLocalImagesStoragePath = function getLocalImagesStoragePath(imagePath) {
21
+ function getLocalImagesStoragePath(imagePath) {
17
22
  // The '/' in urlJoin is necessary to add the '/' to `content/images`, if no subdirectory is setup
18
- const urlRegExp = new RegExp(`^${urlUtils.urlJoin(
19
- urlUtils.urlFor('home', true),
20
- urlUtils.getSubdir(),
21
- '/',
22
- urlUtils.STATIC_IMAGE_URL_PREFIX)}`
23
- );
24
-
25
- const filePathRegExp = new RegExp(`^${urlUtils.urlJoin(
26
- urlUtils.getSubdir(),
27
- '/',
28
- urlUtils.STATIC_IMAGE_URL_PREFIX)}`
29
- );
30
-
23
+ const urlRegExp = new RegExp(`^${url_utils_1.default.urlJoin(url_utils_1.default.urlFor('home', true), url_utils_1.default.getSubdir(), '/', url_utils_1.default.STATIC_IMAGE_URL_PREFIX)}`);
24
+ const filePathRegExp = new RegExp(`^${url_utils_1.default.urlJoin(url_utils_1.default.getSubdir(), '/', url_utils_1.default.STATIC_IMAGE_URL_PREFIX)}`);
31
25
  if (imagePath.match(urlRegExp)) {
32
26
  return imagePath.replace(urlRegExp, '');
33
- } else if (imagePath.match(filePathRegExp)) {
27
+ }
28
+ else if (imagePath.match(filePathRegExp)) {
34
29
  return imagePath.replace(filePathRegExp, '');
35
- } else {
30
+ }
31
+ else {
36
32
  return imagePath;
37
33
  }
38
- };
39
-
34
+ }
40
35
  /**
41
- * @description compares the imagePath with a regex that reflects our local file storage
42
- * @param {string} imagePath as URL or filepath
43
- * @returns {boolean}
36
+ * Compares the imagePath with a regex that reflects our local file storage
37
+ *
38
+ * @param imagePath as URL or filepath
44
39
  */
45
-
46
- exports.isLocalImage = function isLocalImage(imagePath) {
47
- const localImagePath = this.getLocalImagesStoragePath(imagePath);
48
-
49
- if (localImagePath !== imagePath) {
50
- return true;
51
- } else {
52
- return false;
53
- }
54
- };
55
-
40
+ function isLocalImage(imagePath) {
41
+ return getLocalImagesStoragePath(imagePath) !== imagePath;
42
+ }
56
43
  /**
57
- * @description Checks whether the image is managed by Ghost storage (local or CDN)
58
- * @param {string} imagePath as URL or filepath
59
- * @returns {boolean}
44
+ * Checks whether the image is managed by Ghost storage (local or CDN)
45
+ *
46
+ * @param imagePath as URL or filepath
60
47
  */
61
- exports.isInternalImage = function isInternalImage(imagePath) {
62
- if (this.isLocalImage(imagePath)) {
48
+ function isInternalImage(imagePath) {
49
+ if (isLocalImage(imagePath)) {
63
50
  return true;
64
51
  }
65
-
66
- const imageBaseUrl = (config.get('urls:image') || '').replace(/\/+$/, '');
52
+ const imageBaseUrl = (config_1.default.get('urls:image') || '').replace(/\/+$/, '');
67
53
  return !!(imageBaseUrl && imagePath.startsWith(imageBaseUrl + '/'));
68
- };
54
+ }
@@ -0,0 +1,60 @@
1
+ import config from '../../../shared/config';
2
+ import urlUtils from '../../../shared/url-utils';
3
+
4
+ /**
5
+ * @TODO: move `events.js` to here - e.g. storageUtils.getStorage
6
+ */
7
+
8
+ /**
9
+ * Sanitizes a given URL or path for an image to be readable by the local file
10
+ * storage, as storage needs the path without the `/content/images/` prefix.
11
+ *
12
+ * Takes a url or filepath and returns a filepath which is readable for the
13
+ * local file storage.
14
+ */
15
+ export function getLocalImagesStoragePath(imagePath: string): string {
16
+ // The '/' in urlJoin is necessary to add the '/' to `content/images`, if no subdirectory is setup
17
+ const urlRegExp = new RegExp(`^${urlUtils.urlJoin(
18
+ urlUtils.urlFor('home', true),
19
+ urlUtils.getSubdir(),
20
+ '/',
21
+ urlUtils.STATIC_IMAGE_URL_PREFIX)}`
22
+ );
23
+
24
+ const filePathRegExp = new RegExp(`^${urlUtils.urlJoin(
25
+ urlUtils.getSubdir(),
26
+ '/',
27
+ urlUtils.STATIC_IMAGE_URL_PREFIX)}`
28
+ );
29
+
30
+ if (imagePath.match(urlRegExp)) {
31
+ return imagePath.replace(urlRegExp, '');
32
+ } else if (imagePath.match(filePathRegExp)) {
33
+ return imagePath.replace(filePathRegExp, '');
34
+ } else {
35
+ return imagePath;
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Compares the imagePath with a regex that reflects our local file storage
41
+ *
42
+ * @param imagePath as URL or filepath
43
+ */
44
+ export function isLocalImage(imagePath: string): boolean {
45
+ return getLocalImagesStoragePath(imagePath) !== imagePath;
46
+ }
47
+
48
+ /**
49
+ * Checks whether the image is managed by Ghost storage (local or CDN)
50
+ *
51
+ * @param imagePath as URL or filepath
52
+ */
53
+ export function isInternalImage(imagePath: string): boolean {
54
+ if (isLocalImage(imagePath)) {
55
+ return true;
56
+ }
57
+
58
+ const imageBaseUrl = (config.get('urls:image') || '').replace(/\/+$/, '');
59
+ return !!(imageBaseUrl && imagePath.startsWith(imageBaseUrl + '/'));
60
+ }
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const automationsApi = __importStar(require("../../services/automations/automations-api"));
37
+ const controller = {
38
+ docName: 'automation_action_links',
39
+ browse: {
40
+ headers: {
41
+ cacheInvalidate: false
42
+ },
43
+ options: [
44
+ 'automation_id',
45
+ 'action_id'
46
+ ],
47
+ validation: {
48
+ options: {
49
+ automation_id: {
50
+ required: true
51
+ },
52
+ action_id: {
53
+ required: true
54
+ }
55
+ }
56
+ },
57
+ permissions: {
58
+ docName: 'automations',
59
+ method: 'read'
60
+ },
61
+ async query(frame) {
62
+ return {
63
+ data: await automationsApi.browseActionLinks(frame.options.automation_id, frame.options.action_id)
64
+ };
65
+ }
66
+ }
67
+ };
68
+ module.exports = controller;
@@ -0,0 +1,46 @@
1
+ import * as automationsApi from '../../services/automations/automations-api';
2
+
3
+ type BrowseFrame = {
4
+ options: {
5
+ automation_id: string;
6
+ action_id: string;
7
+ };
8
+ };
9
+
10
+ const controller = {
11
+ docName: 'automation_action_links',
12
+
13
+ browse: {
14
+ headers: {
15
+ cacheInvalidate: false
16
+ },
17
+ options: [
18
+ 'automation_id',
19
+ 'action_id'
20
+ ],
21
+ validation: {
22
+ options: {
23
+ automation_id: {
24
+ required: true
25
+ },
26
+ action_id: {
27
+ required: true
28
+ }
29
+ }
30
+ },
31
+ permissions: {
32
+ docName: 'automations',
33
+ method: 'read'
34
+ },
35
+ async query(frame: BrowseFrame) {
36
+ return {
37
+ data: await automationsApi.browseActionLinks(
38
+ frame.options.automation_id,
39
+ frame.options.action_id
40
+ )
41
+ };
42
+ }
43
+ }
44
+ };
45
+
46
+ module.exports = controller;
@@ -1,4 +1,5 @@
1
1
  const moment = require('moment-timezone');
2
+ const path = require('path');
2
3
  const dbBackup = require('../../data/db/backup');
3
4
  const exporter = require('../../data/exporter');
4
5
  const importer = require('../../data/importer');
@@ -29,6 +30,11 @@ const controller = {
29
30
  }
30
31
  },
31
32
  query(frame) {
33
+ const filename = frame.options.filename;
34
+ if (filename && path.basename(filename) !== filename) {
35
+ throw new errors.ValidationError({message: 'Export filename must not contain path separators'});
36
+ }
37
+
32
38
  // NOTE: we need to have `include` property available as backupDatabase uses it internally
33
39
  Object.assign(frame.options, {include: frame.options.withRelated});
34
40
 
@@ -11,6 +11,10 @@ module.exports = {
11
11
  return apiFramework.pipeline(require('./automations'), localUtils);
12
12
  },
13
13
 
14
+ get automationActionLinks() {
15
+ return apiFramework.pipeline(require('./automation-action-links'), localUtils);
16
+ },
17
+
14
18
  get automationEmailPreviews() {
15
19
  return apiFramework.pipeline(require('./automation-email-previews'), localUtils);
16
20
  },