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.
- package/core/boot.js +8 -8
- package/core/built/admin/assets/{PolarAngleAxis-D7m1zwVk.js → PolarAngleAxis-Bh28wbPV.js} +1 -1
- package/core/built/admin/assets/{_baseAssignValue-BDsvYKNK.js → _baseAssignValue-hdDG700N.js} +1 -1
- package/core/built/admin/assets/{a-large-small-DxRNdz0F.js → a-large-small-BiK5_5LV.js} +1 -1
- package/core/built/admin/assets/{account-migration-SYnE1jBW.js → account-migration-DsSHanIB.js} +1 -1
- package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +1 -1
- package/core/built/admin/assets/admin-x-settings/{code-editor-view-Bj9rgbLw.mjs → code-editor-view-BEIdFhw3.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-BrZ9YAzd.mjs → index-BEh3nFEy.mjs} +3 -3
- package/core/built/admin/assets/admin-x-settings/{index-DRndIh9T.mjs → index-BLF2n6YB.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-CIl25Teq.mjs → index-BmPIWs5F.mjs} +3 -3
- package/core/built/admin/assets/admin-x-settings/{index-CNyNY3Wh.mjs → index-CP9YnNzc.mjs} +3 -3
- package/core/built/admin/assets/admin-x-settings/{index-SGr5sE2h.mjs → index-Cd2Ns-D2.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-PGoLP2kh.mjs → index-CjqAcKpK.mjs} +5 -5
- package/core/built/admin/assets/admin-x-settings/{index-pCIv_7bB.mjs → index-CnMcD9aj.mjs} +6212 -6158
- package/core/built/admin/assets/admin-x-settings/{index-CPCcgad-.mjs → index-LzsrZC40.mjs} +3 -3
- package/core/built/admin/assets/admin-x-settings/{index-DPqY4h5h.mjs → index-ZYW0UW7I.mjs} +3 -3
- package/core/built/admin/assets/admin-x-settings/{modals-lo32WZLi.mjs → modals-C48Tq1rW.mjs} +9 -9
- package/core/built/admin/assets/{arrow-right-BixoEua4.js → arrow-right-Bk43nMGd.js} +1 -1
- package/core/built/admin/assets/{at-sign-BsHNViA_.js → at-sign-DWOf36uj.js} +1 -1
- package/core/built/admin/assets/{audience-0RCWjbIK.js → audience-CvP78Gug.js} +1 -1
- package/core/built/admin/assets/{automations-DTUfYvf6.js → automations-BBWZhrha.js} +1 -1
- package/core/built/admin/assets/{automations-DiGkcVt-.js → automations-CmulGWwu.js} +1 -1
- package/core/built/admin/assets/{avatar-flipboard-BgoH-wIq.js → avatar-flipboard-CVf-N6wp.js} +1 -1
- package/core/built/admin/assets/{bluesky-sharing-C2xwSoS-.js → bluesky-sharing-DVC3I9Um.js} +1 -1
- package/core/built/admin/assets/{chart-X5cLIH1X.js → chart-CUKSNmKY.js} +1 -1
- package/core/built/admin/assets/{checkbox-DMamS4oG.js → checkbox-B_yDSM8z.js} +1 -1
- package/core/built/admin/assets/{chevron-up-CVgBkTk5.js → chevron-up-DuaKtE_M.js} +1 -1
- package/core/built/admin/assets/{chunk.941.f47098cbbce89a049e03.js → chunk.331.2ec2e2702eda02de96cd.js} +3 -3
- package/core/built/admin/assets/{chunk.524.a1d668dcc84709a5d702.js → chunk.524.49cc01002e1a5dba8ef9.js} +4 -4
- package/core/built/admin/assets/{chunk.582.180bf2525cf552f5ac5f.js → chunk.582.4bbc54c5f7d3dfb93288.js} +6 -6
- package/core/built/admin/assets/{circle-alert-Dg3UkvRP.js → circle-alert-bcK_Yqap.js} +1 -1
- package/core/built/admin/assets/{code-editor-view-CAICoZJe.js → code-editor-view-Bl4Jzjhk.js} +1 -1
- package/core/built/admin/assets/{comments-BHru9ZQ8.js → comments-DMea0nul.js} +1 -1
- package/core/built/admin/assets/{content-helpers-BByMp-Y5.js → content-helpers-B2yRzdvJ.js} +1 -1
- package/core/built/admin/assets/{copy-D7jqplrv.js → copy-DVtQi_VJ.js} +1 -1
- package/core/built/admin/assets/{data-list-DUaY9nlT.js → data-list-BF4GeUmt.js} +1 -1
- package/core/built/admin/assets/{deleted-feed-item-jLcSKuul.js → deleted-feed-item-DO2u3qmi.js} +1 -1
- package/core/built/admin/assets/{dropzone-DSPf3N_o.js → dropzone-Cs4DrURS.js} +1 -1
- package/core/built/admin/assets/{edit-profile-BvU2-lW5.js → edit-profile-CnuBNQhO.js} +1 -1
- package/core/built/admin/assets/{editor-D3fcjt_F.js → editor-CTO9FIUW.js} +1 -1
- package/core/built/admin/assets/{empty-indicator-2Pjmce3n.js → empty-indicator-BvyaMcmw.js} +1 -1
- package/core/built/admin/assets/{en-Bu-6PYn5.js → en-CjPXRj8j.js} +1 -1
- package/core/built/admin/assets/{feed-CygLK1Ad.js → feed-D2wk4cCS.js} +1 -1
- package/core/built/admin/assets/{filter-query-core-a0PNU2EO.js → filter-query-core-VEnwTCyO.js} +1 -1
- package/core/built/admin/assets/{filters-jiyRsLMH.js → filters-DtIsuqv5.js} +1 -1
- package/core/built/admin/assets/{gh-chart-CEhBBu3F.js → gh-chart-JxzKqaoO.js} +1 -1
- package/core/built/admin/assets/{ghost-0ab96884815339963364804ab266c99f.js → ghost-30c3dedbe1cca943b92efa7efd2710e4.js} +36 -21
- package/core/built/admin/assets/{growth-Fz3Oatml.js → growth-CPNrBpHK.js} +1 -1
- package/core/built/admin/assets/{hash-CeFrjo3C.js → hash-CJzbTA5k.js} +1 -1
- package/core/built/admin/assets/{inbox-CCirPaG2.js → inbox-CVVAkCmd.js} +1 -1
- package/core/built/admin/assets/{index-Dd2BJRZH.js → index-AkQJUr1z.js} +1 -1
- package/core/built/admin/assets/{index-CYPN5csJ.js → index-BLWOUf31.js} +1 -1
- package/core/built/admin/assets/{index-D7Ui3Fih.js → index-BRBRkRqa.js} +1 -1
- package/core/built/admin/assets/{index-CFEzD2_M.js → index-BkbhBpVC.js} +3 -3
- package/core/built/admin/assets/{index-BYwL7gFN.js → index-BxFFWYdP.js} +1 -1
- package/core/built/admin/assets/{index-D3LfHeBl.js → index-C5_ZKpC1.js} +1 -1
- package/core/built/admin/assets/{index-Bjw2qAph.js → index-Cdbufdfy.js} +1 -1
- package/core/built/admin/assets/{index-BTf6YUBb.js → index-Cq5vsvxE.js} +1 -1
- package/core/built/admin/assets/{index-DPbi4ie_.js → index-CuGoX8Ub.js} +1 -1
- package/core/built/admin/assets/{index-CsaU9dfl.js → index-CuH5raTz.js} +1 -1
- package/core/built/admin/assets/{index-Bf83suMc.js → index-D3A8RTi0.js} +1 -1
- package/core/built/admin/assets/{index-Dskrbhv3.js → index-DIyK69JM.js} +1 -1
- package/core/built/admin/assets/{index-GExKMpy_.js → index-DLFVpwt3.js} +1 -1
- package/core/built/admin/assets/{index-BOMdiHRG.js → index-DQZp_NsL.js} +1 -1
- package/core/built/admin/assets/{index-2qcjFCQs.js → index-DR74ulj2.js} +1 -1
- package/core/built/admin/assets/{index-WKMARia9.js → index-DYE2untj.js} +1 -1
- package/core/built/admin/assets/{index-DNZUJQ8v.js → index-DssW7xhk.js} +1 -1
- package/core/built/admin/assets/{index-Cq4mS7QK.js → index-DvyNqFct.js} +1 -1
- package/core/built/admin/assets/{index-C7xLyJVb.js → index-DxB7NHpG.js} +1 -1
- package/core/built/admin/assets/{index-Bw4DM4Pr.js → index-IDaHqjnu.js} +1 -1
- package/core/built/admin/assets/{index-ClOB5u8L.js → index-TtBMzSeP.js} +1 -1
- package/core/built/admin/assets/{index-takfwHSn.js → index-UMWVIyz7.js} +1 -1
- package/core/built/admin/assets/{koenig-lexical-BJM9wAwQ.js → koenig-lexical-DVAmevhl.js} +1 -1
- package/core/built/admin/assets/{kpi-card-BP-2yicn.js → kpi-card-D3CzvsrI.js} +1 -1
- package/core/built/admin/assets/{kpi-card-DOplXdDT.js → kpi-card-DV-nN7xT.js} +1 -1
- package/core/built/admin/assets/{kpi-tabs-Rq5jJW2Z.js → kpi-tabs-B3NkaTwb.js} +1 -1
- package/core/built/admin/assets/{kpis-yHgME_iy.js → kpis-CVwF08QL.js} +1 -1
- package/core/built/admin/assets/{label-fijjy1Mb.js → label-BiPpMny2.js} +1 -1
- package/core/built/admin/assets/{links-s3IeAkbo.js → links-BY0Uo6O5.js} +1 -1
- package/core/built/admin/assets/{list-page-CUcB5ebB.js → list-page-COHNMpOi.js} +1 -1
- package/core/built/admin/assets/{loader-circle-C7mWzo52.js → loader-circle-B1uEfpbZ.js} +1 -1
- package/core/built/admin/assets/{mail-9TFkAaAh.js → mail-ccQhEr7k.js} +1 -1
- package/core/built/admin/assets/{main-layout-XtviVkg1.js → main-layout-BYKdA6Z1.js} +1 -1
- package/core/built/admin/assets/{members-XkHao5Yf.js → members-DkpEnvG5.js} +1 -1
- package/core/built/admin/assets/minus-C68D81nx.js +1 -0
- package/core/built/admin/assets/{modals-B6R7oqvb.js → modals-X6RSWn5Y.js} +3 -3
- package/core/built/admin/assets/{moderation-B2Tixfce.js → moderation-BFrrwfvx.js} +1 -1
- package/core/built/admin/assets/{newsletter-CbESXhxf.js → newsletter-n7kc6cxR.js} +1 -1
- package/core/built/admin/assets/{newsletters-DOIL_cOY.js → newsletters-CSCa3QdE.js} +1 -1
- package/core/built/admin/assets/{note-mUikaB-p.js → note-CMZjeK8e.js} +1 -1
- package/core/built/admin/assets/{onboarding-route-Dqv2nP7b.js → onboarding-route-BaX0a-pu.js} +1 -1
- package/core/built/admin/assets/{overview-yZ-7KJdx.js → overview-CS9dmE_6.js} +1 -1
- package/core/built/admin/assets/{pagemenu-TxGvHwRO.js → pagemenu-O_y8Jm4v.js} +1 -1
- package/core/built/admin/assets/{pencil-CKTgzafp.js → pencil-BcymGPnE.js} +1 -1
- package/core/built/admin/assets/{pin-BU_u-xRX.js → pin-Bl3nSr7I.js} +1 -1
- package/core/built/admin/assets/{post-analytics-CWA8invj.js → post-analytics-DW-TKu-t.js} +1 -1
- package/core/built/admin/assets/{post-analytics-context-BWDhMRq_.js → post-analytics-context-D7PgEMo8.js} +1 -1
- package/core/built/admin/assets/{post-analytics-header-Bm0mcxjK.js → post-analytics-header-B8C0vA5Y.js} +1 -1
- package/core/built/admin/assets/{post-share-modal-D8BtRD2u.js → post-share-modal-D0OlASBQ.js} +1 -1
- package/core/built/admin/assets/{posts-u-CAKddc.js → posts-BZaw4-dc.js} +1 -1
- package/core/built/admin/assets/{power-DR9gpiQR.js → power-DWm6pts5.js} +1 -1
- package/core/built/admin/assets/{referrers-DV_Ihg9y.js → referrers-BsZ85Z0a.js} +1 -1
- package/core/built/admin/assets/{repeat-uXmOJ6y8.js → repeat-DQay9e22.js} +1 -1
- package/core/built/admin/assets/{reply-9ZyqaxI-.js → reply-CoNqmExw.js} +1 -1
- package/core/built/admin/assets/{rocket-DMOr1OCt.js → rocket-Dne6P_8f.js} +1 -1
- package/core/built/admin/assets/{select-CPSy44ld.js → select-BfWJkrKc.js} +1 -1
- package/core/built/admin/assets/{settings-krk0432u.js → settings-BoXzKMhL.js} +1 -1
- package/core/built/admin/assets/{settings-66Zv8QvL.js → settings-rRY210wx.js} +25 -25
- package/core/built/admin/assets/{share-modal-BRmGllZq.js → share-modal-CtdLQbnQ.js} +1 -1
- package/core/built/admin/assets/{sort-button-5fyeViQL.js → sort-button-7pVgpa0d.js} +1 -1
- package/core/built/admin/assets/{source-icon-DeeHyZRn.js → source-icon-Dtua85oN.js} +1 -1
- package/core/built/admin/assets/{sprout-956Q4On2.js → sprout-CpwUJAPT.js} +1 -1
- package/core/built/admin/assets/{square-BaCvbKJY.js → square-CDrQjv8R.js} +1 -1
- package/core/built/admin/assets/{stats-BVKNZJO6.js → stats-Yo9Gco0_.js} +1 -1
- package/core/built/admin/assets/{stats-view-gtyaqudG.js → stats-view-DubukX9V.js} +1 -1
- package/core/built/admin/assets/{step-1-09wdjzdX.js → step-1-CH8jWKi7.js} +1 -1
- package/core/built/admin/assets/{step-2-8iaQ0efq.js → step-2-CWy7Qi0Q.js} +1 -1
- package/core/built/admin/assets/{step-3-BuHAZbdD.js → step-3-CNiDFH17.js} +1 -1
- package/core/built/admin/assets/{table-DTofVyjA.js → table-qQi9EXB1.js} +1 -1
- package/core/built/admin/assets/{tabs-CL5eaCGL.js → tabs-M6kFEqPE.js} +1 -1
- package/core/built/admin/assets/{tags-DJrbEw5-.js → tags-74U6lgwY.js} +1 -1
- package/core/built/admin/assets/{tags-U3lciBmd.js → tags-DuMshkTz.js} +1 -1
- package/core/built/admin/assets/{textarea-DdDuNixZ.js → textarea-CGBp9nOo.js} +1 -1
- package/core/built/admin/assets/{tiers-DXKbghdk.js → tiers-D2DYHaeO.js} +1 -1
- package/core/built/admin/assets/{toggle-group-DaZKTYDS.js → toggle-group-GYp_w6Po.js} +1 -1
- package/core/built/admin/assets/{topic-filter-BzqiVOAU.js → topic-filter-N3CzJ01P.js} +1 -1
- package/core/built/admin/assets/{trash-CKK_p9G5.js → trash-CUqAK3SQ.js} +1 -1
- package/core/built/admin/assets/{underline-Bq0P-5Zy.js → underline-CwuJG-Ql.js} +1 -1
- package/core/built/admin/assets/{undo-2-CcBtw-tW.js → undo-2-D6ZRqONE.js} +1 -1
- package/core/built/admin/assets/{upload-BcylHEMw.js → upload-BLiT66bJ.js} +1 -1
- package/core/built/admin/assets/{use-growth-stats-Ci0gymWp.js → use-growth-stats-u18So4Gq.js} +1 -1
- package/core/built/admin/assets/{use-simple-pagination-ByaFwxfZ.js → use-simple-pagination-Dp2sTt2S.js} +1 -1
- package/core/built/admin/assets/{user-round-check-D0I7NO1g.js → user-round-check-BDomHaso.js} +1 -1
- package/core/built/admin/assets/{user-round-x-DeGVH29o.js → user-round-x-Cc0RL-V9.js} +1 -1
- package/core/built/admin/assets/{virtual-list-window-S0xZxQAf.js → virtual-list-window-BnjCoNIr.js} +1 -1
- package/core/built/admin/assets/{wallet-cards-B9TQg4x3.js → wallet-cards-CVZ99Nm3.js} +1 -1
- package/core/built/admin/assets/{web-BRN2mUpo.js → web-B_MLxGxZ.js} +1 -1
- package/core/built/admin/index.html +5 -5
- package/core/server/{services/custom-redirects/file-store.js → adapters/redirects/FileStore.js} +6 -5
- package/core/server/{services/custom-redirects/file-store.ts → adapters/redirects/FileStore.ts} +6 -4
- package/core/server/adapters/redirects/RedirectsStoreBase.d.ts +9 -0
- package/core/server/adapters/redirects/RedirectsStoreBase.js +8 -0
- package/core/server/{services/custom-redirects/gcs-store.js → adapters/redirects/S3RedirectsStore.js} +50 -18
- package/core/server/adapters/redirects/S3RedirectsStore.ts +163 -0
- package/core/server/adapters/scheduling/scheduling-base.js +41 -0
- package/core/server/adapters/scheduling/types.js +2 -0
- package/core/server/adapters/scheduling/types.ts +36 -0
- package/core/server/api/endpoints/authentication.js +26 -5
- package/core/server/api/endpoints/schedules.js +0 -30
- package/core/server/api/endpoints/utils/serializers/output/authentication.js +10 -0
- package/core/server/data/migrations/versions/6.41/2026-05-13-12-00-00-rename-reset-all-passwords-permission.js +21 -0
- package/core/server/data/schema/fixtures/fixtures.json +3 -3
- package/core/server/models/api-key.js +18 -0
- package/core/server/models/user.js +18 -0
- package/core/server/services/adapter-manager/config.js +6 -0
- package/core/server/services/adapter-manager/index.js +1 -0
- package/core/server/services/auth/index.js +4 -0
- package/core/server/services/auth/reset-authentication.js +50 -0
- package/core/server/services/auth/reset-authentication.ts +75 -0
- package/core/server/services/automations/index.js +21 -15
- package/core/server/services/custom-redirects/index.js +2 -4
- package/core/server/services/gifts/gift-bookshelf-repository.js +7 -0
- package/core/server/services/gifts/gift-bookshelf-repository.ts +10 -0
- package/core/server/services/gifts/gift-reminder-scheduler.js +94 -0
- package/core/server/services/gifts/gift-reminder-scheduler.ts +109 -0
- package/core/server/services/gifts/gift-repository.ts +1 -0
- package/core/server/services/gifts/gift-service-wrapper.js +10 -7
- package/core/server/services/gifts/gift-service.js +1 -32
- package/core/server/services/gifts/gift-service.ts +3 -59
- package/core/server/services/internal-keys/index.js +1 -3
- package/core/server/services/internal-keys/index.ts +11 -3
- package/core/server/services/post-scheduling/index.js +16 -28
- package/core/server/services/post-scheduling/index.ts +14 -0
- package/core/server/services/post-scheduling/post-scheduling.js +115 -0
- package/core/server/services/post-scheduling/post-scheduling.ts +138 -0
- package/core/server/services/users.js +29 -16
- package/core/server/web/api/endpoints/admin/middleware.js +8 -4
- package/core/server/web/api/endpoints/admin/routes.js +1 -1
- package/core/shared/config/defaults.json +5 -0
- package/core/shared/labs.js +2 -1
- package/package.json +1 -1
- package/core/built/admin/assets/minus-DPrXh11r.js +0 -1
- package/core/server/services/custom-redirects/gcs-store.ts +0 -117
- 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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
//
|
|
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
|
|
29
|
-
const
|
|
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/
|
|
309
|
+
router.post('/authentication/reset', mw.authAdminApi, http(api.authentication.reset));
|
|
310
310
|
|
|
311
311
|
// ## Images
|
|
312
312
|
router.post('/images/upload',
|
package/core/shared/labs.js
CHANGED
|
@@ -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 +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;
|