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
|
@@ -11,8 +11,7 @@ const custom_field_types_1 = require("@tryghost/custom-field-types");
|
|
|
11
11
|
const codec_1 = require("./codec");
|
|
12
12
|
const schema_1 = require("./schema");
|
|
13
13
|
const queries_1 = require("./queries");
|
|
14
|
-
|
|
15
|
-
const { slugify } = require('@tryghost/string');
|
|
14
|
+
const key_1 = require("./key");
|
|
16
15
|
// The same NQL -> knex bridge Bookshelf's filter plugin uses, applied directly to
|
|
17
16
|
// our raw-knex query: nql parses the `filter` string to a Mongo query, mongo-knex
|
|
18
17
|
// turns that into parametrised WHERE clauses. Neither needs a Bookshelf model.
|
|
@@ -26,21 +25,19 @@ const TABLE = 'members_custom_fields';
|
|
|
26
25
|
const columns = require('../../data/schema').tables[TABLE];
|
|
27
26
|
const MAX_NAME_LENGTH = columns.name.maxlength;
|
|
28
27
|
const MAX_KEY_LENGTH = columns.key.maxlength;
|
|
29
|
-
const
|
|
30
|
-
// Reserve room for a
|
|
31
|
-
const MAX_KEY_BASE_LENGTH = MAX_KEY_LENGTH - (String(
|
|
28
|
+
const MAX_KEY_ITERATIONS = 1000;
|
|
29
|
+
// Reserve room for a `_<n>` suffix (n up to MAX_KEY_ITERATIONS).
|
|
30
|
+
const MAX_KEY_BASE_LENGTH = MAX_KEY_LENGTH - (String(MAX_KEY_ITERATIONS).length + 1);
|
|
32
31
|
// A key becomes a property name on the plain objects that carry a member's values —
|
|
33
32
|
// on both sides of the wire, since `custom_fields` is JSON and a client gets a plain
|
|
34
33
|
// object from JSON.parse. A key naming a member of Object.prototype reads back as
|
|
35
|
-
// inherited rather than absent wherever one of those objects is indexed
|
|
36
|
-
// `__proto__` holds no value at all: the values schema drops it during parse, and
|
|
37
|
-
// assigning it sets a prototype rather than a property.
|
|
34
|
+
// inherited rather than absent wherever one of those objects is indexed.
|
|
38
35
|
//
|
|
39
36
|
// Derived rather than listed, because the set is a consequence of how keys are
|
|
40
|
-
// minted:
|
|
41
|
-
//
|
|
37
|
+
// minted: minting lowercases and trims leading underscores, so a prototype name
|
|
38
|
+
// survives only if it has neither. `constructor` is the only one.
|
|
42
39
|
const RESERVED_KEYS = Object.getOwnPropertyNames(Object.prototype)
|
|
43
|
-
.filter(name =>
|
|
40
|
+
.filter(name => (0, key_1.mintableKey)(name) === name);
|
|
44
41
|
const FieldName = zod_1.z.string().trim().min(1, { message: 'Custom field name is required.' }).max(MAX_NAME_LENGTH, { message: 'Custom field name is too long.' });
|
|
45
42
|
// The backend mints the key from the name, so create takes just a name and type.
|
|
46
43
|
const AddFieldInput = zod_1.z.object({
|
|
@@ -58,6 +55,11 @@ const MAX_FIELDS_PER_REQUEST = 100;
|
|
|
58
55
|
const AddFieldsInput = zod_1.z.array(AddFieldInput)
|
|
59
56
|
.min(1)
|
|
60
57
|
.max(MAX_FIELDS_PER_REQUEST, { message: `Custom fields can only be created ${MAX_FIELDS_PER_REQUEST} at a time.` });
|
|
58
|
+
// Only the key is read; the client sends whole field objects because that is the shape
|
|
59
|
+
// the API speaks in.
|
|
60
|
+
const ReorderInput = zod_1.z.array(zod_1.z.object({
|
|
61
|
+
key: zod_1.z.string().min(1, { message: 'Every custom field in the order needs a key.' })
|
|
62
|
+
})).min(1, { message: 'The order must name every custom field.' });
|
|
61
63
|
// Name and status are mutable. `key` and `type` are accepted so the immutability
|
|
62
64
|
// rules can reject a change loudly; they are never persisted.
|
|
63
65
|
const EditFieldInput = zod_1.z.object({
|
|
@@ -86,15 +88,21 @@ class CustomFieldDefinitionsService {
|
|
|
86
88
|
// supplied `filter` can widen that — Settings pulls active and archived
|
|
87
89
|
// together in one request (`filter=status:[active,archived]`).
|
|
88
90
|
//
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
+
// Whichever set comes back, it comes back in the publisher's order: filtering
|
|
92
|
+
// narrows the list, it never reorders it.
|
|
91
93
|
const query = options.filter
|
|
92
94
|
? applyFilter(this.knex(TABLE), options.filter)
|
|
93
95
|
: (0, queries_1.activeFields)(this.knex);
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
return this.list(query);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Decode a definition query into the domain, in the publisher's order.
|
|
100
|
+
*
|
|
101
|
+
* Typed off `activeFields` so the builder keeps the table's row type: every caller
|
|
102
|
+
* hands over a query against the definitions table, whatever it has narrowed.
|
|
103
|
+
*/
|
|
104
|
+
async list(query) {
|
|
105
|
+
const rows = await (0, queries_1.inFieldOrder)(query).select('*');
|
|
98
106
|
return rows.map(row => zod_1.z.decode(codec_1.customFieldCodec, row));
|
|
99
107
|
}
|
|
100
108
|
async read(key) {
|
|
@@ -111,8 +119,8 @@ class CustomFieldDefinitionsService {
|
|
|
111
119
|
*
|
|
112
120
|
* Running inside the transaction also makes a batch self-consistent for free —
|
|
113
121
|
* `assertNameAvailable` and `mintKey` see the rows inserted earlier in the same
|
|
114
|
-
* batch, so two items sharing a name are caught and two items
|
|
115
|
-
* get distinct
|
|
122
|
+
* batch, so two items sharing a name are caught and two items deriving the same
|
|
123
|
+
* key get distinct ones, exactly as if they had arrived as separate requests.
|
|
116
124
|
*/
|
|
117
125
|
async add(context, input) {
|
|
118
126
|
const requestedCount = Array.isArray(input) ? input.length : 0;
|
|
@@ -126,10 +134,10 @@ class CustomFieldDefinitionsService {
|
|
|
126
134
|
});
|
|
127
135
|
}
|
|
128
136
|
const fields = parsed.data;
|
|
129
|
-
//
|
|
137
|
+
// Mint before opening the transaction: it needs no database access, and
|
|
130
138
|
// an unusable name is a payload problem worth reporting on its own terms.
|
|
131
139
|
const bases = fields.map((field, index) => {
|
|
132
|
-
const base =
|
|
140
|
+
const base = (0, key_1.mintableKey)(field.name);
|
|
133
141
|
if (!base) {
|
|
134
142
|
throw new errors_1.default.ValidationError({
|
|
135
143
|
message: 'Custom field name must contain at least one usable character.',
|
|
@@ -143,11 +151,22 @@ class CustomFieldDefinitionsService {
|
|
|
143
151
|
try {
|
|
144
152
|
created = await this.knex.transaction(async (trx) => {
|
|
145
153
|
await this.assertWithinLimit(trx, fields.length);
|
|
154
|
+
// Read once, before the loop: a batch appends as consecutive ranks, so
|
|
155
|
+
// the five fields of one request land in the order the request gave
|
|
156
|
+
// them rather than all sharing the end of the list.
|
|
157
|
+
const firstSortOrder = await this.nextSortOrder(trx);
|
|
146
158
|
const keys = [];
|
|
147
159
|
for (const [index, field] of fields.entries()) {
|
|
148
160
|
await this.assertNameAvailable(trx, field.name);
|
|
149
161
|
const key = await this.mintKey(trx, bases[index]);
|
|
150
|
-
await trx(TABLE).insert({
|
|
162
|
+
await trx(TABLE).insert({
|
|
163
|
+
id: new bson_objectid_1.default().toHexString(),
|
|
164
|
+
key,
|
|
165
|
+
name: field.name,
|
|
166
|
+
type: field.type,
|
|
167
|
+
sort_order: firstSortOrder + index,
|
|
168
|
+
created_at: new Date()
|
|
169
|
+
});
|
|
151
170
|
keys.push(key);
|
|
152
171
|
}
|
|
153
172
|
return this.readMany(trx, keys);
|
|
@@ -210,6 +229,66 @@ class CustomFieldDefinitionsService {
|
|
|
210
229
|
errorDetails: { limit: max, total, requested: addedCount }
|
|
211
230
|
});
|
|
212
231
|
}
|
|
232
|
+
/**
|
|
233
|
+
* Set the order of the whole list, stated rather than adjusted: every row gets a
|
|
234
|
+
* fresh rank, so a move is well-defined even where every rank is still the default.
|
|
235
|
+
* Returns every definition, archived included, matching what the request named.
|
|
236
|
+
*/
|
|
237
|
+
async reorder(context, input) {
|
|
238
|
+
const parsed = ReorderInput.safeParse(input);
|
|
239
|
+
if (!parsed.success) {
|
|
240
|
+
const issue = parsed.error.issues[0];
|
|
241
|
+
throw new errors_1.default.ValidationError({ message: issue.message, property: propertyOf(issue.path) });
|
|
242
|
+
}
|
|
243
|
+
const keys = parsed.data.map(item => item.key);
|
|
244
|
+
const ordered = await this.knex.transaction(async (trx) => {
|
|
245
|
+
await this.assertNamesEveryField(trx, keys);
|
|
246
|
+
// Ranks come from the request's order; the statements go out in key order, so
|
|
247
|
+
// two concurrent reorders take their row locks in the same sequence and
|
|
248
|
+
// cannot deadlock. `updated_at` is left alone — the definitions did not
|
|
249
|
+
// change, the list around them did.
|
|
250
|
+
const ranks = new Map(keys.map((key, rank) => [key, rank]));
|
|
251
|
+
for (const key of [...keys].sort()) {
|
|
252
|
+
await trx(TABLE).where('key', key).update({ sort_order: ranks.get(key) });
|
|
253
|
+
}
|
|
254
|
+
return this.list(trx(TABLE));
|
|
255
|
+
});
|
|
256
|
+
await this.recordAction({
|
|
257
|
+
context,
|
|
258
|
+
verb: 'reorder',
|
|
259
|
+
subject: null,
|
|
260
|
+
details: { action_name: 'reordered', count: ordered.length }
|
|
261
|
+
});
|
|
262
|
+
return ordered;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* A partial order is ambiguous, so a reorder that does not name every field exactly
|
|
266
|
+
* once is refused rather than half-applied. A client that loaded before a colleague
|
|
267
|
+
* added a field cannot name it, and is told to reload.
|
|
268
|
+
*/
|
|
269
|
+
async assertNamesEveryField(db, keys) {
|
|
270
|
+
const named = new Set(keys);
|
|
271
|
+
const existing = new Set(await db(TABLE).pluck('key'));
|
|
272
|
+
const matches = named.size === keys.length
|
|
273
|
+
&& named.size === existing.size
|
|
274
|
+
&& keys.every(key => existing.has(key));
|
|
275
|
+
if (!matches) {
|
|
276
|
+
throw new errors_1.default.ValidationError({
|
|
277
|
+
message: 'The order must name every custom field exactly once. Reload and try again.',
|
|
278
|
+
property: 'key'
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/** A new field is appended. Archived fields hold ranks too, so it lands past them. */
|
|
283
|
+
async nextSortOrder(db) {
|
|
284
|
+
const row = await db(TABLE).max({ highest: 'sort_order' }).first();
|
|
285
|
+
const highest = row?.highest;
|
|
286
|
+
// No fields yet, so this one starts the order.
|
|
287
|
+
if (highest === null || highest === undefined) {
|
|
288
|
+
return 0;
|
|
289
|
+
}
|
|
290
|
+
return Number(highest) + 1;
|
|
291
|
+
}
|
|
213
292
|
/** Read back a batch in the order its keys were created, not the table's order. */
|
|
214
293
|
async readMany(db, keys) {
|
|
215
294
|
const rows = await db(TABLE).whereIn('key', keys).select('*');
|
|
@@ -217,12 +296,15 @@ class CustomFieldDefinitionsService {
|
|
|
217
296
|
return keys.map(key => zod_1.z.decode(codec_1.customFieldCodec, byKey.get(key)));
|
|
218
297
|
}
|
|
219
298
|
/**
|
|
220
|
-
* Pick a free key from the name's
|
|
299
|
+
* Pick a free key from the name's base: `base`, then `base_2`, `base_3`, ...
|
|
221
300
|
* Reads the keys already taken by that base — including archived fields, so a
|
|
222
|
-
*
|
|
301
|
+
* key is never reused once minted.
|
|
223
302
|
*/
|
|
224
303
|
async mintKey(db, base) {
|
|
225
|
-
|
|
304
|
+
// Trimmed again after cutting, because the cut can land mid-separator and
|
|
305
|
+
// a key that ends in one is not a shape minting is allowed to produce. The
|
|
306
|
+
// base starts with an alphanumeric, so something always survives.
|
|
307
|
+
const safeBase = base.slice(0, MAX_KEY_BASE_LENGTH).replace(/_+$/, '');
|
|
226
308
|
const taken = new Set([
|
|
227
309
|
...RESERVED_KEYS,
|
|
228
310
|
...await db(TABLE).where('key', 'like', `${safeBase}%`).pluck('key')
|
|
@@ -230,8 +312,8 @@ class CustomFieldDefinitionsService {
|
|
|
230
312
|
if (!taken.has(safeBase)) {
|
|
231
313
|
return safeBase;
|
|
232
314
|
}
|
|
233
|
-
for (let suffix = 2; suffix <=
|
|
234
|
-
const candidate = `${safeBase}
|
|
315
|
+
for (let suffix = 2; suffix <= MAX_KEY_ITERATIONS; suffix += 1) {
|
|
316
|
+
const candidate = `${safeBase}_${suffix}`;
|
|
235
317
|
if (!taken.has(candidate)) {
|
|
236
318
|
return candidate;
|
|
237
319
|
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CUSTOM_FIELDS_RELATION = void 0;
|
|
7
|
+
exports.createCustomFieldsFilterTransformer = createCustomFieldsFilterTransformer;
|
|
8
|
+
// The members filter reaches custom field values through a `custom_fields` relation
|
|
9
|
+
// on the Member model (the values table joined on member_id). A field is named by its
|
|
10
|
+
// `key` — the stable public slug — and matched on its `value`; a composite field's
|
|
11
|
+
// part is named as `value.<part>`, and a part's presence as `path.<part>`:
|
|
12
|
+
//
|
|
13
|
+
// (custom_fields.key:'company'+custom_fields.value:'Ghost') // value
|
|
14
|
+
// (custom_fields.key:'shipping_address'+custom_fields.value.country:'GB')
|
|
15
|
+
// custom_fields.key:'phone' / custom_fields.key:-'phone' // field set / not set
|
|
16
|
+
// (custom_fields.key:'shipping_address'+custom_fields.path:'country') // part set
|
|
17
|
+
// (custom_fields.key:'shipping_address'+custom_fields.path:-'country') // part not set
|
|
18
|
+
//
|
|
19
|
+
// The values table stores one row per leaf: `custom_field_key` (the field's stable key),
|
|
20
|
+
// `path` (empty for a scalar, the part's key for a composite), and `value_text`. A scalar
|
|
21
|
+
// field is just a leaf at path ''; "the field", "a part", and "the whole field" are the
|
|
22
|
+
// same thing — a set of leaf rows pinned by (custom_field_key, [path], [value]). So every
|
|
23
|
+
// filter is one `$elemMatch` over those columns: positive asserts a matching leaf exists,
|
|
24
|
+
// `$not` asserts none does. The public grammar is the storage vocabulary here — the key
|
|
25
|
+
// sits on the row — so the transformer emits it straight, with no id lookup and no field
|
|
26
|
+
// catalog read. That is what lets the Member model chain it inline on every members query,
|
|
27
|
+
// rather than each call site remembering to wire it. A key that names no current field
|
|
28
|
+
// matches no leaf; the admin never offers an archived or deleted field in the filter UI,
|
|
29
|
+
// though the API leaves them queryable.
|
|
30
|
+
const errors_1 = __importDefault(require("@tryghost/errors"));
|
|
31
|
+
const RELATION = 'custom_fields';
|
|
32
|
+
const PREFIX = `${RELATION}.`;
|
|
33
|
+
const KEY_ATTRIBUTE = `${PREFIX}key`;
|
|
34
|
+
const VALUE_ATTRIBUTE = `${PREFIX}value`;
|
|
35
|
+
const PATH_ATTRIBUTE = `${PREFIX}path`;
|
|
36
|
+
const ROOT_PATH = '';
|
|
37
|
+
// Only recurse into plain objects/arrays; a RegExp, Date, etc. is a leaf value that
|
|
38
|
+
// must pass through by reference (recursing would strip a regex to `{}`).
|
|
39
|
+
function isPlainObject(value) {
|
|
40
|
+
return typeof value === 'object'
|
|
41
|
+
&& value !== null
|
|
42
|
+
&& !Array.isArray(value)
|
|
43
|
+
&& (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
|
|
44
|
+
}
|
|
45
|
+
// A single-clause object whose one key targets the custom_fields relation.
|
|
46
|
+
function isCustomFieldClause(node) {
|
|
47
|
+
if (!isPlainObject(node)) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
const keys = Object.keys(node);
|
|
51
|
+
return keys.length === 1 && keys[0].startsWith(PREFIX);
|
|
52
|
+
}
|
|
53
|
+
// A grouped compound: an $and whose clauses all target the relation and name exactly one
|
|
54
|
+
// `key`. Its clauses describe one leaf row.
|
|
55
|
+
//
|
|
56
|
+
// Exactly one, not at least one: two whole-field filters ANDed together arrive as a flat
|
|
57
|
+
// `key:'a'+key:'b'`, since a bare key clause needs no group of its own. That is two
|
|
58
|
+
// filters — a member with a value for both — not one leaf that is somehow both fields, so
|
|
59
|
+
// it has to fall through and be transformed a clause at a time.
|
|
60
|
+
function isCustomFieldCompound(clauses) {
|
|
61
|
+
if (!Array.isArray(clauses) || clauses.length < 2 || !clauses.every(isCustomFieldClause)) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
return clauses.filter(clause => Object.keys(clause)[0] === KEY_ATTRIBUTE).length === 1;
|
|
65
|
+
}
|
|
66
|
+
// `custom_fields.value` addresses the scalar (root) leaf; `custom_fields.value.<part>`
|
|
67
|
+
// a composite's part. Returns the row `path` the attribute selects, or null if the
|
|
68
|
+
// attribute isn't a value clause.
|
|
69
|
+
function pathForValueAttribute(attribute) {
|
|
70
|
+
if (attribute === VALUE_ATTRIBUTE) {
|
|
71
|
+
return ROOT_PATH;
|
|
72
|
+
}
|
|
73
|
+
if (attribute.startsWith(`${VALUE_ATTRIBUTE}.`)) {
|
|
74
|
+
return attribute.slice(`${VALUE_ATTRIBUTE}.`.length);
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
// The single `{$ne}` shape all negations arrive as (is-not-set on a key or part).
|
|
79
|
+
function negatedString(value) {
|
|
80
|
+
return isPlainObject(value) && typeof value.$ne === 'string' ? value.$ne : null;
|
|
81
|
+
}
|
|
82
|
+
function createCustomFieldsFilterTransformer() {
|
|
83
|
+
// The grammar carries the field key in the clause value; take it as a string,
|
|
84
|
+
// defaulting to one no field can hold so a malformed clause matches nothing.
|
|
85
|
+
const keyOf = (value) => (typeof value === 'string' ? value : '');
|
|
86
|
+
// One (maybe-negated) $elemMatch over the leaf columns: positive is "a leaf pinned
|
|
87
|
+
// by these matches", `$not` is "no leaf does".
|
|
88
|
+
function buildElemMatch(fieldKey, conditions, negate) {
|
|
89
|
+
const match = { custom_field_key: fieldKey, ...conditions };
|
|
90
|
+
if (negate) {
|
|
91
|
+
return { [RELATION]: { $not: { $elemMatch: match } } };
|
|
92
|
+
}
|
|
93
|
+
return { [RELATION]: { $elemMatch: match } };
|
|
94
|
+
}
|
|
95
|
+
// A `(key + value[.part] | path)` compound. A `value` clause pins the part and its
|
|
96
|
+
// value; a `path` clause pins the part's presence (no value), and a negated path is
|
|
97
|
+
// is-not-set — no such leaf — which negates the whole match.
|
|
98
|
+
function toElemMatch(clauses) {
|
|
99
|
+
let fieldKey = '';
|
|
100
|
+
let hasKey = false;
|
|
101
|
+
let hasLeaf = false;
|
|
102
|
+
const conditions = {};
|
|
103
|
+
let negate = false;
|
|
104
|
+
const oneLeafOnly = () => {
|
|
105
|
+
if (hasLeaf) {
|
|
106
|
+
throw new errors_1.default.BadRequestError({
|
|
107
|
+
message: 'A custom field filter takes one value or path clause.'
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
hasLeaf = true;
|
|
111
|
+
};
|
|
112
|
+
for (const clause of clauses) {
|
|
113
|
+
const [attribute, value] = Object.entries(clause)[0];
|
|
114
|
+
if (attribute === KEY_ATTRIBUTE) {
|
|
115
|
+
if (hasKey) {
|
|
116
|
+
throw new errors_1.default.BadRequestError({
|
|
117
|
+
message: `A custom field filter takes one "${KEY_ATTRIBUTE}" clause.`
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
hasKey = true;
|
|
121
|
+
fieldKey = keyOf(value);
|
|
122
|
+
}
|
|
123
|
+
else if (attribute === PATH_ATTRIBUTE) {
|
|
124
|
+
oneLeafOnly();
|
|
125
|
+
const negatedPath = negatedString(value);
|
|
126
|
+
conditions.path = negatedPath ?? value;
|
|
127
|
+
negate = negatedPath !== null;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
const path = pathForValueAttribute(attribute);
|
|
131
|
+
if (path === null) {
|
|
132
|
+
// Not the key, a value/value.<part>, or a path: it names no leaf
|
|
133
|
+
// column, so dropping it would silently widen the match to the key
|
|
134
|
+
// alone. Fail closed instead.
|
|
135
|
+
throw new errors_1.default.BadRequestError({
|
|
136
|
+
message: `Unsupported custom field filter clause "${attribute}".`
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
oneLeafOnly();
|
|
140
|
+
conditions.path = path;
|
|
141
|
+
conditions.value_text = value;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return buildElemMatch(fieldKey, conditions, negate);
|
|
145
|
+
}
|
|
146
|
+
// A standalone `key` clause is whole-field is-set / is-not-set: a leaf for this
|
|
147
|
+
// field exists (any part), or none does.
|
|
148
|
+
function fromKeyClause(value) {
|
|
149
|
+
const negatedKey = negatedString(value);
|
|
150
|
+
if (negatedKey !== null) {
|
|
151
|
+
return buildElemMatch(keyOf(negatedKey), {}, true);
|
|
152
|
+
}
|
|
153
|
+
return buildElemMatch(keyOf(value), {}, false);
|
|
154
|
+
}
|
|
155
|
+
function transform(node) {
|
|
156
|
+
if (Array.isArray(node)) {
|
|
157
|
+
return node.map(transform);
|
|
158
|
+
}
|
|
159
|
+
if (!isPlainObject(node)) {
|
|
160
|
+
return node;
|
|
161
|
+
}
|
|
162
|
+
if (isCustomFieldCompound(node.$and)) {
|
|
163
|
+
return toElemMatch(node.$and);
|
|
164
|
+
}
|
|
165
|
+
const out = {};
|
|
166
|
+
for (const [key, value] of Object.entries(node)) {
|
|
167
|
+
if (key === KEY_ATTRIBUTE) {
|
|
168
|
+
Object.assign(out, fromKeyClause(value));
|
|
169
|
+
}
|
|
170
|
+
else if (key.startsWith(PREFIX)) {
|
|
171
|
+
// A value or path clause only means something paired with its key in one
|
|
172
|
+
// leaf, which arrives as the compound handled above. A `custom_fields.*`
|
|
173
|
+
// clause reaching here is unpaired (or its pair was not grouped), so fail
|
|
174
|
+
// closed rather than pass it to mongo-knex as a column that does not exist.
|
|
175
|
+
throw new errors_1.default.BadRequestError({
|
|
176
|
+
message: `A "${key}" filter must be grouped with its "${KEY_ATTRIBUTE}" clause, for example (${KEY_ATTRIBUTE}:'a_field'+${VALUE_ATTRIBUTE}:'a value').`
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
out[key] = transform(value);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return out;
|
|
184
|
+
}
|
|
185
|
+
return (query) => transform(query);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* The Member filter relation that exposes custom field values. A member has many leaf
|
|
189
|
+
* rows (one per field, or per part of a composite field), and a predicate asks whether
|
|
190
|
+
* one of them matches, so this joins the values table on member_id and mongo-knex emits
|
|
191
|
+
* it as a correlated `members.id IN (…)` subquery — composing with every other member
|
|
192
|
+
* filter. Registered only behind the `membersCustomFields` flag.
|
|
193
|
+
*/
|
|
194
|
+
exports.CUSTOM_FIELDS_RELATION = {
|
|
195
|
+
tableName: 'members_custom_field_values',
|
|
196
|
+
tableNameAs: RELATION,
|
|
197
|
+
type: 'oneToOne',
|
|
198
|
+
joinFrom: 'member_id'
|
|
199
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KEY_CHARACTERS = void 0;
|
|
4
|
+
exports.mintableKey = mintableKey;
|
|
5
|
+
// Neither package ships types.
|
|
6
|
+
const { stripInvisibleChars } = require('@tryghost/string');
|
|
7
|
+
const unidecode = require('unidecode');
|
|
8
|
+
/** Every character a key may contain. Nothing else survives minting. */
|
|
9
|
+
exports.KEY_CHARACTERS = /^[a-z0-9_]+$/;
|
|
10
|
+
/**
|
|
11
|
+
* Mint the key a field is addressed by, from the name a publisher chose.
|
|
12
|
+
*
|
|
13
|
+
* A key is typed by hand into member filters, CSV columns, email replacement strings
|
|
14
|
+
* and, later, themes. Those agree on letters, digits and the underscore, and disagree
|
|
15
|
+
* about everything else, so what a key may contain is stated here as an allowlist
|
|
16
|
+
* rather than inherited from slugify, whose separator and reserved characters answer
|
|
17
|
+
* to URLs. The two formats happen to be near-identical today; they are not the same
|
|
18
|
+
* rule, and this one belongs where its consumers are known.
|
|
19
|
+
*
|
|
20
|
+
* Transliteration and invisible-character stripping stay with the libraries that own
|
|
21
|
+
* them. Only the policy is local.
|
|
22
|
+
*
|
|
23
|
+
* Runs collapse and the ends are trimmed, so no key can lead or trail with a
|
|
24
|
+
* separator. `__proto__` is unmintable as a result, which is worth more than
|
|
25
|
+
* reserving it would be.
|
|
26
|
+
*
|
|
27
|
+
* Returns an empty string for a name with nothing usable in it; the caller decides
|
|
28
|
+
* what to do about that.
|
|
29
|
+
*/
|
|
30
|
+
function mintableKey(name) {
|
|
31
|
+
return unidecode(stripInvisibleChars(name))
|
|
32
|
+
.toLowerCase()
|
|
33
|
+
// Dropped rather than separated, so a possessive reads as one word.
|
|
34
|
+
.replace(/'/g, '')
|
|
35
|
+
.replace(/[^a-z0-9]+/g, '_')
|
|
36
|
+
.replace(/^_+|_+$/g, '');
|
|
37
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.activeFields = activeFields;
|
|
4
|
+
exports.inFieldOrder = inFieldOrder;
|
|
4
5
|
const schema_1 = require("./schema");
|
|
5
6
|
const FIELDS_TABLE = 'members_custom_fields';
|
|
6
7
|
// Archived fields must stay out of every read and write, and nothing in the database
|
|
@@ -10,3 +11,17 @@ const FIELDS_TABLE = 'members_custom_fields';
|
|
|
10
11
|
function activeFields(db) {
|
|
11
12
|
return db(FIELDS_TABLE).where('status', schema_1.FIELD_STATUS.active);
|
|
12
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* The publisher's order, applied to every read of the list. Here for the same reason the
|
|
16
|
+
* status filter is: a read that forgets it comes back in whatever order the engine chose.
|
|
17
|
+
*
|
|
18
|
+
* `created_at` orders a site that has never reordered, where every row still holds the
|
|
19
|
+
* default rank; `id` settles the rest so the order is total.
|
|
20
|
+
*/
|
|
21
|
+
function inFieldOrder(query) {
|
|
22
|
+
query
|
|
23
|
+
.orderBy(`${FIELDS_TABLE}.sort_order`, 'asc')
|
|
24
|
+
.orderBy(`${FIELDS_TABLE}.created_at`, 'asc')
|
|
25
|
+
.orderBy(`${FIELDS_TABLE}.id`, 'asc');
|
|
26
|
+
return query;
|
|
27
|
+
}
|
|
@@ -24,7 +24,7 @@ exports.DbCustomField = zod_1.z.object({
|
|
|
24
24
|
// carries it as a plain string.
|
|
25
25
|
exports.DbCustomFieldValue = zod_1.z.object({
|
|
26
26
|
id: zod_1.z.string(),
|
|
27
|
-
|
|
27
|
+
custom_field_key: zod_1.z.string(),
|
|
28
28
|
member_id: zod_1.z.string(),
|
|
29
29
|
path: zod_1.z.string(),
|
|
30
30
|
// Nullable like the column, though nothing here writes a null: a part with no value
|
|
@@ -14,8 +14,11 @@ const queries_1 = require("./queries");
|
|
|
14
14
|
const storage_1 = require("./storage");
|
|
15
15
|
const FIELDS_TABLE = 'members_custom_fields';
|
|
16
16
|
const VALUES_TABLE = 'members_custom_field_values';
|
|
17
|
-
/**
|
|
18
|
-
|
|
17
|
+
/**
|
|
18
|
+
* From the canonical schema, the same source definitions-service reads, so no key a site
|
|
19
|
+
* could hold is refused and this cannot drift from the `members_custom_fields.key` column.
|
|
20
|
+
*/
|
|
21
|
+
const MAX_KEY_LENGTH = require('../../data/schema').tables[FIELDS_TABLE].key.maxlength;
|
|
19
22
|
/**
|
|
20
23
|
* Rows per insert statement, bounded by knex rather than by either database. SQLite takes
|
|
21
24
|
* a multi-row `VALUES` perfectly well; knex's SQLite dialect emits that form only for a
|
|
@@ -62,12 +65,13 @@ class CustomFieldValuesService {
|
|
|
62
65
|
if (memberIds.length === 0) {
|
|
63
66
|
return new Map();
|
|
64
67
|
}
|
|
68
|
+
// Not ordered by field: these rows become an object keyed by field, and an object
|
|
69
|
+
// cannot carry an order. `path` is ordered so composite parts assemble the same
|
|
70
|
+
// way every time.
|
|
65
71
|
const rows = await this.knex(VALUES_TABLE)
|
|
66
|
-
.join(FIELDS_TABLE, `${VALUES_TABLE}.
|
|
72
|
+
.join(FIELDS_TABLE, `${VALUES_TABLE}.custom_field_key`, `${FIELDS_TABLE}.key`)
|
|
67
73
|
.whereIn(`${VALUES_TABLE}.member_id`, memberIds)
|
|
68
74
|
.where(`${FIELDS_TABLE}.status`, schema_1.FIELD_STATUS.active)
|
|
69
|
-
.orderBy(`${FIELDS_TABLE}.created_at`, 'asc')
|
|
70
|
-
.orderBy(`${FIELDS_TABLE}.id`, 'asc')
|
|
71
75
|
.orderBy(`${VALUES_TABLE}.path`, 'asc')
|
|
72
76
|
.select(`${VALUES_TABLE}.member_id`, `${FIELDS_TABLE}.key`, `${FIELDS_TABLE}.type`, `${VALUES_TABLE}.path`, `${VALUES_TABLE}.value_text`);
|
|
73
77
|
const leaves = [];
|
|
@@ -169,36 +173,36 @@ class CustomFieldValuesService {
|
|
|
169
173
|
// than one per part, under one timestamp, because a write happened once
|
|
170
174
|
// however many rows record it.
|
|
171
175
|
const now = new Date();
|
|
172
|
-
const
|
|
176
|
+
const clearedKeys = [];
|
|
173
177
|
const clearedPaths = [];
|
|
174
178
|
const rows = [];
|
|
175
179
|
for (const { field, value } of writes) {
|
|
176
180
|
if (value === undefined) {
|
|
177
|
-
|
|
181
|
+
clearedKeys.push(field.key);
|
|
178
182
|
continue;
|
|
179
183
|
}
|
|
180
184
|
const { set, cleared } = (0, storage_1.leavesToWrite)(value);
|
|
181
185
|
if (cleared.length > 0) {
|
|
182
|
-
clearedPaths.push({
|
|
186
|
+
clearedPaths.push({ fieldKey: field.key, paths: cleared });
|
|
183
187
|
}
|
|
184
188
|
rows.push(...set.map(leaf => ({
|
|
185
189
|
id: new bson_objectid_1.default().toHexString(),
|
|
186
190
|
member_id: memberId,
|
|
187
|
-
|
|
191
|
+
custom_field_key: field.key,
|
|
188
192
|
path: leaf.path,
|
|
189
193
|
value_text: leaf.value_text,
|
|
190
194
|
created_at: now,
|
|
191
195
|
updated_at: now
|
|
192
196
|
})));
|
|
193
197
|
}
|
|
194
|
-
if (
|
|
195
|
-
await trx(VALUES_TABLE).where('member_id', memberId).whereIn('
|
|
198
|
+
if (clearedKeys.length > 0) {
|
|
199
|
+
await trx(VALUES_TABLE).where('member_id', memberId).whereIn('custom_field_key', clearedKeys).del();
|
|
196
200
|
}
|
|
197
201
|
if (clearedPaths.length > 0) {
|
|
198
202
|
// One statement with a group per field, rather than a statement per field.
|
|
199
203
|
await trx(VALUES_TABLE).where('member_id', memberId).where((builder) => {
|
|
200
|
-
for (const {
|
|
201
|
-
builder.orWhere(pair => pair.where('
|
|
204
|
+
for (const { fieldKey, paths } of clearedPaths) {
|
|
205
|
+
builder.orWhere(pair => pair.where('custom_field_key', fieldKey).whereIn('path', paths));
|
|
202
206
|
}
|
|
203
207
|
}).del();
|
|
204
208
|
}
|
|
@@ -210,7 +214,7 @@ class CustomFieldValuesService {
|
|
|
210
214
|
.insert(rows.slice(from, from + UPSERT_CHUNK))
|
|
211
215
|
// Naming the columns rather than giving values takes each from the row
|
|
212
216
|
// that lost the conflict, so every part updates to its own value.
|
|
213
|
-
.onConflict(['member_id', '
|
|
217
|
+
.onConflict(['member_id', 'custom_field_key', 'path'])
|
|
214
218
|
.merge(['value_text', 'updated_at']);
|
|
215
219
|
}
|
|
216
220
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const {MemberPageViewEvent, MemberCommentEvent, MemberLinkClickEvent} = require('../../../shared/events');
|
|
2
2
|
const moment = require('moment-timezone');
|
|
3
3
|
const {IncorrectUsageError} = require('@tryghost/errors');
|
|
4
|
-
const EmailOpenedEvent = require('../email-service/events/email-opened-event');
|
|
4
|
+
const {EmailOpenedEvent} = require('../email-service/events/email-opened-event');
|
|
5
5
|
const logging = require('@tryghost/logging');
|
|
6
6
|
const LastSeenAtCache = require('./last-seen-at-cache');
|
|
7
7
|
|
|
@@ -52,6 +52,10 @@ const GZIP_MAGIC = [0x1f, 0x8b];
|
|
|
52
52
|
const SVG_EXTENSIONS = new Set(['.svg', '.svgz']);
|
|
53
53
|
const SVG_SNIFF_BYTES = 1024;
|
|
54
54
|
|
|
55
|
+
const toBuffer = (bytes) => {
|
|
56
|
+
return Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
57
|
+
};
|
|
58
|
+
|
|
55
59
|
const shouldRasterize = (buffer, ext) => {
|
|
56
60
|
if (SVG_EXTENSIONS.has(ext.toLowerCase())) {
|
|
57
61
|
return true;
|
|
@@ -179,8 +183,8 @@ class OEmbedService {
|
|
|
179
183
|
* @returns {Promise<Buffer>} - Promise resolving to the image buffer
|
|
180
184
|
*/
|
|
181
185
|
async fetchImageBuffer(imageUrl) {
|
|
182
|
-
const
|
|
183
|
-
return
|
|
186
|
+
const bytes = await this.externalRequest(imageUrl).buffer();
|
|
187
|
+
return toBuffer(bytes);
|
|
184
188
|
}
|
|
185
189
|
|
|
186
190
|
/**
|
|
@@ -306,6 +310,8 @@ class OEmbedService {
|
|
|
306
310
|
...options
|
|
307
311
|
});
|
|
308
312
|
|
|
313
|
+
body = toBuffer(body);
|
|
314
|
+
|
|
309
315
|
try {
|
|
310
316
|
// Detect page encoding which might not be utf-8
|
|
311
317
|
// and decode content
|
|
@@ -87,10 +87,13 @@ class CanThisResult {
|
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
// Ensure permission decisions are based on the user's role if present, not their staff-token.
|
|
91
|
+
const permissionsForModel = loadedPermissions.user ? {...loadedPermissions, apiKey: null} : loadedPermissions;
|
|
92
|
+
|
|
90
93
|
// Offer a chance for the TargetModel to override the results
|
|
91
94
|
if (TargetModel && _.isFunction(TargetModel.permissible)) {
|
|
92
95
|
return TargetModel.permissible(
|
|
93
|
-
modelId, actType, context, unsafeAttrs,
|
|
96
|
+
modelId, actType, context, unsafeAttrs, permissionsForModel, hasUserPermission, hasApiKeyPermission
|
|
94
97
|
);
|
|
95
98
|
}
|
|
96
99
|
|