ghost 6.53.0 → 6.54.1
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-scheduling-0.2.0.tgz +0 -0
- package/components/tryghost-adapter-base-sso-0.1.1.tgz +0 -0
- package/components/tryghost-admin-api-schema-0.0.0.tgz +0 -0
- package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
- package/components/tryghost-i18n-0.0.0.tgz +0 -0
- package/components/tryghost-kg-converters-1.2.3.tgz +0 -0
- package/components/tryghost-kg-default-nodes-2.1.3.tgz +0 -0
- package/components/tryghost-kg-lexical-html-renderer-1.4.3.tgz +0 -0
- package/core/boot.js +9 -6
- package/core/built/admin/assets/Combination-CJG7Flp-.js +41 -0
- package/core/built/admin/assets/Growth-BTP1LthE.js +3 -0
- package/core/built/admin/assets/Newsletters-CSEX0myc.js +1 -0
- package/core/built/admin/assets/Overview-BjACEsJF.js +1 -0
- package/core/built/admin/assets/{PolarAngleAxis-pciQ0MOy.js → PolarAngleAxis-BGjf7sBu.js} +1 -1
- package/core/built/admin/assets/{RadialBarChart-DWmw2PcX.js → RadialBarChart-zeFf9mBz.js} +1 -1
- package/core/built/admin/assets/Web-CxxxjCcv.js +2 -0
- package/core/built/admin/assets/account-migration-BklF2I_I.js +1 -0
- package/core/built/admin/assets/activitypub/{account-migration-BPZG37DJ.js → account-migration-CNovr6W-.js} +13 -13
- package/core/built/admin/assets/activitypub/activitypub.js +8 -8
- package/core/built/admin/assets/activitypub/{avatar-ZYco0ITb.js → avatar-B3DEDB1l.js} +10 -10
- package/core/built/admin/assets/activitypub/{avatar-flipboard-D541lc4F.js → avatar-flipboard-Cqrg0XMx.js} +3 -3
- package/core/built/admin/assets/activitypub/{bluesky-sharing-NOHUtHjl.js → bluesky-sharing-Ceslb1_M.js} +38 -37
- package/core/built/admin/assets/activitypub/{chunk-KS7C4IRE-B-GOFDPZ.js → chunk-KS7C4IRE-D4HEcEwi.js} +716 -710
- package/core/built/admin/assets/activitypub/{content-formatters-bwrp4iEO.js → content-formatters-BpQARh28.js} +229 -181
- package/core/built/admin/assets/activitypub/{copy-C7m7ovHw.js → copy-D9UWIAan.js} +2 -2
- package/core/built/admin/assets/activitypub/{dist-BqkDUQxJ2.js → dist-ByFmDSNP2.js} +64 -63
- package/core/built/admin/assets/activitypub/{dist-B5ira32t.js → dist-DHAjBcV3.js} +5 -5
- package/core/built/admin/assets/activitypub/{domain-iqE4YXwD.js → domain-C1qQvJ8C.js} +33 -33
- package/core/built/admin/assets/activitypub/{edit-profile-B8MFYh28.js → edit-profile-BcH-jEfM.js} +46 -45
- package/core/built/admin/assets/activitypub/{explore-BbdKodp3.js → explore-AxwBh9dI.js} +20 -20
- package/core/built/admin/assets/activitypub/{feed-Cl3A5owG.js → feed-BaOlbh7a.js} +15 -15
- package/core/built/admin/assets/activitypub/{field-DL6WyR-c.js → field-B7mYYQpN.js} +13 -13
- package/core/built/admin/assets/activitypub/{hash-6G3DzwL9.js → hash-BC9Q0N4c.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-YmhuBice.js → inbox-DQ4ZnC1m.js} +2 -2
- package/core/built/admin/assets/activitypub/inbox-vXez-5uJ.js +1221 -0
- package/core/built/admin/assets/activitypub/{label-DGbLjDvE.js → label-Chv5T8bw.js} +6 -6
- package/core/built/admin/assets/activitypub/layout-CziktJXo.js +1 -0
- package/core/built/admin/assets/activitypub/{moderation-qnBtk1zS.js → moderation-DtSM2i1I.js} +32 -31
- package/core/built/admin/assets/activitypub/{note-C7ZRoSJv.js → note-Dckp4S-2.js} +21 -21
- package/core/built/admin/assets/activitypub/{notifications-DiEA4b1o.js → notifications-CG5T22vg.js} +17 -17
- package/core/built/admin/assets/activitypub/{onboarding-_JVgEwgV.js → onboarding-BFDkzeR2.js} +2 -2
- package/core/built/admin/assets/activitypub/{onboarding-DG4zu3qM.js → onboarding-D6rcBdPe.js} +5 -5
- package/core/built/admin/assets/activitypub/{preferences-B3FtocnP.js → preferences-D_799jdl.js} +1607 -1606
- package/core/built/admin/assets/activitypub/{profile-D8ZJfYXB.js → profile-D2JfXBQl.js} +264 -263
- package/core/built/admin/assets/activitypub/{reply-C1vggenw.js → reply-CpdMeAO5.js} +2 -2
- package/core/built/admin/assets/activitypub/{routes-BOii-V9n.js → routes-CCHe2dQp.js} +1317 -1298
- package/core/built/admin/assets/activitypub/{separator-1gNokN5S.js → separator-BI1FLsYM.js} +4 -4
- package/core/built/admin/assets/activitypub/{settings--9Sb0nbl.js → settings-kx-9Enkz.js} +14 -14
- package/core/built/admin/assets/activitypub/{site-fkMmqv0V.js → site-x4_IjVJ0.js} +10 -10
- package/core/built/admin/assets/activitypub/{step-1-QhivSMw2.js → step-1-DIDg4aHb.js} +15 -15
- package/core/built/admin/assets/activitypub/{step-2-pMk9YwqD.js → step-2-BfGe8BYt.js} +7 -7
- package/core/built/admin/assets/activitypub/{step-3-DvmVC1fo.js → step-3-mE9spAqw.js} +12 -12
- package/core/built/admin/assets/activitypub/{tabs-Dc0FEEYD.js → tabs-CyPRQQ1N.js} +241 -237
- package/core/built/admin/assets/activitypub/{topic-filter-CuDyM5bD.js → topic-filter-DLHBZT93.js} +4 -4
- package/core/built/admin/assets/activitypub/use-activity-pub-queries-VzbN99u_.js +1957 -0
- package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-Drx1kdAK.js → use-navigate-with-base-path-CAwXTmqH.js} +165 -175
- package/core/built/admin/assets/activitypub/{use-reply-chain-data-BHkCHcW5.js → use-reply-chain-data-BAB6fNGt.js} +8 -8
- package/core/built/admin/assets/activitypub/users-BLeiOTHc.js +1464 -0
- package/core/built/admin/assets/activitypub/{x-BHCEe-c2.js → x-C8tAnXly.js} +2 -2
- package/core/built/admin/assets/{audience-adiFehTD.js → audience-X_XsZivI.js} +1 -1
- package/core/built/admin/assets/{automated-emails-UWM02CMr.js → automated-emails-of1MvVsv.js} +1 -1
- package/core/built/admin/assets/automations-B7-y3dXt.js +1 -0
- package/core/built/admin/assets/automations-DRFjW5fw.js +1 -0
- package/core/built/admin/assets/{avatar-flipboard-Lz3F9fNT.js → avatar-flipboard-C6Kj3Awu.js} +1 -1
- package/core/built/admin/assets/bluesky-sharing-BbbjWdzs.js +1 -0
- package/core/built/admin/assets/box-BWbhXh21.js +1 -0
- package/core/built/admin/assets/breadcrumb-B-M9Ip_2.js +1 -0
- package/core/built/admin/assets/card-DGoDJzrn.js +1 -0
- package/core/built/admin/assets/{chart-ChpW-NzQ.js → chart-BuRT2Y6p.js} +14 -14
- package/core/built/admin/assets/chart-helpers-xiSsW8r0.js +1 -0
- package/core/built/admin/assets/checkbox-EhJKHFdZ.js +1 -0
- package/core/built/admin/assets/{chunk.432.c2caa0b20f03e71d1a35.js → chunk.211.6f7328d63c41536451b3.js} +2025 -2819
- package/core/built/admin/assets/{chunk.432.c2caa0b20f03e71d1a35.js.LICENSE.txt → chunk.211.6f7328d63c41536451b3.js.LICENSE.txt} +1 -62
- package/core/built/admin/assets/{chunk.831.0703a12e49f51cec3f3a.js → chunk.361.3cdb584b973959e52ccc.js} +78 -85
- package/core/built/admin/assets/chunk.524.8365cd9280405c0f6216.js +25 -0
- package/core/built/admin/assets/chunk.582.61d078937e6102769f2c.js +27 -0
- package/core/built/admin/assets/code-editor-view-CZtJFGhR.js +1 -0
- package/core/built/admin/assets/comments-CvyXxOyN.js +1 -0
- package/core/built/admin/assets/container-BYz2Ed39.js +1 -0
- package/core/built/admin/assets/data-list-16rkJYYi.js +1 -0
- package/core/built/admin/assets/date-range-select-CTlyo8zW.js +1 -0
- package/core/built/admin/assets/disabled-sources-indicator-qs2GfLvl.js +1 -0
- package/core/built/admin/assets/{dist-DJ6c4_De.js → dist-2Tnz2Yfq.js} +1 -1
- package/core/built/admin/assets/{dist-CsekE4FL.js → dist-CQJY6SVY.js} +1 -1
- package/core/built/admin/assets/dist-CjY-f5Z1.js +1 -0
- package/core/built/admin/assets/dist-DGzPJ_bO.js +4 -0
- package/core/built/admin/assets/dist-DIC8yz5C.js +1 -0
- package/core/built/admin/assets/{dist-BbGrW05X.js → dist-DyViJwIy.js} +1 -1
- package/core/built/admin/assets/dist-Ndy-PuGN.js +1 -0
- package/core/built/admin/assets/dist-P_ohUlTi.js +1 -0
- package/core/built/admin/assets/domain-CPNXKVOV.js +1 -0
- package/core/built/admin/assets/dropzone-BmCXnw6J.js +5 -0
- package/core/built/admin/assets/edit-profile-SGrJve5M.js +1 -0
- package/core/built/admin/assets/editor-BOG2YPUm.js +8 -0
- package/core/built/admin/assets/empty-indicator-oX1u1Pkn.js +1 -0
- package/core/built/admin/assets/esm-CFX93H5M.js +2 -0
- package/core/built/admin/assets/explore-CUYLv9G9.js +1 -0
- package/core/built/admin/assets/feed-CPdJMttL.js +1 -0
- package/core/built/admin/assets/field-D0XISCK2.js +1 -0
- package/core/built/admin/assets/{filter-sources-v39ihhhV.js → filter-sources-Bieps75g.js} +1 -1
- package/core/built/admin/assets/filters-CuB-A7ma.js +1 -0
- package/core/built/admin/assets/filters-bXkkWro8.js +1 -0
- package/core/built/admin/assets/framework-provider-CmtnPXIT.js +4 -0
- package/core/built/admin/assets/{ghost-b57f077997b739a8ae9a70772dc016cb.js → ghost-05f9a7182ed56903dba05878a5b8422b.js} +46 -41
- package/core/built/admin/assets/ghost-dark-c8d0ab7f515b2010c173e23e9b32ea8a.css +1 -0
- package/core/built/admin/assets/gift-link-modal-su8MsRdg.js +1 -0
- package/core/built/admin/assets/{gift-links-CY3GwiVi.js → gift-links-nBxJjZiQ.js} +1 -1
- package/core/built/admin/assets/growth-Ba6S5FQX.js +1 -0
- package/core/built/admin/assets/hooks-BApwwBE0.js +2 -0
- package/core/built/admin/assets/{inbox-TEISAmth.js → inbox-Xx1CdnfO.js} +38 -38
- package/core/built/admin/assets/index-D8PbzuT9.css +1 -0
- package/core/built/admin/assets/index-ukW_0Uie.js +67 -0
- package/core/built/admin/assets/inline-EMAm3okk.js +1 -0
- package/core/built/admin/assets/input-group-CGOXcy4l.js +1 -0
- package/core/built/admin/assets/input-surface-BpAjZXY5.js +1 -0
- package/core/built/admin/assets/{react-dom-C6aoWSfl.js → jsx-runtime-DX3WBWVO.js} +1 -1
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +5 -2
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +2 -2
- package/core/built/admin/assets/{koenig-lexical-Di7b48ED.js → koenig-lexical-B61IALf5.js} +2 -2
- package/core/built/admin/assets/kpi-card-BlZaG1Lo.js +1 -0
- package/core/built/admin/assets/kpi-card-DCdlyaSS.js +1 -0
- package/core/built/admin/assets/kpi-helpers-DpAuS1Nb.js +1 -0
- package/core/built/admin/assets/kpi-tabs-Dme44bQS.js +1 -0
- package/core/built/admin/assets/label-dSs5aepE.js +1 -0
- package/core/built/admin/assets/label-picker-Uq91atex.js +1 -0
- package/core/built/admin/assets/{labels-GbxlXWtf.js → labels-Cyayfd-e.js} +1 -1
- package/core/built/admin/assets/layout-WUcz6tu1.js +1 -0
- package/core/built/admin/assets/limit-service-9fwD33lU.js +20 -0
- package/core/built/admin/assets/link-helpers-tOlxmuH5.js +1 -0
- package/core/built/admin/assets/list-page-B6ANtzkd.js +1 -0
- package/core/built/admin/assets/mailchimp-Ds_NQ04w.svg +9 -0
- package/core/built/admin/assets/member-custom-fields-NCzrMuba.js +1 -0
- package/core/built/admin/assets/member-detail-DPWDtYnN.js +1 -0
- package/core/built/admin/assets/members-B9rO7qr4.js +10 -0
- package/core/built/admin/assets/modals-CxRaan1J.js +31 -0
- package/core/built/admin/assets/moderation-D8zon60E.js +1 -0
- package/core/built/admin/assets/multi-select-combobox-1wYm3nle.js +1 -0
- package/core/built/admin/assets/newsletter-DbYMRxRS.js +1 -0
- package/core/built/admin/assets/{newsletters-DZm3yLj8.js → newsletters-D1jbtV-h.js} +1 -1
- package/core/built/admin/assets/note-PiWPnnrh.js +1 -0
- package/core/built/admin/assets/notifications-DEgqqB9G.js +1 -0
- package/core/built/admin/assets/nql-lang-B7faUf8D.js +19 -0
- package/core/built/admin/assets/{offers-D11QtPXX.js → offers-CrY6nSRR.js} +1 -1
- package/core/built/admin/assets/onboarding-route-BzF6HeVn.js +1 -0
- package/core/built/admin/assets/overview-DixLeIqY.js +1 -0
- package/core/built/admin/assets/page-header-op0eN36I.js +1 -0
- package/core/built/admin/assets/{pages-BnObVRrf.js → pages-BbNWFcGj.js} +1 -1
- package/core/built/admin/assets/pintura-CXOmq-5H.svg +15 -0
- package/core/built/admin/assets/post-analytics-WwRDEnrV.js +1 -0
- package/core/built/admin/assets/post-analytics-header-DsMoLx0R.js +1 -0
- package/core/built/admin/assets/{post-analytics-provider-DSfBCtnj.js → post-analytics-provider-BJCPLMIb.js} +1 -1
- package/core/built/admin/assets/post-share-modal-Dx-MEjhI.js +1 -0
- package/core/built/admin/assets/{posts-CwH8VJvB.js → posts-EWpZIjkr.js} +1 -1
- package/core/built/admin/assets/preferences-D7-j6A4C.js +5 -0
- package/core/built/admin/assets/profile-Dl69QPp0.js +1 -0
- package/core/built/admin/assets/{referrers-nxR52jPI.js → referrers-BCfFYSHc.js} +1 -1
- package/core/built/admin/assets/router-provider-C1R4AZ2o.js +1 -0
- package/core/built/admin/assets/select-C1qDZnW4.js +1 -0
- package/core/built/admin/assets/settings-BbmC_Iqq.js +4 -0
- package/core/built/admin/assets/settings-H6HxFgfI.js +1 -0
- package/core/built/admin/assets/shade-app-B3heOAfz-CGXQwWeO.js +3 -0
- package/core/built/admin/assets/share-modal-BJCadJyp.js +1 -0
- package/core/built/admin/assets/social-urls-BPL1nADp.js +8 -0
- package/core/built/admin/assets/sort-button-DyYlfYp7.js +1 -0
- package/core/built/admin/assets/source-icon-_ubRDbg_.js +1 -0
- package/core/built/admin/assets/stack-xiN0eQ6Z.js +1 -0
- package/core/built/admin/assets/stats-BQZMGMvn.js +1 -0
- package/core/built/admin/assets/stats-view-Doei-qCX.js +1 -0
- package/core/built/admin/assets/step-1-IKiLBPoC.js +1 -0
- package/core/built/admin/assets/step-2-jJ41PsDN.js +1 -0
- package/core/built/admin/assets/step-3-CaqFAEqf.js +2 -0
- package/core/built/admin/assets/switch-t-WyENU2.js +1 -0
- package/core/built/admin/assets/table-DxUL78jU.js +1 -0
- package/core/built/admin/assets/tabs-BQcJQ1ke.js +1 -0
- package/core/built/admin/assets/tags-CNujGwkT.js +1 -0
- package/core/built/admin/assets/text-DwtjwGkh.js +1 -0
- package/core/built/admin/assets/textarea-Bjccvh_E.js +1 -0
- package/core/built/admin/assets/{tiers-EghDzCKc.js → tiers-BgYLWRJv.js} +1 -1
- package/core/built/admin/assets/toggle-group-BeEpQ69p.js +1 -0
- package/core/built/admin/assets/topic-filter-Bb5MOD-1.js +1 -0
- package/core/built/admin/assets/{use-analytics-data-0l_OHtnR.js → use-analytics-data-Cds6XDjg.js} +1 -1
- package/core/built/admin/assets/use-filter-params-DjXkhO54.js +1 -0
- package/core/built/admin/assets/use-growth-stats-D8uf98-G.js +1 -0
- package/core/built/admin/assets/{use-post-referrers-DwI31Ril.js → use-post-referrers-CRtgyuXB.js} +1 -1
- package/core/built/admin/assets/use-reply-chain-data-DbKYX50v.js +1 -0
- package/core/built/admin/assets/use-simple-pagination-DC01kpo0.js +1 -0
- package/core/built/admin/assets/{use-tinybird-query-DvZBBMG7.js → use-tinybird-query-C7bQRJWw.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-token-CQtebkvU.js → use-tinybird-token-Az4tam7_.js} +1 -1
- package/core/built/admin/assets/{users-Pc8IRjk9.js → users-CcSCQFcQ.js} +1 -1
- package/core/built/admin/assets/virtual-list-Dhuh9fGx.js +1 -0
- package/core/built/admin/assets/web-LtHNiNBV.js +1 -0
- package/core/built/admin/index.html +22 -22
- package/core/frontend/apps/private-blogging/index.js +1 -1
- package/core/frontend/apps/private-blogging/lib/middleware.js +1 -1
- package/core/frontend/helpers/split.js +2 -1
- package/core/frontend/meta/asset-url.js +1 -1
- package/core/frontend/meta/author-image.js +1 -1
- package/core/frontend/meta/blog-logo.js +1 -1
- package/core/frontend/meta/canonical-url.js +1 -1
- package/core/frontend/meta/cover-image.js +1 -1
- package/core/frontend/meta/get-meta.js +1 -1
- package/core/frontend/meta/og-image.js +1 -1
- package/core/frontend/meta/paginated-url.js +1 -1
- package/core/frontend/meta/twitter-image.js +1 -1
- package/core/frontend/meta/url.js +1 -1
- package/core/frontend/services/assets-minification/admin-auth-assets.js +1 -1
- package/core/frontend/services/llms/handler.js +1 -1
- package/core/frontend/services/proxy.js +42 -3
- package/core/frontend/services/routing/collection-router.js +1 -1
- package/core/frontend/services/routing/controllers/email-post.js +1 -1
- package/core/frontend/services/routing/controllers/entry/markdown.js +1 -1
- package/core/frontend/services/routing/controllers/entry/markdown.ts +1 -1
- package/core/frontend/services/routing/controllers/entry.js +1 -1
- package/core/frontend/services/routing/controllers/entry.ts +1 -1
- package/core/frontend/services/routing/controllers/previews.js +6 -7
- package/core/frontend/services/routing/controllers/unsubscribe.js +7 -3
- package/core/frontend/services/routing/email-router.js +1 -1
- package/core/frontend/services/routing/events.js +12 -0
- package/core/frontend/services/routing/middleware/page-param.js +1 -1
- package/core/frontend/services/routing/parent-router.js +1 -1
- package/core/frontend/services/routing/preview-router.js +1 -1
- package/core/frontend/services/routing/router-manager.js +4 -8
- package/core/frontend/services/routing/rss-router.js +1 -1
- package/core/frontend/services/routing/static-pages-router.js +1 -1
- package/core/frontend/services/routing/static-routes-router.js +1 -1
- package/core/frontend/services/routing/taxonomy-router.js +1 -1
- package/core/frontend/services/rss/generate-feed.js +1 -1
- package/core/frontend/services/sitemap/base-site-map-generator.js +23 -4
- package/core/frontend/services/sitemap/site-map-index-generator.js +1 -1
- package/core/frontend/services/sitemap/site-map-manager.js +14 -8
- package/core/frontend/services/sitemap/utils.js +1 -1
- package/core/frontend/services/theme-engine/middleware/update-global-template-options.js +1 -1
- package/core/frontend/services/theme-engine/middleware/update-local-template-options.js +1 -1
- package/core/frontend/utils/images.js +1 -1
- package/core/frontend/web/middleware/admin-toolbar.js +1 -1
- package/core/frontend/web/middleware/handle-image-sizes.js +2 -2
- package/core/frontend/web/middleware/redirect-ghost-to-admin.js +1 -1
- package/core/frontend/web/routers/serve-favicon.js +1 -1
- package/core/frontend/web/routers/serve-public-file.js +1 -1
- package/core/frontend/web/site.js +5 -5
- package/core/server/adapters/cache/AdapterCacheMemoryTTL.js +2 -2
- package/core/server/adapters/cache/MemoryCache.js +2 -2
- package/core/server/adapters/cache/Redis.js +1 -1
- package/core/server/adapters/cache/index.js +1 -1
- package/core/server/adapters/lib/redis/AdapterCacheRedis.js +2 -2
- package/core/server/adapters/route-settings/FileStore.js +2 -8
- package/core/server/adapters/route-settings/FileStore.ts +1 -6
- package/core/server/adapters/route-settings/S3RouteSettingsStore.js +206 -0
- package/core/server/adapters/route-settings/S3RouteSettingsStore.ts +199 -0
- package/core/server/adapters/route-settings/utils.js +13 -0
- package/core/server/adapters/route-settings/utils.ts +7 -0
- package/core/server/adapters/scheduling/deps.d.ts +0 -2
- package/core/server/adapters/scheduling/error-capture.js +76 -0
- package/core/server/adapters/scheduling/error-capture.ts +91 -0
- package/core/server/adapters/scheduling/utils.js +18 -34
- package/core/server/adapters/scheduling/utils.ts +36 -0
- package/core/server/adapters/storage/LocalFilesStorage.js +13 -11
- package/core/server/adapters/storage/LocalFilesStorage.ts +24 -0
- package/core/server/adapters/storage/LocalImagesStorage.js +14 -12
- package/core/server/adapters/storage/LocalImagesStorage.ts +24 -0
- package/core/server/adapters/storage/LocalMediaStorage.js +13 -11
- package/core/server/adapters/storage/LocalMediaStorage.ts +24 -0
- package/core/server/adapters/storage/LocalStorageBase.js +107 -167
- package/core/server/adapters/storage/LocalStorageBase.ts +342 -0
- package/core/server/adapters/storage/S3Storage.js +2 -2
- package/core/server/adapters/storage/S3Storage.ts +1 -1
- package/core/server/adapters/storage/utils.js +36 -50
- package/core/server/adapters/storage/utils.ts +60 -0
- package/core/server/api/endpoints/authentication.js +1 -1
- package/core/server/api/endpoints/db.js +6 -0
- package/core/server/api/endpoints/featurebase.js +1 -1
- package/core/server/api/endpoints/files.js +2 -2
- package/core/server/api/endpoints/images.js +2 -2
- package/core/server/api/endpoints/libraries.d.ts +0 -1
- package/core/server/api/endpoints/media.js +8 -5
- package/core/server/api/endpoints/member-custom-fields.js +4 -1
- package/core/server/api/endpoints/member-custom-fields.ts +4 -1
- package/core/server/api/endpoints/members.js +25 -28
- package/core/server/api/endpoints/pages.js +6 -1
- package/core/server/api/endpoints/posts.js +28 -3
- package/core/server/api/endpoints/search-index-public.js +4 -3
- package/core/server/api/endpoints/search-index.js +5 -4
- package/core/server/api/endpoints/settings-public.js +1 -1
- package/core/server/api/endpoints/settings.js +3 -1
- package/core/server/api/endpoints/utils/serializers/input/utils/url.js +9 -1
- package/core/server/api/endpoints/utils/serializers/output/default.js +7 -0
- package/core/server/api/endpoints/utils/serializers/output/files.js +1 -1
- package/core/server/api/endpoints/utils/serializers/output/mappers/comments.js +4 -1
- package/core/server/api/endpoints/utils/serializers/output/media.js +1 -1
- package/core/server/api/endpoints/utils/serializers/output/member-custom-fields.js +7 -4
- package/core/server/api/endpoints/utils/serializers/output/member-custom-fields.ts +8 -4
- package/core/server/api/endpoints/utils/serializers/output/members.js +27 -106
- package/core/server/api/endpoints/utils/serializers/output/pages.js +7 -0
- package/core/server/api/endpoints/utils/serializers/output/posts.js +13 -0
- package/core/server/api/endpoints/utils/serializers/output/utils/url.js +1 -1
- package/core/server/api/endpoints/utils/validators/input/members.js +2 -52
- package/core/server/data/db/backup.js +1 -1
- package/core/server/data/exporter/export-filename.js +2 -1
- package/core/server/data/importer/handlers/image.js +3 -3
- package/core/server/data/importer/import-manager.js +6 -4
- package/core/server/data/importer/importers/content-file-importer.js +2 -2
- package/core/server/data/importer/importers/data/revue-subscriber-importer.js +6 -10
- package/core/server/data/importer/importers/json-to-html.js +24 -4
- package/core/server/data/migrations/utils/schema.js +4 -2
- package/core/server/data/migrations/versions/6.54/2026-07-19-16-59-00-add-content-import-permission.js +10 -0
- package/core/server/data/migrations/versions/6.54/2026-07-20-13-59-21-drop-email-tracked-sent-count-from-automation-action-revisions.js +7 -0
- package/core/server/data/migrations/versions/6.54/2026-07-21-01-58-20-add-automation-run-steps-poller-index.js +3 -0
- package/core/server/data/migrations/versions/6.54/2026-07-21-21-08-15-add-automated-email-recipients-mailgun-message-id-index.js +3 -0
- package/core/server/data/migrations/versions/6.54/2026-07-22-10-41-08-add-click-tracking-to-automated-email-recipients.js +13 -0
- package/core/server/data/migrations/versions/6.54/2026-07-22-15-10-30-add-email-clicked-count-to-automation-action-revisions.js +7 -0
- package/core/server/data/migrations/versions/6.54/2026-07-23-10-24-18-add-automation-action-revision-to-redirects.js +9 -0
- package/core/server/data/schema/commands.js +37 -3
- package/core/server/data/schema/fixtures/fixtures.json +7 -0
- package/core/server/data/schema/lib/default-index-name.js +20 -0
- package/core/server/data/schema/schema.js +40 -17
- package/core/server/data/seeders/data-generator.js +4 -2
- package/core/server/lib/image/image-size.js +7 -23
- package/core/server/lib/image/image-utils.js +16 -2
- package/core/server/lib/image/index.js +4 -4
- package/core/server/lib/lexical.js +3 -3
- package/core/server/lib/mobiledoc.js +2 -2
- package/core/server/lib/request-external.js +1 -1
- package/core/server/lib/validate-password.js +1 -1
- package/core/server/models/action.js +9 -0
- package/core/server/models/base/plugins/generate-slug.js +1 -1
- package/core/server/models/collection.js +1 -1
- package/core/server/models/custom-theme-setting.js +1 -1
- package/core/server/models/member.js +73 -0
- package/core/server/models/newsletter.js +1 -1
- package/core/server/models/post.js +19 -5
- package/core/server/models/posts-meta.js +1 -1
- package/core/server/models/redirect.js +1 -1
- package/core/server/models/settings.js +1 -1
- package/core/server/models/snippet.js +1 -1
- package/core/server/models/tag.js +1 -1
- package/core/server/models/user.js +47 -71
- package/core/server/models/welcome-email-automated-email.js +1 -1
- package/core/server/services/activitypub/activity-pub-service-wrapper.js +1 -1
- package/core/server/services/adapter-manager/adapter-manager.js +5 -1
- package/core/server/services/adapter-manager/adapter-manager.ts +7 -2
- package/core/server/services/adapter-manager/index.js +18 -15
- package/core/server/services/adapter-manager/index.ts +23 -17
- package/core/server/services/adapter-manager/utils.js +124 -14
- package/core/server/services/adapter-manager/utils.ts +130 -14
- package/core/server/services/api-version-compatibility/mw-version-rewrites.js +1 -1
- package/core/server/services/audience-feedback/index.js +1 -1
- package/core/server/services/auth/passwordreset.js +1 -1
- package/core/server/services/auth/session/express-session.js +1 -1
- package/core/server/services/auth/session/index.js +2 -2
- package/core/server/services/auth/session/session-service.js +29 -2
- package/core/server/services/automations/automations-api.js +8 -25
- package/core/server/services/automations/automations-api.ts +8 -37
- package/core/server/services/automations/automations-repository.ts +16 -0
- package/core/server/services/automations/database-automations-repository.js +37 -4
- package/core/server/services/automations/database-automations-repository.ts +41 -4
- package/core/server/services/automations/poll.js +16 -12
- package/core/server/services/automations/poll.ts +17 -15
- package/core/server/services/automations/service.js +1 -1
- package/core/server/services/automations/service.ts +3 -13
- package/core/server/services/automations/welcome-email-automation-poll.js +2 -1
- package/core/server/services/comments/comments-service-emails.js +4 -1
- package/core/server/services/comments/index.js +1 -1
- package/core/server/services/custom-redirects/index.js +2 -2
- package/core/server/services/email-analytics/email-analytics-service-wrapper.js +14 -13
- package/core/server/services/email-analytics/email-analytics-service.js +1 -1
- package/core/server/services/email-analytics/events/start-email-analytics-job-event.js +11 -15
- package/core/server/services/email-analytics/events/start-email-analytics-job-event.ts +15 -0
- package/core/server/services/email-analytics/index.js +2 -3
- package/core/server/services/email-analytics/index.ts +1 -2
- package/core/server/services/email-analytics/jobs/email-analytics-job-scheduler.js +1 -6
- package/core/server/services/email-analytics/jobs/email-analytics-job-scheduler.ts +0 -9
- package/core/server/services/email-analytics/jobs/fetch-latest/index.js +1 -1
- package/core/server/services/email-analytics/jobs/index.js +0 -2
- package/core/server/services/email-service/email-service-wrapper.js +1 -1
- package/core/server/services/email-service/email-service.js +20 -4
- package/core/server/services/explore-ping/explore-ping-service.js +24 -39
- package/core/server/services/explore-ping/explore-ping-service.ts +198 -0
- package/core/server/services/explore-ping/index.js +11 -11
- package/core/server/services/explore-ping/index.ts +42 -0
- package/core/server/services/gifts/gift-reminder-scheduler.js +1 -1
- package/core/server/services/gifts/gift-reminder-scheduler.ts +1 -1
- package/core/server/services/gifts/gift-service-wrapper.js +1 -1
- package/core/server/services/identity-tokens/identity-token-service-wrapper.js +1 -1
- package/core/server/services/indexnow-ping/constants.js +30 -0
- package/core/server/services/indexnow-ping/constants.ts +32 -0
- package/core/server/services/indexnow-ping/index.js +29 -253
- package/core/server/services/indexnow-ping/index.ts +37 -0
- package/core/server/services/indexnow-ping/indexnow-ping-service.js +229 -0
- package/core/server/services/indexnow-ping/indexnow-ping-service.ts +319 -0
- package/core/server/services/invites/index.js +1 -1
- package/core/server/services/lib/mailgun-client.js +3 -3
- package/core/server/services/link-redirection/index.js +2 -2
- package/core/server/services/link-redirection/link-redirect-repository.js +1 -1
- package/core/server/services/link-redirection/link-redirect.js +12 -13
- package/core/server/services/link-redirection/link-redirect.ts +22 -0
- package/core/server/services/link-redirection/link-redirects-service.js +1 -1
- package/core/server/services/link-tracking/full-post-link.js +2 -2
- package/core/server/services/link-tracking/index.js +1 -1
- package/core/server/services/mail/ghost-mailer.js +1 -1
- package/core/server/services/mail/index.js +1 -1
- package/core/server/services/media-inliner/external-media-inliner.js +2 -2
- package/core/server/services/media-inliner/service.js +4 -3
- package/core/server/services/member-attribution/index.js +1 -1
- package/core/server/services/member-welcome-emails/service.js +1 -1
- package/core/server/services/members/api.js +8 -2
- package/core/server/services/members/import-export/config.js +30 -0
- package/core/server/services/members/import-export/config.ts +34 -0
- package/core/server/services/members/import-export/csv/index.js +10 -0
- package/core/server/services/members/import-export/csv/index.ts +2 -0
- package/core/server/services/members/import-export/csv/parse.js +64 -0
- package/core/server/services/members/import-export/csv/parse.ts +69 -0
- package/core/server/services/members/import-export/csv/serialize.js +13 -0
- package/core/server/services/members/import-export/csv/serialize.ts +18 -0
- package/core/server/services/members/import-export/export/exporter.js +202 -0
- package/core/server/services/members/import-export/export/exporter.ts +325 -0
- package/core/server/services/members/import-export/import/completion-email.js +65 -0
- package/core/server/services/members/import-export/import/completion-email.ts +119 -0
- package/core/server/services/members/import-export/import/importer.js +276 -0
- package/core/server/services/members/import-export/import/importer.ts +430 -0
- package/core/server/services/members/import-export/import/reader.js +32 -0
- package/core/server/services/members/import-export/import/reader.ts +33 -0
- package/core/server/services/members/import-export/import/row.js +33 -0
- package/core/server/services/members/import-export/import/row.ts +52 -0
- package/core/server/services/members/import-export/import/spool.js +30 -0
- package/core/server/services/members/import-export/import/spool.ts +38 -0
- package/core/server/services/members/import-export/index.js +90 -0
- package/core/server/services/members/import-export/index.ts +118 -0
- package/core/server/services/members/members-api/controllers/router-controller.js +7 -0
- package/core/server/services/members/members-api/members-api.js +4 -2
- package/core/server/services/members/members-api/repositories/event-repository.js +13 -2
- package/core/server/services/members/members-api/services/member-bread-service.js +42 -19
- package/core/server/services/members/middleware.js +1 -1
- package/core/server/services/members/service.js +28 -17
- package/core/server/services/members/stripe-connect.js +1 -1
- package/core/server/services/members-custom-fields/actions.js +3 -0
- package/core/server/services/members-custom-fields/actions.ts +6 -2
- package/core/server/services/members-custom-fields/config.js +34 -0
- package/core/server/services/members-custom-fields/config.ts +38 -0
- package/core/server/services/members-custom-fields/definitions-service.js +156 -21
- package/core/server/services/members-custom-fields/definitions-service.ts +170 -25
- package/core/server/services/members-custom-fields/index.js +16 -3
- package/core/server/services/members-custom-fields/index.ts +17 -3
- package/core/server/services/members-custom-fields/values-service.js +56 -10
- package/core/server/services/members-custom-fields/values-service.ts +62 -10
- package/core/server/services/mentions/mention-sending-service.js +22 -2
- package/core/server/services/mentions/service.js +21 -5
- package/core/server/services/newsletters/index.js +1 -1
- package/core/server/services/oembed/oembed-service.js +122 -33
- package/core/server/services/oembed/service.js +4 -2
- package/core/server/services/offers/service.js +1 -1
- package/core/server/services/post-scheduling/index.js +4 -3
- package/core/server/services/post-scheduling/index.ts +4 -3
- package/core/server/services/post-scheduling/post-scheduling.js +1 -1
- package/core/server/services/post-scheduling/post-scheduling.ts +1 -1
- package/core/server/services/posts/post-email-handler.js +13 -6
- package/core/server/services/posts/post-scheduling.js +1 -1
- package/core/server/services/posts/posts-exporter.js +6 -4
- package/core/server/services/posts/posts-service-instance.js +1 -1
- package/core/server/services/posts/posts-service.js +4 -4
- package/core/server/services/posts-public/service.js +1 -1
- package/core/server/services/public-config/site.js +1 -1
- package/core/server/services/recommendations/recommendation-service-wrapper.js +1 -1
- package/core/server/services/recommendations/service/libraries.d.ts +0 -2
- package/core/server/services/route-settings/dynamic-routing-service.js +2 -3
- package/core/server/services/route-settings/index.js +1 -1
- package/core/server/services/route-settings/route-settings-parser.js +134 -124
- package/core/server/services/route-settings/route-settings-parser.ts +160 -125
- package/core/server/services/route-settings/validation-errors.js +178 -0
- package/core/server/services/route-settings/validation-errors.ts +196 -0
- package/core/server/services/settings/settings-service.js +2 -2
- package/core/server/services/settings-helpers/index.js +1 -1
- package/core/server/services/slack-notifications/service.js +1 -1
- package/core/server/services/slack-ping/constants.js +16 -0
- package/core/server/services/slack-ping/constants.ts +15 -0
- package/core/server/services/slack-ping/index.js +26 -204
- package/core/server/services/slack-ping/index.ts +35 -0
- package/core/server/services/slack-ping/slack-ping-service.js +214 -0
- package/core/server/services/slack-ping/slack-ping-service.ts +296 -0
- package/core/server/services/staff/index.js +1 -1
- package/core/server/services/stats/posts-stats-service.js +1 -1
- package/core/server/services/stats/service.js +1 -1
- package/core/server/services/stripe/service.js +1 -1
- package/core/server/services/tags-public/service.js +1 -1
- package/core/server/services/themes/storage.js +9 -0
- package/core/server/services/themes/theme-storage.js +1 -1
- package/core/server/services/themes/validate.js +1 -1
- package/core/server/services/update-check/index.js +1 -1
- package/core/server/services/url/lazy-url-service.js +66 -6
- package/core/server/services/url/lazy-url-service.ts +73 -6
- package/core/server/services/url/url-generator.js +1 -1
- package/core/server/services/url/url-service-facade.js +68 -3
- package/core/server/services/url/url-service-facade.ts +79 -4
- package/core/server/services/url/url-service.js +1 -1
- package/core/server/services/url/urls.js +1 -1
- package/core/server/services/webhooks/index.js +19 -2
- package/core/server/services/webhooks/listen.js +6 -11
- package/core/server/services/webhooks/payload.js +1 -3
- package/core/server/services/webhooks/serialize.js +16 -1
- package/core/server/web/admin/app.js +1 -1
- package/core/server/web/admin/middleware/redirect-admin-urls.js +1 -1
- package/core/server/web/api/endpoints/admin/routes.js +1 -0
- package/core/server/web/api/middleware/cors.js +1 -1
- package/core/server/web/api/middleware/upload.js +4 -0
- package/core/server/web/gift-preview/controller.js +1 -1
- package/core/server/web/shared/middleware/redirect-amp-urls.js +1 -1
- package/core/server/web/shared/middleware/uncapitalise.js +2 -2
- package/core/server/web/shared/middleware/url-redirects.js +1 -1
- package/core/shared/config/defaults.json +10 -2
- package/core/shared/config/overrides.json +4 -0
- package/core/shared/express.js +6 -1
- package/core/shared/labs.js +2 -1
- package/core/shared/url-utils.js +19 -15
- package/core/shared/url-utils.ts +20 -0
- package/ghost.js +0 -1
- package/package.json +30 -35
- package/pnpm-lock.yaml +681 -2789
- package/pnpm-workspace.yaml +31 -17
- package/components/tryghost-adapter-base-scheduling-0.0.0.tgz +0 -0
- package/components/tryghost-adapter-base-sso-0.0.0.tgz +0 -0
- package/core/built/admin/assets/Combination--5EZGaDs.js +0 -43
- package/core/built/admin/assets/Growth-C0NoKcHQ.js +0 -3
- package/core/built/admin/assets/Newsletters-Ed8kDjdy.js +0 -1
- package/core/built/admin/assets/Overview-CArPgRZt.js +0 -1
- package/core/built/admin/assets/Web-CjjYoVM3.js +0 -2
- package/core/built/admin/assets/account-migration-mfa5mNI9.js +0 -1
- package/core/built/admin/assets/activitypub/inbox-B3rjKMTv.js +0 -1221
- package/core/built/admin/assets/activitypub/layout-jcurg3rU.js +0 -1
- package/core/built/admin/assets/activitypub/use-activity-pub-queries-yqGRcnLn.js +0 -2569
- package/core/built/admin/assets/activitypub/users-CfyHIsTG.js +0 -1316
- package/core/built/admin/assets/automations-DkUnxNdv.js +0 -1
- package/core/built/admin/assets/automations-vjXofmV_.js +0 -1
- package/core/built/admin/assets/bluesky-sharing-T-pO3RcP.js +0 -1
- package/core/built/admin/assets/breadcrumb-CekPYTCn.js +0 -1
- package/core/built/admin/assets/button--i2uMDDn.js +0 -4
- package/core/built/admin/assets/card-CSWz_BXq.js +0 -1
- package/core/built/admin/assets/chart-helpers-C_4Ka_vT.js +0 -1
- package/core/built/admin/assets/chunk.524.8ce2c741473e2f0929dc.js +0 -25
- package/core/built/admin/assets/chunk.582.453459d6228d2955408b.js +0 -27
- package/core/built/admin/assets/code-editor-view-BfkwQto2.js +0 -1
- package/core/built/admin/assets/command-K75Jzs0G.js +0 -1
- package/core/built/admin/assets/comments-BEIi1DH-.js +0 -1
- package/core/built/admin/assets/container-BNXvlcjA.js +0 -1
- package/core/built/admin/assets/data-list-BmK5QMab.js +0 -1
- package/core/built/admin/assets/date-range-select-5sKzwbt7.js +0 -1
- package/core/built/admin/assets/disabled-sources-indicator-L_ssJTNr.js +0 -1
- package/core/built/admin/assets/dist-BBIJuyPg.js +0 -1
- package/core/built/admin/assets/dist-BHE0dp6o.js +0 -1
- package/core/built/admin/assets/dist-CT46_f_J.js +0 -1
- package/core/built/admin/assets/dist-DW0QXQO3.js +0 -1
- package/core/built/admin/assets/dist-DxFLceO5.js +0 -1
- package/core/built/admin/assets/dist-QcACAOAD.js +0 -1
- package/core/built/admin/assets/dist-duxRDcQu.js +0 -177
- package/core/built/admin/assets/domain-CLaPZ9Rc.js +0 -1
- package/core/built/admin/assets/dropzone-E0bSX4_a.js +0 -5
- package/core/built/admin/assets/edit-profile-DqNPKaA4.js +0 -1
- package/core/built/admin/assets/editor-CkLXQDRp.js +0 -8
- package/core/built/admin/assets/empty-indicator-BTtOlgIo.js +0 -1
- package/core/built/admin/assets/esm-TSfGW7nz.js +0 -2
- package/core/built/admin/assets/explore-BEstLxKr.js +0 -1
- package/core/built/admin/assets/feed-C58p2voG.js +0 -1
- package/core/built/admin/assets/field-BSbDUHPv.js +0 -1
- package/core/built/admin/assets/filters-BgYv8Gt7.js +0 -1
- package/core/built/admin/assets/filters-DvenFkmK.js +0 -1
- package/core/built/admin/assets/framework-provider-jQktxm9P.js +0 -4
- package/core/built/admin/assets/ghost-dark-8590dea6cd695ff90f27d262c4464538.css +0 -1
- package/core/built/admin/assets/gift-link-modal-BPjXkEFt.js +0 -1
- package/core/built/admin/assets/growth-D4JNiVTe.js +0 -1
- package/core/built/admin/assets/hooks-Cjxexi_I.js +0 -2
- package/core/built/admin/assets/index-BacwlcOy.js +0 -67
- package/core/built/admin/assets/index-DxRNHH0o.css +0 -1
- package/core/built/admin/assets/inline-C22rnPro.js +0 -1
- package/core/built/admin/assets/input-group-CKfrILLP.js +0 -1
- package/core/built/admin/assets/jsx-runtime-CtMZ-PAS.js +0 -1
- package/core/built/admin/assets/kpi-card-DZxQKAIb.js +0 -1
- package/core/built/admin/assets/kpi-card-FidQ5zg3.js +0 -1
- package/core/built/admin/assets/kpi-helpers-C5T8Vaf1.js +0 -1
- package/core/built/admin/assets/kpi-tabs-_VRK6hGV.js +0 -1
- package/core/built/admin/assets/label-Cza7hPgn.js +0 -1
- package/core/built/admin/assets/label-picker-DgC_zLaw.js +0 -1
- package/core/built/admin/assets/layout-BxsXnKFX.js +0 -1
- package/core/built/admin/assets/limit-service-CTurluJQ.js +0 -20
- package/core/built/admin/assets/link-helpers-C3Gqy2cz.js +0 -1
- package/core/built/admin/assets/list-page-BtqCnow_.js +0 -1
- package/core/built/admin/assets/member-custom-fields-9uXFBqSt.js +0 -1
- package/core/built/admin/assets/member-detail-Dq6ZDIxo.js +0 -1
- package/core/built/admin/assets/members-CUWD9jal.js +0 -10
- package/core/built/admin/assets/modals-CIdwHVga.js +0 -31
- package/core/built/admin/assets/moderation-DTt04UDA.js +0 -1
- package/core/built/admin/assets/newsletter-cC8vcc9h.js +0 -1
- package/core/built/admin/assets/note-DZU0IY9e.js +0 -1
- package/core/built/admin/assets/notifications-NoHpBHJ4.js +0 -1
- package/core/built/admin/assets/nql-lang-CvCSOQG3.js +0 -19
- package/core/built/admin/assets/onboarding-route-Deg2hNm8.js +0 -1
- package/core/built/admin/assets/overview-BQPt_9w3.js +0 -1
- package/core/built/admin/assets/page-header-D6bEqPo2.js +0 -1
- package/core/built/admin/assets/post-analytics-header-BPw_B2_D.js +0 -1
- package/core/built/admin/assets/post-analytics-kp2_--XO.js +0 -1
- package/core/built/admin/assets/post-share-modal-Cave3Z4W.js +0 -1
- package/core/built/admin/assets/preferences-C7h3j_uy.js +0 -5
- package/core/built/admin/assets/profile-B2x4q2oB.js +0 -1
- package/core/built/admin/assets/router-provider-5fsX1CYn.js +0 -1
- package/core/built/admin/assets/select-PhPjIBBT.js +0 -1
- package/core/built/admin/assets/settings-D5P9Gwgo.js +0 -6
- package/core/built/admin/assets/settings-zUOzQV5I.js +0 -1
- package/core/built/admin/assets/share-modal-EfpfbuWF.js +0 -1
- package/core/built/admin/assets/social-urls-DgAF08Xc.js +0 -18
- package/core/built/admin/assets/sort-button-WOQYJm2H.js +0 -1
- package/core/built/admin/assets/source-icon-hm8Cj-Gx.js +0 -1
- package/core/built/admin/assets/stats-CnQDb62m.js +0 -1
- package/core/built/admin/assets/stats-view-CCfTJgSe.js +0 -1
- package/core/built/admin/assets/step-1-BGlhY4DS.js +0 -1
- package/core/built/admin/assets/step-2-D3sDEyFH.js +0 -1
- package/core/built/admin/assets/step-3-CvZoUdsc.js +0 -2
- package/core/built/admin/assets/switch-CNjkOmwz.js +0 -1
- package/core/built/admin/assets/table-C5nZYPnW.js +0 -1
- package/core/built/admin/assets/tabs-dtOcExye.js +0 -1
- package/core/built/admin/assets/tags-Dhm0FB_g.js +0 -1
- package/core/built/admin/assets/textarea-CeKw3wmV.js +0 -1
- package/core/built/admin/assets/toggle-group-DfhZIMTW.js +0 -1
- package/core/built/admin/assets/topic-filter-Cxe1Il41.js +0 -1
- package/core/built/admin/assets/typeof-Ca-duI-z.js +0 -1
- package/core/built/admin/assets/use-filter-params-Bub6BqNy.js +0 -1
- package/core/built/admin/assets/use-growth-stats-BJORomv0.js +0 -1
- package/core/built/admin/assets/use-reply-chain-data-B89YjVoo.js +0 -1
- package/core/built/admin/assets/use-simple-pagination-BL2SYXKi.js +0 -1
- package/core/built/admin/assets/virtual-list-DNJOSK9I.js +0 -1
- package/core/built/admin/assets/web-BHUE3YQc.js +0 -1
- package/core/server/adapters/scheduling/scheduling-base.js +0 -5
- package/core/server/adapters/sso/SSOBase.js +0 -5
- package/core/server/adapters/storage/index.js +0 -17
- package/core/server/services/mail/templates/newsletter.html +0 -876
- package/core/server/services/members/exporter/query.js +0 -206
- package/core/server/services/members/importer/index.js +0 -30
- package/core/server/services/members/importer/labels.js +0 -125
- package/core/server/services/members/importer/members-csv-importer.js +0 -502
- /package/core/server/services/members/{importer → import-export/import}/email-template.js +0 -0
- /package/core/server/services/members/{importer/members-csv-importer-stripe-utils.js → import-export/import/stripe-utils.js} +0 -0
|
@@ -0,0 +1,90 @@
|
|
|
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.makeImporter = makeImporter;
|
|
7
|
+
exports.makeExporter = makeExporter;
|
|
8
|
+
const importer_1 = __importDefault(require("./import/importer"));
|
|
9
|
+
const reader_1 = __importDefault(require("./import/reader"));
|
|
10
|
+
const spool_1 = require("./import/spool");
|
|
11
|
+
const exporter_1 = __importDefault(require("./export/exporter"));
|
|
12
|
+
const MembersCSVImporterStripeUtils = require('./import/stripe-utils');
|
|
13
|
+
const db = require('../../../data/db');
|
|
14
|
+
const models = require('../../../models');
|
|
15
|
+
const labs = require('../../../../shared/labs');
|
|
16
|
+
// Build the members CSV importer. This is the composition root: today's models and
|
|
17
|
+
// services are wired behind the collaborators the importer declares, one per
|
|
18
|
+
// concern, so nothing Bookshelf-shaped leaks into the import service itself.
|
|
19
|
+
function makeImporter(deps) {
|
|
20
|
+
// The members repository resolves asynchronously and is stable once ready, so
|
|
21
|
+
// cache the promise and reuse it across every call the import makes.
|
|
22
|
+
let membersRepositoryPromise;
|
|
23
|
+
const getMembersRepository = () => (membersRepositoryPromise ??= deps.getMembersRepository());
|
|
24
|
+
// The members aggregate, plus the import label lookup folded in so the label
|
|
25
|
+
// (member-tagging data) does not need a source of its own.
|
|
26
|
+
const members = {
|
|
27
|
+
get: async (query, options) => (await getMembersRepository()).get(query, options),
|
|
28
|
+
create: async (values, options) => (await getMembersRepository()).create(values, options),
|
|
29
|
+
update: async (values, options) => (await getMembersRepository()).update(values, options),
|
|
30
|
+
getCustomerIdByEmail: async (email) => (await getMembersRepository()).getCustomerIdByEmail(email),
|
|
31
|
+
linkStripeCustomer: async (link, options) => (await getMembersRepository()).linkStripeCustomer(link, options),
|
|
32
|
+
getImportLabel: name => models.Label.findOne({ name })
|
|
33
|
+
};
|
|
34
|
+
// The completion email: its recipient, links and delivery in one collaborator.
|
|
35
|
+
const email = {
|
|
36
|
+
send: deps.sendEmail,
|
|
37
|
+
getDefaultRecipient: async () => (await models.User.getOwnerUser()).get('email'),
|
|
38
|
+
urlFor: deps.urlFor
|
|
39
|
+
};
|
|
40
|
+
// Gifts is initialised at boot and always present at request time; the getter
|
|
41
|
+
// resolves it lazily so the ready service is picked up whenever a row uses it.
|
|
42
|
+
const gifts = {
|
|
43
|
+
reassignRedeemer: (giftId, memberId, options) => deps.getGiftService().reassignRedeemer(giftId, memberId, options)
|
|
44
|
+
};
|
|
45
|
+
return new importer_1.default({
|
|
46
|
+
knex: deps.knex,
|
|
47
|
+
readRows: reader_1.default,
|
|
48
|
+
spool: (0, spool_1.createRowSpool)(),
|
|
49
|
+
members,
|
|
50
|
+
tiers: {
|
|
51
|
+
getDefault: deps.getDefaultTier,
|
|
52
|
+
getByName: deps.getTierByName
|
|
53
|
+
},
|
|
54
|
+
stripe: new MembersCSVImporterStripeUtils({
|
|
55
|
+
stripeAPIService: deps.stripeAPIService,
|
|
56
|
+
productRepository: deps.productRepository
|
|
57
|
+
}),
|
|
58
|
+
gifts,
|
|
59
|
+
email,
|
|
60
|
+
addJob: deps.addJob,
|
|
61
|
+
getTimezone: deps.getTimezone,
|
|
62
|
+
getInlineThreshold: deps.getInlineThreshold
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
// Build the members CSV exporter. The same composition root from the other direction:
|
|
66
|
+
// knex and the members id lookup are wired here, and the custom fields definitions and
|
|
67
|
+
// values services are injected (boot builds them before this one). The labs flag alone
|
|
68
|
+
// decides whether custom field columns appear, so nothing flag-shaped leaks into the
|
|
69
|
+
// exporter itself.
|
|
70
|
+
function makeExporter({ definitions, values }) {
|
|
71
|
+
const exporter = new exporter_1.default({
|
|
72
|
+
knex: db.knex,
|
|
73
|
+
members: {
|
|
74
|
+
// Minimal query, only to fetch the ids of the filtered members; the stream
|
|
75
|
+
// reads their related data itself.
|
|
76
|
+
findFilteredIds: async (options) => {
|
|
77
|
+
const page = await models.Member.findPage({ ...options, withRelated: [], columns: ['id'], limit: 'all' });
|
|
78
|
+
return page.data.map((member) => member.id);
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
customFields: {
|
|
82
|
+
// Boot builds the definitions and values services before this one, so they
|
|
83
|
+
// are always present -- no not-initialised state to guard. The flag decides
|
|
84
|
+
// whether their columns are included at all.
|
|
85
|
+
activeDefinitions: async () => (labs.isSet('membersCustomFields') ? definitions.browse() : []),
|
|
86
|
+
valuesForMembers: memberIds => values.getValuesForMembers(memberIds)
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
return (options = {}) => exporter.export(options);
|
|
90
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type {Knex} from 'knex';
|
|
2
|
+
import MembersCSVImporter, {type MembersRepository, type GiftService, type EmailNotifications, type Tier} from './import/importer';
|
|
3
|
+
import readMemberRows from './import/reader';
|
|
4
|
+
import {createRowSpool} from './import/spool';
|
|
5
|
+
import MembersCSVExporter, {type ExportOptions, type CustomFieldDefinition} from './export/exporter';
|
|
6
|
+
|
|
7
|
+
const MembersCSVImporterStripeUtils = require('./import/stripe-utils');
|
|
8
|
+
const db = require('../../../data/db');
|
|
9
|
+
const models = require('../../../models');
|
|
10
|
+
const labs = require('../../../../shared/labs');
|
|
11
|
+
|
|
12
|
+
// The raw collaborators the members service hands the import composition root, before
|
|
13
|
+
// they are adapted into the ports the importer declares. The members repository is the
|
|
14
|
+
// importer's aggregate minus the import-label lookup, which the root folds in itself.
|
|
15
|
+
interface ImporterServices {
|
|
16
|
+
knex: Knex;
|
|
17
|
+
getMembersRepository(): Promise<Omit<MembersRepository, 'getImportLabel'>>;
|
|
18
|
+
getDefaultTier(): Promise<Tier>;
|
|
19
|
+
getTierByName(name: string): Promise<Tier | null>;
|
|
20
|
+
getGiftService(): GiftService;
|
|
21
|
+
sendEmail: EmailNotifications['send'];
|
|
22
|
+
urlFor: EmailNotifications['urlFor'];
|
|
23
|
+
addJob(job: {job: () => Promise<void>; offloaded: boolean; name: string}): void;
|
|
24
|
+
getTimezone(): string;
|
|
25
|
+
getInlineThreshold(): number;
|
|
26
|
+
stripeAPIService: unknown;
|
|
27
|
+
productRepository: unknown;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// The custom fields services the members service hands the export composition root.
|
|
31
|
+
interface CustomFieldsServices {
|
|
32
|
+
definitions: {browse(): Promise<CustomFieldDefinition[]>};
|
|
33
|
+
values: {getValuesForMembers(memberIds: string[]): Promise<Map<string, Record<string, unknown>>>};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Build the members CSV importer. This is the composition root: today's models and
|
|
37
|
+
// services are wired behind the collaborators the importer declares, one per
|
|
38
|
+
// concern, so nothing Bookshelf-shaped leaks into the import service itself.
|
|
39
|
+
export function makeImporter(deps: ImporterServices) {
|
|
40
|
+
// The members repository resolves asynchronously and is stable once ready, so
|
|
41
|
+
// cache the promise and reuse it across every call the import makes.
|
|
42
|
+
let membersRepositoryPromise: Promise<Omit<MembersRepository, 'getImportLabel'>> | undefined;
|
|
43
|
+
const getMembersRepository = () => (membersRepositoryPromise ??= deps.getMembersRepository());
|
|
44
|
+
|
|
45
|
+
// The members aggregate, plus the import label lookup folded in so the label
|
|
46
|
+
// (member-tagging data) does not need a source of its own.
|
|
47
|
+
const members: MembersRepository = {
|
|
48
|
+
get: async (query, options) => (await getMembersRepository()).get(query, options),
|
|
49
|
+
create: async (values, options) => (await getMembersRepository()).create(values, options),
|
|
50
|
+
update: async (values, options) => (await getMembersRepository()).update(values, options),
|
|
51
|
+
getCustomerIdByEmail: async email => (await getMembersRepository()).getCustomerIdByEmail(email),
|
|
52
|
+
linkStripeCustomer: async (link, options) => (await getMembersRepository()).linkStripeCustomer(link, options),
|
|
53
|
+
getImportLabel: name => models.Label.findOne({name})
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// The completion email: its recipient, links and delivery in one collaborator.
|
|
57
|
+
const email: EmailNotifications = {
|
|
58
|
+
send: deps.sendEmail,
|
|
59
|
+
getDefaultRecipient: async () => (await models.User.getOwnerUser()).get('email'),
|
|
60
|
+
urlFor: deps.urlFor
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Gifts is initialised at boot and always present at request time; the getter
|
|
64
|
+
// resolves it lazily so the ready service is picked up whenever a row uses it.
|
|
65
|
+
const gifts: GiftService = {
|
|
66
|
+
reassignRedeemer: (giftId, memberId, options) => deps.getGiftService().reassignRedeemer(giftId, memberId, options)
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return new MembersCSVImporter({
|
|
70
|
+
knex: deps.knex,
|
|
71
|
+
readRows: readMemberRows,
|
|
72
|
+
spool: createRowSpool(),
|
|
73
|
+
members,
|
|
74
|
+
tiers: {
|
|
75
|
+
getDefault: deps.getDefaultTier,
|
|
76
|
+
getByName: deps.getTierByName
|
|
77
|
+
},
|
|
78
|
+
stripe: new MembersCSVImporterStripeUtils({
|
|
79
|
+
stripeAPIService: deps.stripeAPIService,
|
|
80
|
+
productRepository: deps.productRepository
|
|
81
|
+
}),
|
|
82
|
+
gifts,
|
|
83
|
+
email,
|
|
84
|
+
addJob: deps.addJob,
|
|
85
|
+
getTimezone: deps.getTimezone,
|
|
86
|
+
getInlineThreshold: deps.getInlineThreshold
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Build the members CSV exporter. The same composition root from the other direction:
|
|
91
|
+
// knex and the members id lookup are wired here, and the custom fields definitions and
|
|
92
|
+
// values services are injected (boot builds them before this one). The labs flag alone
|
|
93
|
+
// decides whether custom field columns appear, so nothing flag-shaped leaks into the
|
|
94
|
+
// exporter itself.
|
|
95
|
+
export function makeExporter({definitions, values}: CustomFieldsServices): (options?: ExportOptions) => Promise<NodeJS.ReadableStream> {
|
|
96
|
+
const exporter = new MembersCSVExporter({
|
|
97
|
+
knex: db.knex,
|
|
98
|
+
|
|
99
|
+
members: {
|
|
100
|
+
// Minimal query, only to fetch the ids of the filtered members; the stream
|
|
101
|
+
// reads their related data itself.
|
|
102
|
+
findFilteredIds: async (options) => {
|
|
103
|
+
const page = await models.Member.findPage({...options, withRelated: [], columns: ['id'], limit: 'all'});
|
|
104
|
+
return page.data.map((member: {id: string}) => member.id);
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
customFields: {
|
|
109
|
+
// Boot builds the definitions and values services before this one, so they
|
|
110
|
+
// are always present -- no not-initialised state to guard. The flag decides
|
|
111
|
+
// whether their columns are included at all.
|
|
112
|
+
activeDefinitions: async (): Promise<CustomFieldDefinition[]> => (labs.isSet('membersCustomFields') ? definitions.browse() : []),
|
|
113
|
+
valuesForMembers: memberIds => values.getValuesForMembers(memberIds)
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
return (options = {}) => exporter.export(options);
|
|
118
|
+
}
|
|
@@ -467,6 +467,13 @@ module.exports = class RouterController {
|
|
|
467
467
|
});
|
|
468
468
|
}
|
|
469
469
|
|
|
470
|
+
if (offer.status && offer.status !== 'active') {
|
|
471
|
+
throw new BadRequestError({
|
|
472
|
+
message: tpl(messages.offerArchived),
|
|
473
|
+
context: 'Offer with id "' + offerId + '" is no longer active'
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
|
|
470
477
|
if (!offer.tier) {
|
|
471
478
|
throw new BadRequestError({
|
|
472
479
|
message: 'Offer does not have a tier'
|
|
@@ -81,10 +81,11 @@ module.exports = function MembersAPI({
|
|
|
81
81
|
sentry,
|
|
82
82
|
settingsHelpers,
|
|
83
83
|
urlUtils,
|
|
84
|
+
urlService,
|
|
84
85
|
commentsService,
|
|
85
86
|
emailAddressService,
|
|
86
87
|
giftService,
|
|
87
|
-
|
|
88
|
+
customFieldValues
|
|
88
89
|
}) {
|
|
89
90
|
const tokenService = new TokenService({
|
|
90
91
|
privateKey,
|
|
@@ -123,6 +124,7 @@ module.exports = function MembersAPI({
|
|
|
123
124
|
});
|
|
124
125
|
|
|
125
126
|
const eventRepository = new EventRepository({
|
|
127
|
+
urlService,
|
|
126
128
|
DonationPaymentEvent,
|
|
127
129
|
EmailRecipient,
|
|
128
130
|
MemberSubscribeEvent,
|
|
@@ -167,7 +169,7 @@ module.exports = function MembersAPI({
|
|
|
167
169
|
nextPaymentCalculator,
|
|
168
170
|
commentsService,
|
|
169
171
|
giftService,
|
|
170
|
-
|
|
172
|
+
customFieldValues
|
|
171
173
|
});
|
|
172
174
|
|
|
173
175
|
const geolocationService = new GeolocationService();
|
|
@@ -34,6 +34,7 @@ module.exports = class EventRepository {
|
|
|
34
34
|
Comment,
|
|
35
35
|
labsService,
|
|
36
36
|
memberAttributionService,
|
|
37
|
+
urlService,
|
|
37
38
|
MemberEmailChangeEvent,
|
|
38
39
|
AutomatedEmailRecipient,
|
|
39
40
|
Gift
|
|
@@ -47,6 +48,7 @@ module.exports = class EventRepository {
|
|
|
47
48
|
this._EmailRecipient = EmailRecipient;
|
|
48
49
|
this._Comment = Comment;
|
|
49
50
|
this._labsService = labsService;
|
|
51
|
+
this._urlService = urlService;
|
|
50
52
|
this._MemberCreatedEvent = MemberCreatedEvent;
|
|
51
53
|
this._SubscriptionCreatedEvent = SubscriptionCreatedEvent;
|
|
52
54
|
this._MemberLinkClickEvent = MemberLinkClickEvent;
|
|
@@ -59,6 +61,12 @@ module.exports = class EventRepository {
|
|
|
59
61
|
this._knex = db.knex;
|
|
60
62
|
}
|
|
61
63
|
|
|
64
|
+
// the URL service needs the attributed post's relations (e.g. tags for a
|
|
65
|
+
// tag-filtered collection) to build its URL; prefix them onto the eager-load
|
|
66
|
+
#attributionPostRelations(prefix) {
|
|
67
|
+
return this._urlService.getRequiredRelations().map(relation => `${prefix}.${relation}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
62
70
|
async getEventTimeline(options = {}) {
|
|
63
71
|
if (!options.limit) {
|
|
64
72
|
options.limit = 10;
|
|
@@ -213,6 +221,7 @@ module.exports = class EventRepository {
|
|
|
213
221
|
withRelated: [
|
|
214
222
|
'member',
|
|
215
223
|
'subscriptionCreatedEvent.postAttribution',
|
|
224
|
+
...this.#attributionPostRelations('subscriptionCreatedEvent.postAttribution'),
|
|
216
225
|
'subscriptionCreatedEvent.userAttribution',
|
|
217
226
|
'subscriptionCreatedEvent.tagAttribution',
|
|
218
227
|
'subscriptionCreatedEvent.memberCreatedEvent',
|
|
@@ -343,6 +352,7 @@ module.exports = class EventRepository {
|
|
|
343
352
|
withRelated: [
|
|
344
353
|
'member',
|
|
345
354
|
'postAttribution',
|
|
355
|
+
...this.#attributionPostRelations('postAttribution'),
|
|
346
356
|
'userAttribution',
|
|
347
357
|
'tagAttribution',
|
|
348
358
|
'signupStatusEvent'
|
|
@@ -402,6 +412,7 @@ module.exports = class EventRepository {
|
|
|
402
412
|
withRelated: [
|
|
403
413
|
'member',
|
|
404
414
|
'postAttribution',
|
|
415
|
+
...this.#attributionPostRelations('postAttribution'),
|
|
405
416
|
'userAttribution',
|
|
406
417
|
'tagAttribution'
|
|
407
418
|
],
|
|
@@ -591,7 +602,7 @@ module.exports = class EventRepository {
|
|
|
591
602
|
async getCommentEvents(options = {}, filter) {
|
|
592
603
|
options = {
|
|
593
604
|
...options,
|
|
594
|
-
withRelated: ['member', 'post', 'post
|
|
605
|
+
withRelated: ['member', 'post', ...this.#attributionPostRelations('post'), 'parent'],
|
|
595
606
|
filter: 'member_id:-null+custom:true',
|
|
596
607
|
useBasicCount: true,
|
|
597
608
|
mongoTransformer: chainTransformers(
|
|
@@ -625,7 +636,7 @@ module.exports = class EventRepository {
|
|
|
625
636
|
async getClickEvents(options = {}, filter) {
|
|
626
637
|
options = {
|
|
627
638
|
...options,
|
|
628
|
-
withRelated: ['member', 'link', 'link.post', 'link.post
|
|
639
|
+
withRelated: ['member', 'link', 'link.post', ...this.#attributionPostRelations('link.post')],
|
|
629
640
|
filter: 'custom:true',
|
|
630
641
|
useBasicCount: true,
|
|
631
642
|
mongoTransformer: chainTransformers(
|
|
@@ -6,7 +6,8 @@ const moment = require('moment');
|
|
|
6
6
|
const messages = {
|
|
7
7
|
stripeNotConnected: 'Missing Stripe connection.',
|
|
8
8
|
memberAlreadyExists: 'Member already exists.',
|
|
9
|
-
memberNotFound: 'Member not found.'
|
|
9
|
+
memberNotFound: 'Member not found.',
|
|
10
|
+
customFieldsOnAdd: 'Custom field values cannot be set while creating a member. Create the member, then set values with an edit.'
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
// Stored in the action's `context.action_name`; Admin maps it to a display label.
|
|
@@ -36,15 +37,6 @@ const CUSTOM_FIELDS_EDITED_ACTION = 'custom_fields_edited';
|
|
|
36
37
|
* @prop {{getActiveByMembers: (memberIds: string[]) => Promise<Map<string, {cadence: 'month' | 'year', currency: string, amount: number}>>}} service
|
|
37
38
|
*/
|
|
38
39
|
|
|
39
|
-
/**
|
|
40
|
-
* @typedef {object} ICustomFieldsServiceWrapper
|
|
41
|
-
* @prop {{
|
|
42
|
-
* getValuesForMembers: (memberIds: string[]) => Promise<Map<string, Record<string, unknown>>>,
|
|
43
|
-
* planWrite: (values: unknown) => Promise<object[]>,
|
|
44
|
-
* applyWrite: (memberId: string, writes: object[]) => Promise<void>
|
|
45
|
-
* }} values
|
|
46
|
-
*/
|
|
47
|
-
|
|
48
40
|
module.exports = class MemberBREADService {
|
|
49
41
|
/**
|
|
50
42
|
* @param {object} deps
|
|
@@ -58,9 +50,9 @@ module.exports = class MemberBREADService {
|
|
|
58
50
|
* @param {import('../../../settings-helpers/settings-helpers')} deps.settingsHelpers
|
|
59
51
|
* @param {import('./next-payment-calculator')} deps.nextPaymentCalculator
|
|
60
52
|
* @param {IGiftServiceWrapper} deps.giftService
|
|
61
|
-
* @param {
|
|
53
|
+
* @param {import('../../../members-custom-fields/values-service').CustomFieldValuesService} deps.customFieldValues Required: boot builds it before the members service
|
|
62
54
|
*/
|
|
63
|
-
constructor({memberRepository, labsService, emailService, stripeService, offersAPI, memberAttributionService, emailSuppressionList, settingsHelpers, nextPaymentCalculator, commentsService, giftService,
|
|
55
|
+
constructor({memberRepository, labsService, emailService, stripeService, offersAPI, memberAttributionService, emailSuppressionList, settingsHelpers, nextPaymentCalculator, commentsService, giftService, customFieldValues}) {
|
|
64
56
|
this.offersAPI = offersAPI;
|
|
65
57
|
/** @private */
|
|
66
58
|
this.memberRepository = memberRepository;
|
|
@@ -83,7 +75,7 @@ module.exports = class MemberBREADService {
|
|
|
83
75
|
/** @private */
|
|
84
76
|
this.giftService = giftService;
|
|
85
77
|
/** @private */
|
|
86
|
-
this.
|
|
78
|
+
this.customFieldValues = customFieldValues;
|
|
87
79
|
}
|
|
88
80
|
|
|
89
81
|
/**
|
|
@@ -112,7 +104,7 @@ module.exports = class MemberBREADService {
|
|
|
112
104
|
return null;
|
|
113
105
|
}
|
|
114
106
|
|
|
115
|
-
return this.
|
|
107
|
+
return this.customFieldValues.getValuesForMembers(memberIds);
|
|
116
108
|
}
|
|
117
109
|
|
|
118
110
|
/**
|
|
@@ -444,7 +436,36 @@ module.exports = class MemberBREADService {
|
|
|
444
436
|
return member;
|
|
445
437
|
}
|
|
446
438
|
|
|
439
|
+
/**
|
|
440
|
+
* @private
|
|
441
|
+
* The write-side flag gate, paired with `fetchCustomFieldValues` on the read
|
|
442
|
+
* side. The schema declares `custom_fields` for every site, so the key arrives
|
|
443
|
+
* whether or not the feature is on and this is what decides it goes no further.
|
|
444
|
+
* @param {object} data
|
|
445
|
+
*/
|
|
446
|
+
dropCustomFieldsWhenDisabled(data) {
|
|
447
|
+
if (!this.labsService.isSet('membersCustomFields')) {
|
|
448
|
+
delete data.custom_fields;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
447
452
|
async add(data, options) {
|
|
453
|
+
this.dropCustomFieldsWhenDisabled(data);
|
|
454
|
+
|
|
455
|
+
// Values cannot be set on create, only on a subsequent edit. `namesValues`
|
|
456
|
+
// both judges the body and rejects a malformed one, so a body refused here is
|
|
457
|
+
// refused on edit for the same reason.
|
|
458
|
+
if (this.customFieldValues.namesValues(data.custom_fields)) {
|
|
459
|
+
throw new errors.ValidationError({
|
|
460
|
+
message: tpl(messages.customFieldsOnAdd),
|
|
461
|
+
property: 'custom_fields'
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Not a member column, so it comes off before the repository sees it. Only
|
|
466
|
+
// an absent key or one naming no values gets this far.
|
|
467
|
+
delete data.custom_fields;
|
|
468
|
+
|
|
448
469
|
if (!this.stripeService.configured && (data.comped || data.stripe_customer_id)) {
|
|
449
470
|
const property = data.comped ? 'comped' : 'stripe_customer_id';
|
|
450
471
|
throw new errors.ValidationError({
|
|
@@ -527,10 +548,12 @@ module.exports = class MemberBREADService {
|
|
|
527
548
|
async edit(data, options) {
|
|
528
549
|
delete data.last_seen_at;
|
|
529
550
|
|
|
551
|
+
this.dropCustomFieldsWhenDisabled(data);
|
|
552
|
+
|
|
530
553
|
// Values live in their own table, so they come off the member data before
|
|
531
|
-
// the repository sees it — `custom_fields` is not a member column.
|
|
532
|
-
//
|
|
533
|
-
//
|
|
554
|
+
// the repository sees it — `custom_fields` is not a member column. The gate
|
|
555
|
+
// above has already dropped it when the feature is off, so by this point its
|
|
556
|
+
// presence is the signal to write.
|
|
534
557
|
const customFields = data.custom_fields;
|
|
535
558
|
const writeCustomFields = customFields !== undefined;
|
|
536
559
|
delete data.custom_fields;
|
|
@@ -540,7 +563,7 @@ module.exports = class MemberBREADService {
|
|
|
540
563
|
// plan to apply once below, so the values aren't resolved and validated
|
|
541
564
|
// twice.
|
|
542
565
|
const plannedCustomFields = writeCustomFields
|
|
543
|
-
? await this.
|
|
566
|
+
? await this.customFieldValues.planWrite(customFields)
|
|
544
567
|
: null;
|
|
545
568
|
|
|
546
569
|
let model;
|
|
@@ -589,7 +612,7 @@ module.exports = class MemberBREADService {
|
|
|
589
612
|
}
|
|
590
613
|
|
|
591
614
|
if (plannedCustomFields) {
|
|
592
|
-
await this.
|
|
615
|
+
await this.customFieldValues.applyWrite(model.id, plannedCustomFields);
|
|
593
616
|
|
|
594
617
|
// Custom fields aren't a member column or relation, so an edit touching
|
|
595
618
|
// only them leaves `model._changed` empty and the save fires nothing.
|
|
@@ -4,7 +4,7 @@ const logging = require('@tryghost/logging');
|
|
|
4
4
|
const membersService = require('./service');
|
|
5
5
|
const emailSuppressionList = require('../email-suppression-list');
|
|
6
6
|
const models = require('../../models');
|
|
7
|
-
const urlUtils = require('../../../shared/url-utils');
|
|
7
|
+
const urlUtils = require('../../../shared/url-utils').default;
|
|
8
8
|
const spamPrevention = require('../../web/shared/middleware/api/spam-prevention');
|
|
9
9
|
const {
|
|
10
10
|
formattedMemberResponse,
|
|
@@ -4,12 +4,12 @@ const tpl = require('@tryghost/tpl');
|
|
|
4
4
|
const MembersSSR = require('./members-ssr');
|
|
5
5
|
const db = require('../../data/db');
|
|
6
6
|
const MembersConfigProvider = require('./members-config-provider');
|
|
7
|
-
const
|
|
7
|
+
const {makeImporter, makeExporter} = require('./import-export');
|
|
8
|
+
const {resolveInlineThreshold} = require('./import-export/config');
|
|
8
9
|
const MembersStats = require('./stats/members-stats');
|
|
9
10
|
const memberJobs = require('./jobs');
|
|
10
11
|
const logging = require('@tryghost/logging');
|
|
11
|
-
const urlUtils = require('../../../shared/url-utils');
|
|
12
|
-
const labsService = require('../../../shared/labs');
|
|
12
|
+
const urlUtils = require('../../../shared/url-utils').default;
|
|
13
13
|
const settingsCache = require('../../../shared/settings-cache');
|
|
14
14
|
const config = require('../../../shared/config');
|
|
15
15
|
const models = require('../../models');
|
|
@@ -46,10 +46,12 @@ const membersStats = new MembersStats({
|
|
|
46
46
|
let membersApi;
|
|
47
47
|
let verificationTrigger;
|
|
48
48
|
|
|
49
|
-
const
|
|
50
|
-
return
|
|
51
|
-
storagePath: config.getContentPath('data'),
|
|
49
|
+
const buildImporterDeps = ({stripeAPIService}) => {
|
|
50
|
+
return {
|
|
52
51
|
getTimezone: () => settingsCache.get('timezone'),
|
|
52
|
+
// A getter rather than a value because the threshold is an operator
|
|
53
|
+
// setting that can change between requests
|
|
54
|
+
getInlineThreshold: () => resolveInlineThreshold(config.get('members:importer:inlineThreshold')),
|
|
53
55
|
getMembersRepository: async () => {
|
|
54
56
|
const api = await module.exports.api;
|
|
55
57
|
return api.members;
|
|
@@ -75,16 +77,12 @@ const initMembersCSVImporter = ({stripeAPIService}) => {
|
|
|
75
77
|
},
|
|
76
78
|
getGiftService: () => giftService.service,
|
|
77
79
|
sendEmail: ghostMailer.send.bind(ghostMailer),
|
|
78
|
-
isSet: flag => labsService.isSet(flag),
|
|
79
80
|
addJob: jobsService.addJob.bind(jobsService),
|
|
80
81
|
knex: db.knex,
|
|
81
82
|
urlFor: urlUtils.urlFor.bind(urlUtils),
|
|
82
|
-
context: {
|
|
83
|
-
importer: true
|
|
84
|
-
},
|
|
85
83
|
stripeAPIService,
|
|
86
84
|
productRepository: membersApi.productRepository
|
|
87
|
-
}
|
|
85
|
+
};
|
|
88
86
|
};
|
|
89
87
|
|
|
90
88
|
const initVerificationTrigger = () => {
|
|
@@ -150,10 +148,22 @@ module.exports = {
|
|
|
150
148
|
}
|
|
151
149
|
module.exports.verificationTrigger = verificationTrigger;
|
|
152
150
|
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
151
|
+
const importerDeps = buildImporterDeps({stripeAPIService: stripeService.api});
|
|
152
|
+
const membersCSVImporter = makeImporter(importerDeps);
|
|
153
|
+
// The importer takes plain arguments and returns the domain outcome; callers build
|
|
154
|
+
// the request and shape the response. importCSV decides inline vs deferred by load
|
|
155
|
+
// (the endpoint); importInline always runs now (the Revue data import). This only
|
|
156
|
+
// supplies the verification trigger, a members-service internal.
|
|
157
|
+
module.exports.importCSV = (request) => membersCSVImporter.importCSV(request, verificationTrigger);
|
|
158
|
+
module.exports.importInline = (request) => membersCSVImporter.importInline(request, verificationTrigger);
|
|
159
|
+
|
|
160
|
+
// Constructed here rather than required statically: the exporter needs the
|
|
161
|
+
// custom fields services, which boot builds before this one.
|
|
162
|
+
const customFields = require('../members-custom-fields');
|
|
163
|
+
module.exports.export = makeExporter({
|
|
164
|
+
definitions: customFields.definitions,
|
|
165
|
+
values: customFields.values
|
|
166
|
+
});
|
|
157
167
|
|
|
158
168
|
if (!env?.startsWith('testing')) {
|
|
159
169
|
const membersMigrationJobName = 'members-migrations';
|
|
@@ -193,10 +203,11 @@ module.exports = {
|
|
|
193
203
|
|
|
194
204
|
stripeConnect: require('./stripe-connect'),
|
|
195
205
|
|
|
196
|
-
|
|
206
|
+
importCSV: null,
|
|
207
|
+
importInline: null,
|
|
197
208
|
|
|
198
209
|
stats: membersStats,
|
|
199
|
-
export:
|
|
210
|
+
export: null
|
|
200
211
|
};
|
|
201
212
|
|
|
202
213
|
module.exports.middleware = require('./middleware');
|
|
@@ -4,7 +4,7 @@ const {Buffer} = require('buffer');
|
|
|
4
4
|
const {randomBytes} = require('crypto');
|
|
5
5
|
|
|
6
6
|
const config = require('../../../shared/config');
|
|
7
|
-
const urlUtils = require('../../../shared/url-utils');
|
|
7
|
+
const urlUtils = require('../../../shared/url-utils').default;
|
|
8
8
|
|
|
9
9
|
const messages = {
|
|
10
10
|
incorrectState: 'State did not match.'
|
|
@@ -24,6 +24,9 @@ async function recordCustomFieldAction({ Action, context, verb, subject, details
|
|
|
24
24
|
await Action.add({
|
|
25
25
|
event: COMMANDS[verb],
|
|
26
26
|
resource_type: 'member_custom_field',
|
|
27
|
+
// The field's id: this column holds 24 characters, and a key minted from
|
|
28
|
+
// a publisher-chosen name is bounded by the far wider key column, so only
|
|
29
|
+
// the id fits every field.
|
|
27
30
|
resource_id: subject,
|
|
28
31
|
actor_type: context.actor.type,
|
|
29
32
|
actor_id: context.actor.id,
|
|
@@ -29,8 +29,9 @@ export type CustomFieldVerb = keyof typeof COMMANDS;
|
|
|
29
29
|
// `details` is stored in the action's `context` column (Ghost's slot for "diffs,
|
|
30
30
|
// meta"). We always pass the field's `primary_name` so the log reads as a human
|
|
31
31
|
// label, not a bare key — this is what keeps the timeline legible after a hard
|
|
32
|
-
// delete, when the row itself is gone.
|
|
33
|
-
|
|
32
|
+
// delete, when the row itself is gone. `key` rides alongside it because the key is
|
|
33
|
+
// how a field is addressed publicly — its id never leaves the API.
|
|
34
|
+
export type CustomFieldActionDetails = {primary_name: string; key: string; previous_name?: string};
|
|
34
35
|
|
|
35
36
|
export type RecordCustomFieldAction =
|
|
36
37
|
(input: {context: RequestContext; verb: CustomFieldVerb; subject: string; details: CustomFieldActionDetails}) => Promise<void>;
|
|
@@ -47,6 +48,9 @@ export async function recordCustomFieldAction(
|
|
|
47
48
|
await Action.add({
|
|
48
49
|
event: COMMANDS[verb],
|
|
49
50
|
resource_type: 'member_custom_field',
|
|
51
|
+
// The field's id: this column holds 24 characters, and a key minted from
|
|
52
|
+
// a publisher-chosen name is bounded by the far wider key column, so only
|
|
53
|
+
// the id fits every field.
|
|
50
54
|
resource_id: subject,
|
|
51
55
|
actor_type: context.actor.type,
|
|
52
56
|
actor_id: context.actor.id,
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveMaxDefinitions = resolveMaxDefinitions;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
/**
|
|
6
|
+
* Resolving Ghost's config into the values this service runs on.
|
|
7
|
+
*
|
|
8
|
+
* Config arrives untyped: defaults.json supplies the shipped value, and any
|
|
9
|
+
* higher layer — an operator's config file, an environment variable, argv — can
|
|
10
|
+
* replace it with anything at all. Turning that into something usable is a
|
|
11
|
+
* config concern, so it happens here, at the boundary. The service is handed an
|
|
12
|
+
* answer and never learns where it came from.
|
|
13
|
+
*/
|
|
14
|
+
// A ceiling in the form the service needs it. A numeric string is accepted
|
|
15
|
+
// because a value supplied through an environment variable can arrive as one.
|
|
16
|
+
const Ceiling = zod_1.z
|
|
17
|
+
.union([zod_1.z.number(), zod_1.z.string().trim().min(1)])
|
|
18
|
+
.transform(Number)
|
|
19
|
+
.pipe(zod_1.z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER));
|
|
20
|
+
// defaults.json is the single source of the shipped ceiling: it is the lowest
|
|
21
|
+
// config layer and the file an operator actually reads and edits, so it is read
|
|
22
|
+
// rather than restated. Parsed eagerly because a missing or malformed value
|
|
23
|
+
// there is our bug rather than an operator's, and should fail loudly at boot
|
|
24
|
+
// instead of quietly becoming the fallback for everything below.
|
|
25
|
+
const shippedCeiling = Ceiling.parse(require('../../../shared/config/defaults.json').members.customFields.maxDefinitions);
|
|
26
|
+
// A higher layer can override the shipped value with anything. Something
|
|
27
|
+
// unreadable falls back to the shipped ceiling rather than throwing, because an
|
|
28
|
+
// operator can correct this live and a bad value must not take the feature down
|
|
29
|
+
// with it — and rather than meaning "no ceiling", because a typo must not
|
|
30
|
+
// silently remove the safeguard.
|
|
31
|
+
const MaxDefinitions = Ceiling.catch(shippedCeiling);
|
|
32
|
+
function resolveMaxDefinitions(configured) {
|
|
33
|
+
return MaxDefinitions.parse(configured);
|
|
34
|
+
}
|