ghost 6.57.1 → 6.58.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/LICENSE +22 -0
- package/bin/validate-adapters.js +1 -5
- package/components/ghost-storage-base-3.0.0.tgz +0 -0
- package/components/tryghost-adapter-base-cache-0.2.0.tgz +0 -0
- package/components/tryghost-adapter-base-jobs-0.0.0.tgz +0 -0
- package/components/tryghost-adapter-base-redirects-0.0.0.tgz +0 -0
- package/components/tryghost-adapter-base-route-settings-0.0.0.tgz +0 -0
- package/components/tryghost-adapter-base-scheduling-0.2.2.tgz +0 -0
- package/components/tryghost-adapter-base-sso-0.1.2.tgz +0 -0
- package/components/tryghost-admin-api-schema-0.0.0.tgz +0 -0
- package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
- package/components/tryghost-i18n-0.0.0.tgz +0 -0
- package/components/tryghost-kg-card-factory-5.2.4.tgz +0 -0
- package/components/tryghost-kg-clean-basic-html-4.3.4.tgz +0 -0
- package/components/tryghost-kg-converters-1.2.5.tgz +0 -0
- package/components/tryghost-kg-default-cards-10.3.5.tgz +0 -0
- package/components/tryghost-kg-default-nodes-2.2.0.tgz +0 -0
- package/components/tryghost-kg-default-transforms-1.3.4.tgz +0 -0
- package/components/tryghost-kg-html-to-lexical-1.4.0.tgz +0 -0
- package/components/tryghost-kg-lexical-html-renderer-1.5.0.tgz +0 -0
- package/components/tryghost-kg-markdown-html-renderer-7.2.4.tgz +0 -0
- package/components/tryghost-kg-utils-1.1.4.tgz +0 -0
- package/components/tryghost-nql-string-0.0.0.tgz +0 -0
- package/components/tryghost-parse-email-address-0.0.0.tgz +0 -0
- package/core/boot.js +35 -3
- package/core/built/admin/assets/{about-BNojRaxf.js → about-B_BvSyLF.js} +1 -1
- package/core/built/admin/assets/{account-migration-DMS8JsM-.js → account-migration-oJUGxnzT.js} +1 -1
- package/core/built/admin/assets/activitypub/{account-migration-DHFbePl6.js → account-migration-BpDeIF3G.js} +8 -8
- package/core/built/admin/assets/activitypub/activitypub.js +4 -4
- package/core/built/admin/assets/activitypub/{avatar-CT4tS2_S.js → avatar-B0SpBPVL.js} +3 -3
- package/core/built/admin/assets/activitypub/{avatar-flipboard-ByYWKipo.js → avatar-flipboard-DKt1LGcE.js} +3 -3
- package/core/built/admin/assets/activitypub/{bluesky-sharing-srVnSHDz.js → bluesky-sharing-aPMtrS3L.js} +10 -10
- package/core/built/admin/assets/activitypub/{chunk-KS7C4IRE-D4HEcEwi.js → chunk-KS7C4IRE-CnTrsgmM.js} +26 -15
- package/core/built/admin/assets/activitypub/{content-formatters-C2sos5Ht.js → content-formatters-BORfuA9G.js} +318 -284
- package/core/built/admin/assets/activitypub/{copy-B1UBTOoZ.js → copy-D-YvNi2E.js} +2 -2
- package/core/built/admin/assets/activitypub/{dist-MPsb2WZ1.js → dist-Dqggkccr.js} +5 -5
- package/core/built/admin/assets/activitypub/{domain-DzcM9Dzo.js → domain-CPdiB27_.js} +10 -10
- package/core/built/admin/assets/activitypub/{edit-profile-CsQ_4xAk.js → edit-profile-BY2PawIe.js} +7 -7
- package/core/built/admin/assets/activitypub/{explore-CJe0DZBK.js → explore-Sp9bgAwq.js} +11 -11
- package/core/built/admin/assets/activitypub/{feed-CKV3qWON.js → feed-BjtF4ohL.js} +10 -10
- package/core/built/admin/assets/activitypub/{field-DckHfDJf.js → field-BDOYVTg6.js} +4 -4
- package/core/built/admin/assets/activitypub/{hash-Dlp8RM55.js → hash-DqXYHgzG.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-CsPy4R0M.js → inbox-CZ7IOAMn.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-DAsjzUUe.js → inbox-z1o2j1Tb.js} +11 -11
- package/core/built/admin/assets/activitypub/{label-DWyo9E5T.js → label-1jhN-qhD.js} +4 -4
- package/core/built/admin/assets/activitypub/layout-BRMezuty.js +1 -0
- package/core/built/admin/assets/activitypub/{moderation-B0Inc03M.js → moderation-CZudJi8N.js} +9 -9
- package/core/built/admin/assets/activitypub/{note-CSE54lER.js → note-nr_m_5yb.js} +8 -8
- package/core/built/admin/assets/activitypub/{notifications-RSwbKqdh.js → notifications-BdcyivbT.js} +10 -10
- package/core/built/admin/assets/activitypub/{onboarding-BFDkzeR2.js → onboarding-B4kwav9u.js} +2 -2
- package/core/built/admin/assets/activitypub/{onboarding-D6rcBdPe.js → onboarding-DZNElJYP.js} +3 -3
- package/core/built/admin/assets/activitypub/{preferences-CE231n2_.js → preferences-CNqy1x9C.js} +13 -13
- package/core/built/admin/assets/activitypub/{profile-aaQ1j1Eo.js → profile-DlY4rhbO.js} +15 -15
- package/core/built/admin/assets/activitypub/{reply-Cjb0Vs3m.js → reply-DASIN0Ec.js} +2 -2
- package/core/built/admin/assets/activitypub/{routes-Djb0mc9k.js → routes-CEHdEZzS.js} +31 -31
- package/core/built/admin/assets/activitypub/{separator-D8pROI5n.js → separator-B3k35sUV.js} +4 -4
- package/core/built/admin/assets/activitypub/{settings-DMc32IQl.js → settings-CrSV1cux.js} +6 -6
- package/core/built/admin/assets/activitypub/{site-DVIcsohX.js → site-CtBUCPBz.js} +6 -6
- package/core/built/admin/assets/activitypub/{step-1-9Yey9usF.js → step-1-D9jGWyAy.js} +7 -7
- package/core/built/admin/assets/activitypub/{step-2-DdYmKRXV.js → step-2-Jz_enuo9.js} +7 -7
- package/core/built/admin/assets/activitypub/{step-3-hdSkcjZU.js → step-3-KpUqpGWi.js} +12 -12
- package/core/built/admin/assets/activitypub/{tabs-CUqseYzQ.js → tabs-DvlsxTCI.js} +7 -7
- package/core/built/admin/assets/activitypub/{topic-filter-Coni6OXM.js → topic-filter-CIN1E2jg.js} +4 -4
- package/core/built/admin/assets/activitypub/{use-activity-pub-queries-MeHTPMQv.js → use-activity-pub-queries-CATFd6YV.js} +4 -4
- package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-B046AM38.js → use-navigate-with-base-path-iCkcbMGk.js} +2 -2
- package/core/built/admin/assets/activitypub/{use-reply-chain-data-1AO-Xob6.js → use-reply-chain-data-DJ0vhA8V.js} +6 -6
- package/core/built/admin/assets/activitypub/{users-BLeiOTHc.js → users-sgJ6GNZX.js} +12 -11
- package/core/built/admin/assets/activitypub/{x-BRfRYd16.js → x-Cy2jUiYM.js} +2 -2
- package/core/built/admin/assets/add-integration-modal-BW23PDYs.js +1 -0
- package/core/built/admin/assets/add-newsletter-modal-M0ls8BKO.js +1 -0
- package/core/built/admin/assets/{add-recommendation-modal-D-3LOrdd.js → add-recommendation-modal-B0NeBMzl.js} +1 -1
- package/core/built/admin/assets/{analytics-DTIMRhWw.js → analytics-BC2oR8ay.js} +1 -1
- package/core/built/admin/assets/announcement-bar-modal-pXBqjQE_.js +1 -0
- package/core/built/admin/assets/{api-keys-_iCT4dxr.js → api-keys-BgKr003d.js} +1 -1
- package/core/built/admin/assets/{api-keys-Dx4NeNpK.js → api-keys-Df6uv1dT.js} +1 -1
- package/core/built/admin/assets/{audience-DkknNqhZ.js → audience-BY8od8If.js} +1 -1
- package/core/built/admin/assets/{automated-emails-PayPCT8L.js → automated-emails-28c2Lbb3.js} +1 -1
- package/core/built/admin/assets/{automations-DvSU7QhW.js → automations-DCs6Wady.js} +1 -1
- package/core/built/admin/assets/{automations-162q5HjL.js → automations-Dzp_QnON.js} +1 -1
- package/core/built/admin/assets/{bluesky-sharing-yZ9A_jL8.js → bluesky-sharing-D0P42Cws.js} +1 -1
- package/core/built/admin/assets/{breadcrumb-CjOH7LCU.js → breadcrumb-MTj_cKLb.js} +1 -1
- package/core/built/admin/assets/card-CiiE7QfM.js +1 -0
- package/core/built/admin/assets/{chart-helpers-DGxvWnBT.js → chart-helpers-DcO3k_vA.js} +1 -1
- package/core/built/admin/assets/{checkbox-BSUcnNq_.js → checkbox-LmClhqcS.js} +1 -1
- package/core/built/admin/assets/{chunk.424.4a27628efdade15c2dab.js → chunk.408.1a258af360eb8d905508.js} +40 -37
- package/core/built/admin/assets/{chunk.571.40f093ed6d883993714e.js → chunk.492.a9da46ca956b10116774.js} +1342 -1342
- package/core/built/admin/assets/{chunk.571.40f093ed6d883993714e.js.LICENSE.txt → chunk.492.a9da46ca956b10116774.js.LICENSE.txt} +1 -1
- package/core/built/admin/assets/chunk.524.4c693c3a00ac91df34fa.js +25 -0
- package/core/built/admin/assets/chunk.582.20b600e329e58d12d7c2.js +27 -0
- package/core/built/admin/assets/code-editor-view-DqTOUnbM.js +1 -0
- package/core/built/admin/assets/code-editor-view-bA0lfxUL.js +1 -0
- package/core/built/admin/assets/{debounce-Cx5B_9OI.js → color-picker-CBlJMv3y.js} +2 -2
- package/core/built/admin/assets/color-picker-field-Cv9GOJQZ.js +1 -0
- package/core/built/admin/assets/{combobox-5rdgDrDV.js → combobox-BbPJzjCp.js} +1 -1
- package/core/built/admin/assets/command-BrhXIlDr.js +1 -0
- package/core/built/admin/assets/comments-Cu4rD1aJ.js +1 -0
- package/core/built/admin/assets/confirmation-provider-BEdrnHns.js +1 -0
- package/core/built/admin/assets/{content-api-modal-BtW4Z1-1.js → content-api-modal-BOFWRUkC.js} +1 -1
- package/core/built/admin/assets/{copy-field-CEp_XieW.js → copy-field-8biQvNMD.js} +1 -1
- package/core/built/admin/assets/custom-field-type-option-CNhAgHbJ.js +1 -0
- package/core/built/admin/assets/custom-integration-modal-CrGOI8IB.js +1 -0
- package/core/built/admin/assets/{date-range-select-B3Ij1Rqg.js → date-range-select-Bsh4gIek.js} +1 -1
- package/core/built/admin/assets/debounce-pscB46sW.js +1 -0
- package/core/built/admin/assets/design-and-theme-modal-DWZguwWX.js +23 -0
- package/core/built/admin/assets/{dirty-confirm-dialog-YE9L21gY.js → dirty-confirm-dialog-DmdMpgyN.js} +1 -1
- package/core/built/admin/assets/{disabled-sources-indicator-uIzbWL7K.js → disabled-sources-indicator-BLnmyWgt.js} +1 -1
- package/core/built/admin/assets/dist-4m6pHdc0.js +23 -0
- package/core/built/admin/assets/{dist-DCk0UkGx.js → dist-B6bJuxL3.js} +1 -1
- package/core/built/admin/assets/{dist-CbHbGtQ-.js → dist-BtaTA7Ii.js} +1 -1
- package/core/built/admin/assets/{dist-Q1p9iXwH.js → dist-CZgIBXQN.js} +1 -1
- package/core/built/admin/assets/dist-CiHx7vA5.js +6 -0
- package/core/built/admin/assets/{dist-ARN6XTcR.js → dist-ECAUrdGc.js} +1 -1
- package/core/built/admin/assets/dist-Z97pCNqS.js +11 -0
- package/core/built/admin/assets/{dist-t6AXpf_4.js → dist-zwwGt8Yf.js} +1 -1
- package/core/built/admin/assets/{domain-DzrOAPY8.js → domain-Dp9TRrFp.js} +1 -1
- package/core/built/admin/assets/{dropzone-CzhP33wa.js → dropzone-B21Entqz.js} +1 -1
- package/core/built/admin/assets/{edit-profile-oufs4R4n.js → edit-profile-DG6lpMsr.js} +1 -1
- package/core/built/admin/assets/editor-CPQOrhrn.js +8 -0
- package/core/built/admin/assets/{editor-DtxX27Jz.css → editor-DLioOiRN.css} +1 -1
- package/core/built/admin/assets/{embed-signup-form-modal-Ch3s6lqA.js → embed-signup-form-modal-CgWniDha.js} +2 -2
- package/core/built/admin/assets/esm-CXo5SEPy.js +2 -0
- package/core/built/admin/assets/{explore-CA-R01Br.js → explore-C3rzONJv.js} +1 -1
- package/core/built/admin/assets/{feed-ClC8_Gld.js → feed-DpUwS1cU.js} +1 -1
- package/core/built/admin/assets/{field-B3OPenbP.js → field-BQqLTgHM.js} +1 -1
- package/core/built/admin/assets/{filter-sources-CiPLnG9A.js → filter-sources-FgqoUIL7.js} +1 -1
- package/core/built/admin/assets/{filters-yFvybRMe.js → filters-D0uIMD8n.js} +1 -1
- package/core/built/admin/assets/filters-DZJT29GW.js +1 -0
- package/core/built/admin/assets/{first-promoter-modal-BC0UpYCD.js → first-promoter-modal-DJhQQheG.js} +1 -1
- package/core/built/admin/assets/framework-provider-CFLwK5uS.js +4 -0
- package/core/built/admin/assets/{ghost-d38c30b866e59f2e06a357b5eb844fe5.css → ghost-68f447a1796b93a1f128b649bac8506d.css} +1 -1
- package/core/built/admin/assets/{ghost-d683fbeb945b13155ea669caa5d8db41.js → ghost-ba408063a5922b458b4a8f216cf23251.js} +124 -342
- package/core/built/admin/assets/{ghost-dark-1bffd5099ba7a749303133efacf8480b.css → ghost-dark-28ae4899c92022857f3584694bcb50ef.css} +1 -1
- package/core/built/admin/assets/{gift-link-modal-BKRuqo-k.js → gift-link-modal-sdFLjND9.js} +1 -1
- package/core/built/admin/assets/{gift-links-CVuqLhsZ.js → gift-links-D7BJ9qfe.js} +1 -1
- package/core/built/admin/assets/{global-data-provider-RzW6Bnlf.js → global-data-provider-CixmTX00.js} +1 -1
- package/core/built/admin/assets/{growth-ClNAldfL.js → growth-0YYkBEeS.js} +1 -1
- package/core/built/admin/assets/{growth-Dz8OSG06.js → growth-DpjAPeQT.js} +2 -2
- package/core/built/admin/assets/{history-modal-DIVuQ5Ct.js → history-modal-BWJ4-cJ0.js} +1 -1
- package/core/built/admin/assets/{hooks-D6EE7BIt.js → hooks-CJepWnbB.js} +1 -1
- package/core/built/admin/assets/{html-field-CVZ2sMxw.js → html-field-C-oa5DEl.js} +1 -1
- package/core/built/admin/assets/{iframe-buffering-e8vH4-Qn.js → iframe-buffering-Ba9sDpi2.js} +1 -1
- package/core/built/admin/assets/{images-Ok8FFJ3A.js → images-3cQgwfTD.js} +1 -1
- package/core/built/admin/assets/{inbox-CMUVI6qb.js → inbox-Dz_JblyJ.js} +6 -6
- package/core/built/admin/assets/index-BOJzlYiz.js +67 -0
- package/core/built/admin/assets/index-Bid6LAyc.css +1 -0
- package/core/built/admin/assets/{input-group-2XU-iuh_.js → input-group-DpogNUIt.js} +1 -1
- package/core/built/admin/assets/{integrations-BT6_iJvu.js → integrations-B9yYgci5.js} +1 -1
- package/core/built/admin/assets/{invite-user-modal-DrZ7xxrZ.js → invite-user-modal-DCiQSiuD.js} +1 -1
- package/core/built/admin/assets/{koenig-editor-base-BPj4S760.js → koenig-editor-base-BApy8Q7C.js} +1 -1
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +8328 -8294
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +84 -84
- package/core/built/admin/assets/{koenig-lexical-Cdjv-m3_.js → koenig-lexical-2KuqRwZI.js} +33 -33
- package/core/built/admin/assets/{kpi-card-J8Mj1sDX.js → kpi-card-Ck3tT2Wg.js} +1 -1
- package/core/built/admin/assets/{kpi-card-awos0BMH.js → kpi-card-DDIxptaS.js} +1 -1
- package/core/built/admin/assets/{kpi-helpers-SEhvH-8E.js → kpi-helpers-ssbuy-hD.js} +1 -1
- package/core/built/admin/assets/{kpi-tabs-R6BjsKhd.js → kpi-tabs-BqoIM6bI.js} +1 -1
- package/core/built/admin/assets/{label-BMC3dzLI.js → label-DodDsMzn.js} +1 -1
- package/core/built/admin/assets/label-picker-Cvvbmzb5.js +1 -0
- package/core/built/admin/assets/{labels-DAPjKPy2.js → labels-DqvSR6JF.js} +1 -1
- package/core/built/admin/assets/layout-DkkGyev2.js +1 -0
- package/core/built/admin/assets/limit-service-BhiEvqcP.js +20 -0
- package/core/built/admin/assets/{link-helpers-Bzkk68GD.js → link-helpers-kpbU8TWh.js} +1 -1
- package/core/built/admin/assets/luxon-D1RVjjpq.js +1 -0
- package/core/built/admin/assets/member-custom-fields-CvGcpaiW.js +1 -0
- package/core/built/admin/assets/member-detail-CMCMZ47_.js +1 -0
- package/core/built/admin/assets/members-sRl6fNfq.js +11 -0
- package/core/built/admin/assets/{moderation-mPjYHgZW.js → moderation-KhKwGqI1.js} +1 -1
- package/core/built/admin/assets/{multi-select-combobox-CsoyHAiX.js → multi-select-combobox-nhCff0_K.js} +1 -1
- package/core/built/admin/assets/{navigation-modal-D_c0badj.js → navigation-modal-MDJExWDJ.js} +1 -1
- package/core/built/admin/assets/newsletter-BIBHumfV.js +1 -0
- package/core/built/admin/assets/newsletter-detail-modal-CfjBMH3v.js +1 -0
- package/core/built/admin/assets/{newsletter-emails-B-OLfrvr.js → newsletter-emails-ClDL-7dA.js} +1 -1
- package/core/built/admin/assets/newsletters-CBe-IPzK.js +1 -0
- package/core/built/admin/assets/{newsletters-Du0lzskS.js → newsletters-ILsexy-5.js} +1 -1
- package/core/built/admin/assets/{note-CSVsUKBm.js → note-HnNUOt4d.js} +1 -1
- package/core/built/admin/assets/{notifications-DV9FRtDw.js → notifications-Cj5e-258.js} +1 -1
- package/core/built/admin/assets/nql-lang-CaWrLesS.js +19 -0
- package/core/built/admin/assets/{offers-Um6ab71O.js → offers-DMxebaoK.js} +1 -1
- package/core/built/admin/assets/offers-container-modal-BHwHtixC.js +1 -0
- package/core/built/admin/assets/{onboarding-route--jL5KLPD.js → onboarding-route-D_4jp7Nq.js} +1 -1
- package/core/built/admin/assets/overview-CezDdg-F.js +1 -0
- package/core/built/admin/assets/overview-D5mM51Lj.js +1 -0
- package/core/built/admin/assets/{page-header-DAvDYKO9.js → page-header-VLPJfj_d.js} +1 -1
- package/core/built/admin/assets/{pages-BVMnpI2O.js → pages-Gchxp5c7.js} +1 -1
- package/core/built/admin/assets/{pintura-modal-jiUCxX0Q.js → pintura-modal-BXXJ8Iaa.js} +1 -1
- package/core/built/admin/assets/{portal-frame-D4D4nEO6.js → portal-frame-BLCvF8f-.js} +1 -1
- package/core/built/admin/assets/portal-modal-DN80tAcy.js +1 -0
- package/core/built/admin/assets/{post-analytics-BhAtiFJp.js → post-analytics-Gv81eN_g.js} +1 -1
- package/core/built/admin/assets/post-analytics-header-_ydSY8qO.js +1 -0
- package/core/built/admin/assets/{post-analytics-provider-DGg0FUqs.js → post-analytics-provider-O2YjZMhN.js} +1 -1
- package/core/built/admin/assets/{post-share-modal-DtFAC132.js → post-share-modal-CzYak_a5.js} +1 -1
- package/core/built/admin/assets/{posts-CxG7v0-M.js → posts-DgcUi2rc.js} +1 -1
- package/core/built/admin/assets/preferences-gneLb6LV.js +5 -0
- package/core/built/admin/assets/{preview-modal-DctYx20m.js → preview-modal-Bkq-tgVm.js} +1 -1
- package/core/built/admin/assets/profile-CzXmCNTC.js +1 -0
- package/core/built/admin/assets/{radio-group-CI5_Kf_d.js → radio-group-Dsq5ZEbV.js} +1 -1
- package/core/built/admin/assets/{referrers-B9QDAfYm.js → referrers-C3a2K8qB.js} +1 -1
- package/core/built/admin/assets/{router-provider-DSQfprxH.js → router-provider-CkB_ArLF.js} +1 -1
- package/core/built/admin/assets/{select-CkXqNJDJ.js → select-xuZAuDyS.js} +1 -1
- package/core/built/admin/assets/{settings-ZCj-W56o.js → settings-Ct8pabhr.js} +1 -1
- package/core/built/admin/assets/settings-KSWWQanQ.js +4 -0
- package/core/built/admin/assets/{settings-breadcrumbs-DIq31FwA.js → settings-breadcrumbs-DbPGu-Tu.js} +1 -1
- package/core/built/admin/assets/{share-modal-BNj14g79.js → share-modal-CdiUx9WM.js} +1 -1
- package/core/built/admin/assets/{slack-modal-C6SpMQ-q.js → slack-modal-BP--Z3jE.js} +1 -1
- package/core/built/admin/assets/{sort-button-B-k54ow2.js → sort-button-CjfKdlsL.js} +1 -1
- package/core/built/admin/assets/{source-icon-Cej3sZM6.js → source-icon-DHj1h6Ts.js} +1 -1
- package/core/built/admin/assets/{stats-B2KW8E2y.js → stats-C615kulJ.js} +1 -1
- package/core/built/admin/assets/{stats-view-CVzU2-p5.js → stats-view-BSMRdYiN.js} +1 -1
- package/core/built/admin/assets/{step-1-Dbn1iIMU.js → step-1-pSdGYtIF.js} +1 -1
- package/core/built/admin/assets/{step-2-BahnSuN6.js → step-2-T9Uy4eBD.js} +1 -1
- package/core/built/admin/assets/{step-3-CW04e2ZJ.js → step-3-D4uXe_XG.js} +2 -2
- package/core/built/admin/assets/{stripe-button-BkBZqaGG.js → stripe-button-Bh4p6Wud.js} +1 -1
- package/core/built/admin/assets/stripe-connect-modal-C2vTvepi.js +1 -0
- package/core/built/admin/assets/{table-BtLcEi1M.js → table-DghJxZMH.js} +1 -1
- package/core/built/admin/assets/{tabs-BaWRfg8j.js → tabs-l9c7FjzI.js} +1 -1
- package/core/built/admin/assets/tag-detail-DBw_M2-p.js +2 -0
- package/core/built/admin/assets/{tags-BbygUvNB.js → tags-DoWsAG6l.js} +1 -1
- package/core/built/admin/assets/{tags-BwCgmr5v.js → tags-EmEvJN75.js} +1 -1
- package/core/built/admin/assets/{testimonials-modal--bdzppyh.js → testimonials-modal-BZKePE2T.js} +1 -1
- package/core/built/admin/assets/tier-detail-modal-D65pVWsN.js +1 -0
- package/core/built/admin/assets/{tiers-Da6kxHQP.js → tiers-8iTFjJWg.js} +1 -1
- package/core/built/admin/assets/{toggle-group-CVl-Xigr.js → toggle-group-uJnl42w3.js} +1 -1
- package/core/built/admin/assets/{topic-filter-BoRHRbL1.js → topic-filter-BM7hz0kW.js} +1 -1
- package/core/built/admin/assets/transistor-modal-VZOGN3od.js +1 -0
- package/core/built/admin/assets/{unsplash-modal-CixoRAO2.js → unsplash-modal-CKGfACxF.js} +1 -1
- package/core/built/admin/assets/{use-analytics-data-BiWvcf1u.js → use-analytics-data-IxJcSjc9.js} +1 -1
- package/core/built/admin/assets/use-check-theme-limit-error-CGp3CGT5.js +1 -0
- package/core/built/admin/assets/use-feature-flag-CSq2Lwzt.js +1 -0
- package/core/built/admin/assets/use-filter-params-0hcJJuEJ.js +1 -0
- package/core/built/admin/assets/{use-filterable-api-iMpDZBxD.js → use-filterable-api-CQBtVG-l.js} +1 -1
- package/core/built/admin/assets/{use-growth-stats-Dom8xVzo.js → use-growth-stats-1NW-8eEZ.js} +1 -1
- package/core/built/admin/assets/use-limiter-ffwpsZPp.js +2 -0
- package/core/built/admin/assets/{use-pintura-config-Bj4QmiVk.js → use-pintura-config-BjFChXV1.js} +1 -1
- package/core/built/admin/assets/{use-pintura-editor-DvrslmPE.js → use-pintura-editor-Cb41oJyL.js} +1 -1
- package/core/built/admin/assets/{use-post-referrers-pxIHTXFs.js → use-post-referrers-Dp1BnTAK.js} +1 -1
- package/core/built/admin/assets/{use-reply-chain-data-CmFe0N6F.js → use-reply-chain-data-C0ZUTkfk.js} +1 -1
- package/core/built/admin/assets/{use-setting-group-Cmp_sy-d.js → use-setting-group-Bx8M6FL2.js} +1 -1
- package/core/built/admin/assets/{use-settings-navigation-BmROPrls.js → use-settings-navigation-BOZcat_V.js} +1 -1
- package/core/built/admin/assets/{use-simple-pagination-D5rL3YwO.js → use-simple-pagination-DsEGSJhe.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-query-DP2dMaq7.js → use-tinybird-query-Ci_DiPkX.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-token-DrK_skPQ.js → use-tinybird-token-CsUwc722.js} +1 -1
- package/core/built/admin/assets/use-upgrade-route-B3_DUiuT.js +1 -0
- package/core/built/admin/assets/{use-url-input-ChrUZBFf.js → use-url-input-BEZJpVgN.js} +2 -2
- package/core/built/admin/assets/user-detail-modal-595z1N5r.js +1 -0
- package/core/built/admin/assets/{users-B6hIds6n.js → users-DmGMPfw7.js} +1 -1
- package/core/built/admin/assets/virtual-list-D5ugXyZY.js +1 -0
- package/core/built/admin/assets/{web-D71mjB2r.js → web-CAta3lRE.js} +1 -1
- package/core/built/admin/assets/web-Ds9_wWJK.js +2 -0
- package/core/built/admin/assets/zapier-modal-CcfDZY53.js +1 -0
- package/core/built/admin/index.html +12 -12
- package/core/frontend/apps/private-blogging/lib/middleware.js +5 -58
- package/core/frontend/helpers/ghost_head.js +52 -0
- package/core/frontend/meta/asset-url.js +20 -9
- package/core/frontend/public/cards.manifest.json +1 -0
- package/core/frontend/public/ghost-stats.min.js +1 -1
- package/core/frontend/services/assets-minification/card-assets.js +121 -46
- package/core/frontend/services/assets-minification/index.js +13 -4
- package/core/frontend/services/data/fetch-data.js +7 -14
- package/core/frontend/services/llms/service.js +27 -6
- package/core/frontend/services/proxy.js +1 -0
- package/core/frontend/services/routing/api-adapter.js +11 -5
- package/core/frontend/services/routing/collection-router.js +1 -2
- package/core/frontend/services/routing/controllers/entry/markdown.js +63 -5
- package/core/frontend/services/routing/controllers/entry.js +1 -1
- package/core/frontend/services/routing/controllers/previews.js +0 -3
- package/core/frontend/services/routing/events.js +2 -1
- package/core/frontend/services/routing/parent-router.js +0 -22
- package/core/frontend/services/routing/router-manager.js +2 -2
- package/core/frontend/services/routing/static-pages-router.js +0 -1
- package/core/frontend/services/sitemap/handler.js +3 -4
- package/core/frontend/services/sitemap/site-map-manager.js +1 -2
- package/core/frontend/services/theme-engine/i18n/i18n.js +0 -33
- package/core/frontend/web/middleware/llms-discovery.js +2 -2
- package/core/frontend/web/routers/serve-public-file.js +33 -3
- package/core/frontend/web/site.js +5 -4
- package/core/server/adapters/jobs/InMemoryJobsBackend.js +113 -0
- package/core/server/adapters/redirects/S3RedirectsStore.js +33 -13
- package/core/server/adapters/route-settings/S3RouteSettingsStore.js +33 -13
- package/core/server/adapters/scheduling/scheduling-default.js +6 -7
- package/core/server/adapters/storage/LocalStorageBase.js +1 -3
- package/core/server/api/endpoints/index.js +0 -4
- package/core/server/api/endpoints/member-custom-fields.js +15 -4
- package/core/server/api/endpoints/pages.js +3 -11
- package/core/server/api/endpoints/posts.js +3 -11
- package/core/server/api/endpoints/utils/api-filter-utils.js +15 -1
- package/core/server/api/endpoints/utils/serializers/input/settings.js +4 -0
- package/core/server/api/endpoints/utils/serializers/output/index.js +0 -4
- package/core/server/api/endpoints/utils/serializers/output/mappers/email-batches.js +1 -1
- package/core/server/api/endpoints/utils/serializers/output/mappers/snippets.js +0 -1
- package/core/server/api/endpoints/utils/serializers/output/member-custom-fields.js +1 -0
- package/core/server/data/exporter/table-lists.js +4 -1
- package/core/server/data/migrations/utils/schema.js +5 -3
- package/core/server/data/migrations/versions/6.57/2026-08-04-18-00-00-store-custom-field-values-as-leaf-rows.js +19 -7
- package/core/server/data/migrations/versions/6.58/2026-08-10-08-20-50-add-machine-payments-settings.js +41 -0
- package/core/server/data/migrations/versions/6.58/2026-08-10-08-21-00-add-machine-payment-events-table.js +18 -0
- package/core/server/data/migrations/versions/6.58/2026-08-10-15-24-09-reset-custom-field-definitions-for-underscored-keys.js +66 -0
- package/core/server/data/migrations/versions/6.58/2026-08-11-10-00-00-swap-custom-field-values-to-key-fk.js +65 -0
- package/core/server/data/migrations/versions/6.58/2026-08-11-10-34-25-remove-legacy-explore-integration.js +131 -0
- package/core/server/data/migrations/versions/6.58/2026-08-11-16-00-00-add-sort-order-to-members-custom-fields.js +13 -0
- package/core/server/data/migrations/versions/6.58/2026-08-11-21-22-48-rename-email-batches-provider-id.js +8 -0
- package/core/server/data/migrations/versions/6.58/2026-08-13-16-07-33-add-automation-runs-automation-id-created-at-index.js +28 -0
- package/core/server/data/schema/README.md +87 -0
- package/core/server/data/schema/commands.js +5 -2
- package/core/server/data/schema/default-settings/default-settings.json +27 -0
- package/core/server/data/schema/fixtures/fixtures.json +0 -21
- package/core/server/data/schema/schema.js +37 -9
- package/core/server/data/seeders/data-generator.js +3 -1
- package/core/server/data/seeders/importers/automation-action-edges-importer.js +38 -0
- package/core/server/data/seeders/importers/automation-action-revisions-importer.js +139 -0
- package/core/server/data/seeders/importers/automation-actions-importer.js +84 -0
- package/core/server/data/seeders/importers/automation-run-steps-importer.js +134 -0
- package/core/server/data/seeders/importers/automation-runs-importer.js +97 -0
- package/core/server/data/seeders/importers/automations-importer.js +78 -0
- package/core/server/data/seeders/importers/benefits-importer.js +1 -1
- package/core/server/data/seeders/importers/comment-reports-importer.js +5 -5
- package/core/server/data/seeders/importers/comments-importer.js +6 -6
- package/core/server/data/seeders/importers/email-batches-importer.js +9 -8
- package/core/server/data/seeders/importers/email-recipient-failures-importer.js +1 -1
- package/core/server/data/seeders/importers/email-recipients-importer.js +12 -12
- package/core/server/data/seeders/importers/emails-importer.js +7 -7
- package/core/server/data/seeders/importers/index.js +6 -0
- package/core/server/data/seeders/importers/labels-importer.js +4 -4
- package/core/server/data/seeders/importers/members-click-events-importer.js +4 -4
- package/core/server/data/seeders/importers/members-created-events-importer.js +5 -5
- package/core/server/data/seeders/importers/members-feedback-importer.js +7 -7
- package/core/server/data/seeders/importers/members-importer.js +5 -5
- package/core/server/data/seeders/importers/members-labels-importer.js +1 -1
- package/core/server/data/seeders/importers/members-login-events-importer.js +4 -4
- package/core/server/data/seeders/importers/members-newsletters-importer.js +1 -1
- package/core/server/data/seeders/importers/members-paid-subscription-events-importer.js +1 -1
- package/core/server/data/seeders/importers/members-products-importer.js +3 -3
- package/core/server/data/seeders/importers/members-status-events-importer.js +4 -4
- package/core/server/data/seeders/importers/members-stripe-customers-importer.js +3 -3
- package/core/server/data/seeders/importers/members-stripe-customers-subscriptions-importer.js +8 -8
- package/core/server/data/seeders/importers/members-subscribe-events-importer.js +3 -3
- package/core/server/data/seeders/importers/members-subscription-created-events-importer.js +4 -4
- package/core/server/data/seeders/importers/newsletters-importer.js +1 -1
- package/core/server/data/seeders/importers/offer-redemptions-importer.js +6 -6
- package/core/server/data/seeders/importers/offers-importer.js +4 -4
- package/core/server/data/seeders/importers/posts-authors-importer.js +1 -1
- package/core/server/data/seeders/importers/posts-importer.js +5 -5
- package/core/server/data/seeders/importers/posts-products-importer.js +1 -1
- package/core/server/data/seeders/importers/posts-tags-importer.js +1 -1
- package/core/server/data/seeders/importers/products-benefits-importer.js +1 -1
- package/core/server/data/seeders/importers/products-importer.js +1 -1
- package/core/server/data/seeders/importers/recommendation-click-events-importer.js +1 -1
- package/core/server/data/seeders/importers/recommendation-subscribe-events-importer.js +1 -1
- package/core/server/data/seeders/importers/recommendations-importer.js +1 -1
- package/core/server/data/seeders/importers/redirects-importer.js +1 -1
- package/core/server/data/seeders/importers/roles-users-importer.js +1 -1
- package/core/server/data/seeders/importers/stripe-prices-importer.js +1 -1
- package/core/server/data/seeders/importers/stripe-products-importer.js +1 -1
- package/core/server/data/seeders/importers/table-importer.js +92 -91
- package/core/server/data/seeders/importers/tags-importer.js +3 -3
- package/core/server/data/seeders/importers/users-importer.js +3 -3
- package/core/server/data/seeders/importers/web-mentions-importer.js +3 -3
- package/core/server/data/seeders/utils/database-date.js +14 -18
- package/core/server/data/seeders/utils/json-importer.js +1 -1
- package/core/server/data/seeders/utils/random.js +8 -7
- package/core/server/data/seeders/utils/topological-sort.js +5 -3
- package/core/server/data/tinybird/ARCHITECTURE.md +1 -2
- package/core/server/models/base/plugins/crud.js +0 -5
- package/core/server/models/index.js +2 -0
- package/core/server/models/machine-payment-event.js +18 -0
- package/core/server/models/member.js +24 -0
- package/core/server/models/settings.js +32 -3
- package/core/server/models/user.js +1 -1
- package/core/server/services/README.md +30 -0
- package/core/server/services/activitypub/activity-pub-service.js +2 -3
- package/core/server/services/adapter-manager/base-classes.js +3 -1
- package/core/server/services/auth/passwordreset.js +47 -51
- package/core/server/services/automations/database-automations-repository.js +19 -3
- package/core/server/services/automations/poll.js +2 -8
- package/core/server/services/comments/comments-service.js +4 -16
- package/core/server/services/comments/index.js +1 -3
- package/core/server/services/email-analytics/email-analytics-service-wrapper.js +68 -119
- package/core/server/services/email-analytics/email-analytics-service.js +73 -229
- package/core/server/services/email-analytics/event-processing-result.js +0 -10
- package/core/server/services/email-analytics/fetch-mailgun-events.js +25 -0
- package/core/server/services/email-analytics/index.js +35 -67
- package/core/server/services/email-analytics/lib/queries.js +88 -51
- package/core/server/services/email-analytics/newsletter-email-analytics-batch-processor.js +4 -1
- package/core/server/services/email-service/batch-sending-service.js +1 -1
- package/core/server/services/email-service/email-event-processor.js +12 -12
- package/core/server/services/email-service/events/email-bounced-event.js +8 -46
- package/core/server/services/email-service/events/email-delivered-event.js +8 -36
- package/core/server/services/email-service/events/email-opened-event.js +8 -36
- package/core/server/services/email-service/events/email-temporary-bounced-event.js +8 -46
- package/core/server/services/email-service/events/email-unsubscribed-event.js +8 -31
- package/core/server/services/email-service/events/spam-complaint-event.js +8 -31
- package/core/server/services/email-service/newsletter-email-event-storage.js +8 -7
- package/core/server/services/email-service/sending-service.js +1 -1
- package/core/server/services/email-suppression-list/mailgun-email-suppression-list.js +2 -2
- package/core/server/services/email-suppression-list/service.js +1 -3
- package/core/server/services/explore-ping/explore-ping-service.js +1 -6
- package/core/server/services/explore-ping/index.js +0 -2
- package/core/server/services/gifts/index.js +109 -3
- package/core/server/services/gifts/jobs/clean-gifts-job.js +1 -1
- package/core/server/services/gifts/jobs/send-gift-reminders-job.js +1 -1
- package/core/server/services/jobs-service/index.js +36 -0
- package/core/server/services/jobs-service/job.js +6 -0
- package/core/server/services/jobs-service/jobs-service.js +91 -0
- package/core/server/services/jobs-service/register-job-handlers.js +6 -0
- package/core/server/services/lib/mailgun-client.js +1 -1
- package/core/server/services/machine-payments/README.md +23 -0
- package/core/server/services/machine-payments/adapters/mpp-adapter.js +195 -0
- package/core/server/services/machine-payments/content-loader.js +89 -0
- package/core/server/services/machine-payments/eligibility.js +6 -0
- package/core/server/services/machine-payments/events/machine-payment-event-repository.js +58 -0
- package/core/server/services/machine-payments/events/machine-payment-event.js +46 -0
- package/core/server/services/machine-payments/index.js +87 -0
- package/core/server/services/machine-payments/pricing.js +54 -0
- package/core/server/services/machine-payments/service.js +275 -0
- package/core/server/services/machine-payments/stripe/deposit-address-store.js +157 -0
- package/core/server/services/machine-payments/stripe/payment-recorder.js +85 -0
- package/core/server/services/machine-payments/stripe/stripe-client-options.js +25 -0
- package/core/server/services/machine-payments/types.js +2 -0
- package/core/server/services/members/import-export/csv/parse.js +9 -1
- package/core/server/services/members/import-export/import/completion-email.js +61 -27
- package/core/server/services/members/import-export/import/email-template.js +28 -15
- package/core/server/services/members/import-export/import/importer.js +99 -51
- package/core/server/services/members/import-export/import/spool.js +1 -1
- package/core/server/services/members/import-export/index.js +24 -1
- package/core/server/services/members/members-api/services/member-bread-service.js +2 -2
- package/core/server/services/members/members-config-provider.js +12 -2
- package/core/server/services/members-custom-fields/actions.js +2 -1
- package/core/server/services/members-custom-fields/definitions-service.js +109 -27
- package/core/server/services/members-custom-fields/filter.js +199 -0
- package/core/server/services/members-custom-fields/key.js +37 -0
- package/core/server/services/members-custom-fields/queries.js +15 -0
- package/core/server/services/members-custom-fields/schema.js +1 -1
- package/core/server/services/members-custom-fields/values-service.js +18 -14
- package/core/server/services/members-events/last-seen-at-updater.js +1 -1
- package/core/server/services/oembed/oembed-service.js +8 -2
- package/core/server/services/permissions/can-this.js +4 -1
- package/core/server/services/posts/posts-service.js +18 -16
- package/core/server/services/public-config/config.js +1 -1
- package/core/server/services/route-settings/dynamic-routing-service.js +2 -0
- package/core/server/services/settings-helpers/settings-helpers.js +0 -1
- package/core/server/services/stats/posts-stats-service.js +0 -2
- package/core/server/services/stripe/stripe-api.js +2 -9
- package/core/server/web/api/endpoints/admin/middleware.js +0 -1
- package/core/server/web/api/endpoints/admin/routes.js +2 -3
- package/core/server/web/comments/routes.js +31 -8
- package/core/shared/config/defaults.json +17 -2
- package/core/shared/config/loader.js +11 -2
- package/core/shared/config/overrides.json +2 -0
- package/core/shared/config/secrets.js +71 -0
- package/core/shared/labs.js +4 -13
- package/core/shared/machine-payments.js +30 -0
- package/core/shared/private-site-access/index.js +68 -0
- package/package.json +48 -43
- package/pnpm-lock.yaml +1412 -1583
- package/pnpm-workspace.yaml +85 -48
- package/bin/validate-adapters.ts +0 -87
- package/components/tryghost-adapter-base-scheduling-0.2.1.tgz +0 -0
- package/components/tryghost-adapter-base-sso-0.1.1.tgz +0 -0
- package/components/tryghost-kg-card-factory-5.2.3.tgz +0 -0
- package/components/tryghost-kg-clean-basic-html-4.3.3.tgz +0 -0
- package/components/tryghost-kg-converters-1.2.4.tgz +0 -0
- package/components/tryghost-kg-default-cards-10.3.4.tgz +0 -0
- package/components/tryghost-kg-default-nodes-2.1.5.tgz +0 -0
- package/components/tryghost-kg-default-transforms-1.3.3.tgz +0 -0
- package/components/tryghost-kg-html-to-lexical-1.3.3.tgz +0 -0
- package/components/tryghost-kg-lexical-html-renderer-1.4.3.tgz +0 -0
- package/components/tryghost-kg-markdown-html-renderer-7.2.3.tgz +0 -0
- package/components/tryghost-kg-utils-1.1.3.tgz +0 -0
- package/content/themes/casper/assets/built/casper.js.map +0 -1
- package/content/themes/casper/assets/built/global.css.map +0 -1
- package/content/themes/casper/assets/built/screen.css.map +0 -1
- package/content/themes/source/assets/built/screen.css.map +0 -1
- package/content/themes/source/assets/built/source.js.map +0 -1
- package/core/built/admin/assets/activitypub/layout-DM_3pqsU.js +0 -1
- package/core/built/admin/assets/add-integration-modal-DU9ghGLS.js +0 -1
- package/core/built/admin/assets/add-newsletter-modal-DFxuQrYu.js +0 -1
- package/core/built/admin/assets/announcement-bar-modal-DGCcezB6.js +0 -1
- package/core/built/admin/assets/card-aUHX0hyR.js +0 -1
- package/core/built/admin/assets/chunk.524.dce1c51e0bc30fb2718e.js +0 -25
- package/core/built/admin/assets/chunk.582.3a430d5c6a744fd475b6.js +0 -27
- package/core/built/admin/assets/code-editor-view-BcyKABrG.js +0 -1
- package/core/built/admin/assets/color-picker-field-8NoSnhNI.js +0 -1
- package/core/built/admin/assets/command-BOYMbZa_.js +0 -1
- package/core/built/admin/assets/comments-BhNM5CYl.js +0 -1
- package/core/built/admin/assets/confirmation-modal-CYD7x3UQ.js +0 -1
- package/core/built/admin/assets/custom-integration-modal-bk3lNEuQ.js +0 -1
- package/core/built/admin/assets/design-and-theme-modal-B2xYKS1I.js +0 -23
- package/core/built/admin/assets/dist-Bg8bSvLA.js +0 -6
- package/core/built/admin/assets/dist-BnFdlebk.js +0 -11
- package/core/built/admin/assets/dist-CsvXCJSe.js +0 -23
- package/core/built/admin/assets/editor-BBKrunMK.js +0 -8
- package/core/built/admin/assets/filters-Bz0HsRie.js +0 -1
- package/core/built/admin/assets/framework-provider-CmtnPXIT.js +0 -4
- package/core/built/admin/assets/index-DUXM0NlS.js +0 -67
- package/core/built/admin/assets/index-Ds2iUoF6.css +0 -1
- package/core/built/admin/assets/label-picker-Bnlru5tO.js +0 -1
- package/core/built/admin/assets/layout-yR9rlvEw.js +0 -1
- package/core/built/admin/assets/limit-modal-COmLevuY.js +0 -1
- package/core/built/admin/assets/limit-service-9fwD33lU.js +0 -20
- package/core/built/admin/assets/member-custom-fields-BvFKEpxJ.js +0 -1
- package/core/built/admin/assets/member-detail-CWdPAe3p.js +0 -1
- package/core/built/admin/assets/members-Bnt0ih2R.js +0 -11
- package/core/built/admin/assets/newsletter-DR49NyGS.js +0 -1
- package/core/built/admin/assets/newsletter-detail-modal-B45pL1pb.js +0 -1
- package/core/built/admin/assets/newsletters-DGs5BaKH.js +0 -1
- package/core/built/admin/assets/nql-lang-B7faUf8D.js +0 -19
- package/core/built/admin/assets/offers-container-modal-B0hFS8nk.js +0 -1
- package/core/built/admin/assets/overview-B-ZPNqMo.js +0 -1
- package/core/built/admin/assets/overview-D96iwoJi.js +0 -1
- package/core/built/admin/assets/portal-modal-BLFdYeDH.js +0 -1
- package/core/built/admin/assets/post-analytics-header-DK27VjJm.js +0 -1
- package/core/built/admin/assets/preferences-CuWLISHa.js +0 -5
- package/core/built/admin/assets/profile-BsKYYtuL.js +0 -1
- package/core/built/admin/assets/settings-B4vM-rgV.js +0 -4
- package/core/built/admin/assets/stripe-connect-modal-CYDDrSJm.js +0 -1
- package/core/built/admin/assets/tag-detail-CmqiC32L.js +0 -1
- package/core/built/admin/assets/tier-detail-modal-BZmAsIy4.js +0 -1
- package/core/built/admin/assets/transistor-modal-CBPDqEGy.js +0 -1
- package/core/built/admin/assets/use-check-theme-limit-error-B14lZukR.js +0 -2
- package/core/built/admin/assets/use-feature-flag-BmJoc5WN.js +0 -1
- package/core/built/admin/assets/use-filter-params-ChoV72Hw.js +0 -1
- package/core/built/admin/assets/use-limiter-Dwg8ivgQ.js +0 -2
- package/core/built/admin/assets/user-detail-modal-XODpOqNC.js +0 -1
- package/core/built/admin/assets/virtual-list-Dnoafqbk.js +0 -1
- package/core/built/admin/assets/web-z71lA1Y3.js +0 -2
- package/core/built/admin/assets/zapier-modal-Bp507Um7.js +0 -1
- package/core/frontend/meta/og-type.ts +0 -13
- package/core/frontend/services/routing/api-adapter.ts +0 -157
- package/core/frontend/services/routing/config.ts +0 -59
- package/core/frontend/services/routing/controllers/entry/canonical-url.ts +0 -14
- package/core/frontend/services/routing/controllers/entry/gift-links.ts +0 -56
- package/core/frontend/services/routing/controllers/entry/markdown.ts +0 -72
- package/core/frontend/services/routing/controllers/entry.ts +0 -120
- package/core/frontend/services/routing/controllers/rss.ts +0 -66
- package/core/frontend/services/routing/permalink-adapter.ts +0 -10
- package/core/frontend/src/utils/privacy.ts +0 -66
- package/core/server/adapters/redirects/FileStore.ts +0 -113
- package/core/server/adapters/redirects/S3RedirectsStore.ts +0 -185
- package/core/server/adapters/route-settings/FileStore.ts +0 -144
- package/core/server/adapters/route-settings/S3RouteSettingsStore.ts +0 -199
- package/core/server/adapters/route-settings/utils.ts +0 -17
- package/core/server/adapters/scheduling/deps.d.ts +0 -1
- package/core/server/adapters/scheduling/error-capture.ts +0 -91
- package/core/server/adapters/scheduling/scheduling-default.ts +0 -248
- package/core/server/adapters/scheduling/utils.ts +0 -36
- package/core/server/adapters/sso/DefaultSSOAdapter.ts +0 -15
- package/core/server/adapters/storage/LocalFilesStorage.ts +0 -24
- package/core/server/adapters/storage/LocalImagesStorage.ts +0 -24
- package/core/server/adapters/storage/LocalMediaStorage.ts +0 -24
- package/core/server/adapters/storage/LocalStorageBase.ts +0 -342
- package/core/server/adapters/storage/S3Storage.ts +0 -506
- package/core/server/adapters/storage/utils.ts +0 -60
- package/core/server/api/endpoints/automation-action-links.ts +0 -46
- package/core/server/api/endpoints/explore.js +0 -18
- package/core/server/api/endpoints/gift-links.ts +0 -87
- package/core/server/api/endpoints/libraries.d.ts +0 -2
- package/core/server/api/endpoints/member-commenting.ts +0 -92
- package/core/server/api/endpoints/member-custom-fields.ts +0 -106
- package/core/server/api/endpoints/utils/api-filter-utils.ts +0 -22
- package/core/server/api/endpoints/utils/gift-link-access.ts +0 -82
- package/core/server/api/endpoints/utils/public-cache-keys.ts +0 -47
- package/core/server/api/endpoints/utils/serializers/input/member-commenting.ts +0 -29
- package/core/server/api/endpoints/utils/serializers/output/explore.js +0 -11
- package/core/server/api/endpoints/utils/serializers/output/gift-links.ts +0 -21
- package/core/server/api/endpoints/utils/serializers/output/member-commenting.ts +0 -8
- package/core/server/api/endpoints/utils/serializers/output/member-custom-fields.ts +0 -25
- package/core/server/api/endpoints/utils/serializers/output/members.d.ts +0 -20
- package/core/server/api/endpoints/utils/serializers/output/posts-csv-transform.ts +0 -46
- package/core/server/api/endpoints/utils/serializers/output/stream-csv-response.ts +0 -47
- package/core/server/api/endpoints/utils/validate-email-sender-fields.ts +0 -42
- package/core/server/data/importer/email-template.ts +0 -213
- package/core/server/data/seeders/utils/topological-sort.ts +0 -48
- package/core/server/lib/auto-filling-map.ts +0 -23
- package/core/server/lib/bootstrap-socket.ts +0 -114
- package/core/server/lib/case-keys.ts +0 -30
- package/core/server/lib/common/to-plain.ts +0 -25
- package/core/server/lib/db-date.ts +0 -8
- package/core/server/lib/get-inbox-links.ts +0 -276
- package/core/server/lib/post-revisions.ts +0 -152
- package/core/server/lib/soonest-timer.ts +0 -57
- package/core/server/models/base/plugins/bulk-filters.ts +0 -35
- package/core/server/models/role-utils.ts +0 -56
- package/core/server/services/activitypub/activity-pub-service.ts +0 -216
- package/core/server/services/adapter-manager/adapter-manager.ts +0 -253
- package/core/server/services/adapter-manager/adapter-paths.ts +0 -26
- package/core/server/services/adapter-manager/base-classes.ts +0 -22
- package/core/server/services/adapter-manager/index.ts +0 -16
- package/core/server/services/adapter-manager/types.ts +0 -45
- package/core/server/services/adapter-manager/utils.ts +0 -308
- package/core/server/services/auth/reset-authentication.ts +0 -72
- package/core/server/services/automations/automations-api.ts +0 -396
- package/core/server/services/automations/automations-repository.ts +0 -218
- package/core/server/services/automations/constants.ts +0 -4
- package/core/server/services/automations/database-automations-repository.ts +0 -1513
- package/core/server/services/automations/database-date.ts +0 -26
- package/core/server/services/automations/fake-wait-hours-multiplier.ts +0 -12
- package/core/server/services/automations/get-scheduler-idempotency-key.ts +0 -8
- package/core/server/services/automations/mailgun-message-id.ts +0 -27
- package/core/server/services/automations/poll.ts +0 -325
- package/core/server/services/automations/scheduler-poll-time.ts +0 -38
- package/core/server/services/automations/service.ts +0 -131
- package/core/server/services/automations/stale-lock-cutoff.ts +0 -5
- package/core/server/services/comments/email-templates/new-comment-reply.txt.ts +0 -25
- package/core/server/services/comments/email-templates/new-comment.txt.ts +0 -25
- package/core/server/services/comments/email-templates/report.txt.ts +0 -34
- package/core/server/services/custom-redirects/redirect-config-parser.ts +0 -148
- package/core/server/services/custom-redirects/redirects-service.ts +0 -181
- package/core/server/services/custom-redirects/utils.ts +0 -7
- package/core/server/services/donations/donation-bookshelf-repository.ts +0 -63
- package/core/server/services/donations/donation-payment-event.ts +0 -51
- package/core/server/services/email-address/email-address-parser.d.ts +0 -12
- package/core/server/services/email-address/email-address-service.ts +0 -241
- package/core/server/services/email-analytics/automation-email-analytics-batch-processor.ts +0 -130
- package/core/server/services/email-analytics/batch-event-processor.ts +0 -18
- package/core/server/services/email-analytics/email-analytics-provider-mailgun.js +0 -57
- package/core/server/services/email-analytics/event-processing-result.ts +0 -66
- package/core/server/services/email-analytics/events/start-automation-email-analytics-job-event.ts +0 -14
- package/core/server/services/email-analytics/events/start-email-analytics-job-event.ts +0 -15
- package/core/server/services/email-analytics/index.ts +0 -109
- package/core/server/services/email-analytics/jobs/email-analytics-job-scheduler.ts +0 -126
- package/core/server/services/email-analytics/lib/queries.ts +0 -341
- package/core/server/services/email-service/domain-warming-service.ts +0 -91
- package/core/server/services/explore/explore-service.js +0 -59
- package/core/server/services/explore/index.js +0 -18
- package/core/server/services/explore-ping/explore-ping-service.ts +0 -198
- package/core/server/services/explore-ping/index.ts +0 -42
- package/core/server/services/files/file-type-utils.ts +0 -56
- package/core/server/services/gift-links/actions.ts +0 -51
- package/core/server/services/gift-links/codec.ts +0 -16
- package/core/server/services/gift-links/index.ts +0 -20
- package/core/server/services/gift-links/models.ts +0 -22
- package/core/server/services/gift-links/schema.ts +0 -33
- package/core/server/services/gift-links/serializers.ts +0 -20
- package/core/server/services/gift-links/service.ts +0 -95
- package/core/server/services/gifts/constants.ts +0 -3
- package/core/server/services/gifts/email-templates/gift-purchase-confirmation.ts +0 -43
- package/core/server/services/gifts/email-templates/gift-reminder.ts +0 -50
- package/core/server/services/gifts/gift-bookshelf-repository.ts +0 -366
- package/core/server/services/gifts/gift-checkout-offer.ts +0 -127
- package/core/server/services/gifts/gift-codec.ts +0 -62
- package/core/server/services/gifts/gift-controller.ts +0 -51
- package/core/server/services/gifts/gift-email-renderer.ts +0 -97
- package/core/server/services/gifts/gift-email-service.ts +0 -158
- package/core/server/services/gifts/gift-reminder-scheduler.ts +0 -107
- package/core/server/services/gifts/gift-schema.ts +0 -39
- package/core/server/services/gifts/gift-service-wrapper.js +0 -142
- package/core/server/services/gifts/gift-service.ts +0 -1034
- package/core/server/services/gifts/gift.ts +0 -234
- package/core/server/services/identity-tokens/identity-token-service.ts +0 -36
- package/core/server/services/indexnow-ping/constants.ts +0 -32
- package/core/server/services/indexnow-ping/index.ts +0 -35
- package/core/server/services/indexnow-ping/indexnow-ping-service.ts +0 -311
- package/core/server/services/internal-keys/index.ts +0 -53
- package/core/server/services/lib/dynamic-redirect-manager.d.ts +0 -20
- package/core/server/services/lib/in-memory-repository.ts +0 -78
- package/core/server/services/lib/member-signup-contexts.ts +0 -22
- package/core/server/services/link-redirection/link-redirect.ts +0 -24
- package/core/server/services/member-welcome-emails/constants.ts +0 -35
- package/core/server/services/members/commenting/index.ts +0 -2
- package/core/server/services/members/commenting/member-commenting-codec.ts +0 -73
- package/core/server/services/members/commenting/member-commenting.ts +0 -69
- package/core/server/services/members/create-paid-member-shim.ts +0 -40
- package/core/server/services/members/import-export/config.ts +0 -34
- package/core/server/services/members/import-export/csv/formula.ts +0 -14
- package/core/server/services/members/import-export/csv/index.ts +0 -3
- package/core/server/services/members/import-export/csv/parse.ts +0 -69
- package/core/server/services/members/import-export/csv/serialize.ts +0 -18
- package/core/server/services/members/import-export/export/exporter.ts +0 -325
- package/core/server/services/members/import-export/import/completion-email.ts +0 -146
- package/core/server/services/members/import-export/import/importer.ts +0 -489
- package/core/server/services/members/import-export/import/reader.ts +0 -33
- package/core/server/services/members/import-export/import/row.ts +0 -56
- package/core/server/services/members/import-export/import/spool.ts +0 -38
- package/core/server/services/members/import-export/index.ts +0 -143
- package/core/server/services/members/index.d.ts +0 -32
- package/core/server/services/members-custom-fields/actions.ts +0 -62
- package/core/server/services/members-custom-fields/codec.ts +0 -13
- package/core/server/services/members-custom-fields/config.ts +0 -38
- package/core/server/services/members-custom-fields/definitions-service.ts +0 -413
- package/core/server/services/members-custom-fields/index.ts +0 -50
- package/core/server/services/members-custom-fields/models.ts +0 -15
- package/core/server/services/members-custom-fields/queries.ts +0 -13
- package/core/server/services/members-custom-fields/schema.ts +0 -71
- package/core/server/services/members-custom-fields/serializers.ts +0 -22
- package/core/server/services/members-custom-fields/storage.ts +0 -122
- package/core/server/services/members-custom-fields/values-service.ts +0 -269
- package/core/server/services/notifications/notification-email.ts +0 -66
- package/core/server/services/notifications/sanitize-email-html.ts +0 -35
- package/core/server/services/post-scheduling/index.ts +0 -13
- package/core/server/services/post-scheduling/post-scheduling.ts +0 -133
- package/core/server/services/recommendations/service/bookshelf-click-event-repository.ts +0 -49
- package/core/server/services/recommendations/service/bookshelf-recommendation-repository.ts +0 -117
- package/core/server/services/recommendations/service/bookshelf-repository.ts +0 -192
- package/core/server/services/recommendations/service/bookshelf-subscribe-event-repository.ts +0 -49
- package/core/server/services/recommendations/service/click-event.ts +0 -32
- package/core/server/services/recommendations/service/in-memory-recommendation-repository.ts +0 -20
- package/core/server/services/recommendations/service/incoming-recommendation-controller.ts +0 -51
- package/core/server/services/recommendations/service/incoming-recommendation-email-renderer.ts +0 -37
- package/core/server/services/recommendations/service/incoming-recommendation-service.ts +0 -159
- package/core/server/services/recommendations/service/index.ts +0 -15
- package/core/server/services/recommendations/service/libraries.d.ts +0 -2
- package/core/server/services/recommendations/service/recommendation-controller.ts +0 -257
- package/core/server/services/recommendations/service/recommendation-metadata-service.ts +0 -132
- package/core/server/services/recommendations/service/recommendation-repository.ts +0 -13
- package/core/server/services/recommendations/service/recommendation-service.ts +0 -285
- package/core/server/services/recommendations/service/recommendation.ts +0 -200
- package/core/server/services/recommendations/service/subscribe-event.ts +0 -32
- package/core/server/services/recommendations/service/unsafe-data.ts +0 -217
- package/core/server/services/recommendations/service/well-known-service.ts +0 -47
- package/core/server/services/remote-flags/index.ts +0 -101
- package/core/server/services/remote-flags/remote-flags-service.ts +0 -236
- package/core/server/services/remote-flags/resolve.ts +0 -108
- package/core/server/services/route-settings/route-settings-parser.ts +0 -455
- package/core/server/services/route-settings/validation-errors.ts +0 -271
- package/core/server/services/route-settings/yaml-parser.d.ts +0 -4
- package/core/server/services/slack-ping/constants.ts +0 -15
- package/core/server/services/slack-ping/index.ts +0 -35
- package/core/server/services/slack-ping/slack-ping-service.ts +0 -295
- package/core/server/services/staff/email-templates/donation.txt.ts +0 -26
- package/core/server/services/staff/email-templates/gift.txt.ts +0 -27
- package/core/server/services/staff/email-templates/new-free-signup.txt.ts +0 -15
- package/core/server/services/staff/email-templates/new-gift-subscription.txt.ts +0 -26
- package/core/server/services/staff/email-templates/new-milestone-received.txt.ts +0 -20
- package/core/server/services/staff/email-templates/new-paid-cancellation.txt.ts +0 -15
- package/core/server/services/staff/email-templates/new-paid-started.txt.ts +0 -15
- package/core/server/services/staff/email-templates/recommendation-received.txt.ts +0 -23
- package/core/server/services/staff/email-templates/types.ts +0 -13
- package/core/server/services/url/lazy-find-resource.ts +0 -104
- package/core/server/services/url/lazy-url-service.ts +0 -647
- package/core/server/services/url/permalink-matcher.ts +0 -119
- package/core/server/services/url/router-filter.ts +0 -80
- package/core/server/services/verification/verification-webhook-service.ts +0 -137
- package/core/shared/config/helpers.ts +0 -133
- package/core/shared/config/index.ts +0 -12
- package/core/shared/config/loader.ts +0 -87
- package/core/shared/config/utils.ts +0 -104
- package/core/shared/errify.ts +0 -31
- package/core/shared/events-ts/index.ts +0 -8
- package/core/shared/events-ts/post-deleted-event.ts +0 -15
- package/core/shared/events-ts/posts-bulk-add-tags-event.ts +0 -13
- package/core/shared/events-ts/posts-bulk-destroyed-event.ts +0 -13
- package/core/shared/events-ts/posts-bulk-featured-event.ts +0 -13
- package/core/shared/events-ts/posts-bulk-unfeatured-event.ts +0 -13
- package/core/shared/events-ts/posts-bulk-unpublished-event.ts +0 -13
- package/core/shared/events-ts/posts-bulk-unscheduled-event.ts +0 -13
- package/core/shared/labs-flag-overrides.ts +0 -35
- package/core/shared/one-at-a-time.ts +0 -47
- package/core/shared/url-utils.ts +0 -20
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import errors from '@tryghost/errors';
|
|
2
|
-
|
|
3
|
-
// A value is a tree; the database stores one row per leaf, under the path addressing it.
|
|
4
|
-
// A value with no parts is a single leaf at the root, a part is a leaf under its key, and
|
|
5
|
-
// a nested part is addressed with a dotted path.
|
|
6
|
-
//
|
|
7
|
-
// Nothing here may consult a field type. Values arrive already parsed against their own,
|
|
8
|
-
// and rows are rebuilt as what they were written as rather than as what the type says
|
|
9
|
-
// today, so a type that changes shape does not rewrite what members already have.
|
|
10
|
-
|
|
11
|
-
/** Also the notation a segment filter addresses a part by, so the two cannot drift. */
|
|
12
|
-
const SEPARATOR = '.';
|
|
13
|
-
|
|
14
|
-
/** Empty rather than null: a unique index does not constrain nulls, which would let the same leaf be stored twice. */
|
|
15
|
-
export const ROOT_PATH = '';
|
|
16
|
-
|
|
17
|
-
export interface Leaf {
|
|
18
|
-
path: string;
|
|
19
|
-
value_text: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface StoredLeaf extends Leaf {
|
|
23
|
-
member_id: string;
|
|
24
|
-
key: string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
28
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Values are rebuilt into plain objects, so a segment naming something every object
|
|
33
|
-
* already has would reach the prototype instead of the value — and a write through
|
|
34
|
-
* `__proto__` reaches every object in the process. Paths come out of the database, so
|
|
35
|
-
* this holds here rather than trusting whatever put them there.
|
|
36
|
-
*/
|
|
37
|
-
function isSafeSegment(segment: string): boolean {
|
|
38
|
-
return !(segment in Object.prototype);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** Every leaf a value names, at any depth, including the ones it names as empty. */
|
|
42
|
-
export function leavesFor(value: unknown, path: string = ROOT_PATH): Leaf[] {
|
|
43
|
-
if (typeof value === 'string') {
|
|
44
|
-
return [{path, value_text: value}];
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Thrown rather than stored: the read path rejects a non-string row, so writing one
|
|
48
|
-
// loses the value with nothing to say it ever arrived.
|
|
49
|
-
if (!isRecord(value)) {
|
|
50
|
-
throw new errors.IncorrectUsageError({
|
|
51
|
-
message: `A custom field value must be a string or a record of them, not ${value === null ? 'null' : typeof value}.`
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Undefined means the value does not name the part, which is not the same as naming
|
|
56
|
-
// it empty: the first leaves what is stored alone, the second clears it.
|
|
57
|
-
return Object.entries(value)
|
|
58
|
-
.filter(([, part]) => part !== undefined)
|
|
59
|
-
.flatMap(([key, part]) => leavesFor(part, path === ROOT_PATH ? key : `${path}${SEPARATOR}${key}`));
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** A value split into the leaves it sets and the paths it clears. */
|
|
63
|
-
export function leavesToWrite(value: unknown): {set: Leaf[], cleared: string[]} {
|
|
64
|
-
const named = leavesFor(value);
|
|
65
|
-
|
|
66
|
-
return {
|
|
67
|
-
set: named.filter(leaf => leaf.value_text !== ''),
|
|
68
|
-
cleared: named.filter(leaf => leaf.value_text === '').map(leaf => leaf.path)
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** The value a set of leaves adds up to, rebuilt to whatever depth their paths describe. */
|
|
73
|
-
export function valueFromLeaves(leaves: readonly Leaf[]): unknown {
|
|
74
|
-
const root = leaves.find(leaf => leaf.path === ROOT_PATH);
|
|
75
|
-
if (root) {
|
|
76
|
-
return root.value_text;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const value: Record<string, unknown> = {};
|
|
80
|
-
for (const leaf of leaves) {
|
|
81
|
-
const segments = leaf.path.split(SEPARATOR);
|
|
82
|
-
if (!segments.every(isSafeSegment)) {
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const last = segments.pop() as string;
|
|
87
|
-
// A segment already holding a string is replaced rather than descended into: this
|
|
88
|
-
// runs over every member of a list response, so a contradictory pair of paths
|
|
89
|
-
// should cost one odd value rather than the whole response.
|
|
90
|
-
const parent = segments.reduce<Record<string, unknown>>((target, segment) => {
|
|
91
|
-
if (!isRecord(target[segment])) {
|
|
92
|
-
target[segment] = {};
|
|
93
|
-
}
|
|
94
|
-
return target[segment] as Record<string, unknown>;
|
|
95
|
-
}, value);
|
|
96
|
-
parent[last] = leaf.value_text;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return value;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/** Every member's values, keyed by member and then field key; a member with no rows is absent. */
|
|
103
|
-
export function valuesFromLeaves(leaves: readonly StoredLeaf[]): Map<string, Record<string, unknown>> {
|
|
104
|
-
const byMemberAndField = new Map<string, Map<string, Leaf[]>>();
|
|
105
|
-
|
|
106
|
-
for (const {member_id: memberId, key, path, value_text: valueText} of leaves) {
|
|
107
|
-
const fields = byMemberAndField.get(memberId) ?? new Map<string, Leaf[]>();
|
|
108
|
-
const forField = fields.get(key) ?? [];
|
|
109
|
-
forField.push({path, value_text: valueText});
|
|
110
|
-
fields.set(key, forField);
|
|
111
|
-
byMemberAndField.set(memberId, fields);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const byMember = new Map<string, Record<string, unknown>>();
|
|
115
|
-
for (const [memberId, fields] of byMemberAndField) {
|
|
116
|
-
byMember.set(memberId, Object.fromEntries(
|
|
117
|
-
[...fields].map(([key, forField]) => [key, valueFromLeaves(forField)])
|
|
118
|
-
));
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return byMember;
|
|
122
|
-
}
|
|
@@ -1,269 +0,0 @@
|
|
|
1
|
-
import ObjectID from 'bson-objectid';
|
|
2
|
-
import errors from '@tryghost/errors';
|
|
3
|
-
import logging from '@tryghost/logging';
|
|
4
|
-
import type {Knex} from 'knex';
|
|
5
|
-
import {z} from 'zod';
|
|
6
|
-
import {FIELD_TYPES, subFieldsOf, type FieldType} from '@tryghost/custom-field-types';
|
|
7
|
-
import {DbCustomFieldLeaf, DbCustomFieldValue, FIELD_STATUS} from './schema';
|
|
8
|
-
import {activeFields} from './queries';
|
|
9
|
-
import {leavesToWrite, valuesFromLeaves, type StoredLeaf} from './storage';
|
|
10
|
-
|
|
11
|
-
const FIELDS_TABLE = 'members_custom_fields';
|
|
12
|
-
const VALUES_TABLE = 'members_custom_field_values';
|
|
13
|
-
|
|
14
|
-
/** Matches the `members_custom_fields.key` column, so no key a site could hold is refused. */
|
|
15
|
-
const MAX_KEY_LENGTH = 191;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Rows per insert statement, bounded by knex rather than by either database. SQLite takes
|
|
19
|
-
* a multi-row `VALUES` perfectly well; knex's SQLite dialect emits that form only for a
|
|
20
|
-
* single row and compiles anything longer into `INSERT ... SELECT ? UNION ALL SELECT ?`,
|
|
21
|
-
* which SQLite refuses past 500 terms.
|
|
22
|
-
*
|
|
23
|
-
* Open as knex#721 since 2015, with an approved but unmerged fix in knex#5780. This can
|
|
24
|
-
* go when that lands; until then the alternative is hand-written upsert SQL per engine.
|
|
25
|
-
*/
|
|
26
|
-
const UPSERT_CHUNK = 400;
|
|
27
|
-
|
|
28
|
-
/** Derived, not restated, so a column changing shape in `schema.ts` changes here too. */
|
|
29
|
-
type DbLeafRow = z.infer<typeof DbCustomFieldValue>;
|
|
30
|
-
|
|
31
|
-
// Values stay `unknown`: each is validated by its own field type, which is not known
|
|
32
|
-
// until the key is resolved to a definition.
|
|
33
|
-
const ValuesInput = z.record(z.string().max(MAX_KEY_LENGTH), z.unknown());
|
|
34
|
-
|
|
35
|
-
interface ActiveField {
|
|
36
|
-
id: string;
|
|
37
|
-
key: string;
|
|
38
|
-
name: string;
|
|
39
|
-
type: FieldType;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** An absent `value` means clear the field. */
|
|
43
|
-
interface PlannedWrite {
|
|
44
|
-
field: ActiveField;
|
|
45
|
-
value?: unknown;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* What a member holds for each defined field. Separate from the definitions service
|
|
50
|
-
* because a value belongs to the member and a definition belongs to the site's settings,
|
|
51
|
-
* which are different aggregates rather than different layers.
|
|
52
|
-
*/
|
|
53
|
-
export class CustomFieldValuesService {
|
|
54
|
-
private knex: Knex;
|
|
55
|
-
/** A getter, not a number: the ceiling is an operator setting that changes between requests. */
|
|
56
|
-
private getMaxDefinitions: () => number;
|
|
57
|
-
|
|
58
|
-
constructor({knex, getMaxDefinitions}: {knex: Knex, getMaxDefinitions: () => number}) {
|
|
59
|
-
this.knex = knex;
|
|
60
|
-
this.getMaxDefinitions = getMaxDefinitions;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
private async activeFieldsByKey(keys: string[]): Promise<Map<string, ActiveField>> {
|
|
64
|
-
if (keys.length === 0) {
|
|
65
|
-
return new Map();
|
|
66
|
-
}
|
|
67
|
-
const fields = await activeFields(this.knex)
|
|
68
|
-
.whereIn('key', keys)
|
|
69
|
-
.select('id', 'key', 'name', 'type');
|
|
70
|
-
return new Map(fields.map(field => [field.key, field]));
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Members' values, keyed by member id then field key; anything absent is unset.
|
|
75
|
-
*
|
|
76
|
-
* Archived fields are excluded to match the definitions browse: their values stay in
|
|
77
|
-
* the database and stop being addressable. A row that will not parse is dropped and
|
|
78
|
-
* logged rather than failing the read, so one stale row cannot take down a member.
|
|
79
|
-
*/
|
|
80
|
-
async getValuesForMembers(memberIds: string[]): Promise<Map<string, Record<string, unknown>>> {
|
|
81
|
-
if (memberIds.length === 0) {
|
|
82
|
-
return new Map();
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const rows = await this.knex(VALUES_TABLE)
|
|
86
|
-
.join(FIELDS_TABLE, `${VALUES_TABLE}.custom_field_id`, `${FIELDS_TABLE}.id`)
|
|
87
|
-
.whereIn(`${VALUES_TABLE}.member_id`, memberIds)
|
|
88
|
-
.where(`${FIELDS_TABLE}.status`, FIELD_STATUS.active)
|
|
89
|
-
.orderBy(`${FIELDS_TABLE}.created_at`, 'asc')
|
|
90
|
-
.orderBy(`${FIELDS_TABLE}.id`, 'asc')
|
|
91
|
-
.orderBy(`${VALUES_TABLE}.path`, 'asc')
|
|
92
|
-
.select(`${VALUES_TABLE}.member_id`, `${FIELDS_TABLE}.key`, `${FIELDS_TABLE}.type`, `${VALUES_TABLE}.path`, `${VALUES_TABLE}.value_text`);
|
|
93
|
-
|
|
94
|
-
const leaves: StoredLeaf[] = [];
|
|
95
|
-
for (const row of rows) {
|
|
96
|
-
try {
|
|
97
|
-
leaves.push(DbCustomFieldLeaf.parse(row));
|
|
98
|
-
} catch (err) {
|
|
99
|
-
logging.warn(`Skipping unreadable custom field value (field '${row.key}', path '${row.path}'): ${err instanceof Error ? err.message : String(err)}`);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return valuesFromLeaves(leaves);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/** Shared by every caller so they cannot disagree on what a values object is. */
|
|
107
|
-
private parseValues(input: unknown): Record<string, unknown> {
|
|
108
|
-
const parsed = ValuesInput.safeParse(input);
|
|
109
|
-
if (!parsed.success) {
|
|
110
|
-
throw new errors.ValidationError({message: 'Custom field values must be an object keyed by field key.', property: 'custom_fields'});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return parsed.data;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Whether input names any values. Asks the shape question alone, with no catalog
|
|
118
|
-
* lookup, so it can be asked before a write is known to be permitted.
|
|
119
|
-
*/
|
|
120
|
-
namesValues(input: unknown): boolean {
|
|
121
|
-
if (input === undefined) {
|
|
122
|
-
return false;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return Object.keys(this.parseValues(input)).length > 0;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Resolve input into the writes it implies, writing nothing. Returned so a caller can
|
|
130
|
-
* validate before opening a transaction it would otherwise have to unwind, then apply
|
|
131
|
-
* the same plan without re-resolving it.
|
|
132
|
-
*/
|
|
133
|
-
async planWrite(input: unknown): Promise<PlannedWrite[]> {
|
|
134
|
-
const values = this.parseValues(input);
|
|
135
|
-
const keys = Object.keys(values);
|
|
136
|
-
|
|
137
|
-
// Bounded by the definitions ceiling, which also holds the lookup below inside
|
|
138
|
-
// the driver's bound-parameter limit.
|
|
139
|
-
const maxKeys = this.getMaxDefinitions();
|
|
140
|
-
if (keys.length > maxKeys) {
|
|
141
|
-
throw new errors.ValidationError({
|
|
142
|
-
message: `Custom field values are limited to ${maxKeys} fields per request.`,
|
|
143
|
-
property: 'custom_fields'
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const byKey = await this.activeFieldsByKey(keys);
|
|
148
|
-
const writes: PlannedWrite[] = [];
|
|
149
|
-
|
|
150
|
-
for (const [key, raw] of Object.entries(values)) {
|
|
151
|
-
const field = byKey.get(key);
|
|
152
|
-
if (!field) {
|
|
153
|
-
// Unknown or archived. Refused rather than ignored: a typo that silently
|
|
154
|
-
// drops what somebody typed is worse than a save that fails. The catalog
|
|
155
|
-
// applies the same rule to the parts of a composite value.
|
|
156
|
-
throw new errors.ValidationError({message: `Unknown custom field: ${key}`, property: `custom_fields.${key}`});
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// `null` clears any field, and `''` clears one with no parts. For a value
|
|
160
|
-
// with parts `''` names nothing, so it is left to fail validation rather than
|
|
161
|
-
// being read as a silent delete.
|
|
162
|
-
if (raw === null || (raw === '' && subFieldsOf(field.type) === null)) {
|
|
163
|
-
writes.push({field});
|
|
164
|
-
continue;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Message only, no `context`: the API error handler moves a message into
|
|
168
|
-
// `context` when `context` is empty and prepends it when it is not, so
|
|
169
|
-
// anything added here reaches the client glued to the front of the reason.
|
|
170
|
-
// Which field failed rides in `property`.
|
|
171
|
-
const value = FIELD_TYPES[field.type].value.safeParse(raw);
|
|
172
|
-
if (!value.success) {
|
|
173
|
-
const issue = value.error.issues[0];
|
|
174
|
-
throw new errors.ValidationError({
|
|
175
|
-
message: issue.message,
|
|
176
|
-
property: [`custom_fields.${key}`, ...issue.path].join('.')
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
writes.push({field, value: value.data});
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return writes;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Apply a plan from `planWrite`.
|
|
187
|
-
*
|
|
188
|
-
* A write touches the paths it names and nothing else, at every level: naming a path
|
|
189
|
-
* with an empty value clears that part, naming the field with `null` clears all of
|
|
190
|
-
* them, and saying nothing about a path leaves it alone. There is no whole-value
|
|
191
|
-
* replace, so a caller that does not know about a field cannot erase it.
|
|
192
|
-
*
|
|
193
|
-
* Always transactional. Given an executor it joins that transaction, so the importer's
|
|
194
|
-
* failed value write takes its member with it; given none it opens its own.
|
|
195
|
-
*/
|
|
196
|
-
async applyWrite(
|
|
197
|
-
memberId: string,
|
|
198
|
-
writes: PlannedWrite[],
|
|
199
|
-
{executor = this.knex}: {executor?: Knex} = {}
|
|
200
|
-
): Promise<void> {
|
|
201
|
-
if (writes.length === 0) {
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const apply = async (trx: Knex) => {
|
|
206
|
-
// Built first, then sent as whole statements: a handful per member rather
|
|
207
|
-
// than one per part, under one timestamp, because a write happened once
|
|
208
|
-
// however many rows record it.
|
|
209
|
-
const now = new Date();
|
|
210
|
-
const clearedFields: string[] = [];
|
|
211
|
-
const clearedPaths: Array<{fieldId: string, paths: string[]}> = [];
|
|
212
|
-
const rows: DbLeafRow[] = [];
|
|
213
|
-
|
|
214
|
-
for (const {field, value} of writes) {
|
|
215
|
-
if (value === undefined) {
|
|
216
|
-
clearedFields.push(field.id);
|
|
217
|
-
continue;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const {set, cleared} = leavesToWrite(value);
|
|
221
|
-
if (cleared.length > 0) {
|
|
222
|
-
clearedPaths.push({fieldId: field.id, paths: cleared});
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
rows.push(...set.map(leaf => ({
|
|
226
|
-
id: new ObjectID().toHexString(),
|
|
227
|
-
member_id: memberId,
|
|
228
|
-
custom_field_id: field.id,
|
|
229
|
-
path: leaf.path,
|
|
230
|
-
value_text: leaf.value_text,
|
|
231
|
-
created_at: now,
|
|
232
|
-
updated_at: now
|
|
233
|
-
})));
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
if (clearedFields.length > 0) {
|
|
237
|
-
await trx(VALUES_TABLE).where('member_id', memberId).whereIn('custom_field_id', clearedFields).del();
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
if (clearedPaths.length > 0) {
|
|
241
|
-
// One statement with a group per field, rather than a statement per field.
|
|
242
|
-
await trx(VALUES_TABLE).where('member_id', memberId).where((builder) => {
|
|
243
|
-
for (const {fieldId, paths} of clearedPaths) {
|
|
244
|
-
builder.orWhere(pair => pair.where('custom_field_id', fieldId).whereIn('path', paths));
|
|
245
|
-
}
|
|
246
|
-
}).del();
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
for (let from = 0; from < rows.length; from += UPSERT_CHUNK) {
|
|
250
|
-
// Typed as the plain row because `merge` takes its columns as `keyof` the
|
|
251
|
-
// builder's record, which for a composite table registration is the scope
|
|
252
|
-
// names rather than the columns.
|
|
253
|
-
await trx<DbLeafRow>(VALUES_TABLE)
|
|
254
|
-
.insert(rows.slice(from, from + UPSERT_CHUNK))
|
|
255
|
-
// Naming the columns rather than giving values takes each from the row
|
|
256
|
-
// that lost the conflict, so every part updates to its own value.
|
|
257
|
-
.onConflict(['member_id', 'custom_field_id', 'path'])
|
|
258
|
-
.merge(['value_text', 'updated_at']);
|
|
259
|
-
}
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
// knex's marker for a transactor: join it rather than nesting a savepoint under it.
|
|
263
|
-
if (executor.isTransaction) {
|
|
264
|
-
await apply(executor);
|
|
265
|
-
} else {
|
|
266
|
-
await executor.transaction(apply);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import {sanitizeEmailHtml} from './sanitize-email-html';
|
|
2
|
-
|
|
3
|
-
interface Mailer {
|
|
4
|
-
send(options: {to: string; subject: string; html: string; text?: string}): Promise<unknown>;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
interface GenerateEmailContent {
|
|
8
|
-
(options: {template: string; data: Record<string, unknown>}): Promise<{html: string; text?: string}>;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface NotificationEmailDeps {
|
|
12
|
-
mailer: Mailer;
|
|
13
|
-
generateEmailContent: GenerateEmailContent;
|
|
14
|
-
getSiteUrl: () => string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface NotificationEmailMessage {
|
|
18
|
-
/**
|
|
19
|
-
* Resolved recipient addresses. The service does not decide who receives
|
|
20
|
-
* the email; callers compose recipients via a separate audience helper.
|
|
21
|
-
*/
|
|
22
|
-
to: string[];
|
|
23
|
-
subject: string;
|
|
24
|
-
/**
|
|
25
|
-
* Untrusted HTML content. Sanitised before being rendered into the shell,
|
|
26
|
-
* so the email cannot ship scripts, event handlers or unsafe links even
|
|
27
|
-
* if the upstream source is compromised.
|
|
28
|
-
*/
|
|
29
|
-
content: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Renders a notification email in the shared shell and sends it to each
|
|
34
|
-
* recipient. Recipient resolution is a separate concern handled by the caller.
|
|
35
|
-
*/
|
|
36
|
-
export class NotificationEmailService {
|
|
37
|
-
private readonly mailer: Mailer;
|
|
38
|
-
private readonly generateEmailContent: GenerateEmailContent;
|
|
39
|
-
private readonly getSiteUrl: () => string;
|
|
40
|
-
|
|
41
|
-
constructor(deps: NotificationEmailDeps) {
|
|
42
|
-
this.mailer = deps.mailer;
|
|
43
|
-
this.generateEmailContent = deps.generateEmailContent;
|
|
44
|
-
this.getSiteUrl = deps.getSiteUrl;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async send({to, subject, content}: NotificationEmailMessage): Promise<void> {
|
|
48
|
-
if (!to.length) {
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
const message = sanitizeEmailHtml(content);
|
|
52
|
-
const siteUrl = this.getSiteUrl();
|
|
53
|
-
for (const recipient of to) {
|
|
54
|
-
const {html, text} = await this.generateEmailContent({
|
|
55
|
-
template: 'notification',
|
|
56
|
-
data: {message, siteUrl, recipientEmail: recipient}
|
|
57
|
-
});
|
|
58
|
-
await this.mailer.send({
|
|
59
|
-
to: recipient,
|
|
60
|
-
subject,
|
|
61
|
-
html,
|
|
62
|
-
...(text ? {text} : {})
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import sanitizeHtml from 'sanitize-html';
|
|
2
|
-
|
|
3
|
-
// Even though the upstream feed is operated by Ghost org, the resulting HTML
|
|
4
|
-
// is rendered into admin inboxes on the receiving install. A compromised or
|
|
5
|
-
// malformed feed entry must not be able to ship scripts, event handlers, or
|
|
6
|
-
// non-http(s) URLs to recipients via this path.
|
|
7
|
-
const ALLOWED_TAGS = [
|
|
8
|
-
'p', 'br', 'hr',
|
|
9
|
-
'strong', 'b', 'em', 'i', 'u', 'code',
|
|
10
|
-
'a',
|
|
11
|
-
'ul', 'ol', 'li',
|
|
12
|
-
'blockquote',
|
|
13
|
-
'h1', 'h2', 'h3', 'h4'
|
|
14
|
-
];
|
|
15
|
-
|
|
16
|
-
const ALLOWED_SCHEMES = ['http', 'https', 'mailto'];
|
|
17
|
-
|
|
18
|
-
const SANITIZE_OPTIONS: sanitizeHtml.IOptions = {
|
|
19
|
-
allowedTags: ALLOWED_TAGS,
|
|
20
|
-
allowedAttributes: {
|
|
21
|
-
a: ['href', 'title', 'target', 'rel']
|
|
22
|
-
},
|
|
23
|
-
allowedSchemes: ALLOWED_SCHEMES,
|
|
24
|
-
allowProtocolRelative: false,
|
|
25
|
-
transformTags: {
|
|
26
|
-
a: sanitizeHtml.simpleTransform('a', {
|
|
27
|
-
target: '_blank',
|
|
28
|
-
rel: 'noopener noreferrer'
|
|
29
|
-
})
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export function sanitizeEmailHtml(html: string): string {
|
|
34
|
-
return sanitizeHtml(html, SANITIZE_OPTIONS);
|
|
35
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import PostScheduling from './post-scheduling';
|
|
2
|
-
import internalKeys from '../internal-keys';
|
|
3
|
-
import adapterManager from '../adapter-manager';
|
|
4
|
-
import {withErrorCapture} from '../../adapters/scheduling/error-capture';
|
|
5
|
-
|
|
6
|
-
// CJS modules without TS declarations — typed loosely at the boundary.
|
|
7
|
-
const urlUtils = require('../../../shared/url-utils').default;
|
|
8
|
-
|
|
9
|
-
export default new PostScheduling({
|
|
10
|
-
apiUrl: urlUtils.urlFor('api', {type: 'admin'}, true),
|
|
11
|
-
adapter: withErrorCapture(adapterManager.getAdapter('scheduling')),
|
|
12
|
-
internalKeys
|
|
13
|
-
});
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import moment from 'moment';
|
|
2
|
-
import logging from '@tryghost/logging';
|
|
3
|
-
import type {SchedulerAdapter, SchedulerJob} from '@tryghost/adapter-base-scheduling';
|
|
4
|
-
import type {InternalApiKey, InternalKeys} from '../internal-keys';
|
|
5
|
-
|
|
6
|
-
// CJS-only modules — typed loosely below. models is the Bookshelf registry
|
|
7
|
-
// without TS declarations; the rest are JS modules without types.
|
|
8
|
-
const models = require('../../models');
|
|
9
|
-
const urlUtils = require('../../../shared/url-utils').default;
|
|
10
|
-
const {getSignedAdminToken} = require('../../adapters/scheduling/utils');
|
|
11
|
-
const events = require('../../lib/common/events');
|
|
12
|
-
|
|
13
|
-
interface PostSchedulingDeps {
|
|
14
|
-
apiUrl: string;
|
|
15
|
-
// Optional in deps so the JS wrapper can pass options.schedulerAdapter
|
|
16
|
-
// through without TS complaining at the JS/TS boundary. The class field
|
|
17
|
-
// below is non-optional; the constructor's adapter.register(this) call
|
|
18
|
-
// throws if undefined is passed through in practice.
|
|
19
|
-
adapter?: SchedulerAdapter;
|
|
20
|
-
internalKeys: InternalKeys;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Pages live in the posts table with type:'page', so both types are
|
|
24
|
-
// queried through models.Post and discriminated via the type filter.
|
|
25
|
-
const SCHEDULED_RESOURCES = ['post', 'page'] as const;
|
|
26
|
-
type ScheduledResource = typeof SCHEDULED_RESOURCES[number];
|
|
27
|
-
|
|
28
|
-
export default class PostScheduling {
|
|
29
|
-
readonly #apiUrl: string;
|
|
30
|
-
readonly #adapter: SchedulerAdapter;
|
|
31
|
-
readonly #internalKeys: InternalKeys;
|
|
32
|
-
|
|
33
|
-
constructor({apiUrl, adapter, internalKeys}: PostSchedulingDeps) {
|
|
34
|
-
this.#apiUrl = apiUrl;
|
|
35
|
-
this.#adapter = adapter!;
|
|
36
|
-
this.#internalKeys = internalKeys;
|
|
37
|
-
|
|
38
|
-
SCHEDULED_RESOURCES.forEach((resource) => {
|
|
39
|
-
events.on(`${resource}.scheduled`, async (model: any) => {
|
|
40
|
-
try {
|
|
41
|
-
const key = await internalKeys.get('ghost-scheduler');
|
|
42
|
-
this.#adapter.schedule(this.#normalize({model, key, resourceType: resource}));
|
|
43
|
-
} catch (err) {
|
|
44
|
-
logging.error({event: {name: 'post-scheduling.schedule.error'}, err, resource, id: model.get('id')}, 'Failed to schedule resource');
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
// Reschedule = matched unschedule + fresh schedule, because tokens
|
|
49
|
-
// are signed against the published_at timestamp.
|
|
50
|
-
events.on(`${resource}.rescheduled`, async (model: any) => {
|
|
51
|
-
try {
|
|
52
|
-
const key = await internalKeys.get('ghost-scheduler');
|
|
53
|
-
this.#adapter.unschedule(this.#normalize({model, key, resourceType: resource}, 'unscheduled'));
|
|
54
|
-
this.#adapter.schedule(this.#normalize({model, key, resourceType: resource}));
|
|
55
|
-
} catch (err) {
|
|
56
|
-
logging.error({event: {name: 'post-scheduling.reschedule.error'}, err, resource, id: model.get('id')}, 'Failed to reschedule resource');
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
events.on(`${resource}.unscheduled`, async (model: any) => {
|
|
61
|
-
try {
|
|
62
|
-
const key = await internalKeys.get('ghost-scheduler');
|
|
63
|
-
this.#adapter.unschedule(this.#normalize({model, key, resourceType: resource}, 'unscheduled'));
|
|
64
|
-
} catch (err) {
|
|
65
|
-
logging.error({event: {name: 'post-scheduling.unschedule.error'}, err, resource, id: model.get('id')}, 'Failed to unschedule resource');
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
this.#adapter.register(this);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Re-issue every queued schedule under the current internal-keys cache.
|
|
75
|
-
* On boot the previous key is the same as the current key. For key
|
|
76
|
-
* rotation, the caller passes `previousKey` so unschedule URLs match
|
|
77
|
-
* the entries the adapter already holds (signed under the previous
|
|
78
|
-
* secret); schedule URLs are reissued under the current secret.
|
|
79
|
-
*/
|
|
80
|
-
async rescheduleAll({previousKey}: {previousKey?: InternalApiKey} = {}): Promise<void> {
|
|
81
|
-
const scheduledResources = await this.#loadScheduledResources();
|
|
82
|
-
const currentKey = await this.#internalKeys.get('ghost-scheduler');
|
|
83
|
-
const unscheduleKey = previousKey ?? currentKey;
|
|
84
|
-
|
|
85
|
-
// Same-key rebuild (no previousKey, boot path) → URL signature is
|
|
86
|
-
// identical to the about-to-be-scheduled job. The default adapter
|
|
87
|
-
// implements unschedule via tombstones keyed by URL+time, so a same-URL
|
|
88
|
-
// unschedule poisons the scheduled job. Bootstrap mode skips the
|
|
89
|
-
// tombstone write. Rotation (previousKey provided) → URLs differ, so
|
|
90
|
-
// the tombstone correctly targets the old queued entry.
|
|
91
|
-
const bootstrap = !previousKey;
|
|
92
|
-
|
|
93
|
-
for (const resourceType of Object.keys(scheduledResources) as ScheduledResource[]) {
|
|
94
|
-
for (const model of scheduledResources[resourceType]) {
|
|
95
|
-
this.#adapter.unschedule(
|
|
96
|
-
this.#normalize({model, key: unscheduleKey, resourceType}),
|
|
97
|
-
{bootstrap}
|
|
98
|
-
);
|
|
99
|
-
this.#adapter.schedule(
|
|
100
|
-
this.#normalize({model, key: currentKey, resourceType})
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
async #loadScheduledResources(): Promise<Record<ScheduledResource, any[]>> {
|
|
107
|
-
const entries = await Promise.all(SCHEDULED_RESOURCES.map(async (resourceType) => {
|
|
108
|
-
const found = await models.Post.findAll({
|
|
109
|
-
filter: `status:scheduled+type:${resourceType}`,
|
|
110
|
-
columns: ['id', 'published_at', 'created_at', 'type']
|
|
111
|
-
});
|
|
112
|
-
return [resourceType, found];
|
|
113
|
-
}));
|
|
114
|
-
return Object.fromEntries(entries);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
#normalize({model, resourceType, key}: {model: any; resourceType: ScheduledResource; key: InternalApiKey}, event: '' | 'unscheduled' = ''): SchedulerJob {
|
|
118
|
-
const resource = `${resourceType}s`;
|
|
119
|
-
const publishedAt = (event === 'unscheduled') ? model.previous('published_at') : model.get('published_at');
|
|
120
|
-
const signedAdminToken = getSignedAdminToken({publishedAt, apiUrl: this.#apiUrl, key});
|
|
121
|
-
const url = `${urlUtils.urlJoin(this.#apiUrl, 'schedules', resource, model.get('id'))}/?token=${signedAdminToken}`;
|
|
122
|
-
|
|
123
|
-
return {
|
|
124
|
-
// NOTE: The scheduler expects a unix timestamp.
|
|
125
|
-
time: moment(publishedAt).valueOf(),
|
|
126
|
-
url,
|
|
127
|
-
extra: {
|
|
128
|
-
httpMethod: 'PUT',
|
|
129
|
-
oldTime: model.previous('published_at') ? moment(model.previous('published_at')).valueOf() : null
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
}
|