ghost 6.40.0 → 6.41.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 (184) hide show
  1. package/core/boot.js +8 -8
  2. package/core/built/admin/assets/{PolarAngleAxis-D7m1zwVk.js → PolarAngleAxis-Bh28wbPV.js} +1 -1
  3. package/core/built/admin/assets/{_baseAssignValue-BDsvYKNK.js → _baseAssignValue-hdDG700N.js} +1 -1
  4. package/core/built/admin/assets/{a-large-small-DxRNdz0F.js → a-large-small-BiK5_5LV.js} +1 -1
  5. package/core/built/admin/assets/{account-migration-SYnE1jBW.js → account-migration-DsSHanIB.js} +1 -1
  6. package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +1 -1
  7. package/core/built/admin/assets/admin-x-settings/{code-editor-view-Bj9rgbLw.mjs → code-editor-view-BEIdFhw3.mjs} +2 -2
  8. package/core/built/admin/assets/admin-x-settings/{index-BrZ9YAzd.mjs → index-BEh3nFEy.mjs} +3 -3
  9. package/core/built/admin/assets/admin-x-settings/{index-DRndIh9T.mjs → index-BLF2n6YB.mjs} +2 -2
  10. package/core/built/admin/assets/admin-x-settings/{index-CIl25Teq.mjs → index-BmPIWs5F.mjs} +3 -3
  11. package/core/built/admin/assets/admin-x-settings/{index-CNyNY3Wh.mjs → index-CP9YnNzc.mjs} +3 -3
  12. package/core/built/admin/assets/admin-x-settings/{index-SGr5sE2h.mjs → index-Cd2Ns-D2.mjs} +2 -2
  13. package/core/built/admin/assets/admin-x-settings/{index-PGoLP2kh.mjs → index-CjqAcKpK.mjs} +5 -5
  14. package/core/built/admin/assets/admin-x-settings/{index-pCIv_7bB.mjs → index-CnMcD9aj.mjs} +6212 -6158
  15. package/core/built/admin/assets/admin-x-settings/{index-CPCcgad-.mjs → index-LzsrZC40.mjs} +3 -3
  16. package/core/built/admin/assets/admin-x-settings/{index-DPqY4h5h.mjs → index-ZYW0UW7I.mjs} +3 -3
  17. package/core/built/admin/assets/admin-x-settings/{modals-lo32WZLi.mjs → modals-C48Tq1rW.mjs} +9 -9
  18. package/core/built/admin/assets/{arrow-right-BixoEua4.js → arrow-right-Bk43nMGd.js} +1 -1
  19. package/core/built/admin/assets/{at-sign-BsHNViA_.js → at-sign-DWOf36uj.js} +1 -1
  20. package/core/built/admin/assets/{audience-0RCWjbIK.js → audience-CvP78Gug.js} +1 -1
  21. package/core/built/admin/assets/{automations-DTUfYvf6.js → automations-BBWZhrha.js} +1 -1
  22. package/core/built/admin/assets/{automations-DiGkcVt-.js → automations-CmulGWwu.js} +1 -1
  23. package/core/built/admin/assets/{avatar-flipboard-BgoH-wIq.js → avatar-flipboard-CVf-N6wp.js} +1 -1
  24. package/core/built/admin/assets/{bluesky-sharing-C2xwSoS-.js → bluesky-sharing-DVC3I9Um.js} +1 -1
  25. package/core/built/admin/assets/{chart-X5cLIH1X.js → chart-CUKSNmKY.js} +1 -1
  26. package/core/built/admin/assets/{checkbox-DMamS4oG.js → checkbox-B_yDSM8z.js} +1 -1
  27. package/core/built/admin/assets/{chevron-up-CVgBkTk5.js → chevron-up-DuaKtE_M.js} +1 -1
  28. package/core/built/admin/assets/{chunk.941.f47098cbbce89a049e03.js → chunk.331.2ec2e2702eda02de96cd.js} +3 -3
  29. package/core/built/admin/assets/{chunk.524.a1d668dcc84709a5d702.js → chunk.524.49cc01002e1a5dba8ef9.js} +4 -4
  30. package/core/built/admin/assets/{chunk.582.180bf2525cf552f5ac5f.js → chunk.582.4bbc54c5f7d3dfb93288.js} +6 -6
  31. package/core/built/admin/assets/{circle-alert-Dg3UkvRP.js → circle-alert-bcK_Yqap.js} +1 -1
  32. package/core/built/admin/assets/{code-editor-view-CAICoZJe.js → code-editor-view-Bl4Jzjhk.js} +1 -1
  33. package/core/built/admin/assets/{comments-BHru9ZQ8.js → comments-DMea0nul.js} +1 -1
  34. package/core/built/admin/assets/{content-helpers-BByMp-Y5.js → content-helpers-B2yRzdvJ.js} +1 -1
  35. package/core/built/admin/assets/{copy-D7jqplrv.js → copy-DVtQi_VJ.js} +1 -1
  36. package/core/built/admin/assets/{data-list-DUaY9nlT.js → data-list-BF4GeUmt.js} +1 -1
  37. package/core/built/admin/assets/{deleted-feed-item-jLcSKuul.js → deleted-feed-item-DO2u3qmi.js} +1 -1
  38. package/core/built/admin/assets/{dropzone-DSPf3N_o.js → dropzone-Cs4DrURS.js} +1 -1
  39. package/core/built/admin/assets/{edit-profile-BvU2-lW5.js → edit-profile-CnuBNQhO.js} +1 -1
  40. package/core/built/admin/assets/{editor-D3fcjt_F.js → editor-CTO9FIUW.js} +1 -1
  41. package/core/built/admin/assets/{empty-indicator-2Pjmce3n.js → empty-indicator-BvyaMcmw.js} +1 -1
  42. package/core/built/admin/assets/{en-Bu-6PYn5.js → en-CjPXRj8j.js} +1 -1
  43. package/core/built/admin/assets/{feed-CygLK1Ad.js → feed-D2wk4cCS.js} +1 -1
  44. package/core/built/admin/assets/{filter-query-core-a0PNU2EO.js → filter-query-core-VEnwTCyO.js} +1 -1
  45. package/core/built/admin/assets/{filters-jiyRsLMH.js → filters-DtIsuqv5.js} +1 -1
  46. package/core/built/admin/assets/{gh-chart-CEhBBu3F.js → gh-chart-JxzKqaoO.js} +1 -1
  47. package/core/built/admin/assets/{ghost-0ab96884815339963364804ab266c99f.js → ghost-30c3dedbe1cca943b92efa7efd2710e4.js} +36 -21
  48. package/core/built/admin/assets/{growth-Fz3Oatml.js → growth-CPNrBpHK.js} +1 -1
  49. package/core/built/admin/assets/{hash-CeFrjo3C.js → hash-CJzbTA5k.js} +1 -1
  50. package/core/built/admin/assets/{inbox-CCirPaG2.js → inbox-CVVAkCmd.js} +1 -1
  51. package/core/built/admin/assets/{index-Dd2BJRZH.js → index-AkQJUr1z.js} +1 -1
  52. package/core/built/admin/assets/{index-CYPN5csJ.js → index-BLWOUf31.js} +1 -1
  53. package/core/built/admin/assets/{index-D7Ui3Fih.js → index-BRBRkRqa.js} +1 -1
  54. package/core/built/admin/assets/{index-CFEzD2_M.js → index-BkbhBpVC.js} +3 -3
  55. package/core/built/admin/assets/{index-BYwL7gFN.js → index-BxFFWYdP.js} +1 -1
  56. package/core/built/admin/assets/{index-D3LfHeBl.js → index-C5_ZKpC1.js} +1 -1
  57. package/core/built/admin/assets/{index-Bjw2qAph.js → index-Cdbufdfy.js} +1 -1
  58. package/core/built/admin/assets/{index-BTf6YUBb.js → index-Cq5vsvxE.js} +1 -1
  59. package/core/built/admin/assets/{index-DPbi4ie_.js → index-CuGoX8Ub.js} +1 -1
  60. package/core/built/admin/assets/{index-CsaU9dfl.js → index-CuH5raTz.js} +1 -1
  61. package/core/built/admin/assets/{index-Bf83suMc.js → index-D3A8RTi0.js} +1 -1
  62. package/core/built/admin/assets/{index-Dskrbhv3.js → index-DIyK69JM.js} +1 -1
  63. package/core/built/admin/assets/{index-GExKMpy_.js → index-DLFVpwt3.js} +1 -1
  64. package/core/built/admin/assets/{index-BOMdiHRG.js → index-DQZp_NsL.js} +1 -1
  65. package/core/built/admin/assets/{index-2qcjFCQs.js → index-DR74ulj2.js} +1 -1
  66. package/core/built/admin/assets/{index-WKMARia9.js → index-DYE2untj.js} +1 -1
  67. package/core/built/admin/assets/{index-DNZUJQ8v.js → index-DssW7xhk.js} +1 -1
  68. package/core/built/admin/assets/{index-Cq4mS7QK.js → index-DvyNqFct.js} +1 -1
  69. package/core/built/admin/assets/{index-C7xLyJVb.js → index-DxB7NHpG.js} +1 -1
  70. package/core/built/admin/assets/{index-Bw4DM4Pr.js → index-IDaHqjnu.js} +1 -1
  71. package/core/built/admin/assets/{index-ClOB5u8L.js → index-TtBMzSeP.js} +1 -1
  72. package/core/built/admin/assets/{index-takfwHSn.js → index-UMWVIyz7.js} +1 -1
  73. package/core/built/admin/assets/{koenig-lexical-BJM9wAwQ.js → koenig-lexical-DVAmevhl.js} +1 -1
  74. package/core/built/admin/assets/{kpi-card-BP-2yicn.js → kpi-card-D3CzvsrI.js} +1 -1
  75. package/core/built/admin/assets/{kpi-card-DOplXdDT.js → kpi-card-DV-nN7xT.js} +1 -1
  76. package/core/built/admin/assets/{kpi-tabs-Rq5jJW2Z.js → kpi-tabs-B3NkaTwb.js} +1 -1
  77. package/core/built/admin/assets/{kpis-yHgME_iy.js → kpis-CVwF08QL.js} +1 -1
  78. package/core/built/admin/assets/{label-fijjy1Mb.js → label-BiPpMny2.js} +1 -1
  79. package/core/built/admin/assets/{links-s3IeAkbo.js → links-BY0Uo6O5.js} +1 -1
  80. package/core/built/admin/assets/{list-page-CUcB5ebB.js → list-page-COHNMpOi.js} +1 -1
  81. package/core/built/admin/assets/{loader-circle-C7mWzo52.js → loader-circle-B1uEfpbZ.js} +1 -1
  82. package/core/built/admin/assets/{mail-9TFkAaAh.js → mail-ccQhEr7k.js} +1 -1
  83. package/core/built/admin/assets/{main-layout-XtviVkg1.js → main-layout-BYKdA6Z1.js} +1 -1
  84. package/core/built/admin/assets/{members-XkHao5Yf.js → members-DkpEnvG5.js} +1 -1
  85. package/core/built/admin/assets/minus-C68D81nx.js +1 -0
  86. package/core/built/admin/assets/{modals-B6R7oqvb.js → modals-X6RSWn5Y.js} +3 -3
  87. package/core/built/admin/assets/{moderation-B2Tixfce.js → moderation-BFrrwfvx.js} +1 -1
  88. package/core/built/admin/assets/{newsletter-CbESXhxf.js → newsletter-n7kc6cxR.js} +1 -1
  89. package/core/built/admin/assets/{newsletters-DOIL_cOY.js → newsletters-CSCa3QdE.js} +1 -1
  90. package/core/built/admin/assets/{note-mUikaB-p.js → note-CMZjeK8e.js} +1 -1
  91. package/core/built/admin/assets/{onboarding-route-Dqv2nP7b.js → onboarding-route-BaX0a-pu.js} +1 -1
  92. package/core/built/admin/assets/{overview-yZ-7KJdx.js → overview-CS9dmE_6.js} +1 -1
  93. package/core/built/admin/assets/{pagemenu-TxGvHwRO.js → pagemenu-O_y8Jm4v.js} +1 -1
  94. package/core/built/admin/assets/{pencil-CKTgzafp.js → pencil-BcymGPnE.js} +1 -1
  95. package/core/built/admin/assets/{pin-BU_u-xRX.js → pin-Bl3nSr7I.js} +1 -1
  96. package/core/built/admin/assets/{post-analytics-CWA8invj.js → post-analytics-DW-TKu-t.js} +1 -1
  97. package/core/built/admin/assets/{post-analytics-context-BWDhMRq_.js → post-analytics-context-D7PgEMo8.js} +1 -1
  98. package/core/built/admin/assets/{post-analytics-header-Bm0mcxjK.js → post-analytics-header-B8C0vA5Y.js} +1 -1
  99. package/core/built/admin/assets/{post-share-modal-D8BtRD2u.js → post-share-modal-D0OlASBQ.js} +1 -1
  100. package/core/built/admin/assets/{posts-u-CAKddc.js → posts-BZaw4-dc.js} +1 -1
  101. package/core/built/admin/assets/{power-DR9gpiQR.js → power-DWm6pts5.js} +1 -1
  102. package/core/built/admin/assets/{referrers-DV_Ihg9y.js → referrers-BsZ85Z0a.js} +1 -1
  103. package/core/built/admin/assets/{repeat-uXmOJ6y8.js → repeat-DQay9e22.js} +1 -1
  104. package/core/built/admin/assets/{reply-9ZyqaxI-.js → reply-CoNqmExw.js} +1 -1
  105. package/core/built/admin/assets/{rocket-DMOr1OCt.js → rocket-Dne6P_8f.js} +1 -1
  106. package/core/built/admin/assets/{select-CPSy44ld.js → select-BfWJkrKc.js} +1 -1
  107. package/core/built/admin/assets/{settings-krk0432u.js → settings-BoXzKMhL.js} +1 -1
  108. package/core/built/admin/assets/{settings-66Zv8QvL.js → settings-rRY210wx.js} +25 -25
  109. package/core/built/admin/assets/{share-modal-BRmGllZq.js → share-modal-CtdLQbnQ.js} +1 -1
  110. package/core/built/admin/assets/{sort-button-5fyeViQL.js → sort-button-7pVgpa0d.js} +1 -1
  111. package/core/built/admin/assets/{source-icon-DeeHyZRn.js → source-icon-Dtua85oN.js} +1 -1
  112. package/core/built/admin/assets/{sprout-956Q4On2.js → sprout-CpwUJAPT.js} +1 -1
  113. package/core/built/admin/assets/{square-BaCvbKJY.js → square-CDrQjv8R.js} +1 -1
  114. package/core/built/admin/assets/{stats-BVKNZJO6.js → stats-Yo9Gco0_.js} +1 -1
  115. package/core/built/admin/assets/{stats-view-gtyaqudG.js → stats-view-DubukX9V.js} +1 -1
  116. package/core/built/admin/assets/{step-1-09wdjzdX.js → step-1-CH8jWKi7.js} +1 -1
  117. package/core/built/admin/assets/{step-2-8iaQ0efq.js → step-2-CWy7Qi0Q.js} +1 -1
  118. package/core/built/admin/assets/{step-3-BuHAZbdD.js → step-3-CNiDFH17.js} +1 -1
  119. package/core/built/admin/assets/{table-DTofVyjA.js → table-qQi9EXB1.js} +1 -1
  120. package/core/built/admin/assets/{tabs-CL5eaCGL.js → tabs-M6kFEqPE.js} +1 -1
  121. package/core/built/admin/assets/{tags-DJrbEw5-.js → tags-74U6lgwY.js} +1 -1
  122. package/core/built/admin/assets/{tags-U3lciBmd.js → tags-DuMshkTz.js} +1 -1
  123. package/core/built/admin/assets/{textarea-DdDuNixZ.js → textarea-CGBp9nOo.js} +1 -1
  124. package/core/built/admin/assets/{tiers-DXKbghdk.js → tiers-D2DYHaeO.js} +1 -1
  125. package/core/built/admin/assets/{toggle-group-DaZKTYDS.js → toggle-group-GYp_w6Po.js} +1 -1
  126. package/core/built/admin/assets/{topic-filter-BzqiVOAU.js → topic-filter-N3CzJ01P.js} +1 -1
  127. package/core/built/admin/assets/{trash-CKK_p9G5.js → trash-CUqAK3SQ.js} +1 -1
  128. package/core/built/admin/assets/{underline-Bq0P-5Zy.js → underline-CwuJG-Ql.js} +1 -1
  129. package/core/built/admin/assets/{undo-2-CcBtw-tW.js → undo-2-D6ZRqONE.js} +1 -1
  130. package/core/built/admin/assets/{upload-BcylHEMw.js → upload-BLiT66bJ.js} +1 -1
  131. package/core/built/admin/assets/{use-growth-stats-Ci0gymWp.js → use-growth-stats-u18So4Gq.js} +1 -1
  132. package/core/built/admin/assets/{use-simple-pagination-ByaFwxfZ.js → use-simple-pagination-Dp2sTt2S.js} +1 -1
  133. package/core/built/admin/assets/{user-round-check-D0I7NO1g.js → user-round-check-BDomHaso.js} +1 -1
  134. package/core/built/admin/assets/{user-round-x-DeGVH29o.js → user-round-x-Cc0RL-V9.js} +1 -1
  135. package/core/built/admin/assets/{virtual-list-window-S0xZxQAf.js → virtual-list-window-BnjCoNIr.js} +1 -1
  136. package/core/built/admin/assets/{wallet-cards-B9TQg4x3.js → wallet-cards-CVZ99Nm3.js} +1 -1
  137. package/core/built/admin/assets/{web-BRN2mUpo.js → web-B_MLxGxZ.js} +1 -1
  138. package/core/built/admin/index.html +5 -5
  139. package/core/server/{services/custom-redirects/file-store.js → adapters/redirects/FileStore.js} +6 -5
  140. package/core/server/{services/custom-redirects/file-store.ts → adapters/redirects/FileStore.ts} +6 -4
  141. package/core/server/adapters/redirects/RedirectsStoreBase.d.ts +9 -0
  142. package/core/server/adapters/redirects/RedirectsStoreBase.js +8 -0
  143. package/core/server/{services/custom-redirects/gcs-store.js → adapters/redirects/S3RedirectsStore.js} +50 -18
  144. package/core/server/adapters/redirects/S3RedirectsStore.ts +163 -0
  145. package/core/server/adapters/scheduling/scheduling-base.js +41 -0
  146. package/core/server/adapters/scheduling/types.js +2 -0
  147. package/core/server/adapters/scheduling/types.ts +36 -0
  148. package/core/server/api/endpoints/authentication.js +26 -5
  149. package/core/server/api/endpoints/schedules.js +0 -30
  150. package/core/server/api/endpoints/utils/serializers/output/authentication.js +10 -0
  151. package/core/server/data/migrations/versions/6.41/2026-05-13-12-00-00-rename-reset-all-passwords-permission.js +21 -0
  152. package/core/server/data/schema/fixtures/fixtures.json +3 -3
  153. package/core/server/models/api-key.js +18 -0
  154. package/core/server/models/user.js +18 -0
  155. package/core/server/services/adapter-manager/config.js +6 -0
  156. package/core/server/services/adapter-manager/index.js +1 -0
  157. package/core/server/services/auth/index.js +4 -0
  158. package/core/server/services/auth/reset-authentication.js +50 -0
  159. package/core/server/services/auth/reset-authentication.ts +75 -0
  160. package/core/server/services/automations/index.js +21 -15
  161. package/core/server/services/custom-redirects/index.js +2 -4
  162. package/core/server/services/gifts/gift-bookshelf-repository.js +7 -0
  163. package/core/server/services/gifts/gift-bookshelf-repository.ts +10 -0
  164. package/core/server/services/gifts/gift-reminder-scheduler.js +94 -0
  165. package/core/server/services/gifts/gift-reminder-scheduler.ts +109 -0
  166. package/core/server/services/gifts/gift-repository.ts +1 -0
  167. package/core/server/services/gifts/gift-service-wrapper.js +10 -7
  168. package/core/server/services/gifts/gift-service.js +1 -32
  169. package/core/server/services/gifts/gift-service.ts +3 -59
  170. package/core/server/services/internal-keys/index.js +1 -3
  171. package/core/server/services/internal-keys/index.ts +11 -3
  172. package/core/server/services/post-scheduling/index.js +16 -28
  173. package/core/server/services/post-scheduling/index.ts +14 -0
  174. package/core/server/services/post-scheduling/post-scheduling.js +115 -0
  175. package/core/server/services/post-scheduling/post-scheduling.ts +138 -0
  176. package/core/server/services/users.js +29 -16
  177. package/core/server/web/api/endpoints/admin/middleware.js +8 -4
  178. package/core/server/web/api/endpoints/admin/routes.js +1 -1
  179. package/core/shared/config/defaults.json +5 -0
  180. package/core/shared/labs.js +2 -1
  181. package/package.json +1 -1
  182. package/core/built/admin/assets/minus-DPrXh11r.js +0 -1
  183. package/core/server/services/custom-redirects/gcs-store.ts +0 -117
  184. package/core/server/services/post-scheduling/post-scheduler-service.js +0 -126
@@ -56,24 +56,37 @@ class Users {
56
56
  this.assignTagToUserPosts = this.assignTagToUserPosts.bind(this);
57
57
  }
58
58
 
59
- async resetAllPasswords(frameOptions) {
60
- return this.models.Base.transaction(async (t) => {
61
- frameOptions.transacting = t;
62
-
63
- // Reset all passwords
64
- const users = await this.models.User.findAll(frameOptions);
65
- for (const user of users) {
66
- await user.save({
67
- status: 'locked' // Prevent signins before password reset
68
- }, frameOptions);
59
+ /**
60
+ * Lock every user and invalidate their password.
61
+ *
62
+ * Locked users hit `PasswordResetRequiredError` on their next signin
63
+ * attempt; the session endpoint catches that, generates a reset token,
64
+ * and emails the user *in context of their own signin*. That avoids
65
+ * blasting unsolicited "your password has been reset" emails to people
66
+ * who are not actively signing in, while still funnelling everyone
67
+ * through a fresh password before they regain access.
68
+ *
69
+ * @param {Object} frameOptions
70
+ * @returns {Promise<{count: number}>}
71
+ */
72
+ async lockAll(frameOptions) {
73
+ const lockUsers = async (txOptions) => {
74
+ // Every staff user has their password rotated. user.lock()
75
+ // preserves `inactive` status for suspended users so they remain
76
+ // on the suspended-signin path; everyone else transitions to
77
+ // `locked` and hits the reset-on-signin flow.
78
+ const users = await this.models.User.findAll(txOptions);
79
+ for (const user of users.models) {
80
+ await user.lock(txOptions);
69
81
  }
82
+ return users.models;
83
+ };
70
84
 
71
- //Send all password resets
72
- for (const user of users) {
73
- const token = await this.auth.passwordreset.generateToken(user.get('email'), this.apiSettings, t);
74
- await this.auth.passwordreset.sendResetNotification(token, this.apiMail);
75
- }
76
- });
85
+ const users = frameOptions.transacting
86
+ ? await lockUsers(frameOptions)
87
+ : await this.models.Base.transaction(t => lockUsers({...frameOptions, transacting: t}));
88
+
89
+ return {count: users.length};
77
90
  }
78
91
 
79
92
  async assignTagToUserPosts({id, context, transacting}) {
@@ -23,12 +23,16 @@ const tokenPermissionCheck = function tokenPermissionCheck(req, res, next) {
23
23
  // CASE: user is requesting with staff token, check blocklist, else skip to permission system
24
24
  // Staff tokens have a user_id associated with them, integration tokens don't
25
25
  if (req.api_key?.get('user_id')) {
26
- // Check if staff token is trying to access blocked endpoints
26
+ // Express matches routes case-insensitively but req.path preserves the
27
+ // original case. Normalise before comparing so a mixed-case URL can't
28
+ // slip past the blocklist while still routing to the lowercase handler.
27
29
  // Match both with and without trailing slash since Express routes accept both
28
- const isDeleteAllContent = req.method === 'DELETE' && (req.path === '/db/' || req.path === '/db');
29
- const isTransferOwnership = req.method === 'PUT' && (req.path === '/users/owner/' || req.path === '/users/owner');
30
+ const path = req.path.toLowerCase();
31
+ const isDeleteAllContent = req.method === 'DELETE' && (path === '/db/' || path === '/db');
32
+ const isTransferOwnership = req.method === 'PUT' && (path === '/users/owner/' || path === '/users/owner');
33
+ const isResetAuthentication = req.method === 'POST' && (path === '/authentication/reset/' || path === '/authentication/reset');
30
34
 
31
- if (isDeleteAllContent || isTransferOwnership) {
35
+ if (isDeleteAllContent || isTransferOwnership || isResetAuthentication) {
32
36
  return next(new errors.NoPermissionError({
33
37
  message: tpl(messages.staffTokenBlocked)
34
38
  }));
@@ -306,7 +306,7 @@ module.exports = function apiRoutes() {
306
306
  router.post('/authentication/setup', http(api.authentication.setup));
307
307
  router.put('/authentication/setup', mw.authAdminApi, http(api.authentication.updateSetup));
308
308
  router.get('/authentication/setup', http(api.authentication.isSetup));
309
- router.post('/authentication/global_password_reset', mw.authAdminApi, http(api.authentication.resetAllPasswords));
309
+ router.post('/authentication/reset', mw.authAdminApi, http(api.authentication.reset));
310
310
 
311
311
  // ## Images
312
312
  router.post('/images/upload',
@@ -34,6 +34,11 @@
34
34
  "settings": {},
35
35
  "imageSizes": {},
36
36
  "gscan": {}
37
+ },
38
+ "redirects": {
39
+ "active": "FileStore",
40
+ "FileStore": {},
41
+ "S3RedirectsStore": {}
37
42
  }
38
43
  },
39
44
  "storage": {
@@ -25,7 +25,8 @@ const GA_FEATURES = [
25
25
  'explore',
26
26
  'commentModeration',
27
27
  'featurebaseFeedback',
28
- 'giftSubscriptions'
28
+ 'giftSubscriptions',
29
+ 'dangerZoneResetAuth'
29
30
  ];
30
31
 
31
32
  // These features are considered publicly available and can be enabled/disabled by users
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ghost",
3
- "version": "6.40.0",
3
+ "version": "6.41.0",
4
4
  "description": "The professional publishing platform",
5
5
  "author": "Ghost Foundation",
6
6
  "homepage": "https://ghost.org",
@@ -1 +0,0 @@
1
- import{aq as o}from"./index-CFEzD2_M.js";const s=[["path",{d:"M5 12h14",key:"1ays0h"}]],c=o("minus",s);export{c as M};
@@ -1,117 +0,0 @@
1
- import {
2
- CopyObjectCommand,
3
- GetObjectCommand,
4
- HeadObjectCommand,
5
- NoSuchKey,
6
- NotFound,
7
- PutObjectCommand,
8
- S3Client
9
- } from '@aws-sdk/client-s3';
10
- import tpl from '@tryghost/tpl';
11
- import * as errors from '@tryghost/errors';
12
-
13
- import {parseJson} from './redirect-config-parser';
14
- import {getBackupRedirectsFilePath} from './utils';
15
- import type {RedirectConfig, RedirectsStore} from './types';
16
-
17
- const DEFAULT_KEY = 'redirects.json';
18
-
19
- const messages = {
20
- missingBucket: 'GCSStore requires a bucket name',
21
- missingClient: 'GCSStore requires an S3 client',
22
- missingResponseBody: 'S3 GetObject returned no body'
23
- };
24
-
25
- export interface GCSStoreOptions {
26
- bucket: string;
27
- s3Client: S3Client;
28
- getBackupKey?: (key: string) => string;
29
- }
30
-
31
- /**
32
- * Implements RedirectsStore against an S3-compatible bucket. Reads and
33
- * writes a single JSON object at the configured key, keeping a
34
- * timestamped server-side copy of the previous contents on each
35
- * overwrite.
36
- */
37
- export class GCSStore implements RedirectsStore {
38
- private readonly client: S3Client;
39
- private readonly bucket: string;
40
- private readonly getBackupKey: (key: string) => string;
41
-
42
- constructor(options: GCSStoreOptions) {
43
- if (!options.bucket) {
44
- throw new errors.IncorrectUsageError({
45
- message: tpl(messages.missingBucket)
46
- });
47
- }
48
- if (!options.s3Client) {
49
- throw new errors.IncorrectUsageError({
50
- message: tpl(messages.missingClient)
51
- });
52
- }
53
-
54
- this.bucket = options.bucket;
55
- this.client = options.s3Client;
56
- this.getBackupKey = options.getBackupKey || getBackupRedirectsFilePath;
57
- }
58
-
59
- async getAll(): Promise<RedirectConfig[]> {
60
- let body: string;
61
- try {
62
- const response = await this.client.send(new GetObjectCommand({
63
- Bucket: this.bucket,
64
- Key: DEFAULT_KEY
65
- }));
66
- if (!response.Body) {
67
- throw new errors.InternalServerError({
68
- message: tpl(messages.missingResponseBody)
69
- });
70
- }
71
- body = await response.Body.transformToString('utf-8');
72
- } catch (err) {
73
- if (this._isNotFound(err)) {
74
- return [];
75
- }
76
- throw err;
77
- }
78
-
79
- return parseJson(body);
80
- }
81
-
82
- async replaceAll(redirects: RedirectConfig[]): Promise<void> {
83
- if (await this._canonicalExists()) {
84
- await this.client.send(new CopyObjectCommand({
85
- Bucket: this.bucket,
86
- Key: this.getBackupKey(DEFAULT_KEY),
87
- CopySource: `${this.bucket}/${DEFAULT_KEY}`
88
- }));
89
- }
90
-
91
- await this.client.send(new PutObjectCommand({
92
- Bucket: this.bucket,
93
- Key: DEFAULT_KEY,
94
- Body: JSON.stringify(redirects),
95
- ContentType: 'application/json'
96
- }));
97
- }
98
-
99
- private async _canonicalExists(): Promise<boolean> {
100
- try {
101
- await this.client.send(new HeadObjectCommand({
102
- Bucket: this.bucket,
103
- Key: DEFAULT_KEY
104
- }));
105
- return true;
106
- } catch (err) {
107
- if (this._isNotFound(err)) {
108
- return false;
109
- }
110
- throw err;
111
- }
112
- }
113
-
114
- private _isNotFound(err: unknown): boolean {
115
- return err instanceof NotFound || err instanceof NoSuchKey;
116
- }
117
- }
@@ -1,126 +0,0 @@
1
- const moment = require('moment');
2
- const errors = require('@tryghost/errors');
3
- const logging = require('@tryghost/logging');
4
-
5
- const urlUtils = require('../../../shared/url-utils');
6
- const {getSignedAdminToken} = require('../../adapters/scheduling/utils');
7
-
8
- const SCHEDULED_RESOURCES = ['post', 'page'];
9
-
10
- class PostSchedulerService {
11
- constructor({apiUrl, internalKeys, adapter, events} = {}) {
12
- if (!apiUrl) {
13
- throw new errors.IncorrectUsageError({message: 'post-scheduling: no apiUrl was provided'});
14
- }
15
- if (!internalKeys) {
16
- throw new errors.IncorrectUsageError({message: 'post-scheduling: no internalKeys was provided'});
17
- }
18
-
19
- this.apiUrl = apiUrl;
20
- this.adapter = adapter;
21
- this.internalKeys = internalKeys;
22
-
23
- adapter.run();
24
-
25
- SCHEDULED_RESOURCES.forEach((resource) => {
26
- events.on(`${resource}.scheduled`, async (model) => {
27
- try {
28
- const key = await internalKeys.get('ghost-scheduler');
29
- adapter.schedule(this.normalize({model, apiUrl, key, resourceType: resource}));
30
- } catch (err) {
31
- logging.error({
32
- event: {name: 'post-scheduling.schedule.error'},
33
- err,
34
- resource,
35
- id: model.get('id')
36
- }, 'Failed to schedule resource');
37
- }
38
- });
39
-
40
- /** We want to do reschedule as (unschedule + schedule) due to how token(+url) is generated
41
- * We want to first remove existing schedule by generating a matching token(+url)
42
- * followed by generating a new token(+url) for the new schedule
43
- */
44
- events.on(`${resource}.rescheduled`, async (model) => {
45
- try {
46
- const key = await internalKeys.get('ghost-scheduler');
47
- adapter.unschedule(this.normalize({model, apiUrl, key, resourceType: resource}, 'unscheduled'));
48
- adapter.schedule(this.normalize({model, apiUrl, key, resourceType: resource}));
49
- } catch (err) {
50
- logging.error({
51
- event: {name: 'post-scheduling.reschedule.error'},
52
- err,
53
- resource,
54
- id: model.get('id')
55
- }, 'Failed to reschedule resource');
56
- }
57
- });
58
-
59
- events.on(`${resource}.unscheduled`, async (model) => {
60
- try {
61
- const key = await internalKeys.get('ghost-scheduler');
62
- adapter.unschedule(this.normalize({model, apiUrl, key, resourceType: resource}, 'unscheduled'));
63
- } catch (err) {
64
- logging.error({
65
- event: {name: 'post-scheduling.unschedule.error'},
66
- err,
67
- resource,
68
- id: model.get('id')
69
- }, 'Failed to unschedule resource');
70
- }
71
- });
72
- });
73
- }
74
-
75
- /**
76
- * Re-issue every queued schedule. On boot the caller passes the resources
77
- * loaded from the DB. For key rotation, the caller additionally passes
78
- * `previousKey` so unschedule URLs reproduce the entries the adapter
79
- * already has (signed under the previous secret); schedule URLs are
80
- * reissued under the current secret.
81
- *
82
- * @param {Object} scheduledResources - {post: [...], page: [...]}
83
- * @param {Object} [opts]
84
- * @param {{id: string, secret: string}} [opts.previousKey]
85
- */
86
- async reschedule(scheduledResources, {previousKey} = {}) {
87
- const currentKey = await this.internalKeys.get('ghost-scheduler');
88
- const unscheduleKey = previousKey ?? currentKey;
89
-
90
- for (const resourceType of Object.keys(scheduledResources)) {
91
- for (const model of scheduledResources[resourceType]) {
92
- this.adapter.unschedule(
93
- this.normalize({model, apiUrl: this.apiUrl, key: unscheduleKey, resourceType}),
94
- {bootstrap: true}
95
- );
96
- this.adapter.schedule(
97
- this.normalize({model, apiUrl: this.apiUrl, key: currentKey, resourceType})
98
- );
99
- }
100
- }
101
- }
102
-
103
- /**
104
- * @description Normalize model data into scheduler notation.
105
- * @param {Object} options
106
- * @return {Object}
107
- */
108
- normalize({model, apiUrl, resourceType, key}, event = '') {
109
- const resource = `${resourceType}s`;
110
- const publishedAt = (event === 'unscheduled') ? model.previous('published_at') : model.get('published_at');
111
- const signedAdminToken = getSignedAdminToken({publishedAt, apiUrl, key});
112
- let url = `${urlUtils.urlJoin(apiUrl, 'schedules', resource, model.get('id'))}/?token=${signedAdminToken}`;
113
-
114
- return {
115
- // NOTE: The scheduler expects a unix timestamp.
116
- time: moment(publishedAt).valueOf(),
117
- url: url,
118
- extra: {
119
- httpMethod: 'PUT',
120
- oldTime: model.previous('published_at') ? moment(model.previous('published_at')).valueOf() : null
121
- }
122
- };
123
- }
124
- }
125
-
126
- module.exports = PostSchedulerService;