ghost 6.61.0 → 6.63.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/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.3.tgz +0 -0
- package/components/tryghost-adapter-base-sso-0.1.3.tgz +0 -0
- package/components/tryghost-admin-api-schema-0.0.0.tgz +0 -0
- package/components/tryghost-checkout-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.1.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-limit-service-0.0.0.tgz +0 -0
- package/components/tryghost-metafield-types-0.0.0.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/content/settings/README.md +1 -1
- package/content/themes/casper/.gitignore +28 -0
- package/content/themes/casper/assets/built/casper.js +1 -1
- package/content/themes/casper/assets/built/screen.css +1 -1
- package/content/themes/casper/assets/css/screen.css +2 -1
- package/content/themes/casper/assets/js/lightbox.js +63 -46
- package/content/themes/casper/package.json +6 -6
- package/content/themes/casper/page.hbs +1 -0
- package/content/themes/casper/post.hbs +1 -0
- package/content/themes/source/.gitignore +28 -0
- package/content/themes/source/assets/built/screen.css +1 -1
- package/content/themes/source/assets/built/source.js +1 -1
- package/content/themes/source/assets/css/screen.css +2 -1
- package/content/themes/source/assets/js/lightbox.js +62 -45
- package/content/themes/source/assets/js/main.js +2 -2
- package/content/themes/source/package.json +4 -4
- package/content/themes/source/partials/feature-image.hbs +1 -0
- package/content/themes/source/partials/post-card.hbs +1 -1
- package/core/boot.js +61 -27
- package/core/built/admin/assets/Combination-B0l4MKVh.js +41 -0
- package/core/built/admin/assets/InterVariable-DiVDrmQJ.woff2 +0 -0
- package/core/built/admin/assets/InterVariable-Italic-FCBEiFp6.woff2 +0 -0
- package/core/built/admin/assets/{about-Bve7IJS2.js → about-C1C3s5-g.js} +1 -1
- package/core/built/admin/assets/accordion-CxGsDKwa.js +1 -0
- package/core/built/admin/assets/account-migration-CbUhW6gk.js +1 -0
- package/core/built/admin/assets/activitypub/{account-migration-C-aej4x_.js → account-migration-KA6Jjf9L.js} +16 -16
- package/core/built/admin/assets/activitypub/activitypub.js +9 -9
- package/core/built/admin/assets/activitypub/{avatar-DLPL3Abs.js → avatar-DVJF5bxP.js} +2 -2
- package/core/built/admin/assets/activitypub/{avatar-flipboard-C9iDY-QV.js → avatar-flipboard-csQaNeYK.js} +2 -2
- package/core/built/admin/assets/activitypub/{bluesky-sharing-BXkA5mqV.js → bluesky-sharing-C7nTDMvw.js} +37 -37
- package/core/built/admin/assets/activitypub/{content-formatters-CHcyMFk4.js → content-formatters-CLcjaBwV.js} +4 -4
- package/core/built/admin/assets/activitypub/{copy-DIAOQoCZ.js → copy-DcOh8kXw.js} +2 -2
- package/core/built/admin/assets/activitypub/{domain-B3ihkPde.js → domain-fZ-X8i4-.js} +121 -121
- package/core/built/admin/assets/activitypub/{edit-profile-DsZM3rTX.js → edit-profile-BqMLQV88.js} +40 -40
- package/core/built/admin/assets/activitypub/{explore-BodNrZgR.js → explore-B4yPoP8E.js} +27 -27
- package/core/built/admin/assets/activitypub/{feed-PwH1iJ84.js → feed-BRC_mvAe.js} +51 -51
- package/core/built/admin/assets/activitypub/{field-D06-kmOJ.js → field-COacNgOt.js} +3 -3
- package/core/built/admin/assets/activitypub/{hash-Bm3WxYN4.js → hash-C7u-irQQ.js} +2 -2
- package/core/built/admin/assets/activitypub/inbox-BW-yvUoN.js +1284 -0
- package/core/built/admin/assets/activitypub/{inbox-d8oJRTf4.js → inbox-CbTTdDQ3.js} +2 -2
- package/core/built/admin/assets/activitypub/label-kPMmYI7l.js +16 -0
- package/core/built/admin/assets/activitypub/layout-BL4_jtuV.js +1 -0
- package/core/built/admin/assets/activitypub/{moderation-D1Yapsod.js → moderation-CuidJzc0.js} +91 -91
- package/core/built/admin/assets/activitypub/{note-C5zjqq5C.js → note-CAMRRSqD.js} +24 -24
- package/core/built/admin/assets/activitypub/{notifications-B9p8eCHD.js → notifications-BmNlXGgp.js} +51 -51
- package/core/built/admin/assets/activitypub/{onboarding-CNelD_uF.js → onboarding-Bqg7fh_L.js} +2 -2
- package/core/built/admin/assets/activitypub/{onboarding-mat5Hrl0.js → onboarding-Dmun9jr_.js} +2 -2
- package/core/built/admin/assets/activitypub/{preferences-BCqUdC3v.js → preferences--oH19wf9.js} +1519 -1519
- package/core/built/admin/assets/activitypub/{profile-BRCWoFem.js → profile-DH_tQx2D.js} +185 -185
- package/core/built/admin/assets/activitypub/{reply-DYK5vVqb.js → reply-Cap9PxSH.js} +2 -2
- package/core/built/admin/assets/activitypub/{routes-BSXT992i.js → routes-DdyY9uFF.js} +744 -735
- package/core/built/admin/assets/activitypub/{separator-BbQqTm-p.js → separator-CAxxu8VV.js} +3 -3
- package/core/built/admin/assets/activitypub/settings-B6EEcVkW.js +315 -0
- package/core/built/admin/assets/activitypub/{site-DHoMupSB.js → site-BYps63oh.js} +2 -2
- package/core/built/admin/assets/activitypub/{step-1-B-YfsVeO.js → step-1-CKjR2KAm.js} +6 -6
- package/core/built/admin/assets/activitypub/{step-2-DtPQzlTH.js → step-2-B6PKfOUQ.js} +6 -6
- package/core/built/admin/assets/activitypub/{step-3-D6BFMXL_.js → step-3-C-qBbcUo.js} +11 -11
- package/core/built/admin/assets/activitypub/{tabs-GpNecvfB.js → tabs-DLhZtVde.js} +306 -306
- package/core/built/admin/assets/activitypub/{topic-filter-BPDeQQys.js → topic-filter-DsUOuu0k.js} +3 -3
- package/core/built/admin/assets/activitypub/{use-activity-pub-queries-BoVOxXkV.js → use-activity-pub-queries-CX9snsLs.js} +3 -3
- package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-Bmk5h4lW.js → use-navigate-with-base-path-DsJTGDSS.js} +988 -959
- package/core/built/admin/assets/activitypub/{use-reply-chain-data-wVBX7swM.js → use-reply-chain-data-AJi9zkzm.js} +30 -30
- package/core/built/admin/assets/activitypub/{users-CoX6FT_K.js → users-inweyKL5.js} +192 -164
- package/core/built/admin/assets/activitypub/{x-DbQZOLiI.js → x-DR8AT3-4.js} +2 -2
- package/core/built/admin/assets/add-integration-modal-BT_onDJy.js +1 -0
- package/core/built/admin/assets/add-newsletter-modal-Cx1kQpiY.js +1 -0
- package/core/built/admin/assets/add-recommendation-modal-B5nvAhvr.js +1 -0
- package/core/built/admin/assets/analytics-DjoJWPI7.js +1 -0
- package/core/built/admin/assets/{announcement-bar-modal-DM3cYOpo.js → announcement-bar-modal-Dwd0EGFP.js} +1 -1
- package/core/built/admin/assets/{api-keys-3-0vJC3Y.js → api-keys-BGPzLHzA.js} +1 -1
- package/core/built/admin/assets/{api-keys-BLm2NDBh.js → api-keys-BoeacM-s.js} +1 -1
- package/core/built/admin/assets/{audience-9mwU5TW6.js → audience-ceV3XVHq.js} +1 -1
- package/core/built/admin/assets/automated-emails-BLesE41L.js +1 -0
- package/core/built/admin/assets/{automations-sAcJ1334.js → automations-BN4u3GR8.js} +1 -1
- package/core/built/admin/assets/automations-C-ZTUwW3.js +1 -0
- package/core/built/admin/assets/bluesky-sharing-vzh-oPXL.js +1 -0
- package/core/built/admin/assets/{breadcrumb-DLiqCyFc.js → breadcrumb-CaLk2b7D.js} +1 -1
- package/core/built/admin/assets/{button-B2jcu6bk.js → button-Dvcv1QSR.js} +1 -1
- package/core/built/admin/assets/calendar-Bl9RLcZX.js +1 -0
- package/core/built/admin/assets/{chart-helpers-Fo3bUL3K.js → chart-helpers-FHywU0pj.js} +1 -1
- package/core/built/admin/assets/{checkbox-BfKktMph.js → checkbox-BtayFtjg.js} +1 -1
- package/core/built/admin/assets/chunk.254.035b85ec456ebbd7336b.js +440 -0
- package/core/built/admin/assets/{chunk.524.60ca4692d91c6022042f.js → chunk.524.e6975095fccdf22d90f8.js} +5 -5
- package/core/built/admin/assets/{chunk.582.525c7617c2d9e94c940a.js → chunk.582.98b485f9b2802f540897.js} +6 -6
- package/core/built/admin/assets/{chunk.492.f24d855d7e3f3aad261c.js → chunk.901.4ccc639821020230bcca.js} +9949 -7921
- package/core/built/admin/assets/{code-editor-view-DCQJxsfU.js → code-editor-view-C3ZfoF1X.js} +1 -1
- package/core/built/admin/assets/{code-editor-view-CYVu4EW6.js → code-editor-view-CAyRWy1s.js} +1 -1
- package/core/built/admin/assets/color-picker-G4kGR7rp.js +3 -0
- package/core/built/admin/assets/color-picker-field-DQFVTNnT.js +1 -0
- package/core/built/admin/assets/combobox-BnYzgEB4.js +1 -0
- package/core/built/admin/assets/command-C_vyTikI.js +1 -0
- package/core/built/admin/assets/comments-aBrxfnZf.js +1 -0
- package/core/built/admin/assets/{config-DOMQDZGZ.js → config-kSHvwdTG.js} +1 -1
- package/core/built/admin/assets/confirmation-context-DwXX_Y5h.js +1 -0
- package/core/built/admin/assets/{content-api-modal-CyQEC3fr.js → content-api-modal-D2G0nJP6.js} +1 -1
- package/core/built/admin/assets/context-menu-DOVB6yaz.js +1 -0
- package/core/built/admin/assets/{copy-field-B3M1s78L.js → copy-field-DCJwh9qf.js} +1 -1
- package/core/built/admin/assets/create-ghost-browse-value-source-5C53jJaX.js +1 -0
- package/core/built/admin/assets/custom-field-type-option-BAuW0F-E.js +1 -0
- package/core/built/admin/assets/custom-integration-modal-AbJGVyKc.js +1 -0
- package/core/built/admin/assets/{date-range-select-DD2RKkgB.js → date-range-select-BY3K-ukj.js} +1 -1
- package/core/built/admin/assets/design-and-theme-modal-BvEy4_pC.js +2 -0
- package/core/built/admin/assets/dirty-confirm-dialog-DVPuGz3U.js +1 -0
- package/core/built/admin/assets/{disabled-sources-indicator-Cvy47bE2.js → disabled-sources-indicator-Cy7Zp4c8.js} +1 -1
- package/core/built/admin/assets/dist-Dcsslyo72.js +1 -0
- package/core/built/admin/assets/domain-Dl61QGuk.js +1 -0
- package/core/built/admin/assets/dropzone-Dtz5Rmjt.js +5 -0
- package/core/built/admin/assets/edit-profile-DNJSMDwm.js +1 -0
- package/core/built/admin/assets/editor-DbAen9zf.js +8 -0
- package/core/built/admin/assets/editor-screen-BFWThIai.js +26 -0
- package/core/built/admin/assets/email-sending-status-context-CWDOD1FN.js +1 -0
- package/core/built/admin/assets/email-sending-status-provider-DCPkDICt.js +1 -0
- package/core/built/admin/assets/emails-vFMk4yYl.js +1 -0
- package/core/built/admin/assets/{embed-signup-form-modal-DeBMLxlL.js → embed-signup-form-modal-DGqsORxS.js} +1 -1
- package/core/built/admin/assets/en-B1jX3WMs.js +1 -0
- package/core/built/admin/assets/explore-nnRr1Fja.js +1 -0
- package/core/built/admin/assets/feature-flag-overrides-context-DQjdJFAk.js +1 -0
- package/core/built/admin/assets/feature-image-placeholder-BrIHmPs8.js +1 -0
- package/core/built/admin/assets/feed-BNUcNu-W.js +1 -0
- package/core/built/admin/assets/feedback-DJ4Zb9rZ.js +1 -0
- package/core/built/admin/assets/{field-Cj8lmDOo.js → field-C93_Mg6j.js} +1 -1
- package/core/built/admin/assets/filter-bar-Da1qxybu.js +1 -0
- package/core/built/admin/assets/filter-sources-CGJb4C5r.js +1 -0
- package/core/built/admin/assets/filters--kKP-EC8.js +1 -0
- package/core/built/admin/assets/filters-DxA_a4FP.js +1 -0
- package/core/built/admin/assets/first-promoter-modal-BYDtrz2S.js +1 -0
- package/core/built/admin/assets/focus-koenig-editor-on-bottom-click-BbSnGOtK.js +1 -0
- package/core/built/admin/assets/{ghost-c8807202a07c20030acb719ee0114815.css → ghost-8fd5bb799356bbefb254210ba7e1236d.css} +1 -1
- package/core/built/admin/assets/{ghost-3894eedcc6968f658578c0abfe942aab.js → ghost-b4e8a403baba61905cd7560e30b9732d.js} +63 -44
- package/core/built/admin/assets/{ghost-dark-fbf1e384aaef3ae315e540f0eb7373a7.css → ghost-dark-cac0cb2d5bb78e9bd14d8d1c2768ed45.css} +1 -1
- package/core/built/admin/assets/gift-link-modal-B7u5bD4e.js +1 -0
- package/core/built/admin/assets/{gift-links-DLDS7ljO.js → gift-links-CfLSnst2.js} +1 -1
- package/core/built/admin/assets/{growth-D6mhcx3q.js → growth-Cd1uO9d2.js} +1 -1
- package/core/built/admin/assets/growth-aG32xfdE.js +3 -0
- package/core/built/admin/assets/history-modal-Df8lW8h-.js +1 -0
- package/core/built/admin/assets/hooks-Cy_-oZ4o.js +2 -0
- package/core/built/admin/assets/{html-field-C8qVGGC1.js → html-field-DA4eU7eq.js} +1 -1
- package/core/built/admin/assets/{images-h8VZEQ9k.js → images-KXmyO3Ss.js} +1 -1
- package/core/built/admin/assets/{inbox-BYd1M06Q.js → inbox-gJHG7_xd.js} +14 -14
- package/core/built/admin/assets/index-BHHXtR5C.css +2 -0
- package/core/built/admin/assets/index-CgMCwsxN.js +4 -0
- package/core/built/admin/assets/index.es-CY6-yuw9.js +1 -0
- package/core/built/admin/assets/{input-group-D8sCXRF5.js → input-group-BE8rM2y1.js} +1 -1
- package/core/built/admin/assets/{integrations-Bjbu86Z0.js → integrations-B4kuRBFY.js} +1 -1
- package/core/built/admin/assets/invite-user-modal-B4CuNcx4.js +1 -0
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +14 -14
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +3 -3
- package/core/built/admin/assets/koenig-lexical/style.css +1 -1
- package/core/built/admin/assets/{koenig-lexical-_-IsVI7E.js → koenig-lexical-CGXb0F8o.js} +3 -3
- package/core/built/admin/assets/koenig-loader-BeM5rFPX.js +2 -0
- package/core/built/admin/assets/{kpi-card-DZQk_0W8.js → kpi-card-B21aToe8.js} +1 -1
- package/core/built/admin/assets/{kpi-card-DW_2y3ug.js → kpi-card-pz6Wv4Ae.js} +1 -1
- package/core/built/admin/assets/{kpi-helpers-PueTKuA6.js → kpi-helpers-Dq5jelmP.js} +1 -1
- package/core/built/admin/assets/{kpi-tabs-CojDs-Qz.js → kpi-tabs-DEZHpKUt.js} +1 -1
- package/core/built/admin/assets/{label-DjbT91GM.js → label-OLCzze8N.js} +1 -1
- package/core/built/admin/assets/label-picker-BigOvMPV.js +1 -0
- package/core/built/admin/assets/{labels-CH3Kn8RV.js → labels-Dr0SgUQD.js} +1 -1
- package/core/built/admin/assets/layout-DUTL8SH5.js +1 -0
- package/core/built/admin/assets/lib-7QPTI0FS.js +1 -0
- package/core/built/admin/assets/link-helpers-BRAg9SCj.js +1 -0
- package/core/built/admin/assets/links-BDHPTlyj.js +1 -0
- package/core/built/admin/assets/member-custom-fields-TRTHCAz9.js +1 -0
- package/core/built/admin/assets/member-detail-b6dRwc3C.js +1 -0
- package/core/built/admin/assets/members-Czt5lJSc.js +4 -0
- package/core/built/admin/assets/moderation-ZWefD9uH.js +1 -0
- package/core/built/admin/assets/{multi-select-combobox-B6LiRzDO.js → multi-select-combobox-CT13MTo-.js} +1 -1
- package/core/built/admin/assets/navigation-modal-C8FEhBK5.js +1 -0
- package/core/built/admin/assets/newsletter-5VYW4RyJ.js +1 -0
- package/core/built/admin/assets/newsletter-detail-modal-ChjC0ZqH.js +1 -0
- package/core/built/admin/assets/{newsletter-emails-CFeLlt4z.js → newsletter-emails-DZH6I-PY.js} +1 -1
- package/core/built/admin/assets/newsletters-DOC9KBcX.js +1 -0
- package/core/built/admin/assets/newsletters-Rq1M3GD-.js +1 -0
- package/core/built/admin/assets/note-DIV-Pojr.js +1 -0
- package/core/built/admin/assets/notifications-s20-H49V.js +1 -0
- package/core/built/admin/assets/nql-BLpCn7qX.js +21 -0
- package/core/built/admin/assets/{offers-9J1CSiLy.js → offers--e2nGvkW.js} +1 -1
- package/core/built/admin/assets/offers-container-modal-BmG5M5Tv.js +1 -0
- package/core/built/admin/assets/onboarding-CUuy9unR.js +1 -0
- package/core/built/admin/assets/{onboarding-B4YhlZ0i.js → onboarding-pXyct0Jm.js} +1 -1
- package/core/built/admin/assets/{onboarding-route-Dmc4IPm3.js → onboarding-route-CK4pcXoR.js} +1 -1
- package/core/built/admin/assets/overview-CaPsciMs.js +1 -0
- package/core/built/admin/assets/overview-a8GfcsKX.js +1 -0
- package/core/built/admin/assets/{page-header-BR_nbIPp.js → page-header-DM_KyoFI.js} +1 -1
- package/core/built/admin/assets/pagemenu-vgZVUVKR.js +1 -0
- package/core/built/admin/assets/pages-CjDY2CBI.js +1 -0
- package/core/built/admin/assets/pages-route-BzKZYdV5.js +1 -0
- package/core/built/admin/assets/papaparse.min-cQwnxGCG.js +10 -0
- package/core/built/admin/assets/{pintura-modal-BR9hEvS8.js → pintura-modal-DExCF1ii.js} +1 -1
- package/core/built/admin/assets/portal-frame-Cqanidq4.js +1 -0
- package/core/built/admin/assets/portal-modal-oinBgA27.js +1 -0
- package/core/built/admin/assets/post-analytics-C5oJ1dWV.js +1 -0
- package/core/built/admin/assets/post-analytics-header-OATo3X_r.js +1 -0
- package/core/built/admin/assets/post-analytics-provider-tOpIVGT5.js +1 -0
- package/core/built/admin/assets/post-helpers-C88JBa4D.js +1 -0
- package/core/built/admin/assets/post-share-modal-DvhC1XgV.js +1 -0
- package/core/built/admin/assets/post-utils-DKF4mfuo.js +1 -0
- package/core/built/admin/assets/posts-list-screen-o4ZjbgS3.js +2 -0
- package/core/built/admin/assets/posts-pCPrhQeR.js +1 -0
- package/core/built/admin/assets/posts-route-9F--xdIb.js +1 -0
- package/core/built/admin/assets/preferences--vNubsqD.js +5 -0
- package/core/built/admin/assets/{preview-modal-C-BL-F9k.js → preview-modal-YYbzlYdO.js} +1 -1
- package/core/built/admin/assets/profile-BllKV3dR.js +1 -0
- package/core/built/admin/assets/{radio-group-Bgz60i81.js → radio-group-DL8GI8rt.js} +1 -1
- package/core/built/admin/assets/{referrers-CnhCMWCs.js → referrers-D8oHPmDB.js} +1 -1
- package/core/built/admin/assets/router-provider-BSvTXgZY.js +1 -0
- package/core/built/admin/assets/schemas-BoYdB6YU.js +64 -0
- package/core/built/admin/assets/select-v5YbA3Mw.js +1 -0
- package/core/built/admin/assets/settings-PEsZupw1.js +4 -0
- package/core/built/admin/assets/{settings-breadcrumbs-CyP_9zTo.js → settings-breadcrumbs-NwPnCTC7.js} +1 -1
- package/core/built/admin/assets/settings-u_s1CRkr.js +1 -0
- package/core/built/admin/assets/share-modal-SskCmVGL.js +1 -0
- package/core/built/admin/assets/slack-modal-XxSJiYgo.js +1 -0
- package/core/built/admin/assets/{sort-button-Cc-Z96sA.js → sort-button-Cx1KtINE.js} +1 -1
- package/core/built/admin/assets/{source-icon-v6PUnmyM.js → source-icon-Bdp9Im_5.js} +1 -1
- package/core/built/admin/assets/stats-D9PDLS3x.js +1 -0
- package/core/built/admin/assets/stats-view-W2g6Jsd-.js +1 -0
- package/core/built/admin/assets/{step-1-DSRp2vBj.js → step-1-BQYzvBzr.js} +1 -1
- package/core/built/admin/assets/{step-2-COqv2g0u.js → step-2-xES32xJV.js} +1 -1
- package/core/built/admin/assets/{step-3-6W1NMVQc.js → step-3-DA_XK5kW.js} +2 -2
- package/core/built/admin/assets/{stripe-button-D2K42Uty.js → stripe-button-CVsLHelt.js} +1 -1
- package/core/built/admin/assets/stripe-connect-modal-ClmLOMGS.js +1 -0
- package/core/built/admin/assets/style-Wd9nQubI.css +1 -0
- package/core/built/admin/assets/{table-B2pIKjVh.js → table-BenqVupI.js} +1 -1
- package/core/built/admin/assets/{tabs-SaW_UT-Q.js → tabs-eBBlxPp8.js} +1 -1
- package/core/built/admin/assets/tag-detail-Ce8Se2qd.js +2 -0
- package/core/built/admin/assets/tag-picker-C3UR-uJ-.js +1 -0
- package/core/built/admin/assets/tags-B-kJxCs7.js +1 -0
- package/core/built/admin/assets/{tags-zTFiKRpb.js → tags-ChfdwJyS.js} +1 -1
- package/core/built/admin/assets/testimonials-modal-9Q6fT06V.js +1 -0
- package/core/built/admin/assets/tier-detail-modal-CDyYdqK7.js +1 -0
- package/core/built/admin/assets/tiers-BCpIOGQr.js +1 -0
- package/core/built/admin/assets/{toggle-group-EM9WpESu.js → toggle-group-CviIkyok.js} +1 -1
- package/core/built/admin/assets/{topic-filter-DY4gNHn2.js → topic-filter-I2jCBGxm.js} +1 -1
- package/core/built/admin/assets/{transistor-modal-CpyPG-MH.js → transistor-modal-p53u3W8y.js} +1 -1
- package/core/built/admin/assets/{trend-badge-ByMkuAuH.js → trend-badge-CideVzQd.js} +1 -1
- package/core/built/admin/assets/{unsplash-modal-Cv2Q6GtY.js → unsplash-modal-DY8UcKcE.js} +1 -1
- package/core/built/admin/assets/{use-analytics-data-CHvjMYuT.js → use-analytics-data-DAIGcr2f.js} +1 -1
- package/core/built/admin/assets/use-check-theme-limit-error-B8bjF-gO.css +1 -0
- package/core/built/admin/assets/{use-check-theme-limit-error-Qv4SVXqw.js → use-check-theme-limit-error-DRUjK4YM.js} +1 -1
- package/core/built/admin/assets/use-filter-params-JkZhIS8L.js +1 -0
- package/core/built/admin/assets/{use-filterable-api-CzXcpUh8.js → use-filterable-api-BVEVMrhZ.js} +1 -1
- package/core/built/admin/assets/use-growth-stats-937CUj6j.js +1 -0
- package/core/built/admin/assets/use-host-limits-D0Dv3tKA.js +1 -0
- package/core/built/admin/assets/{use-limiter-cwJvsNBo.js → use-limiter-CohWfzvK.js} +1 -1
- package/core/built/admin/assets/{use-navigate-with-base-path-BHPA228f.js → use-navigate-with-base-path-BkxcPj5l.js} +1 -1
- package/core/built/admin/assets/use-pintura-config-xgytjU35.js +1 -0
- package/core/built/admin/assets/{use-pintura-editor-BDBXWUYq.js → use-pintura-editor-CaV1mbfO.js} +1 -1
- package/core/built/admin/assets/{use-post-referrers-DbY-mzci.js → use-post-referrers-BCQI7fDg.js} +1 -1
- package/core/built/admin/assets/use-reply-chain-data-CYRhoaxY.js +1 -0
- package/core/built/admin/assets/{use-router-history-entry-Bg6BhBQQ.js → use-router-history-entry-NOKcd--l.js} +1 -1
- package/core/built/admin/assets/{use-setting-group-VM7FQwMY.js → use-setting-group-bLKMwoBT.js} +1 -1
- package/core/built/admin/assets/use-settings-navigation-B-qrriKD.js +1 -0
- package/core/built/admin/assets/{use-simple-pagination-Dum6Irbs.js → use-simple-pagination-CNPd6Rf6.js} +1 -1
- package/core/built/admin/assets/use-staff-users-pRIMIyiW.js +1 -0
- package/core/built/admin/assets/use-tinybird-query-CmdbCS9X.js +1 -0
- package/core/built/admin/assets/use-tinybird-token-KLne235s.js +1 -0
- package/core/built/admin/assets/{use-unsaved-changes-guard-BAy0RANR.js → use-unsaved-changes-guard-7-ug0JdH.js} +1 -1
- package/core/built/admin/assets/use-upgrade-route-Dm_qqKr0.js +1 -0
- package/core/built/admin/assets/{use-url-input-Df5QzNa7.js → use-url-input-fvp-TpPx.js} +1 -1
- package/core/built/admin/assets/user-detail-modal-D3KlGYv-.js +1 -0
- package/core/built/admin/assets/users-DVNWrqg9.js +1 -0
- package/core/built/admin/assets/{vendor-5753232c7d6ffb3c44e1eb0a02133953.js → vendor-2dd263291938d407802568d3357ec69e.js} +19 -17
- package/core/built/admin/assets/{virtual-list-DEx8WgiO.js → virtual-list-CZ3RK2Yf.js} +1 -1
- package/core/built/admin/assets/web-ClEJsCAj.js +2 -0
- package/core/built/admin/assets/web-X9rdnrtq.js +1 -0
- package/core/built/admin/assets/{zapier-modal-Cw3BTBUu.js → zapier-modal-DfVhV17j.js} +1 -1
- package/core/built/admin/index.html +23 -27
- package/core/frontend/apps/private-blogging/lib/views/private.hbs +44 -43
- package/core/frontend/helpers/cancel_link.js +2 -2
- package/core/frontend/helpers/pagination.js +2 -2
- package/core/frontend/helpers/social_accounts.js +2 -2
- package/core/frontend/public/ghost.css +1 -1
- package/core/frontend/public/ghost.min.css +1 -1
- package/core/frontend/services/sitemap/user-map-generator.js +9 -2
- package/core/server/adapters/jobs/InMemoryJobsBackend.js +74 -29
- package/core/server/api/endpoints/authentication.js +1 -2
- package/core/server/api/endpoints/authors-public.js +15 -5
- package/core/server/api/endpoints/emails.js +13 -0
- package/core/server/api/endpoints/index.js +10 -2
- package/core/server/api/endpoints/mail.js +1 -1
- package/core/server/api/endpoints/member-metafields-members.js +23 -0
- package/core/server/api/endpoints/member-metafields.js +102 -0
- package/core/server/api/endpoints/members-account.js +42 -0
- package/core/server/api/endpoints/members.js +4 -4
- package/core/server/api/endpoints/newsletters-public.js +10 -1
- package/core/server/api/endpoints/pages-public.js +15 -5
- package/core/server/api/endpoints/posts-public.js +15 -5
- package/core/server/api/endpoints/posts.js +3 -3
- package/core/server/api/endpoints/tags-public.js +22 -3
- package/core/server/api/endpoints/tiers-checkout-config.js +2 -2
- package/core/server/api/endpoints/tiers-public.js +38 -1
- package/core/server/api/endpoints/users.js +2 -2
- package/core/server/api/endpoints/utils/api-filter-utils.js +170 -3
- package/core/server/api/endpoints/utils/public-cache-keys.js +3 -0
- package/core/server/api/endpoints/utils/serializers/input/authors.js +2 -0
- package/core/server/api/endpoints/utils/serializers/input/members.js +20 -9
- package/core/server/api/endpoints/utils/serializers/input/pages.js +2 -0
- package/core/server/api/endpoints/utils/serializers/input/posts.js +2 -0
- package/core/server/api/endpoints/utils/serializers/input/tags.js +2 -0
- package/core/server/api/endpoints/utils/serializers/output/emails.js +7 -0
- package/core/server/api/endpoints/utils/serializers/output/index.js +6 -2
- package/core/server/api/endpoints/utils/serializers/output/member-metafields.js +18 -0
- package/core/server/api/endpoints/utils/serializers/output/members-account.js +19 -0
- package/core/server/api/endpoints/utils/serializers/output/members-csv-transform.js +1 -1
- package/core/server/api/endpoints/utils/serializers/output/members.js +2 -4
- package/core/server/api/endpoints/utils/serializers/output/tiers.js +10 -2
- package/core/server/api/endpoints/utils/serializers/output/users.js +0 -10
- package/core/server/api/endpoints/utils/validators/input/invitations.js +0 -5
- package/core/server/api/endpoints/utils/validators/input/pages.js +1 -1
- package/core/server/api/endpoints/utils/validators/input/posts.js +1 -1
- package/core/server/data/exporter/table-lists.js +3 -3
- package/core/server/data/importer/importers/data/data-importer.js +3 -3
- package/core/server/data/migrations/utils/migrations.js +1 -1
- package/core/server/data/migrations/versions/6.53/2026-07-15-20-22-53-add-members-custom-field-values.js +1 -1
- package/core/server/data/migrations/versions/6.57/2026-08-04-18-00-00-store-custom-field-values-as-leaf-rows.js +10 -1
- package/core/server/data/migrations/versions/6.63/2026-09-03-10-07-03-rename-custom-field-tables-to-metafields.js +169 -0
- package/core/server/data/migrations/versions/6.63/2026-09-03-11-03-31-remove-member-custom-field-read-permissions.js +30 -0
- package/core/server/data/schema/fixtures/fixtures.json +0 -11
- package/core/server/data/schema/lib/default-index-name.js +8 -11
- package/core/server/data/schema/schema.js +18 -18
- package/core/server/data/seeders/README.md +48 -0
- package/core/server/data/tinybird/pipes/mv_hits.pipe +86 -50
- package/core/server/lib/db-types/count.js +16 -0
- package/core/server/lib/image/image-size.js +24 -0
- package/core/server/lib/package-json/parse.js +2 -2
- package/core/server/lib/validate-password.js +81 -120
- package/core/server/models/action.js +4 -4
- package/core/server/models/base/plugins/filtered-collection.js +16 -0
- package/core/server/models/member.js +10 -21
- package/core/server/models/post.js +2 -2
- package/core/server/models/relations/authors.js +2 -16
- package/core/server/models/user.js +10 -3
- package/core/server/services/auth/passwordreset.js +13 -0
- package/core/server/services/automations/automations-api.js +1 -1
- package/core/server/services/automations/database-automations-repository.js +11 -4
- package/core/server/services/comments/comments-controller.js +30 -9
- package/core/server/services/comments/comments-service.js +55 -5
- package/core/server/services/content-import/csv/formula.js +12 -0
- package/core/server/services/content-import/csv/index.js +6 -1
- package/core/server/services/content-import/csv/parse.js +27 -6
- package/core/server/services/content-import/csv/serialize.js +13 -0
- package/core/server/services/content-import/import/completion-email.js +164 -0
- package/core/server/services/content-import/import/errors-file.js +36 -0
- package/core/server/services/content-import/import/importer.js +160 -53
- package/core/server/services/content-import/import/local-media-url.js +63 -0
- package/core/server/services/content-import/import/media.js +254 -0
- package/core/server/services/content-import/import/post-data.js +0 -1
- package/core/server/services/content-import/import/post-link.js +11 -0
- package/core/server/services/content-import/import/post-repository.js +25 -3
- package/core/server/services/content-import/import/reader.js +9 -2
- package/core/server/services/content-import/import/report.js +34 -0
- package/core/server/services/content-import/import/row.js +0 -2
- package/core/server/services/content-import/import/schema.js +1 -0
- package/core/server/services/content-import/import/staged-file.js +29 -0
- package/core/server/services/content-import/import/store.js +25 -2
- package/core/server/services/content-import/index.js +52 -6
- package/core/server/services/content-import/jobs/content-csv-import-job.js +20 -0
- package/core/server/services/custom-redirects/redirect-config-parser.js +2 -2
- package/core/server/services/custom-redirects/redirects-service.js +1 -1
- package/core/server/services/custom-redirects/validation.js +1 -1
- package/core/server/services/email-address/email-address-parser.js +30 -38
- package/core/server/services/email-address/email-address-service.js +37 -4
- package/core/server/services/email-analytics/index.js +2 -7
- package/core/server/services/email-service/CONTEXT.md +37 -0
- package/core/server/services/email-service/README.md +42 -0
- package/core/server/services/email-service/batch-sending-service.js +2 -4
- package/core/server/services/email-service/email-controller.js +18 -2
- package/core/server/services/email-service/email-renderer.js +14 -4
- package/core/server/services/email-service/email-service-wrapper.js +3 -9
- package/core/server/services/email-service/mailgun-email-provider.js +20 -9
- package/core/server/services/email-service/sending-status-schema.js +21 -0
- package/core/server/services/email-service/sending-status-serializers.js +33 -0
- package/core/server/services/email-service/sending-status-service.js +47 -0
- package/core/server/services/email-service/sending-status.js +111 -0
- package/core/server/services/email-suppression-list/email-suppression-list.js +0 -33
- package/core/server/services/email-suppression-list/mailgun-email-suppression-list.js +6 -7
- package/core/server/services/gifts/README.md +2 -2
- package/core/server/services/gifts/constants.js +1 -1
- package/core/server/services/gifts/email-templates/gift-delivery.hbs +3 -3
- package/core/server/services/gifts/gift-email-service.js +0 -1
- package/core/server/services/gifts/gift-service.js +19 -57
- package/core/server/services/gifts/index.js +3 -5
- package/core/server/services/gifts/jobs/index.js +4 -4
- package/core/server/services/indexnow-ping/indexnow-ping-service.js +2 -2
- package/core/server/services/invitations/accept.js +4 -2
- package/core/server/services/jobs/job-service.js +0 -9
- package/core/server/services/jobs-service/index.js +8 -0
- package/core/server/services/jobs-service/jobs-service.js +52 -3
- package/core/server/services/jobs-service/register-job-handlers.js +32 -11
- package/core/server/services/mail/ghost-mailer.js +4 -4
- package/core/server/services/member-welcome-emails/service.js +7 -7
- package/core/server/services/members/account-service.js +98 -0
- package/core/server/services/members/api.js +4 -3
- package/core/server/services/members/import-export/csv/parse.js +0 -7
- package/core/server/services/members/import-export/export/exporter.js +20 -20
- package/core/server/services/members/import-export/import/completion-email.js +8 -11
- package/core/server/services/members/import-export/import/importer.js +13 -13
- package/core/server/services/members/import-export/import/row.js +0 -3
- package/core/server/services/members/import-export/index.js +10 -17
- package/core/server/services/members/jobs/clean-expired-comped-job.js +7 -0
- package/core/server/services/members/jobs/clean-expired-comped-task.js +145 -0
- package/core/server/services/members/jobs/index.js +48 -24
- package/core/server/services/members/members-api/controllers/member-controller.js +11 -0
- package/core/server/services/members/members-api/controllers/router-controller.js +3 -6
- package/core/server/services/members/members-api/members-api.js +26 -8
- package/core/server/services/members/members-api/repositories/event-repository.js +0 -4
- package/core/server/services/members/members-api/repositories/member-repository.js +4 -4
- package/core/server/services/members/members-api/services/member-bread-service.js +62 -84
- package/core/server/services/members/members-api/services/payments-service.js +1 -1
- package/core/server/services/members/members-ssr.js +18 -0
- package/core/server/services/members/middleware.js +95 -81
- package/core/server/services/members/service.js +71 -47
- package/core/server/services/members/utils.js +9 -0
- package/core/server/services/members-metafields/access.js +28 -0
- package/core/server/services/{members-custom-fields → members-metafields}/actions.js +5 -5
- package/core/server/services/members-metafields/bindings-service.js +147 -0
- package/core/server/services/members-metafields/codec.js +14 -0
- package/core/server/services/{members-custom-fields → members-metafields}/config.js +1 -1
- package/core/server/services/{members-custom-fields → members-metafields}/definitions-service.js +68 -22
- package/core/server/services/members-metafields/filter.js +181 -0
- package/core/server/services/{members-custom-fields → members-metafields}/index.js +16 -7
- package/core/server/services/{members-custom-fields → members-metafields}/key.js +2 -1
- package/core/server/services/{members-custom-fields → members-metafields}/models.js +5 -5
- package/core/server/services/members-metafields/namespaces.js +34 -0
- package/core/server/services/{members-custom-fields → members-metafields}/queries.js +1 -1
- package/core/server/services/{members-custom-fields → members-metafields}/schema.js +16 -12
- package/core/server/services/members-metafields/serializers.js +22 -0
- package/core/server/services/{members-custom-fields → members-metafields}/values-service.js +80 -48
- package/core/server/services/mentions/mention-controller.js +23 -21
- package/core/server/services/mentions/mention-sending-service.js +26 -9
- package/core/server/services/mentions/process-webmention-job.js +16 -0
- package/core/server/services/mentions/send-webmentions-job.js +18 -0
- package/core/server/services/mentions/service.js +7 -33
- package/core/server/services/route-settings/validation-errors.js +1 -1
- package/core/server/services/route-settings/yaml-parser.js +1 -1
- package/core/server/services/settings/settings-service.js +1 -8
- package/core/server/services/settings-helpers/settings-helpers.js +4 -6
- package/core/server/services/slack-ping/slack-ping-service.js +1 -1
- package/core/server/services/staff/staff-service-emails.js +2 -2
- package/core/server/services/stripe/config.js +14 -8
- package/core/server/services/stripe/service.js +10 -1
- package/core/server/services/stripe/services/checkout/completed-session.js +2 -2
- package/core/server/services/stripe/services/checkout/session-options.js +18 -15
- package/core/server/services/stripe/services/webhook/checkout-session-event-service.js +28 -7
- package/core/server/services/stripe/stripe-api.js +2 -0
- package/core/server/services/stripe/stripe-service.js +24 -14
- package/core/server/services/stripe/webhook-controller.js +10 -0
- package/core/server/services/tier-checkout-config/codec.js +11 -7
- package/core/server/services/tier-checkout-config/index.js +2 -1
- package/core/server/services/tier-checkout-config/models.js +11 -5
- package/core/server/services/tier-checkout-config/queries.js +12 -12
- package/core/server/services/tier-checkout-config/serializers.js +73 -10
- package/core/server/services/tier-checkout-config/service.js +14 -13
- package/core/server/services/tiers/service.js +3 -3
- package/core/server/services/update-check/index.js +13 -13
- package/core/server/services/update-check/jobs/update-check-job.js +7 -0
- package/core/server/services/update-check/update-check-service.js +2 -2
- package/core/server/services/url/router-filter.js +8 -10
- package/core/server/services/users.js +1 -19
- package/core/server/web/admin/controller.js +1 -1
- package/core/server/web/api/endpoints/admin/routes.js +20 -16
- package/core/server/web/members/account/index.js +2 -0
- package/core/server/web/members/account/routes.js +46 -0
- package/core/server/web/members/app.js +4 -18
- package/core/shared/config/defaults.json +1 -1
- package/core/shared/config/env/config.testing-mysql.json +1 -1
- package/core/shared/labs.js +7 -2
- package/package.json +42 -41
- package/pnpm-lock.yaml +1159 -582
- package/pnpm-workspace.yaml +18 -15
- package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
- package/components/tryghost-kg-default-nodes-2.2.0.tgz +0 -0
- package/core/built/admin/assets/Combination-5fYEzgbs.js +0 -41
- package/core/built/admin/assets/account-migration-DRhAehfw.js +0 -1
- package/core/built/admin/assets/activitypub/inbox-BluD2ucT.js +0 -1284
- package/core/built/admin/assets/activitypub/label-FHSh6ZAw.js +0 -16
- package/core/built/admin/assets/activitypub/layout-Cq4NG0Xr.js +0 -1
- package/core/built/admin/assets/activitypub/settings-DQDkr06X.js +0 -315
- package/core/built/admin/assets/add-integration-modal-BC0jExDZ.js +0 -1
- package/core/built/admin/assets/add-newsletter-modal-DchTkA7l.js +0 -1
- package/core/built/admin/assets/add-recommendation-modal-DMvaJZKp.js +0 -1
- package/core/built/admin/assets/alert-dialog-QhXzdHfc.js +0 -1
- package/core/built/admin/assets/analytics-No1DDY3A.js +0 -1
- package/core/built/admin/assets/automated-emails-te1D5kq-.js +0 -1
- package/core/built/admin/assets/automations-DpUNUdcZ.js +0 -1
- package/core/built/admin/assets/bluesky-sharing-Cjrv_f62.js +0 -1
- package/core/built/admin/assets/chunk.896.435fe0637944d4a69810.js +0 -366
- package/core/built/admin/assets/color-picker-BTY_NdEm.js +0 -3
- package/core/built/admin/assets/color-picker-field-CPlkzOH0.js +0 -1
- package/core/built/admin/assets/combobox-DrMtv_4P.js +0 -1
- package/core/built/admin/assets/command-ZdgYJpoI.js +0 -1
- package/core/built/admin/assets/comments-kidj60-h.js +0 -1
- package/core/built/admin/assets/confirmation-context-BhbmQQEl.js +0 -1
- package/core/built/admin/assets/custom-field-type-option-CsGIZk1J.js +0 -10
- package/core/built/admin/assets/custom-integration-modal-BY37O_9X.js +0 -1
- package/core/built/admin/assets/design-and-theme-modal-DUSKM4uC.js +0 -22
- package/core/built/admin/assets/dialog-B44iRwlc.js +0 -1
- package/core/built/admin/assets/dirty-confirm-dialog-BxgQgQ6H.js +0 -1
- package/core/built/admin/assets/dist-Cy8tfrp_.js +0 -1
- package/core/built/admin/assets/domain-Ca6KhoQt.js +0 -1
- package/core/built/admin/assets/dropzone-Bb-5zQTk.js +0 -5
- package/core/built/admin/assets/edit-profile-BdupLlus.js +0 -1
- package/core/built/admin/assets/editor-BP883Lpn.js +0 -8
- package/core/built/admin/assets/explore-FB-CimXw.js +0 -1
- package/core/built/admin/assets/feed-C1GxsmI2.js +0 -1
- package/core/built/admin/assets/filter-sources-D5n5bFA2.js +0 -1
- package/core/built/admin/assets/filters-DJEzkMUK.js +0 -1
- package/core/built/admin/assets/filters-MZXvdH5D.js +0 -1
- package/core/built/admin/assets/first-promoter-modal-CGTGLCr5.js +0 -1
- package/core/built/admin/assets/gift-link-modal-B6v0tnRN.js +0 -1
- package/core/built/admin/assets/growth-BGAJD1rs.js +0 -3
- package/core/built/admin/assets/history-modal-iKwUY3XR.js +0 -1
- package/core/built/admin/assets/hooks-CuEZV2-q.js +0 -2
- package/core/built/admin/assets/index-DjQe-e5s.js +0 -4
- package/core/built/admin/assets/index-Dqf2x-ry.css +0 -1
- package/core/built/admin/assets/index.es-VihPwfeH.js +0 -1
- package/core/built/admin/assets/input--W-rCTrX.js +0 -1
- package/core/built/admin/assets/invite-user-modal-MdImLCaJ.js +0 -1
- package/core/built/admin/assets/koenig-loader-DYZpm6El.js +0 -2
- package/core/built/admin/assets/label-picker-cB51xl1r.js +0 -1
- package/core/built/admin/assets/layout-c_B_1hax.js +0 -1
- package/core/built/admin/assets/link-helpers-MVDdnFCY.js +0 -1
- package/core/built/admin/assets/loading-indicator-BpFWTVgh.js +0 -1
- package/core/built/admin/assets/member-custom-fields-qs0dIkxM.js +0 -1
- package/core/built/admin/assets/member-detail-B6uec2a6.js +0 -1
- package/core/built/admin/assets/members-BQF3CqhI.js +0 -2
- package/core/built/admin/assets/moderation-DY4wF1Q3.js +0 -1
- package/core/built/admin/assets/navigation-modal-DX-gNF3X.js +0 -1
- package/core/built/admin/assets/newsletter-CyQA7m3m.js +0 -1
- package/core/built/admin/assets/newsletter-detail-modal-BDrptuxn.js +0 -1
- package/core/built/admin/assets/newsletters-DXuNmC42.js +0 -1
- package/core/built/admin/assets/newsletters-DhNigJUh.js +0 -1
- package/core/built/admin/assets/note-Cgo2LCJ6.js +0 -1
- package/core/built/admin/assets/notifications-BC50AMku.js +0 -1
- package/core/built/admin/assets/offers-container-modal-DWycIMzT.js +0 -1
- package/core/built/admin/assets/onboarding-DGQuV7Ew.js +0 -1
- package/core/built/admin/assets/overview-CVbFtn-k.js +0 -1
- package/core/built/admin/assets/overview-YKj7SZaK.js +0 -1
- package/core/built/admin/assets/pages-B6vr9F_D.js +0 -1
- package/core/built/admin/assets/popover-CCy0h5rq.js +0 -1
- package/core/built/admin/assets/portal-frame-dc4XKr0e.js +0 -1
- package/core/built/admin/assets/portal-modal-BvvdF_B9.js +0 -1
- package/core/built/admin/assets/post-analytics-DX470WRG.js +0 -1
- package/core/built/admin/assets/post-analytics-header-CXpJmdDv.js +0 -1
- package/core/built/admin/assets/post-analytics-provider-CLfKGWZy.js +0 -1
- package/core/built/admin/assets/post-helpers-C5OQMBoi.js +0 -1
- package/core/built/admin/assets/post-share-modal-Cl0NXRJr.js +0 -1
- package/core/built/admin/assets/posts-KY3aUhVV.js +0 -1
- package/core/built/admin/assets/preferences-DO0UUWFp.js +0 -5
- package/core/built/admin/assets/profile-CxT-0l4d.js +0 -1
- package/core/built/admin/assets/router-provider-B0gZyiGJ.js +0 -1
- package/core/built/admin/assets/schemas-4UPic-oD.js +0 -64
- package/core/built/admin/assets/select-DuDUFdPq.js +0 -1
- package/core/built/admin/assets/settings-BjCii7BM.js +0 -1
- package/core/built/admin/assets/settings-b8kNzZSQ.js +0 -4
- package/core/built/admin/assets/share-modal-BV70mr9k.js +0 -1
- package/core/built/admin/assets/shim-ClF_m5ux.js +0 -1
- package/core/built/admin/assets/slack-modal-Be2_KK-z.js +0 -1
- package/core/built/admin/assets/stats-C4reGh9O.js +0 -1
- package/core/built/admin/assets/stats-view-DP9pQ62k.js +0 -1
- package/core/built/admin/assets/stripe-connect-modal-Z-UT_BGc.js +0 -1
- package/core/built/admin/assets/tag-detail-Dc0nTOtU.js +0 -2
- package/core/built/admin/assets/tags-BLaXBwnb.js +0 -1
- package/core/built/admin/assets/testimonials-modal-sFwRnNLh.js +0 -1
- package/core/built/admin/assets/tier-detail-modal-BCFpWwUS.js +0 -1
- package/core/built/admin/assets/tiers-Nd_z4BZY.js +0 -1
- package/core/built/admin/assets/use-filter-params-CAvfDvf1.js +0 -1
- package/core/built/admin/assets/use-growth-stats-D6mZXGhK.js +0 -1
- package/core/built/admin/assets/use-host-limits-DZtR08oa.js +0 -1
- package/core/built/admin/assets/use-pintura-config-CzTfnufz.js +0 -1
- package/core/built/admin/assets/use-reply-chain-data-BeQwz8oF.js +0 -1
- package/core/built/admin/assets/use-settings-navigation-BbrSUVcf.js +0 -1
- package/core/built/admin/assets/use-staff-users-cZg4ugOF.js +0 -1
- package/core/built/admin/assets/use-tinybird-query-B1IMyAcv.js +0 -39
- package/core/built/admin/assets/use-tinybird-token-Bow0Zwn8.js +0 -1
- package/core/built/admin/assets/use-upgrade-route-DhRSo7at.js +0 -1
- package/core/built/admin/assets/user-detail-modal-yipSoQfv.js +0 -1
- package/core/built/admin/assets/users-5msE1WMs.js +0 -1
- package/core/built/admin/assets/web-CKcXAu86.js +0 -1
- package/core/built/admin/assets/web-DR4TqM3E.js +0 -2
- package/core/server/api/endpoints/member-custom-fields.js +0 -90
- package/core/server/api/endpoints/utils/serializers/output/member-custom-fields.js +0 -20
- package/core/server/services/email-suppression-list/in-memory-email-suppression-list.js +0 -39
- package/core/server/services/jobs/worker-model-event-bridge.js +0 -149
- package/core/server/services/members/jobs/clean-expired-comped.js +0 -132
- package/core/server/services/members-custom-fields/bindings-service.js +0 -119
- package/core/server/services/members-custom-fields/codec.js +0 -15
- package/core/server/services/members-custom-fields/filter.js +0 -199
- package/core/server/services/members-custom-fields/serializers.js +0 -24
- package/core/server/services/stripe/services/checkout/allowed-countries.js +0 -267
- package/core/server/services/stripe/services/checkout/field-ports.js +0 -34
- package/core/server/services/tier-checkout-config/destinations.js +0 -17
- package/core/server/services/update-check/run-update-check.js +0 -63
- /package/core/built/admin/assets/{chunk.492.f24d855d7e3f3aad261c.js.LICENSE.txt → chunk.901.4ccc639821020230bcca.js.LICENSE.txt} +0 -0
- /package/core/server/services/{members-custom-fields → members-metafields}/storage.js +0 -0
|
@@ -2,11 +2,10 @@ const crypto = require('crypto');
|
|
|
2
2
|
const _ = require('lodash');
|
|
3
3
|
const logging = require('@tryghost/logging');
|
|
4
4
|
const membersService = require('./service');
|
|
5
|
-
const emailSuppressionList = require('../email-suppression-list');
|
|
6
5
|
const models = require('../../models');
|
|
7
6
|
const urlUtils = require('../../../shared/url-utils').default;
|
|
8
7
|
const spamPrevention = require('../../web/shared/middleware/api/spam-prevention');
|
|
9
|
-
const {
|
|
8
|
+
const { formatNewsletterResponse } = require('./utils');
|
|
10
9
|
const errors = require('@tryghost/errors');
|
|
11
10
|
const tpl = require('@tryghost/tpl');
|
|
12
11
|
const onHeaders = require('on-headers');
|
|
@@ -18,6 +17,9 @@ const messages = {
|
|
|
18
17
|
missingUuid: 'Missing uuid.',
|
|
19
18
|
invalidUuid: 'Invalid uuid.',
|
|
20
19
|
invalidKey: 'Invalid key.',
|
|
20
|
+
// Says that nobody is signed in, without naming the cookie that would have said
|
|
21
|
+
// so. Which cookie carries a session is Ghost's business, not the caller's.
|
|
22
|
+
noMemberSession: 'You must be signed in to do this.',
|
|
21
23
|
};
|
|
22
24
|
|
|
23
25
|
const getFreeTier = async function getFreeTier() {
|
|
@@ -116,6 +118,94 @@ const getRedirectUrl = function getRedirectUrl({ action, referrer, searchParams,
|
|
|
116
118
|
return redirectUrl.href;
|
|
117
119
|
};
|
|
118
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Establish who is signed in, without loading them.
|
|
123
|
+
*
|
|
124
|
+
* Sets `req.identity` to an id and an email address, or to null when nobody is
|
|
125
|
+
* signed in. One lookup against an indexed column, with no relations attached and
|
|
126
|
+
* nothing fetched from outside Ghost.
|
|
127
|
+
*
|
|
128
|
+
* Deliberately not the member's record. Most endpoints that serve a member need to
|
|
129
|
+
* act on one rather than describe one, and the one that describes a member reads
|
|
130
|
+
* it again after writing anyway, because the response has to say what Ghost now
|
|
131
|
+
* holds rather than what was sent. What to load is the endpoint's own business.
|
|
132
|
+
*
|
|
133
|
+
* Distinct from `loadMemberSession`, which loads the whole member and is what a
|
|
134
|
+
* themed page renders from.
|
|
135
|
+
*/
|
|
136
|
+
const loadMemberIdentity = async function loadMemberIdentity(req, res, next) {
|
|
137
|
+
try {
|
|
138
|
+
Object.assign(req, {
|
|
139
|
+
identity: await membersService.ssr.getMemberIdentityFromSession(req, res),
|
|
140
|
+
});
|
|
141
|
+
} catch (err) {
|
|
142
|
+
// Only a missing or unreadable cookie means nobody is signed in. Establishing
|
|
143
|
+
// an identity also reaches the database, and answering "you are not signed in"
|
|
144
|
+
// to that failing would tell a signed-in member something false and leave the
|
|
145
|
+
// real fault unreported.
|
|
146
|
+
if (!errors.utils.isGhostError(err) || err.errorType !== 'BadRequestError') {
|
|
147
|
+
return next(err);
|
|
148
|
+
}
|
|
149
|
+
Object.assign(req, { identity: null });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return next();
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Hand the identity to whatever comes next, in the place the API framework reads.
|
|
157
|
+
*
|
|
158
|
+
* The framework takes whoever is acting from `req.member` and nowhere else, so a
|
|
159
|
+
* controller behind either gate below finds the identity there.
|
|
160
|
+
*/
|
|
161
|
+
const carryIdentity = function carryIdentity(req, res) {
|
|
162
|
+
Object.assign(req, { member: req.identity });
|
|
163
|
+
res.locals.member = req.member;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Refuse a request that `loadMemberIdentity` could not put a name to.
|
|
168
|
+
*
|
|
169
|
+
* For endpoints where an unknown caller is asking for something they have no right
|
|
170
|
+
* to: changing a member's record, or reading what a publisher collects.
|
|
171
|
+
*
|
|
172
|
+
* Reads only `req.identity`, so a route using this must register
|
|
173
|
+
* `loadMemberIdentity` first. It looks nothing up itself: what a route needs is
|
|
174
|
+
* declared by the middleware it lists, rather than depending on what some earlier
|
|
175
|
+
* one happened to leave behind.
|
|
176
|
+
*/
|
|
177
|
+
const rejectWhenAnonymous = function rejectWhenAnonymous(req, res, next) {
|
|
178
|
+
if (!req.identity) {
|
|
179
|
+
return next(
|
|
180
|
+
new errors.UnauthorizedError({
|
|
181
|
+
message: tpl(messages.noMemberSession),
|
|
182
|
+
}),
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
carryIdentity(req, res);
|
|
187
|
+
return next();
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Answer with nothing when `loadMemberIdentity` could not put a name to the request.
|
|
192
|
+
*
|
|
193
|
+
* For reading who you are, where not knowing is an ordinary answer rather than a
|
|
194
|
+
* failure: a themed page asks on every view and most of those views have no member.
|
|
195
|
+
*
|
|
196
|
+
* The counterpart to `rejectWhenAnonymous`, and separate from it because which of
|
|
197
|
+
* the two applies is a fact about the endpoint, not a setting on a shared one.
|
|
198
|
+
*/
|
|
199
|
+
const emptyWhenAnonymous = function emptyWhenAnonymous(req, res, next) {
|
|
200
|
+
if (!req.identity) {
|
|
201
|
+
res.writeHead(204);
|
|
202
|
+
return res.end();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
carryIdentity(req, res);
|
|
206
|
+
return next();
|
|
207
|
+
};
|
|
208
|
+
|
|
119
209
|
/**
|
|
120
210
|
* Require member authentication, and make it possible to authenticate via uuid + hashed key.
|
|
121
211
|
* You can chain this after loadMemberSession to make it possible to authenticate via both the uuid and the session.
|
|
@@ -243,42 +333,6 @@ const deleteSession = async function deleteSession(req, res) {
|
|
|
243
333
|
}
|
|
244
334
|
};
|
|
245
335
|
|
|
246
|
-
const getMemberData = async function getMemberData(req, res) {
|
|
247
|
-
try {
|
|
248
|
-
const member = await membersService.ssr.getMemberDataFromSession(req, res);
|
|
249
|
-
if (member) {
|
|
250
|
-
res.json(formattedMemberResponse(member));
|
|
251
|
-
} else {
|
|
252
|
-
res.json(null);
|
|
253
|
-
}
|
|
254
|
-
} catch (err) {
|
|
255
|
-
res.writeHead(204);
|
|
256
|
-
res.end();
|
|
257
|
-
}
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
const deleteSuppression = async function deleteSuppression(req, res) {
|
|
261
|
-
try {
|
|
262
|
-
const member = await membersService.ssr.getMemberDataFromSession(req, res);
|
|
263
|
-
const options = {
|
|
264
|
-
id: member.id,
|
|
265
|
-
};
|
|
266
|
-
await emailSuppressionList.removeEmail(member.email);
|
|
267
|
-
await membersService.api.members.update({ email_disabled: false }, options);
|
|
268
|
-
|
|
269
|
-
res.writeHead(204);
|
|
270
|
-
res.end();
|
|
271
|
-
} catch (err) {
|
|
272
|
-
if (!err.statusCode) {
|
|
273
|
-
logging.error(err);
|
|
274
|
-
}
|
|
275
|
-
res.writeHead(err.statusCode ?? 500, {
|
|
276
|
-
'Content-Type': 'text/plain;charset=UTF-8',
|
|
277
|
-
});
|
|
278
|
-
res.end(err.message);
|
|
279
|
-
}
|
|
280
|
-
};
|
|
281
|
-
|
|
282
336
|
const getMemberNewsletters = async function getMemberNewsletters(req, res) {
|
|
283
337
|
try {
|
|
284
338
|
const memberData = req.member; // validation assumed
|
|
@@ -351,46 +405,6 @@ const updateMemberNewsletters = async function updateMemberNewsletters(req, res)
|
|
|
351
405
|
}
|
|
352
406
|
};
|
|
353
407
|
|
|
354
|
-
const updateMemberData = async function updateMemberData(req, res) {
|
|
355
|
-
try {
|
|
356
|
-
const data = _.pick(
|
|
357
|
-
req.body,
|
|
358
|
-
'name',
|
|
359
|
-
'expertise',
|
|
360
|
-
'subscribed',
|
|
361
|
-
'newsletters',
|
|
362
|
-
'enable_comment_notifications',
|
|
363
|
-
'enable_updates_and_announcements',
|
|
364
|
-
);
|
|
365
|
-
const member = await membersService.ssr.getMemberDataFromSession(req, res);
|
|
366
|
-
if (member) {
|
|
367
|
-
const options = {
|
|
368
|
-
id: member.id,
|
|
369
|
-
withRelated: [
|
|
370
|
-
'stripeSubscriptions',
|
|
371
|
-
'stripeSubscriptions.customer',
|
|
372
|
-
'stripeSubscriptions.stripePrice',
|
|
373
|
-
'newsletters',
|
|
374
|
-
],
|
|
375
|
-
};
|
|
376
|
-
await membersService.api.members.update(data, options);
|
|
377
|
-
const updatedMember = await membersService.ssr.getMemberDataFromSession(req, res);
|
|
378
|
-
|
|
379
|
-
res.json(formattedMemberResponse(updatedMember));
|
|
380
|
-
} else {
|
|
381
|
-
res.json(null);
|
|
382
|
-
}
|
|
383
|
-
} catch (err) {
|
|
384
|
-
if (!err.statusCode) {
|
|
385
|
-
logging.error(err);
|
|
386
|
-
}
|
|
387
|
-
res.writeHead(err.statusCode ?? 500, {
|
|
388
|
-
'Content-Type': 'text/plain;charset=UTF-8',
|
|
389
|
-
});
|
|
390
|
-
res.end(err.message);
|
|
391
|
-
}
|
|
392
|
-
};
|
|
393
|
-
|
|
394
408
|
const createSessionFromMagicLink = async function createSessionFromMagicLink(req, res, next) {
|
|
395
409
|
if (!req.url.includes('token=')) {
|
|
396
410
|
return next();
|
|
@@ -500,17 +514,17 @@ const createSessionFromMagicLink = async function createSessionFromMagicLink(req
|
|
|
500
514
|
// Set req.member & res.locals.member if a cookie is set
|
|
501
515
|
module.exports = {
|
|
502
516
|
loadMemberSession,
|
|
517
|
+
loadMemberIdentity,
|
|
518
|
+
rejectWhenAnonymous,
|
|
519
|
+
emptyWhenAnonymous,
|
|
503
520
|
authMemberByUuid,
|
|
504
521
|
createSessionFromMagicLink,
|
|
505
522
|
getIdentityToken,
|
|
506
523
|
getEntitlementToken,
|
|
507
524
|
getMemberNewsletters,
|
|
508
|
-
getMemberData,
|
|
509
|
-
updateMemberData,
|
|
510
525
|
updateMemberNewsletters,
|
|
511
526
|
deleteSession,
|
|
512
527
|
accessInfoSession,
|
|
513
|
-
deleteSuppression,
|
|
514
528
|
createIntegrityToken,
|
|
515
529
|
verifyIntegrityToken,
|
|
516
530
|
};
|
|
@@ -7,7 +7,6 @@ const MembersConfigProvider = require('./members-config-provider');
|
|
|
7
7
|
const { makeImporter, makeExporter } = require('./import-export');
|
|
8
8
|
const { resolveInlineThreshold } = require('./import-export/config');
|
|
9
9
|
const MembersStats = require('./stats/members-stats');
|
|
10
|
-
const memberJobs = require('./jobs');
|
|
11
10
|
const logging = require('@tryghost/logging');
|
|
12
11
|
const urlUtils = require('../../../shared/url-utils').default;
|
|
13
12
|
const settingsCache = require('../../../shared/settings-cache');
|
|
@@ -29,7 +28,7 @@ const messages = {
|
|
|
29
28
|
sslRequiredForStripe:
|
|
30
29
|
'Cannot run Ghost without SSL when Stripe is connected. Please update your url config to use "https://".',
|
|
31
30
|
remoteWebhooksInDevelopment:
|
|
32
|
-
'Cannot use remote webhooks in development. See https://ghost.org/
|
|
31
|
+
'Cannot use remote webhooks in development. See https://docs.ghost.org/webhooks/#stripe-webhooks for developing with Stripe.',
|
|
33
32
|
};
|
|
34
33
|
|
|
35
34
|
const ghostMailer = new GhostMailer();
|
|
@@ -50,9 +49,9 @@ let membersApi;
|
|
|
50
49
|
let verificationTrigger;
|
|
51
50
|
|
|
52
51
|
const buildImporterDeps = ({ stripeAPIService }) => {
|
|
53
|
-
// Required here, not statically: boot builds the
|
|
52
|
+
// Required here, not statically: boot builds the metafields services before this
|
|
54
53
|
// one (the exporter below relies on the same).
|
|
55
|
-
const
|
|
54
|
+
const metafields = require('../members-metafields');
|
|
56
55
|
return {
|
|
57
56
|
getTimezone: () => settingsCache.get('timezone'),
|
|
58
57
|
// A getter rather than a value because the threshold is an operator
|
|
@@ -89,9 +88,9 @@ const buildImporterDeps = ({ stripeAPIService }) => {
|
|
|
89
88
|
urlFor: urlUtils.urlFor.bind(urlUtils),
|
|
90
89
|
stripeAPIService,
|
|
91
90
|
productRepository: membersApi.productRepository,
|
|
92
|
-
|
|
93
|
-
definitions:
|
|
94
|
-
values:
|
|
91
|
+
metafields: {
|
|
92
|
+
definitions: metafields.definitions,
|
|
93
|
+
values: metafields.values,
|
|
95
94
|
},
|
|
96
95
|
};
|
|
97
96
|
};
|
|
@@ -116,6 +115,66 @@ const initVerificationTrigger = () => {
|
|
|
116
115
|
});
|
|
117
116
|
};
|
|
118
117
|
|
|
118
|
+
const membersMigrationJobName = 'members-migrations';
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Runs the historical Stripe backfills once per site, retried only after a
|
|
122
|
+
* failed run.
|
|
123
|
+
*
|
|
124
|
+
* The `jobs` row named `members-migrations` is the only guard: any status other
|
|
125
|
+
* than `failed` means the run was already attempted and is skipped forever. The
|
|
126
|
+
* row is written after the run, so a boot that dies mid-way retries next time.
|
|
127
|
+
* The row is written even when Stripe is not configured, matching the job-based
|
|
128
|
+
* version: a site that connects Stripe later never runs these 2021-era backfills.
|
|
129
|
+
* Two processes booting a site with no row will both run the backfills, where the
|
|
130
|
+
* job-based version claimed the row first. Accepted: every site that has booted
|
|
131
|
+
* since Ghost 5.6 has the row, and the backfills do nothing without Stripe.
|
|
132
|
+
*
|
|
133
|
+
* @TODO: Delete the backfills, this runner and the `jobs` rows in the next major
|
|
134
|
+
*
|
|
135
|
+
* @param {import('../stripe')} stripeService
|
|
136
|
+
*/
|
|
137
|
+
async function runStripeMigrations(stripeService) {
|
|
138
|
+
const existingJob = await models.Job.findOne({ name: membersMigrationJobName });
|
|
139
|
+
|
|
140
|
+
if (existingJob && existingJob.get('status') !== 'failed') {
|
|
141
|
+
logging.info(`Stripe ${membersMigrationJobName} skipped because it has already run`);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const startedAt = Date.now();
|
|
146
|
+
logging.info(`Stripe ${membersMigrationJobName} started`);
|
|
147
|
+
|
|
148
|
+
let status = 'finished';
|
|
149
|
+
try {
|
|
150
|
+
await stripeService.migrations.execute();
|
|
151
|
+
logging.info(`Stripe ${membersMigrationJobName} completed in ${Date.now() - startedAt}ms`);
|
|
152
|
+
} catch (err) {
|
|
153
|
+
status = 'failed';
|
|
154
|
+
logging.error(
|
|
155
|
+
err,
|
|
156
|
+
`Stripe ${membersMigrationJobName} failed after ${Date.now() - startedAt}ms`,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const attrs = { status, started_at: new Date(startedAt), finished_at: new Date() };
|
|
161
|
+
if (existingJob) {
|
|
162
|
+
await models.Job.edit(attrs, { id: existingJob.id });
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
await models.Job.add({ name: membersMigrationJobName, ...attrs });
|
|
168
|
+
} catch (err) {
|
|
169
|
+
// Two processes booting a site with no row yet both get here, and the unique
|
|
170
|
+
// index on jobs.name rejects the second insert. The row exists, so move on.
|
|
171
|
+
if (!(await models.Job.findOne({ name: membersMigrationJobName }))) {
|
|
172
|
+
throw err;
|
|
173
|
+
}
|
|
174
|
+
logging.warn(`Stripe ${membersMigrationJobName} row was already written by another process`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
119
178
|
module.exports = {
|
|
120
179
|
async init() {
|
|
121
180
|
const stripeService = require('../stripe');
|
|
@@ -176,49 +235,14 @@ module.exports = {
|
|
|
176
235
|
membersCSVImporter.importInline(request, verificationTrigger);
|
|
177
236
|
|
|
178
237
|
// Constructed here rather than required statically: the exporter needs the
|
|
179
|
-
//
|
|
180
|
-
const
|
|
238
|
+
// metafields services, which boot builds before this one.
|
|
239
|
+
const metafields = require('../members-metafields');
|
|
181
240
|
module.exports.export = makeExporter({
|
|
182
|
-
definitions:
|
|
183
|
-
values:
|
|
241
|
+
definitions: metafields.definitions,
|
|
242
|
+
values: metafields.values,
|
|
184
243
|
});
|
|
185
244
|
|
|
186
|
-
|
|
187
|
-
const membersMigrationJobName = 'members-migrations';
|
|
188
|
-
if (!(await jobsService.hasExecutedSuccessfully(membersMigrationJobName))) {
|
|
189
|
-
logging.info(`[Background Job] ${membersMigrationJobName} queued`);
|
|
190
|
-
jobsService.addOneOffJob({
|
|
191
|
-
name: membersMigrationJobName,
|
|
192
|
-
offloaded: false,
|
|
193
|
-
job: async () => {
|
|
194
|
-
const startedAt = Date.now();
|
|
195
|
-
logging.info(`[Background Job] ${membersMigrationJobName} started`);
|
|
196
|
-
try {
|
|
197
|
-
const result = await stripeService.migrations.execute();
|
|
198
|
-
logging.info(
|
|
199
|
-
`[Background Job] ${membersMigrationJobName} completed in ${Date.now() - startedAt}ms`,
|
|
200
|
-
);
|
|
201
|
-
return result;
|
|
202
|
-
} catch (err) {
|
|
203
|
-
logging.error(
|
|
204
|
-
err,
|
|
205
|
-
`[Background Job] ${membersMigrationJobName} failed after ${Date.now() - startedAt}ms`,
|
|
206
|
-
);
|
|
207
|
-
throw err;
|
|
208
|
-
}
|
|
209
|
-
},
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
await jobsService.awaitOneOffCompletion(membersMigrationJobName);
|
|
213
|
-
} else {
|
|
214
|
-
logging.info(
|
|
215
|
-
`[Background Job] ${membersMigrationJobName} skipped because it has already run`,
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Schedule daily cron job to clean expired comp subs
|
|
221
|
-
memberJobs.scheduleExpiredCompCleanupJob();
|
|
245
|
+
await runStripeMigrations(stripeService);
|
|
222
246
|
},
|
|
223
247
|
contentGating: require('./content-gating'),
|
|
224
248
|
|
|
@@ -41,5 +41,14 @@ module.exports.formattedMemberResponse = function formattedMemberResponse(member
|
|
|
41
41
|
data.email_suppression = member.email_suppression;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
// Absent rather than empty on a site that has defined no extra fields, which is
|
|
45
|
+
// most of them, so those members' accounts read exactly as they did before this
|
|
46
|
+
// existed. Present but empty means the publisher has defined fields and this
|
|
47
|
+
// member has answered none, which a client renders as blank inputs rather than
|
|
48
|
+
// as nothing to fill in.
|
|
49
|
+
if (member.metafields) {
|
|
50
|
+
data.metafields = member.metafields;
|
|
51
|
+
}
|
|
52
|
+
|
|
44
53
|
return data;
|
|
45
54
|
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.INTERNAL = exports.MEMBERS = exports.ADMIN = void 0;
|
|
4
|
+
exports.readableFields = readableFields;
|
|
5
|
+
exports.canWrite = canWrite;
|
|
6
|
+
exports.ADMIN = { entry: 'admin' };
|
|
7
|
+
exports.MEMBERS = { entry: 'members' };
|
|
8
|
+
exports.INTERNAL = { entry: 'internal' };
|
|
9
|
+
/**
|
|
10
|
+
* Which of the site's fields this audience may see.
|
|
11
|
+
*
|
|
12
|
+
* Every field, whichever door they came through. Written as a function rather than
|
|
13
|
+
* left unwritten so that when a publisher can mark a field as staff only, one
|
|
14
|
+
* function changes and every caller is already asking.
|
|
15
|
+
*/
|
|
16
|
+
function readableFields(_audience, fields) {
|
|
17
|
+
return fields;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Whether this audience may write this field.
|
|
21
|
+
*
|
|
22
|
+
* Also total today. Kept separate from `readableFields` because the asymmetry to
|
|
23
|
+
* expect is a field a member may read but not change, which one combined
|
|
24
|
+
* permission could not express.
|
|
25
|
+
*/
|
|
26
|
+
function canWrite(_audience, _field) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
@@ -4,12 +4,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.actingContext = actingContext;
|
|
7
|
-
exports.
|
|
7
|
+
exports.recordMetafieldAction = recordMetafieldAction;
|
|
8
8
|
const logging_1 = __importDefault(require("@tryghost/logging"));
|
|
9
9
|
/**
|
|
10
10
|
* Who is acting, read off an API frame's context.
|
|
11
11
|
*
|
|
12
|
-
* Here rather than in each endpoint, because two resources now record
|
|
12
|
+
* Here rather than in each endpoint, because two resources now record metafield history
|
|
13
13
|
* and a second reading of the same shape is a second chance to disagree about it. An
|
|
14
14
|
* integration and a user are both actors; a request that is neither — a webhook, a job —
|
|
15
15
|
* has none, and the history says so rather than guessing.
|
|
@@ -36,7 +36,7 @@ const COMMANDS = {
|
|
|
36
36
|
delete: 'deleted',
|
|
37
37
|
};
|
|
38
38
|
// Best-effort action-log write: a failed action must never fail the command that triggered it.
|
|
39
|
-
async function
|
|
39
|
+
async function recordMetafieldAction({ Action, context, verb, subject, details, }) {
|
|
40
40
|
if (!context.actor) {
|
|
41
41
|
return;
|
|
42
42
|
}
|
|
@@ -55,12 +55,12 @@ async function recordCustomFieldAction({ Action, context, verb, subject, details
|
|
|
55
55
|
}
|
|
56
56
|
catch (err) {
|
|
57
57
|
logging_1.default.error({
|
|
58
|
-
event: { name: 'members.
|
|
58
|
+
event: { name: 'members.metafields.action_log_failed' },
|
|
59
59
|
err,
|
|
60
60
|
verb,
|
|
61
61
|
subject,
|
|
62
62
|
actorType: context.actor.type,
|
|
63
63
|
actorId: context.actor.id,
|
|
64
|
-
}, 'Failed to record a member
|
|
64
|
+
}, 'Failed to record a member metafield action');
|
|
65
65
|
}
|
|
66
66
|
}
|
|
@@ -0,0 +1,147 @@
|
|
|
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.MetafieldBindingsService = void 0;
|
|
7
|
+
const bson_objectid_1 = __importDefault(require("bson-objectid"));
|
|
8
|
+
const logging_1 = __importDefault(require("@tryghost/logging"));
|
|
9
|
+
const access_1 = require("./access");
|
|
10
|
+
const schema_1 = require("./schema");
|
|
11
|
+
const FIELDS_TABLE = 'members_metafields';
|
|
12
|
+
const { CUSTOM_NAMESPACE } = require('@tryghost/metafield-types/identity');
|
|
13
|
+
const BINDINGS_TABLE = 'members_metafield_bindings';
|
|
14
|
+
/**
|
|
15
|
+
* Where a source sends what it collected: a `port` is the name that source uses for a
|
|
16
|
+
* thing, and the binding resolves it to one of the publisher's fields.
|
|
17
|
+
*/
|
|
18
|
+
class MetafieldBindingsService {
|
|
19
|
+
knex;
|
|
20
|
+
values;
|
|
21
|
+
constructor({ knex, values }) {
|
|
22
|
+
this.knex = knex;
|
|
23
|
+
this.values = values;
|
|
24
|
+
}
|
|
25
|
+
async bind(db, productId, port, metafieldKey, now) {
|
|
26
|
+
const existing = await db(BINDINGS_TABLE).where({ product_id: productId, port }).first();
|
|
27
|
+
if (existing?.metafield_key === metafieldKey) {
|
|
28
|
+
await db(BINDINGS_TABLE).where('id', existing.id).update({ updated_at: now });
|
|
29
|
+
return existing.id;
|
|
30
|
+
}
|
|
31
|
+
if (existing) {
|
|
32
|
+
await db(BINDINGS_TABLE).where('id', existing.id).del();
|
|
33
|
+
}
|
|
34
|
+
const bindingId = new bson_objectid_1.default().toHexString();
|
|
35
|
+
await db(BINDINGS_TABLE).insert({
|
|
36
|
+
id: bindingId,
|
|
37
|
+
product_id: productId,
|
|
38
|
+
port,
|
|
39
|
+
metafield_key: metafieldKey,
|
|
40
|
+
created_at: now,
|
|
41
|
+
updated_at: now,
|
|
42
|
+
});
|
|
43
|
+
return bindingId;
|
|
44
|
+
}
|
|
45
|
+
/** Stops the writing. Whatever hangs off the binding cascades with it. */
|
|
46
|
+
async remove(db, productId, port) {
|
|
47
|
+
await db(BINDINGS_TABLE).where({ product_id: productId, port }).del();
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Values a processor collected on Ghost's behalf and sent back.
|
|
51
|
+
*
|
|
52
|
+
* Nobody supplied these in a sense the site can name — a payment page asked, and a
|
|
53
|
+
* machine reported the answers — so what is recorded against them is the binding that
|
|
54
|
+
* routed each one. That resolves back to the tier that asked, what it was collected
|
|
55
|
+
* as, and the field it landed in, which is everything worth knowing about how the
|
|
56
|
+
* value arrived.
|
|
57
|
+
*/
|
|
58
|
+
async writeCollected(memberId, productId, collected) {
|
|
59
|
+
return this.writeThrough(memberId, productId, collected, (binding) => ({
|
|
60
|
+
type: 'binding',
|
|
61
|
+
id: binding.bindingId,
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Values a member supplied about themselves, through the same ports a checkout uses.
|
|
66
|
+
*
|
|
67
|
+
* A member typing their own address into a form is answerable for it in a way no
|
|
68
|
+
* routing is, and a record saying a binding wrote it would be wrong. The member is the
|
|
69
|
+
* one whose record this is, so there is nobody else it could be.
|
|
70
|
+
*/
|
|
71
|
+
async writeSuppliedByMember(memberId, productId, supplied) {
|
|
72
|
+
return this.writeThrough(memberId, productId, supplied, () => ({
|
|
73
|
+
type: 'member',
|
|
74
|
+
id: memberId,
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Values arrive in the order they are to be applied: where two land in one field, the
|
|
79
|
+
* last of them is what the field holds.
|
|
80
|
+
*
|
|
81
|
+
* Every value is attempted, and then the first failure is raised. Attempting them all
|
|
82
|
+
* is this method's business: one refused answer must not cost a publisher the address
|
|
83
|
+
* a courier needs, and the values have nothing to do with each other beyond arriving
|
|
84
|
+
* together. Whether the failure is worth acting on is the caller's, which is why it
|
|
85
|
+
* leaves here rather than being logged and forgotten — a checkout webhook has already
|
|
86
|
+
* taken the money and must never fail, while a member filling in a form is owed the
|
|
87
|
+
* news.
|
|
88
|
+
*/
|
|
89
|
+
async writeThrough(memberId, productId, values, attribute) {
|
|
90
|
+
let failure;
|
|
91
|
+
for (const { port, value } of values) {
|
|
92
|
+
try {
|
|
93
|
+
// Inside, because working out where a value goes can fail the same way storing
|
|
94
|
+
// it can, and a value nobody could place is no more reason to abandon the rest
|
|
95
|
+
// than one nobody could store.
|
|
96
|
+
const destination = await this.resolve(productId, port);
|
|
97
|
+
if (!destination) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
await this.writeOne(memberId, destination, value, attribute);
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
failure = failure ?? err;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (failure) {
|
|
107
|
+
throw failure;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async writeOne(memberId, into, value, attribute) {
|
|
111
|
+
// Internal: what may be set is bounded by the binding rather than by who is
|
|
112
|
+
// looking. A port exists because a publisher pointed it at a field, and that
|
|
113
|
+
// decision is what admits the value, whoever supplied it.
|
|
114
|
+
const planned = await this.values.planWrite({ [`${CUSTOM_NAMESPACE}.${into.key}`]: value }, access_1.INTERNAL);
|
|
115
|
+
await this.values.applyWrite(memberId, planned, { writtenBy: attribute(into) });
|
|
116
|
+
}
|
|
117
|
+
async resolve(productId, port) {
|
|
118
|
+
const row = await this.knex(BINDINGS_TABLE)
|
|
119
|
+
.join(FIELDS_TABLE, `${BINDINGS_TABLE}.metafield_key`, `${FIELDS_TABLE}.key`)
|
|
120
|
+
.where(`${BINDINGS_TABLE}.product_id`, productId)
|
|
121
|
+
.where(`${BINDINGS_TABLE}.port`, port)
|
|
122
|
+
// An archived destination is still where this goes, and still not somewhere a value
|
|
123
|
+
// can land, so the write drops rather than waiting.
|
|
124
|
+
.where(`${FIELDS_TABLE}.status`, schema_1.FIELD_STATUS.active)
|
|
125
|
+
.select(`${BINDINGS_TABLE}.id as binding_id`, `${FIELDS_TABLE}.key`, `${FIELDS_TABLE}.type`)
|
|
126
|
+
.first();
|
|
127
|
+
if (!row) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
// Decoded rather than trusted: a join is a read boundary, and `type` is what decides
|
|
131
|
+
// how the collected value is read. Unreadable counts as unresolved rather than
|
|
132
|
+
// throwing, so one bad row skips its value the way an unbound port does instead of
|
|
133
|
+
// failing everything else the same checkout collected.
|
|
134
|
+
const bound = schema_1.DbBoundField.safeParse(row);
|
|
135
|
+
if (!bound.success) {
|
|
136
|
+
logging_1.default.warn({
|
|
137
|
+
event: { name: 'members.metafields.binding_unreadable' },
|
|
138
|
+
err: bound.error,
|
|
139
|
+
productId,
|
|
140
|
+
port,
|
|
141
|
+
}, 'A binding could not be read');
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
return { bindingId: bound.data.binding_id, key: bound.data.key, type: bound.data.type };
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
exports.MetafieldBindingsService = MetafieldBindingsService;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.metafieldCodec = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const identity_1 = require("@tryghost/metafield-types/identity");
|
|
6
|
+
const case_keys_1 = require("../../lib/case-keys");
|
|
7
|
+
const schema_1 = require("./schema");
|
|
8
|
+
const models_1 = require("./models");
|
|
9
|
+
exports.metafieldCodec = zod_1.z.codec(schema_1.DbMetafield, models_1.Metafield, {
|
|
10
|
+
// DbMetafield validates `type` as the field-type enum, so the decoded row
|
|
11
|
+
// already carries a FieldType and camelKeys preserves it — no cast needed.
|
|
12
|
+
decode: (row) => ({ ...(0, case_keys_1.camelKeys)(row), namespace: identity_1.CUSTOM_NAMESPACE }),
|
|
13
|
+
encode: ({ namespace: _namespace, ...field }) => (0, case_keys_1.snakeKeys)(field),
|
|
14
|
+
});
|
|
@@ -22,7 +22,7 @@ const Ceiling = zod_1.z
|
|
|
22
22
|
// rather than restated. Parsed eagerly because a missing or malformed value
|
|
23
23
|
// there is our bug rather than an operator's, and should fail loudly at boot
|
|
24
24
|
// instead of quietly becoming the fallback for everything below.
|
|
25
|
-
const shippedCeiling = Ceiling.parse(require('../../../shared/config/defaults.json').members.
|
|
25
|
+
const shippedCeiling = Ceiling.parse(require('../../../shared/config/defaults.json').members.metafields.maxDefinitions);
|
|
26
26
|
// A higher layer can override the shipped value with anything. Something
|
|
27
27
|
// unreadable falls back to the shipped ceiling rather than throwing, because an
|
|
28
28
|
// operator can correct this live and a bad value must not take the feature down
|