ghost 6.62.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/tryghost-adapter-base-jobs-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/content/themes/source/package.json +2 -2
- package/content/themes/source/partials/post-card.hbs +1 -1
- package/core/boot.js +7 -5
- package/core/built/admin/assets/{about-Dyae_UT6.js → about-C1C3s5-g.js} +1 -1
- package/core/built/admin/assets/accordion-CxGsDKwa.js +1 -0
- package/core/built/admin/assets/{account-migration-CESa0mnX.js → account-migration-CbUhW6gk.js} +1 -1
- package/core/built/admin/assets/activitypub/{account-migration-By69CgCp.js → account-migration-KA6Jjf9L.js} +7 -7
- package/core/built/admin/assets/activitypub/activitypub.js +3 -3
- 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-BJcPMrDd.js → bluesky-sharing-C7nTDMvw.js} +9 -9
- package/core/built/admin/assets/activitypub/{content-formatters-3rTHg3mp.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-DUR4xSkV.js → domain-fZ-X8i4-.js} +9 -9
- package/core/built/admin/assets/activitypub/{edit-profile-BqtfzxCp.js → edit-profile-BqMLQV88.js} +6 -6
- package/core/built/admin/assets/activitypub/{explore-BxuJd-J4.js → explore-B4yPoP8E.js} +10 -10
- package/core/built/admin/assets/activitypub/{feed-BM1TcksA.js → feed-BRC_mvAe.js} +9 -9
- package/core/built/admin/assets/activitypub/{field-Dn7HHx2-.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-CCjkuow2.js → inbox-BW-yvUoN.js} +10 -10
- package/core/built/admin/assets/activitypub/{inbox-d8oJRTf4.js → inbox-CbTTdDQ3.js} +2 -2
- package/core/built/admin/assets/activitypub/{label-1AA_s9Fa.js → label-kPMmYI7l.js} +3 -3
- package/core/built/admin/assets/activitypub/layout-BL4_jtuV.js +1 -0
- package/core/built/admin/assets/activitypub/{moderation-CnmxEB8w.js → moderation-CuidJzc0.js} +10 -10
- package/core/built/admin/assets/activitypub/{note-ZKc_Bnba.js → note-CAMRRSqD.js} +7 -7
- package/core/built/admin/assets/activitypub/{notifications-CB2tJZau.js → notifications-BmNlXGgp.js} +9 -9
- package/core/built/admin/assets/activitypub/{onboarding-BsJ9E5qf.js → onboarding-Bqg7fh_L.js} +2 -2
- package/core/built/admin/assets/activitypub/{onboarding-CSUJJIlh.js → onboarding-Dmun9jr_.js} +2 -2
- package/core/built/admin/assets/activitypub/{preferences-0-JZnUkI.js → preferences--oH19wf9.js} +11 -11
- package/core/built/admin/assets/activitypub/{profile-C35cFIhM.js → profile-DH_tQx2D.js} +14 -14
- package/core/built/admin/assets/activitypub/{reply-DYK5vVqb.js → reply-Cap9PxSH.js} +2 -2
- package/core/built/admin/assets/activitypub/{routes-BD4vscyc.js → routes-DdyY9uFF.js} +30 -30
- package/core/built/admin/assets/activitypub/{separator-BbQqTm-p.js → separator-CAxxu8VV.js} +3 -3
- package/core/built/admin/assets/activitypub/{settings-BsMROdqh.js → settings-B6EEcVkW.js} +6 -6
- package/core/built/admin/assets/activitypub/{site-O0RL9YCN.js → site-BYps63oh.js} +2 -2
- package/core/built/admin/assets/activitypub/{step-1-BMSx7c_X.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-CLlOZTyt.js → step-3-C-qBbcUo.js} +11 -11
- package/core/built/admin/assets/activitypub/{tabs-Dxk-b9M-.js → tabs-DLhZtVde.js} +6 -6
- package/core/built/admin/assets/activitypub/{topic-filter-5yZH6tId.js → topic-filter-DsUOuu0k.js} +3 -3
- package/core/built/admin/assets/activitypub/{use-activity-pub-queries-CV50qe03.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-Cbvzgjzd.js → use-reply-chain-data-AJi9zkzm.js} +5 -5
- package/core/built/admin/assets/activitypub/{users-BjcoP_HC.js → users-inweyKL5.js} +186 -160
- package/core/built/admin/assets/activitypub/{x-DbQZOLiI.js → x-DR8AT3-4.js} +2 -2
- package/core/built/admin/assets/{add-integration-modal-BlF6rs_f.js → add-integration-modal-BT_onDJy.js} +1 -1
- package/core/built/admin/assets/{add-newsletter-modal-CglVQqah.js → add-newsletter-modal-Cx1kQpiY.js} +1 -1
- package/core/built/admin/assets/{add-recommendation-modal-wz7BsuaG.js → add-recommendation-modal-B5nvAhvr.js} +1 -1
- package/core/built/admin/assets/{analytics-C4HE1gBg.js → analytics-DjoJWPI7.js} +1 -1
- package/core/built/admin/assets/{announcement-bar-modal-Ci4zz8jO.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-Ck-Yteiv.js → api-keys-BoeacM-s.js} +1 -1
- package/core/built/admin/assets/{audience-CtbA54Sn.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-BEtJ_gL_.js → bluesky-sharing-vzh-oPXL.js} +1 -1
- package/core/built/admin/assets/{breadcrumb-zUrZrBtW.js → breadcrumb-CaLk2b7D.js} +1 -1
- package/core/built/admin/assets/{button-eNMKQWXr.js → button-Dvcv1QSR.js} +1 -1
- package/core/built/admin/assets/calendar-Bl9RLcZX.js +1 -0
- package/core/built/admin/assets/{chart-helpers-CfBKt0vT.js → chart-helpers-FHywU0pj.js} +1 -1
- package/core/built/admin/assets/{checkbox-CgOn_tKW.js → checkbox-BtayFtjg.js} +1 -1
- package/core/built/admin/assets/chunk.254.035b85ec456ebbd7336b.js +440 -0
- package/core/built/admin/assets/{chunk.524.a697dee88db67fdf6021.js → chunk.524.e6975095fccdf22d90f8.js} +5 -5
- package/core/built/admin/assets/{chunk.582.5ed5d124cfca9c259b9c.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-K2wI-I5f.js → code-editor-view-C3ZfoF1X.js} +1 -1
- package/core/built/admin/assets/{code-editor-view-BYNoAoNV.js → code-editor-view-CAyRWy1s.js} +1 -1
- package/core/built/admin/assets/{color-picker-Ddw80xX6.js → color-picker-G4kGR7rp.js} +1 -1
- package/core/built/admin/assets/{color-picker-field-D9nUhvA7.js → color-picker-field-DQFVTNnT.js} +1 -1
- package/core/built/admin/assets/{combobox-w9j8PPQT.js → combobox-BnYzgEB4.js} +1 -1
- package/core/built/admin/assets/{command-BfpHORBv.js → command-C_vyTikI.js} +1 -1
- 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-CBM8G0Vj.js → confirmation-context-DwXX_Y5h.js} +1 -1
- package/core/built/admin/assets/{content-api-modal-TXRz3U7S.js → content-api-modal-D2G0nJP6.js} +1 -1
- package/core/built/admin/assets/{context-menu-_QMdd3kp.js → context-menu-DOVB6yaz.js} +1 -1
- package/core/built/admin/assets/{copy-field-6N01S07E.js → copy-field-DCJwh9qf.js} +1 -1
- package/core/built/admin/assets/{create-ghost-browse-value-source-qIBymvvf.js → create-ghost-browse-value-source-5C53jJaX.js} +1 -1
- package/core/built/admin/assets/custom-field-type-option-BAuW0F-E.js +1 -0
- package/core/built/admin/assets/{custom-integration-modal-DmNquxUV.js → custom-integration-modal-AbJGVyKc.js} +1 -1
- package/core/built/admin/assets/{date-range-select-uvz_DtPh.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-BZ1iytEd.js → dirty-confirm-dialog-DVPuGz3U.js} +1 -1
- package/core/built/admin/assets/{disabled-sources-indicator-DMRrxsiJ.js → disabled-sources-indicator-Cy7Zp4c8.js} +1 -1
- package/core/built/admin/assets/{domain-BNYrt0aD.js → domain-Dl61QGuk.js} +1 -1
- package/core/built/admin/assets/{dropzone-D7nFdAe5.js → dropzone-Dtz5Rmjt.js} +4 -4
- 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-Ds6mPEi5.js → embed-signup-form-modal-DGqsORxS.js} +1 -1
- package/core/built/admin/assets/{explore-UY10Egsp.js → explore-nnRr1Fja.js} +1 -1
- package/core/built/admin/assets/feature-flag-overrides-context-DQjdJFAk.js +1 -0
- package/core/built/admin/assets/{feature-image-placeholder-D1iF_8bc.js → feature-image-placeholder-BrIHmPs8.js} +1 -1
- package/core/built/admin/assets/{feed-81C8mJWR.js → feed-BNUcNu-W.js} +1 -1
- package/core/built/admin/assets/feedback-DJ4Zb9rZ.js +1 -0
- package/core/built/admin/assets/{field-Dihp5p18.js → field-C93_Mg6j.js} +1 -1
- package/core/built/admin/assets/filter-sources-CGJb4C5r.js +1 -0
- package/core/built/admin/assets/{filters-DirqNXjH.js → filters--kKP-EC8.js} +1 -1
- package/core/built/admin/assets/filters-DxA_a4FP.js +1 -0
- package/core/built/admin/assets/{first-promoter-modal-CT6MxYZb.js → first-promoter-modal-BYDtrz2S.js} +1 -1
- package/core/built/admin/assets/focus-koenig-editor-on-bottom-click-BbSnGOtK.js +1 -0
- package/core/built/admin/assets/{ghost-8eabfc1ece81b03cf7b90780d31487e8.css → ghost-8fd5bb799356bbefb254210ba7e1236d.css} +1 -1
- package/core/built/admin/assets/{ghost-c2848508292937415f20f5a830eea59e.js → ghost-b4e8a403baba61905cd7560e30b9732d.js} +43 -27
- package/core/built/admin/assets/{ghost-dark-a7bebe2c561f55369dcf29f4c820f7ff.css → ghost-dark-cac0cb2d5bb78e9bd14d8d1c2768ed45.css} +1 -1
- package/core/built/admin/assets/{gift-link-modal-CQVAye17.js → gift-link-modal-B7u5bD4e.js} +1 -1
- package/core/built/admin/assets/{gift-links-DLDS7ljO.js → gift-links-CfLSnst2.js} +1 -1
- package/core/built/admin/assets/growth-Cd1uO9d2.js +1 -0
- package/core/built/admin/assets/growth-aG32xfdE.js +3 -0
- package/core/built/admin/assets/{history-modal-CFd1Hvx6.js → history-modal-Df8lW8h-.js} +1 -1
- package/core/built/admin/assets/hooks-Cy_-oZ4o.js +2 -0
- package/core/built/admin/assets/{html-field-CJBCLTvv.js → html-field-DA4eU7eq.js} +1 -1
- package/core/built/admin/assets/{images-scfkWZiK.js → images-KXmyO3Ss.js} +1 -1
- package/core/built/admin/assets/{inbox-Mcp4DXZN.js → inbox-gJHG7_xd.js} +6 -6
- 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-CMhe2DvY.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-DfheMXs0.js → invite-user-modal-B4CuNcx4.js} +1 -1
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +2 -2
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +2 -2
- package/core/built/admin/assets/{koenig-lexical-DBSqAuiI.js → koenig-lexical-CGXb0F8o.js} +2 -2
- package/core/built/admin/assets/koenig-loader-BeM5rFPX.js +2 -0
- package/core/built/admin/assets/{kpi-card-DBRMfGOW.js → kpi-card-B21aToe8.js} +1 -1
- package/core/built/admin/assets/{kpi-card-DFafD1qq.js → kpi-card-pz6Wv4Ae.js} +1 -1
- package/core/built/admin/assets/{kpi-helpers-Cv8i8hns.js → kpi-helpers-Dq5jelmP.js} +1 -1
- package/core/built/admin/assets/{kpi-tabs-BJItvLYm.js → kpi-tabs-DEZHpKUt.js} +1 -1
- package/core/built/admin/assets/{label-BnSCvffu.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-DDQV6QeG.js → member-custom-fields-TRTHCAz9.js} +1 -1
- 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-AgCIEso4.js → moderation-ZWefD9uH.js} +1 -1
- package/core/built/admin/assets/{multi-select-combobox-BcITkUVr.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-DKzwafuh.js → newsletter-emails-DZH6I-PY.js} +1 -1
- package/core/built/admin/assets/{newsletters-BRBb_uQC.js → newsletters-DOC9KBcX.js} +1 -1
- package/core/built/admin/assets/newsletters-Rq1M3GD-.js +1 -0
- package/core/built/admin/assets/{note-DyGHtS9a.js → note-DIV-Pojr.js} +1 -1
- package/core/built/admin/assets/{notifications-ZgWniKna.js → notifications-s20-H49V.js} +1 -1
- package/core/built/admin/assets/{offers-9J1CSiLy.js → offers--e2nGvkW.js} +1 -1
- package/core/built/admin/assets/{offers-container-modal-B-E8TIWr.js → offers-container-modal-BmG5M5Tv.js} +1 -1
- package/core/built/admin/assets/onboarding-CUuy9unR.js +1 -0
- package/core/built/admin/assets/{onboarding-CzIsre-d.js → onboarding-pXyct0Jm.js} +1 -1
- package/core/built/admin/assets/{onboarding-route-CaavZulg.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-De0VzKBi.js → page-header-DM_KyoFI.js} +1 -1
- package/core/built/admin/assets/{pagemenu-B5KKCpbA.js → pagemenu-vgZVUVKR.js} +1 -1
- 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/{pintura-modal-BZ2J9w3n.js → pintura-modal-DExCF1ii.js} +1 -1
- package/core/built/admin/assets/{portal-frame-B6Hr9wHc.js → portal-frame-Cqanidq4.js} +1 -1
- 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-C5YJLp74.js → preferences--vNubsqD.js} +2 -2
- package/core/built/admin/assets/{preview-modal-BfeFqi5y.js → preview-modal-YYbzlYdO.js} +1 -1
- package/core/built/admin/assets/profile-BllKV3dR.js +1 -0
- package/core/built/admin/assets/radio-group-DL8GI8rt.js +1 -0
- 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-cb0t_TAJ.js → select-v5YbA3Mw.js} +1 -1
- package/core/built/admin/assets/settings-PEsZupw1.js +4 -0
- package/core/built/admin/assets/{settings-breadcrumbs-DLzbRRSt.js → settings-breadcrumbs-NwPnCTC7.js} +1 -1
- package/core/built/admin/assets/{settings-DZqxR0Da.js → settings-u_s1CRkr.js} +1 -1
- package/core/built/admin/assets/{share-modal-D2-Bz1_I.js → share-modal-SskCmVGL.js} +1 -1
- package/core/built/admin/assets/{slack-modal-DYDTUria.js → slack-modal-XxSJiYgo.js} +1 -1
- package/core/built/admin/assets/{sort-button-VVeQjjbu.js → sort-button-Cx1KtINE.js} +1 -1
- package/core/built/admin/assets/{source-icon-Ck5_5Kgq.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-CsaBjeN4.js → step-1-BQYzvBzr.js} +1 -1
- package/core/built/admin/assets/{step-2-D2vnxOOM.js → step-2-xES32xJV.js} +1 -1
- package/core/built/admin/assets/{step-3-wBoLdGEb.js → step-3-DA_XK5kW.js} +2 -2
- package/core/built/admin/assets/{stripe-button-CDgLz0S0.js → stripe-button-CVsLHelt.js} +1 -1
- package/core/built/admin/assets/{stripe-connect-modal-CSjMJetP.js → stripe-connect-modal-ClmLOMGS.js} +1 -1
- package/core/built/admin/assets/style-Wd9nQubI.css +1 -0
- package/core/built/admin/assets/{table-Do6iQ83Q.js → table-BenqVupI.js} +1 -1
- package/core/built/admin/assets/{tabs-D2yWLMp_.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-CtnZBJKl.js → testimonials-modal-9Q6fT06V.js} +1 -1
- 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-ByUJsxwD.js → toggle-group-CviIkyok.js} +1 -1
- package/core/built/admin/assets/{topic-filter-BgC3g5_v.js → topic-filter-I2jCBGxm.js} +1 -1
- package/core/built/admin/assets/{transistor-modal-Drr0SNfo.js → transistor-modal-p53u3W8y.js} +1 -1
- package/core/built/admin/assets/{trend-badge-6ye93YAW.js → trend-badge-CideVzQd.js} +1 -1
- package/core/built/admin/assets/{unsplash-modal-p6AoQoWp.js → unsplash-modal-DY8UcKcE.js} +1 -1
- package/core/built/admin/assets/{use-analytics-data-CWCMAESo.js → use-analytics-data-DAIGcr2f.js} +1 -1
- package/core/built/admin/assets/{use-check-theme-limit-error-Cw3WQHy8.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-DCVG3s9w.js → use-growth-stats-937CUj6j.js} +1 -1
- package/core/built/admin/assets/use-host-limits-D0Dv3tKA.js +1 -0
- package/core/built/admin/assets/{use-limiter-D2WRRx0H.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-BfN1UwzH.js → use-pintura-editor-CaV1mbfO.js} +1 -1
- package/core/built/admin/assets/{use-post-referrers-Czg47m8s.js → use-post-referrers-BCQI7fDg.js} +1 -1
- package/core/built/admin/assets/{use-reply-chain-data-ns0caK8k.js → use-reply-chain-data-CYRhoaxY.js} +1 -1
- package/core/built/admin/assets/{use-router-history-entry-uldn7zJO.js → use-router-history-entry-NOKcd--l.js} +1 -1
- package/core/built/admin/assets/{use-setting-group-DpcuCUDg.js → use-setting-group-bLKMwoBT.js} +1 -1
- package/core/built/admin/assets/{use-settings-navigation-CZjGrhsp.js → use-settings-navigation-B-qrriKD.js} +1 -1
- package/core/built/admin/assets/{use-simple-pagination-DzJWobPR.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-CSnGXzZh.js → use-tinybird-query-CmdbCS9X.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-token-DTOKRR7e.js → use-tinybird-token-KLne235s.js} +1 -1
- package/core/built/admin/assets/{use-unsaved-changes-guard-CBzs-KPi.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-DFr8lCbS.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-Cg-OeGzC.js → users-DVNWrqg9.js} +1 -1
- package/core/built/admin/assets/{vendor-5753232c7d6ffb3c44e1eb0a02133953.js → vendor-2dd263291938d407802568d3357ec69e.js} +19 -17
- package/core/built/admin/assets/{virtual-list-BW-padMe.js → virtual-list-CZ3RK2Yf.js} +1 -1
- package/core/built/admin/assets/{web-C1v33ehA.js → web-ClEJsCAj.js} +2 -2
- package/core/built/admin/assets/web-X9rdnrtq.js +1 -0
- package/core/built/admin/assets/{zapier-modal-BBFfI6Ml.js → zapier-modal-DfVhV17j.js} +1 -1
- package/core/built/admin/index.html +19 -18
- 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 +8 -0
- package/core/server/api/endpoints/member-metafields-members.js +23 -0
- package/core/server/api/endpoints/member-metafields.js +33 -20
- package/core/server/api/endpoints/members-account.js +42 -0
- 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/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 +16 -2
- 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 +4 -0
- package/core/server/api/endpoints/utils/serializers/output/member-metafields.js +3 -3
- 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/tiers.js +10 -2
- package/core/server/api/endpoints/utils/serializers/output/users.js +0 -10
- package/core/server/data/exporter/table-lists.js +3 -3
- 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/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 +6 -6
- package/core/server/models/user.js +1 -1
- 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/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 -0
- package/core/server/services/email-service/email-controller.js +18 -2
- package/core/server/services/email-service/email-service-wrapper.js +3 -0
- 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/gift-service.js +19 -57
- package/core/server/services/gifts/index.js +0 -2
- 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 +13 -2
- package/core/server/services/members/account-service.js +98 -0
- package/core/server/services/members/api.js +4 -4
- package/core/server/services/members/import-export/export/exporter.js +20 -20
- package/core/server/services/members/import-export/import/completion-email.js +6 -6
- package/core/server/services/members/import-export/import/importer.js +13 -13
- package/core/server/services/members/import-export/index.js +7 -7
- package/core/server/services/members/members-api/members-api.js +26 -6
- 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 +45 -39
- 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 +70 -42
- 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-custom-fields → members-metafields}/codec.js +4 -4
- 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 +17 -23
- package/core/server/services/{members-custom-fields → members-metafields}/filter.js +9 -9
- package/core/server/services/{members-custom-fields → members-metafields}/index.js +16 -7
- package/core/server/services/{members-custom-fields → members-metafields}/key.js +1 -1
- package/core/server/services/{members-custom-fields → members-metafields}/models.js +4 -4
- 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-custom-fields → members-metafields}/serializers.js +6 -6
- package/core/server/services/{members-custom-fields → members-metafields}/values-service.js +32 -22
- package/core/server/services/mentions/mention-sending-service.js +26 -9
- package/core/server/services/mentions/send-webmentions-job.js +18 -0
- package/core/server/services/mentions/service.js +1 -31
- package/core/server/services/stripe/services/webhook/checkout-session-event-service.js +2 -2
- package/core/server/services/stripe/stripe-service.js +4 -4
- package/core/server/services/tier-checkout-config/codec.js +2 -2
- package/core/server/services/tier-checkout-config/index.js +2 -1
- package/core/server/services/tier-checkout-config/models.js +2 -2
- package/core/server/services/tier-checkout-config/queries.js +8 -8
- package/core/server/services/tier-checkout-config/serializers.js +57 -0
- package/core/server/services/tier-checkout-config/service.js +1 -1
- package/core/server/services/tiers/service.js +3 -3
- package/core/server/services/url/router-filter.js +8 -10
- package/core/server/services/users.js +1 -19
- package/core/server/web/api/endpoints/admin/routes.js +1 -0
- 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/labs.js +4 -2
- package/package.json +22 -22
- package/pnpm-lock.yaml +513 -571
- package/pnpm-workspace.yaml +12 -12
- package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
- package/core/built/admin/assets/activitypub/layout-2-0bw6HN.js +0 -1
- package/core/built/admin/assets/automated-emails-D5VVMND9.js +0 -1
- package/core/built/admin/assets/automations-CzKt5fb8.js +0 -1
- package/core/built/admin/assets/chunk.598.7e056fabb47ce7dbd73f.js +0 -366
- package/core/built/admin/assets/comments-BLmCmyCs.js +0 -1
- package/core/built/admin/assets/custom-field-type-option-BddFlJFa.js +0 -1
- package/core/built/admin/assets/design-and-theme-modal-BSmo12Op.js +0 -2
- package/core/built/admin/assets/edit-profile-C5Asm4LA.js +0 -1
- package/core/built/admin/assets/editor-REokrzci.js +0 -8
- package/core/built/admin/assets/filter-sources-BO9xUPj7.js +0 -1
- package/core/built/admin/assets/filters-CRUWecfY.js +0 -1
- package/core/built/admin/assets/growth-BWIu-6xj.js +0 -3
- package/core/built/admin/assets/growth-DU29oxeA.js +0 -1
- package/core/built/admin/assets/hooks-CuEZV2-q.js +0 -2
- package/core/built/admin/assets/index-6MkPdCtr.js +0 -4
- package/core/built/admin/assets/index-Bn7QhcZu.css +0 -2
- package/core/built/admin/assets/index.es-CNnuNH0d.js +0 -1
- package/core/built/admin/assets/koenig-loader-aFQNlJlD.js +0 -2
- package/core/built/admin/assets/label-picker-KVoo91sh.js +0 -1
- package/core/built/admin/assets/layout-Bjv2Ll-5.js +0 -1
- package/core/built/admin/assets/link-helpers-MVDdnFCY.js +0 -1
- package/core/built/admin/assets/member-detail-CdJF6Agw.js +0 -1
- package/core/built/admin/assets/members-DjQbq9Z2.js +0 -4
- package/core/built/admin/assets/navigation-modal-BkT7b1M2.js +0 -1
- package/core/built/admin/assets/newsletter-BMMAuvgq.js +0 -1
- package/core/built/admin/assets/newsletter-detail-modal-0SCJ7Zpm.js +0 -1
- package/core/built/admin/assets/newsletters-DhNigJUh.js +0 -1
- package/core/built/admin/assets/onboarding-qg8Akovm.js +0 -1
- package/core/built/admin/assets/overview-BW8GLeYi.js +0 -1
- package/core/built/admin/assets/overview-BkXZNzaZ.js +0 -1
- package/core/built/admin/assets/pages-jgaFm69d.js +0 -1
- package/core/built/admin/assets/pages-route-CIUAQxtY.js +0 -1
- package/core/built/admin/assets/portal-modal-D4ceei3t.js +0 -1
- package/core/built/admin/assets/post-analytics-header-BYCsT0GP.js +0 -1
- package/core/built/admin/assets/post-analytics-ot7f6pih.js +0 -1
- package/core/built/admin/assets/post-analytics-provider-Bo1gmYaP.js +0 -1
- package/core/built/admin/assets/post-helpers-C5OQMBoi.js +0 -1
- package/core/built/admin/assets/post-share-modal-C-PaYZ_e.js +0 -1
- package/core/built/admin/assets/posts-DuGazgTb.js +0 -1
- package/core/built/admin/assets/posts-list-screen-CBGGgjjj.js +0 -2
- package/core/built/admin/assets/posts-route-D6xmS7Vf.js +0 -1
- package/core/built/admin/assets/profile-B8mx2yAu.js +0 -1
- package/core/built/admin/assets/radio-group-DczlLKOn.js +0 -1
- package/core/built/admin/assets/router-provider-B0gZyiGJ.js +0 -1
- package/core/built/admin/assets/schemas-DppqmpeJ.js +0 -64
- package/core/built/admin/assets/settings-DvfTNdHQ.js +0 -4
- package/core/built/admin/assets/stats-Rjmk5owg.js +0 -1
- package/core/built/admin/assets/stats-view-BGGmkmgv.js +0 -1
- package/core/built/admin/assets/tag-detail-BhXlOsCa.js +0 -2
- package/core/built/admin/assets/tags-ldukRApe.js +0 -1
- package/core/built/admin/assets/tier-detail-modal-BOs8e_G8.js +0 -1
- package/core/built/admin/assets/tiers-Nd_z4BZY.js +0 -1
- package/core/built/admin/assets/use-filter-params-BIni9taJ.js +0 -1
- package/core/built/admin/assets/use-host-limits-DZtR08oa.js +0 -1
- package/core/built/admin/assets/use-pintura-config-h-J0Lvta.js +0 -1
- package/core/built/admin/assets/use-staff-users-Csipb-O0.js +0 -1
- package/core/built/admin/assets/use-upgrade-route-DhRSo7at.js +0 -1
- package/core/built/admin/assets/user-detail-modal-B_iUClI6.js +0 -1
- package/core/built/admin/assets/web-C4BKzn2T.js +0 -1
- package/core/server/services/email-suppression-list/in-memory-email-suppression-list.js +0 -39
- package/core/server/services/members-custom-fields/bindings-service.js +0 -120
- /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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const errors = require('@tryghost/errors');
|
|
2
|
+
const { ADMIN } = require('../../../members-metafields');
|
|
2
3
|
const logging = require('@tryghost/logging');
|
|
3
4
|
const tpl = require('@tryghost/tpl');
|
|
4
5
|
const moment = require('moment');
|
|
@@ -7,9 +8,9 @@ const messages = {
|
|
|
7
8
|
stripeNotConnected: 'Missing Stripe connection.',
|
|
8
9
|
memberAlreadyExists: 'Member already exists.',
|
|
9
10
|
memberNotFound: 'Member not found.',
|
|
10
|
-
|
|
11
|
+
metafieldsOnAdd:
|
|
11
12
|
'Custom field values cannot be set while creating a member. Create the member, then set values with an edit.',
|
|
12
|
-
|
|
13
|
+
metafieldsWithoutWriter:
|
|
13
14
|
'Custom field values cannot be set by a request with no authenticated user or integration.',
|
|
14
15
|
};
|
|
15
16
|
|
|
@@ -47,8 +48,8 @@ module.exports = class MemberBREADService {
|
|
|
47
48
|
* @param {import('../../../settings-helpers/settings-helpers')} deps.settingsHelpers
|
|
48
49
|
* @param {import('./next-payment-calculator')} deps.nextPaymentCalculator
|
|
49
50
|
* @param {IGiftsModule} deps.giftService
|
|
50
|
-
* @param {import('../../../members-
|
|
51
|
-
* @param {import('../../../members-
|
|
51
|
+
* @param {import('../../../members-metafields/values-service').MetafieldValuesService} deps.metafieldValues Required: boot builds it before the members service
|
|
52
|
+
* @param {import('../../../members-metafields/definitions-service').MetafieldDefinitionsService} deps.metafieldDefinitions Required: boot builds it before the members service
|
|
52
53
|
*/
|
|
53
54
|
constructor({
|
|
54
55
|
memberRepository,
|
|
@@ -61,8 +62,8 @@ module.exports = class MemberBREADService {
|
|
|
61
62
|
nextPaymentCalculator,
|
|
62
63
|
commentsService,
|
|
63
64
|
giftService,
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
metafieldValues,
|
|
66
|
+
metafieldDefinitions,
|
|
66
67
|
}) {
|
|
67
68
|
this.offersAPI = offersAPI;
|
|
68
69
|
/** @private */
|
|
@@ -84,9 +85,9 @@ module.exports = class MemberBREADService {
|
|
|
84
85
|
/** @private */
|
|
85
86
|
this.giftService = giftService;
|
|
86
87
|
/** @private */
|
|
87
|
-
this.
|
|
88
|
+
this.metafieldValues = metafieldValues;
|
|
88
89
|
/** @private */
|
|
89
|
-
this.
|
|
90
|
+
this.metafieldDefinitions = metafieldDefinitions;
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
/**
|
|
@@ -102,12 +103,12 @@ module.exports = class MemberBREADService {
|
|
|
102
103
|
* @param {string[]} memberIds
|
|
103
104
|
* @returns {Promise<Map<string, Record<string, unknown>> | null>}
|
|
104
105
|
*/
|
|
105
|
-
async
|
|
106
|
-
if (!(await this.
|
|
106
|
+
async fetchMetafieldValues(memberIds, audience) {
|
|
107
|
+
if (!(await this.metafieldDefinitions.hasAnyActive())) {
|
|
107
108
|
return null;
|
|
108
109
|
}
|
|
109
110
|
|
|
110
|
-
return this.
|
|
111
|
+
return this.metafieldValues.getValuesForMembers(memberIds, audience);
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
/**
|
|
@@ -384,13 +385,15 @@ module.exports = class MemberBREADService {
|
|
|
384
385
|
/**
|
|
385
386
|
* @param {object} data
|
|
386
387
|
* @param {object} [options]
|
|
387
|
-
* @param {
|
|
388
|
-
*
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
388
|
+
* @param {import('../../../members-metafields').Audience | null} [options.metafieldsFor]
|
|
389
|
+
* Who the extra fields a publisher defined are being read for, or null to leave them
|
|
390
|
+
* off entirely. Null is not the same as "nobody may see them": it means this caller
|
|
391
|
+
* never shows them, so fetching them is two database queries whose results are thrown
|
|
392
|
+
* away. Ghost identifies a signed-in reader on every page view of a themed site
|
|
393
|
+
* through this method, and that caller renders a member through a fixed list of
|
|
394
|
+
* fields which has never included these.
|
|
392
395
|
*/
|
|
393
|
-
async read(data, {
|
|
396
|
+
async read(data, { metafieldsFor = ADMIN, ...options } = {}) {
|
|
394
397
|
const defaultWithRelated = [
|
|
395
398
|
'labels',
|
|
396
399
|
'stripeSubscriptions',
|
|
@@ -451,10 +454,10 @@ module.exports = class MemberBREADService {
|
|
|
451
454
|
const unsubscribeUrl = this.settingsHelpers.createUnsubscribeUrl(member.uuid);
|
|
452
455
|
member.unsubscribe_url = unsubscribeUrl;
|
|
453
456
|
|
|
454
|
-
if (
|
|
455
|
-
const
|
|
456
|
-
if (
|
|
457
|
-
member.metafields =
|
|
457
|
+
if (metafieldsFor) {
|
|
458
|
+
const metafields = await this.fetchMetafieldValues([member.id], metafieldsFor);
|
|
459
|
+
if (metafields) {
|
|
460
|
+
member.metafields = metafields.get(member.id) ?? {};
|
|
458
461
|
}
|
|
459
462
|
}
|
|
460
463
|
|
|
@@ -462,9 +465,9 @@ module.exports = class MemberBREADService {
|
|
|
462
465
|
}
|
|
463
466
|
|
|
464
467
|
async add(data, options) {
|
|
465
|
-
if (this.
|
|
468
|
+
if (this.metafieldValues.namesValues(this.metafieldValues.unwrapWire(data.metafields))) {
|
|
466
469
|
throw new errors.ValidationError({
|
|
467
|
-
message: tpl(messages.
|
|
470
|
+
message: tpl(messages.metafieldsOnAdd),
|
|
468
471
|
property: 'metafields',
|
|
469
472
|
});
|
|
470
473
|
}
|
|
@@ -565,16 +568,16 @@ module.exports = class MemberBREADService {
|
|
|
565
568
|
async edit(data, options) {
|
|
566
569
|
delete data.last_seen_at;
|
|
567
570
|
|
|
568
|
-
const
|
|
569
|
-
const
|
|
571
|
+
const metafields = this.metafieldValues.unwrapWire(data.metafields);
|
|
572
|
+
const writeMetafields = metafields !== undefined;
|
|
570
573
|
delete data.metafields;
|
|
571
574
|
|
|
572
575
|
// Plan (which validates) before the member is touched, so a bad value 422s
|
|
573
576
|
// here rather than after the member edit has been applied — and keep the
|
|
574
577
|
// plan to apply once below, so the values aren't resolved and validated
|
|
575
578
|
// twice.
|
|
576
|
-
const
|
|
577
|
-
? await this.
|
|
579
|
+
const plannedMetafields = writeMetafields
|
|
580
|
+
? await this.metafieldValues.planWrite(metafields, ADMIN)
|
|
578
581
|
: null;
|
|
579
582
|
|
|
580
583
|
let model;
|
|
@@ -627,7 +630,7 @@ module.exports = class MemberBREADService {
|
|
|
627
630
|
}
|
|
628
631
|
}
|
|
629
632
|
|
|
630
|
-
if (
|
|
633
|
+
if (plannedMetafields) {
|
|
631
634
|
// Every value reaching here was typed into the Admin API, so the writer is
|
|
632
635
|
// whoever made the request — the same pair the action log records, so the two
|
|
633
636
|
// agree about who did it rather than one saying only that it was "admin".
|
|
@@ -638,15 +641,15 @@ module.exports = class MemberBREADService {
|
|
|
638
641
|
const context = options.context || {};
|
|
639
642
|
if (!context.integration && !context.user) {
|
|
640
643
|
throw new errors.IncorrectUsageError({
|
|
641
|
-
message: tpl(messages.
|
|
644
|
+
message: tpl(messages.metafieldsWithoutWriter),
|
|
642
645
|
});
|
|
643
646
|
}
|
|
644
647
|
const writtenBy = context.integration
|
|
645
648
|
? { type: 'integration', id: context.integration.id }
|
|
646
649
|
: { type: 'user', id: context.user };
|
|
647
|
-
await this.
|
|
650
|
+
await this.metafieldValues.applyWrite(model.id, plannedMetafields, { writtenBy });
|
|
648
651
|
|
|
649
|
-
//
|
|
652
|
+
// Metafields aren't a member column or relation, so an edit touching
|
|
650
653
|
// only them leaves `model._changed` empty and the save fires nothing.
|
|
651
654
|
// Declare the change into `_changed` — as bookshelf-relations does for a
|
|
652
655
|
// labels change — so the member's edited lifecycle fires its usual signals
|
|
@@ -656,13 +659,13 @@ module.exports = class MemberBREADService {
|
|
|
656
659
|
// populated `_changed` and fired the edited event during update(), so
|
|
657
660
|
// re-firing would duplicate it (this also covers a full PUT that resends
|
|
658
661
|
// unchanged member fields — `_changed` stays empty there too). That
|
|
659
|
-
// combined event omits `
|
|
660
|
-
// reads:
|
|
662
|
+
// combined event omits `metafields` from `_changed`, which nothing
|
|
663
|
+
// reads: metafields aren't in the webhook payload (they're injected
|
|
661
664
|
// into read/browse responses, not the model), and `_changed` only gates
|
|
662
665
|
// whether the event fires.
|
|
663
666
|
const memberUnchanged = !model._changed || Object.keys(model._changed).length === 0;
|
|
664
|
-
if (memberUnchanged &&
|
|
665
|
-
model._changed = {
|
|
667
|
+
if (memberUnchanged && plannedMetafields.length > 0) {
|
|
668
|
+
model._changed = { metafields: true };
|
|
666
669
|
// A mixed edit keeps the generic label on purpose: relabelling the
|
|
667
670
|
// one action a member change already fired would bury that change
|
|
668
671
|
// behind this one.
|
|
@@ -791,8 +794,11 @@ module.exports = class MemberBREADService {
|
|
|
791
794
|
|
|
792
795
|
// One query for the whole page, not one per member. `null` when the flag
|
|
793
796
|
// is off or the caller didn't ask — the same truthiness guard read uses.
|
|
794
|
-
const
|
|
795
|
-
? await this.
|
|
797
|
+
const metafieldsByMember = options.includeMetafields
|
|
798
|
+
? await this.fetchMetafieldValues(
|
|
799
|
+
page.data.map((model) => model.id),
|
|
800
|
+
ADMIN,
|
|
801
|
+
)
|
|
796
802
|
: null;
|
|
797
803
|
|
|
798
804
|
const data = page.data.map((model, index) => {
|
|
@@ -804,8 +810,8 @@ module.exports = class MemberBREADService {
|
|
|
804
810
|
if (!originalWithRelated.includes('products')) {
|
|
805
811
|
delete member.products;
|
|
806
812
|
}
|
|
807
|
-
if (
|
|
808
|
-
member.metafields =
|
|
813
|
+
if (metafieldsByMember) {
|
|
814
|
+
member.metafields = metafieldsByMember.get(model.id) ?? {};
|
|
809
815
|
}
|
|
810
816
|
member.email_suppression = {
|
|
811
817
|
suppressed: bulkSuppressionData[index].suppressed || !!model.get('email_disabled'),
|
|
@@ -307,6 +307,24 @@ class MembersSSR {
|
|
|
307
307
|
*
|
|
308
308
|
* @returns {Promise<Member>}
|
|
309
309
|
*/
|
|
310
|
+
/**
|
|
311
|
+
* Who this session belongs to, without loading them.
|
|
312
|
+
*
|
|
313
|
+
* The counterpart to `getMemberDataFromSession`, for callers that need to know a
|
|
314
|
+
* member is there rather than what is on their record.
|
|
315
|
+
*
|
|
316
|
+
* @method getMemberIdentityFromSession
|
|
317
|
+
* @param {Request} req
|
|
318
|
+
* @param {Response} res
|
|
319
|
+
*
|
|
320
|
+
* @returns {Promise<{id: string, email: string} | null>}
|
|
321
|
+
*/
|
|
322
|
+
async getMemberIdentityFromSession(req, res) {
|
|
323
|
+
const transientId = this._getSessionCookies(req, res);
|
|
324
|
+
const api = await this._getMembersApi();
|
|
325
|
+
return api.getMemberIdentity(transientId);
|
|
326
|
+
}
|
|
327
|
+
|
|
310
328
|
async getMemberDataFromSession(req, res) {
|
|
311
329
|
const transientId = this._getSessionCookies(req, res);
|
|
312
330
|
const member = await this._getMemberIdentityDataFromTransientId(transientId);
|
|
@@ -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
|
};
|
|
@@ -49,9 +49,9 @@ let membersApi;
|
|
|
49
49
|
let verificationTrigger;
|
|
50
50
|
|
|
51
51
|
const buildImporterDeps = ({ stripeAPIService }) => {
|
|
52
|
-
// Required here, not statically: boot builds the
|
|
52
|
+
// Required here, not statically: boot builds the metafields services before this
|
|
53
53
|
// one (the exporter below relies on the same).
|
|
54
|
-
const
|
|
54
|
+
const metafields = require('../members-metafields');
|
|
55
55
|
return {
|
|
56
56
|
getTimezone: () => settingsCache.get('timezone'),
|
|
57
57
|
// A getter rather than a value because the threshold is an operator
|
|
@@ -88,9 +88,9 @@ const buildImporterDeps = ({ stripeAPIService }) => {
|
|
|
88
88
|
urlFor: urlUtils.urlFor.bind(urlUtils),
|
|
89
89
|
stripeAPIService,
|
|
90
90
|
productRepository: membersApi.productRepository,
|
|
91
|
-
|
|
92
|
-
definitions:
|
|
93
|
-
values:
|
|
91
|
+
metafields: {
|
|
92
|
+
definitions: metafields.definitions,
|
|
93
|
+
values: metafields.values,
|
|
94
94
|
},
|
|
95
95
|
};
|
|
96
96
|
};
|
|
@@ -115,6 +115,66 @@ const initVerificationTrigger = () => {
|
|
|
115
115
|
});
|
|
116
116
|
};
|
|
117
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
|
+
|
|
118
178
|
module.exports = {
|
|
119
179
|
async init() {
|
|
120
180
|
const stripeService = require('../stripe');
|
|
@@ -175,46 +235,14 @@ module.exports = {
|
|
|
175
235
|
membersCSVImporter.importInline(request, verificationTrigger);
|
|
176
236
|
|
|
177
237
|
// Constructed here rather than required statically: the exporter needs the
|
|
178
|
-
//
|
|
179
|
-
const
|
|
238
|
+
// metafields services, which boot builds before this one.
|
|
239
|
+
const metafields = require('../members-metafields');
|
|
180
240
|
module.exports.export = makeExporter({
|
|
181
|
-
definitions:
|
|
182
|
-
values:
|
|
241
|
+
definitions: metafields.definitions,
|
|
242
|
+
values: metafields.values,
|
|
183
243
|
});
|
|
184
244
|
|
|
185
|
-
|
|
186
|
-
const membersMigrationJobName = 'members-migrations';
|
|
187
|
-
if (!(await jobsService.hasExecutedSuccessfully(membersMigrationJobName))) {
|
|
188
|
-
logging.info(`[Background Job] ${membersMigrationJobName} queued`);
|
|
189
|
-
jobsService.addOneOffJob({
|
|
190
|
-
name: membersMigrationJobName,
|
|
191
|
-
offloaded: false,
|
|
192
|
-
job: async () => {
|
|
193
|
-
const startedAt = Date.now();
|
|
194
|
-
logging.info(`[Background Job] ${membersMigrationJobName} started`);
|
|
195
|
-
try {
|
|
196
|
-
const result = await stripeService.migrations.execute();
|
|
197
|
-
logging.info(
|
|
198
|
-
`[Background Job] ${membersMigrationJobName} completed in ${Date.now() - startedAt}ms`,
|
|
199
|
-
);
|
|
200
|
-
return result;
|
|
201
|
-
} catch (err) {
|
|
202
|
-
logging.error(
|
|
203
|
-
err,
|
|
204
|
-
`[Background Job] ${membersMigrationJobName} failed after ${Date.now() - startedAt}ms`,
|
|
205
|
-
);
|
|
206
|
-
throw err;
|
|
207
|
-
}
|
|
208
|
-
},
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
await jobsService.awaitOneOffCompletion(membersMigrationJobName);
|
|
212
|
-
} else {
|
|
213
|
-
logging.info(
|
|
214
|
-
`[Background Job] ${membersMigrationJobName} skipped because it has already run`,
|
|
215
|
-
);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
245
|
+
await runStripeMigrations(stripeService);
|
|
218
246
|
},
|
|
219
247
|
contentGating: require('./content-gating'),
|
|
220
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
|
+
}
|