ghost 6.60.0 → 6.62.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/ghost-storage-base-3.0.0.tgz +0 -0
- package/components/tryghost-adapter-base-cache-0.2.0.tgz +0 -0
- package/components/tryghost-adapter-base-jobs-0.0.0.tgz +0 -0
- package/components/tryghost-adapter-base-redirects-0.0.0.tgz +0 -0
- package/components/tryghost-adapter-base-route-settings-0.0.0.tgz +0 -0
- package/components/tryghost-adapter-base-scheduling-0.2.3.tgz +0 -0
- package/components/tryghost-adapter-base-sso-0.1.3.tgz +0 -0
- package/components/tryghost-admin-api-schema-0.0.0.tgz +0 -0
- package/components/tryghost-checkout-0.0.0.tgz +0 -0
- package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
- package/components/tryghost-i18n-0.0.0.tgz +0 -0
- package/components/tryghost-kg-card-factory-5.2.4.tgz +0 -0
- package/components/tryghost-kg-clean-basic-html-4.3.4.tgz +0 -0
- package/components/tryghost-kg-converters-1.2.5.tgz +0 -0
- package/components/tryghost-kg-default-cards-10.3.5.tgz +0 -0
- package/components/tryghost-kg-default-nodes-2.2.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-nql-string-0.0.0.tgz +0 -0
- package/components/tryghost-parse-email-address-0.0.0.tgz +0 -0
- package/content/settings/README.md +1 -1
- package/content/themes/casper/.gitignore +28 -0
- package/content/themes/casper/assets/built/casper.js +1 -1
- package/content/themes/casper/assets/built/screen.css +1 -1
- package/content/themes/casper/assets/css/screen.css +2 -1
- package/content/themes/casper/assets/js/lightbox.js +63 -46
- package/content/themes/casper/package.json +6 -6
- package/content/themes/casper/page.hbs +1 -0
- package/content/themes/casper/post.hbs +1 -0
- package/content/themes/source/.gitignore +28 -0
- package/content/themes/source/assets/built/screen.css +1 -1
- package/content/themes/source/assets/built/source.js +1 -1
- package/content/themes/source/assets/css/screen.css +2 -1
- package/content/themes/source/assets/js/lightbox.js +62 -45
- package/content/themes/source/assets/js/main.js +2 -2
- package/content/themes/source/package.json +4 -4
- package/content/themes/source/partials/feature-image.hbs +1 -0
- package/core/boot.js +62 -21
- package/core/built/admin/assets/Combination-B0l4MKVh.js +41 -0
- package/core/built/admin/assets/InterVariable-DiVDrmQJ.woff2 +0 -0
- package/core/built/admin/assets/InterVariable-Italic-FCBEiFp6.woff2 +0 -0
- package/core/built/admin/assets/{PolarAngleAxis-C4qtHy1B.js → PolarAngleAxis-pkMyqz8g.js} +1 -1
- package/core/built/admin/assets/{RadialBarChart-Bh09Stsj.js → RadialBarChart-JAKoS4KT.js} +1 -1
- package/core/built/admin/assets/{about-BgpmhCuM.js → about-Dyae_UT6.js} +1 -1
- package/core/built/admin/assets/account-migration-CESa0mnX.js +1 -0
- package/core/built/admin/assets/{action-list-lGzZLHc8.js → action-list-C4v0-du8.js} +1 -1
- package/core/built/admin/assets/activitypub/{account-migration-DMHuPnJn.js → account-migration-By69CgCp.js} +16 -16
- package/core/built/admin/assets/activitypub/activitypub.js +9 -10
- package/core/built/admin/assets/activitypub/{avatar-DhTo5D1B.js → avatar-DLPL3Abs.js} +59 -68
- package/core/built/admin/assets/activitypub/{avatar-flipboard-DnVgbEfa.js → avatar-flipboard-C9iDY-QV.js} +2 -2
- package/core/built/admin/assets/activitypub/{bluesky-sharing-BT4zAlW7.js → bluesky-sharing-BJcPMrDd.js} +37 -37
- package/core/built/admin/assets/activitypub/{content-formatters-BeDZ-DIa.js → content-formatters-3rTHg3mp.js} +18 -18
- package/core/built/admin/assets/activitypub/{copy-8sqxUuNd.js → copy-DIAOQoCZ.js} +2 -2
- package/core/built/admin/assets/activitypub/{domain-xQDDu1CS.js → domain-DUR4xSkV.js} +121 -121
- package/core/built/admin/assets/activitypub/{edit-profile-BXdF_r2G.js → edit-profile-BqtfzxCp.js} +40 -40
- package/core/built/admin/assets/activitypub/{explore-CMt7gKlL.js → explore-BxuJd-J4.js} +27 -27
- package/core/built/admin/assets/activitypub/{feed-Bwqdddp_.js → feed-BM1TcksA.js} +51 -51
- package/core/built/admin/assets/activitypub/{field-4co_8FVg.js → field-Dn7HHx2-.js} +3 -3
- package/core/built/admin/assets/activitypub/{hash-DwakMs4X.js → hash-Bm3WxYN4.js} +2 -2
- package/core/built/admin/assets/activitypub/inbox-CCjkuow2.js +1284 -0
- package/core/built/admin/assets/activitypub/{inbox-ckD4wo_r.js → inbox-d8oJRTf4.js} +2 -2
- package/core/built/admin/assets/activitypub/label-1AA_s9Fa.js +16 -0
- package/core/built/admin/assets/activitypub/layout-2-0bw6HN.js +1 -0
- package/core/built/admin/assets/activitypub/{moderation-KCCO7L2J.js → moderation-CnmxEB8w.js} +91 -91
- package/core/built/admin/assets/activitypub/{note-BX_Bz5A0.js → note-ZKc_Bnba.js} +24 -24
- package/core/built/admin/assets/activitypub/{notifications-BQOYGvRv.js → notifications-CB2tJZau.js} +51 -51
- package/core/built/admin/assets/activitypub/{onboarding-CNelD_uF.js → onboarding-BsJ9E5qf.js} +2 -2
- package/core/built/admin/assets/activitypub/{onboarding-mat5Hrl0.js → onboarding-CSUJJIlh.js} +2 -2
- package/core/built/admin/assets/activitypub/{preferences-6h43G9_q.js → preferences-0-JZnUkI.js} +1519 -1519
- package/core/built/admin/assets/activitypub/{profile-BMN9VVZ2.js → profile-C35cFIhM.js} +256 -256
- package/core/built/admin/assets/activitypub/{reply-ZNo04xsO.js → reply-DYK5vVqb.js} +2 -2
- package/core/built/admin/assets/activitypub/{routes-3B9mUQ7i.js → routes-BD4vscyc.js} +796 -790
- package/core/built/admin/assets/activitypub/{separator-D9taVCCa.js → separator-BbQqTm-p.js} +3 -3
- package/core/built/admin/assets/activitypub/settings-BsMROdqh.js +315 -0
- package/core/built/admin/assets/activitypub/{site-DHoMupSB.js → site-O0RL9YCN.js} +2 -2
- package/core/built/admin/assets/activitypub/{step-1-BsGGnBuT.js → step-1-BMSx7c_X.js} +6 -6
- package/core/built/admin/assets/activitypub/{step-2-teosIS0k.js → step-2-DtPQzlTH.js} +6 -6
- package/core/built/admin/assets/activitypub/{step-3-BjD5TokC.js → step-3-CLlOZTyt.js} +11 -11
- package/core/built/admin/assets/activitypub/{tabs-Cvwl1wXU.js → tabs-Dxk-b9M-.js} +306 -306
- package/core/built/admin/assets/activitypub/{topic-filter-C3LZJc2X.js → topic-filter-5yZH6tId.js} +3 -3
- package/core/built/admin/assets/activitypub/{use-activity-pub-queries-Dp9f0gov.js → use-activity-pub-queries-CV50qe03.js} +3 -3
- package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-C4RI0sa9.js → use-navigate-with-base-path-Bmk5h4lW.js} +220 -220
- package/core/built/admin/assets/activitypub/{use-reply-chain-data-C2oNDMUH.js → use-reply-chain-data-Cbvzgjzd.js} +30 -30
- package/core/built/admin/assets/activitypub/{users-CoX6FT_K.js → users-BjcoP_HC.js} +7 -5
- package/core/built/admin/assets/activitypub/{x-Dxm6pV9X.js → x-DbQZOLiI.js} +2 -2
- package/core/built/admin/assets/add-integration-modal-BlF6rs_f.js +1 -0
- package/core/built/admin/assets/add-newsletter-modal-CglVQqah.js +1 -0
- package/core/built/admin/assets/add-recommendation-modal-wz7BsuaG.js +1 -0
- package/core/built/admin/assets/analytics-C4HE1gBg.js +1 -0
- package/core/built/admin/assets/{announcement-bar-modal-BTHiy1ZO.js → announcement-bar-modal-Ci4zz8jO.js} +1 -1
- package/core/built/admin/assets/{api-keys-Boy5pLbD.js → api-keys-Ck-Yteiv.js} +1 -1
- package/core/built/admin/assets/audience-CtbA54Sn.js +1 -0
- package/core/built/admin/assets/{automated-emails-te1D5kq-.js → automated-emails-D5VVMND9.js} +1 -1
- package/core/built/admin/assets/automations-CzKt5fb8.js +1 -0
- package/core/built/admin/assets/bluesky-sharing-BEtJ_gL_.js +1 -0
- package/core/built/admin/assets/{box-By_EgGtm.js → box-CdfQAP_F.js} +1 -1
- package/core/built/admin/assets/{brand-icon-09YqFZjV.js → brand-icon-CrIGOA3Z.js} +1 -1
- package/core/built/admin/assets/{breadcrumb-CcFr_ltt.js → breadcrumb-zUrZrBtW.js} +1 -1
- package/core/built/admin/assets/{button-BshWPHvn.js → button-eNMKQWXr.js} +1 -1
- package/core/built/admin/assets/{card-CiiE7QfM.js → card--VNbfgo3.js} +1 -1
- package/core/built/admin/assets/{chart-BnXyfH11.js → chart-BrhA41Zi.js} +1 -1
- package/core/built/admin/assets/chart-helpers-CfBKt0vT.js +1 -0
- package/core/built/admin/assets/{checkbox-Dwwr_3Py.js → checkbox-CgOn_tKW.js} +1 -1
- package/core/built/admin/assets/{chunk.524.91ecb33e6afc3bd8949b.js → chunk.524.a697dee88db67fdf6021.js} +4 -4
- package/core/built/admin/assets/{chunk.582.512549909741883f960c.js → chunk.582.5ed5d124cfca9c259b9c.js} +6 -6
- package/core/built/admin/assets/{chunk.16.f76a6314082d813d67e0.js → chunk.598.7e056fabb47ce7dbd73f.js} +14 -14
- package/core/built/admin/assets/{code-editor-view-BwIdPy_k.js → code-editor-view-BYNoAoNV.js} +1 -1
- package/core/built/admin/assets/{code-editor-view-CtkF82Fv.js → code-editor-view-K2wI-I5f.js} +1 -1
- package/core/built/admin/assets/color-picker-Ddw80xX6.js +3 -0
- package/core/built/admin/assets/color-picker-field-D9nUhvA7.js +1 -0
- package/core/built/admin/assets/combobox-w9j8PPQT.js +1 -0
- package/core/built/admin/assets/command-BfpHORBv.js +1 -0
- package/core/built/admin/assets/comments-BLmCmyCs.js +1 -0
- package/core/built/admin/assets/confirmation-context-CBM8G0Vj.js +1 -0
- package/core/built/admin/assets/{container-LiZf3XdY.js → container-BuQG1wuP.js} +1 -1
- package/core/built/admin/assets/{content-api-modal-BtpNI92-.js → content-api-modal-TXRz3U7S.js} +1 -1
- package/core/built/admin/assets/context-menu-_QMdd3kp.js +1 -0
- package/core/built/admin/assets/{copy-field-CglgRZyG.js → copy-field-6N01S07E.js} +1 -1
- package/core/built/admin/assets/create-ghost-browse-value-source-qIBymvvf.js +1 -0
- package/core/built/admin/assets/custom-field-type-option-BddFlJFa.js +1 -0
- package/core/built/admin/assets/custom-integration-modal-DmNquxUV.js +1 -0
- package/core/built/admin/assets/{data-list-CupF7bjM.js → data-list-CA2jYGPq.js} +1 -1
- package/core/built/admin/assets/{date-range-select-JPwM0yUj.js → date-range-select-uvz_DtPh.js} +1 -1
- package/core/built/admin/assets/design-and-theme-modal-BSmo12Op.js +2 -0
- package/core/built/admin/assets/{detail-page-D8lx2SdP.js → detail-page-Dnv-CdR1.js} +1 -1
- package/core/built/admin/assets/dirty-confirm-dialog-BZ1iytEd.js +1 -0
- package/core/built/admin/assets/{disabled-sources-indicator-BTJ2ClQu.js → disabled-sources-indicator-DMRrxsiJ.js} +1 -1
- package/core/built/admin/assets/{dist-DzcfoDJ3.js → dist-8YJUlf3B.js} +1 -1
- package/core/built/admin/assets/{dist-Cb5RSHSg.js → dist-B3keLvjj.js} +1 -1
- package/core/built/admin/assets/dist-Dcsslyo72.js +1 -0
- package/core/built/admin/assets/{dist-BT_Z-eCn.js → dist-Dgt2cr6a.js} +1 -1
- package/core/built/admin/assets/domain-BNYrt0aD.js +1 -0
- package/core/built/admin/assets/dropzone-D7nFdAe5.js +5 -0
- package/core/built/admin/assets/edit-profile-C5Asm4LA.js +1 -0
- package/core/built/admin/assets/editor-REokrzci.js +8 -0
- package/core/built/admin/assets/{embed-signup-form-modal-BFTPf04i.js → embed-signup-form-modal-Ds6mPEi5.js} +1 -1
- package/core/built/admin/assets/{empty-indicator-WB1WgT9h.js → empty-indicator-D9QdpuNX.js} +1 -1
- package/core/built/admin/assets/en-B1jX3WMs.js +1 -0
- package/core/built/admin/assets/explore-UY10Egsp.js +1 -0
- package/core/built/admin/assets/feature-image-placeholder-D1iF_8bc.js +1 -0
- package/core/built/admin/assets/feed-81C8mJWR.js +1 -0
- package/core/built/admin/assets/{field-LmNk7wZh.js → field-Dihp5p18.js} +1 -1
- package/core/built/admin/assets/filter-bar-Da1qxybu.js +1 -0
- package/core/built/admin/assets/filter-sources-BO9xUPj7.js +1 -0
- package/core/built/admin/assets/filters-CRUWecfY.js +1 -0
- package/core/built/admin/assets/filters-DirqNXjH.js +1 -0
- package/core/built/admin/assets/first-promoter-modal-CT6MxYZb.js +1 -0
- package/core/built/admin/assets/{ghost-c8807202a07c20030acb719ee0114815.css → ghost-8eabfc1ece81b03cf7b90780d31487e8.css} +1 -1
- package/core/built/admin/assets/{ghost-403cb39badad3df411fc0ba27d220047.js → ghost-c2848508292937415f20f5a830eea59e.js} +31 -28
- package/core/built/admin/assets/{ghost-dark-fbf1e384aaef3ae315e540f0eb7373a7.css → ghost-dark-a7bebe2c561f55369dcf29f4c820f7ff.css} +1 -1
- package/core/built/admin/assets/gift-link-modal-CQVAye17.js +1 -0
- package/core/built/admin/assets/{grid-CUciBfjf.js → grid-Dj29PY05.js} +1 -1
- package/core/built/admin/assets/growth-BWIu-6xj.js +3 -0
- package/core/built/admin/assets/growth-DU29oxeA.js +1 -0
- package/core/built/admin/assets/{heading-CRZZ2FUe.js → heading-Dh5SFVjL.js} +1 -1
- package/core/built/admin/assets/history-modal-CFd1Hvx6.js +1 -0
- package/core/built/admin/assets/html-field-CJBCLTvv.js +1 -0
- package/core/built/admin/assets/{images-C0XvUYMb.js → images-scfkWZiK.js} +1 -1
- package/core/built/admin/assets/{inbox-DZXDUt4s.js → inbox-Mcp4DXZN.js} +14 -14
- package/core/built/admin/assets/index-6MkPdCtr.js +4 -0
- package/core/built/admin/assets/index-Bn7QhcZu.css +2 -0
- package/core/built/admin/assets/index.es-CNnuNH0d.js +1 -0
- package/core/built/admin/assets/{inline-Cw-gigDW.js → inline-BWSs_VzR.js} +1 -1
- package/core/built/admin/assets/{input-group-BPAxETpQ.js → input-group-CMhe2DvY.js} +1 -1
- package/core/built/admin/assets/{input-surface-BdOe4KzO.js → input-surface-BsrIZeSF.js} +1 -1
- package/core/built/admin/assets/{integration-header-B4DyXV06.js → integration-header-Bm6IDb1A.js} +1 -1
- package/core/built/admin/assets/invite-user-modal-DfheMXs0.js +1 -0
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +14 -14
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +3 -3
- package/core/built/admin/assets/koenig-lexical/style.css +1 -1
- package/core/built/admin/assets/{koenig-lexical-60O_8aiO.js → koenig-lexical-DBSqAuiI.js} +3 -3
- package/core/built/admin/assets/koenig-loader-aFQNlJlD.js +2 -0
- package/core/built/admin/assets/kpi-card-DBRMfGOW.js +1 -0
- package/core/built/admin/assets/{kpi-card-fUHZlzBX.js → kpi-card-DFafD1qq.js} +1 -1
- package/core/built/admin/assets/kpi-helpers-Cv8i8hns.js +1 -0
- package/core/built/admin/assets/kpi-tabs-BJItvLYm.js +1 -0
- package/core/built/admin/assets/{label-VIG969IC.js → label-BnSCvffu.js} +1 -1
- package/core/built/admin/assets/label-picker-KVoo91sh.js +1 -0
- package/core/built/admin/assets/layout-Bjv2Ll-5.js +1 -0
- package/core/built/admin/assets/{link-helpers-Dtq9fxQg.js → link-helpers-MVDdnFCY.js} +1 -1
- package/core/built/admin/assets/{list-page-CdsnQR9N.js → list-page-BucXJ0hi.js} +1 -1
- package/core/built/admin/assets/member-custom-fields-DDQV6QeG.js +1 -0
- package/core/built/admin/assets/member-detail-CdJF6Agw.js +1 -0
- package/core/built/admin/assets/members-DjQbq9Z2.js +4 -0
- package/core/built/admin/assets/{modal-page-BMTzFHVO.js → modal-page-xCivTMvI.js} +1 -1
- package/core/built/admin/assets/moderation-AgCIEso4.js +1 -0
- package/core/built/admin/assets/{multi-select-combobox-CGohnsxJ.js → multi-select-combobox-BcITkUVr.js} +1 -1
- package/core/built/admin/assets/navigation-modal-BkT7b1M2.js +1 -0
- package/core/built/admin/assets/newsletter-BMMAuvgq.js +1 -0
- package/core/built/admin/assets/newsletter-detail-modal-0SCJ7Zpm.js +1 -0
- package/core/built/admin/assets/{newsletter-emails-D5ql1rud.js → newsletter-emails-DKzwafuh.js} +1 -1
- package/core/built/admin/assets/newsletters-BRBb_uQC.js +1 -0
- package/core/built/admin/assets/note-DyGHtS9a.js +1 -0
- package/core/built/admin/assets/notifications-ZgWniKna.js +1 -0
- package/core/built/admin/assets/nql-BLpCn7qX.js +21 -0
- package/core/built/admin/assets/offers-container-modal-B-E8TIWr.js +1 -0
- package/core/built/admin/assets/{onboarding-B4YhlZ0i.js → onboarding-CzIsre-d.js} +1 -1
- package/core/built/admin/assets/onboarding-qg8Akovm.js +1 -0
- package/core/built/admin/assets/{onboarding-route-Cu_ROWPn.js → onboarding-route-CaavZulg.js} +1 -1
- package/core/built/admin/assets/overview-BW8GLeYi.js +1 -0
- package/core/built/admin/assets/overview-BkXZNzaZ.js +1 -0
- package/core/built/admin/assets/{page-header-Cfds8pCg.js → page-header-De0VzKBi.js} +1 -1
- package/core/built/admin/assets/pagemenu-B5KKCpbA.js +1 -0
- package/core/built/admin/assets/pages-jgaFm69d.js +1 -0
- package/core/built/admin/assets/pages-route-CIUAQxtY.js +1 -0
- package/core/built/admin/assets/papaparse.min-cQwnxGCG.js +10 -0
- package/core/built/admin/assets/{pintura-modal-TDTrJ89x.js → pintura-modal-BZ2J9w3n.js} +1 -1
- package/core/built/admin/assets/portal-frame-B6Hr9wHc.js +1 -0
- package/core/built/admin/assets/portal-modal-D4ceei3t.js +1 -0
- package/core/built/admin/assets/post-analytics-header-BYCsT0GP.js +1 -0
- package/core/built/admin/assets/post-analytics-ot7f6pih.js +1 -0
- package/core/built/admin/assets/{post-analytics-provider-Cu3jNeFh.js → post-analytics-provider-Bo1gmYaP.js} +1 -1
- package/core/built/admin/assets/{post-share-modal-P1_M6o-H.js → post-share-modal-C-PaYZ_e.js} +1 -1
- package/core/built/admin/assets/posts-DuGazgTb.js +1 -0
- package/core/built/admin/assets/posts-list-screen-CBGGgjjj.js +2 -0
- package/core/built/admin/assets/posts-route-D6xmS7Vf.js +1 -0
- package/core/built/admin/assets/preferences-C5YJLp74.js +5 -0
- package/core/built/admin/assets/{preview-chrome-BOVO6_PE.js → preview-chrome-BljUBlOk.js} +1 -1
- package/core/built/admin/assets/{preview-modal-MlTjP8UU.js → preview-modal-BfeFqi5y.js} +1 -1
- package/core/built/admin/assets/profile-B8mx2yAu.js +1 -0
- package/core/built/admin/assets/{radio-group-CjtmsCPl.js → radio-group-DczlLKOn.js} +1 -1
- package/core/built/admin/assets/{router-provider-CJC58zTy.js → router-provider-B0gZyiGJ.js} +1 -1
- package/core/built/admin/assets/{schemas-CRsC53ls.js → schemas-DppqmpeJ.js} +2 -2
- package/core/built/admin/assets/select-cb0t_TAJ.js +1 -0
- package/core/built/admin/assets/settings-DZqxR0Da.js +1 -0
- package/core/built/admin/assets/settings-DvfTNdHQ.js +4 -0
- package/core/built/admin/assets/{settings-breadcrumbs-sLDNedBW.js → settings-breadcrumbs-DLzbRRSt.js} +1 -1
- package/core/built/admin/assets/{shade-app-F7WxOyfk-nufqw1hd.js → shade-app-CcS3f8MZ-vwURazk8.js} +2 -2
- package/core/built/admin/assets/share-modal-D2-Bz1_I.js +1 -0
- package/core/built/admin/assets/{skeleton-D7hwjdry.js → skeleton-Ce9GgUpf.js} +1 -1
- package/core/built/admin/assets/slack-modal-DYDTUria.js +1 -0
- package/core/built/admin/assets/sort-button-VVeQjjbu.js +1 -0
- package/core/built/admin/assets/{source-icon-BPsD5ees.js → source-icon-Ck5_5Kgq.js} +1 -1
- package/core/built/admin/assets/{stack-CIfOAAVf.js → stack-B2eszIiq.js} +1 -1
- package/core/built/admin/assets/stats-Rjmk5owg.js +1 -0
- package/core/built/admin/assets/stats-view-BGGmkmgv.js +1 -0
- package/core/built/admin/assets/{step-1-CMta6YFO.js → step-1-CsaBjeN4.js} +1 -1
- package/core/built/admin/assets/{step-2-BG04nh5q.js → step-2-D2vnxOOM.js} +1 -1
- package/core/built/admin/assets/{step-3-ZnMaVIPj.js → step-3-wBoLdGEb.js} +2 -2
- package/core/built/admin/assets/{stripe-button-DDoUKN5a.js → stripe-button-CDgLz0S0.js} +1 -1
- package/core/built/admin/assets/stripe-connect-modal-CSjMJetP.js +1 -0
- package/core/built/admin/assets/{switch-C9djUIMO.js → switch-CI4gLWqb.js} +1 -1
- package/core/built/admin/assets/{table-CrqQtkR2.js → table-Do6iQ83Q.js} +1 -1
- package/core/built/admin/assets/{tabs-CGisSbWq.js → tabs-D2yWLMp_.js} +1 -1
- package/core/built/admin/assets/tag-detail-BhXlOsCa.js +2 -0
- package/core/built/admin/assets/tags-ldukRApe.js +1 -0
- package/core/built/admin/assets/testimonials-modal-CtnZBJKl.js +1 -0
- package/core/built/admin/assets/{text-DU9Hv33_.js → text-BQDSDsKs.js} +1 -1
- package/core/built/admin/assets/{textarea-d9qEkMOP.js → textarea-DJ1Q5V39.js} +1 -1
- package/core/built/admin/assets/tier-detail-modal-BOs8e_G8.js +1 -0
- package/core/built/admin/assets/{toggle-group-CtKZURJd.js → toggle-group-ByUJsxwD.js} +1 -1
- package/core/built/admin/assets/{topic-filter-D8JT8Tq1.js → topic-filter-BgC3g5_v.js} +1 -1
- package/core/built/admin/assets/{transistor-modal-6PUf0idx.js → transistor-modal-Drr0SNfo.js} +1 -1
- package/core/built/admin/assets/trend-badge-6ye93YAW.js +1 -0
- package/core/built/admin/assets/{unsplash-modal-BPuDBPja.js → unsplash-modal-p6AoQoWp.js} +1 -1
- package/core/built/admin/assets/{use-analytics-data--opuzwcz.js → use-analytics-data-CWCMAESo.js} +1 -1
- package/core/built/admin/assets/use-check-theme-limit-error-B8bjF-gO.css +1 -0
- package/core/built/admin/assets/use-check-theme-limit-error-Cw3WQHy8.js +2 -0
- package/core/built/admin/assets/use-filter-params-BIni9taJ.js +1 -0
- package/core/built/admin/assets/use-growth-stats-DCVG3s9w.js +1 -0
- package/core/built/admin/assets/{use-limiter-TDSNiHPB.js → use-limiter-D2WRRx0H.js} +1 -1
- package/core/built/admin/assets/{use-navigate-with-base-path-CK989MlC.js → use-navigate-with-base-path-BHPA228f.js} +1 -1
- package/core/built/admin/assets/{use-pintura-config-CnCGrDFk.js → use-pintura-config-h-J0Lvta.js} +1 -1
- package/core/built/admin/assets/{use-pintura-editor-ntphvtUd.js → use-pintura-editor-BfN1UwzH.js} +1 -1
- package/core/built/admin/assets/use-post-referrers-Czg47m8s.js +1 -0
- package/core/built/admin/assets/use-reply-chain-data-ns0caK8k.js +1 -0
- package/core/built/admin/assets/{use-router-history-entry-BwfQS2mq.js → use-router-history-entry-uldn7zJO.js} +1 -1
- package/core/built/admin/assets/{use-setting-group-DQxV42-O.js → use-setting-group-DpcuCUDg.js} +1 -1
- package/core/built/admin/assets/use-settings-navigation-CZjGrhsp.js +1 -0
- package/core/built/admin/assets/{use-simple-pagination-B2TdNX67.js → use-simple-pagination-DzJWobPR.js} +1 -1
- package/core/built/admin/assets/{use-staff-users-QrR9voeC.js → use-staff-users-Csipb-O0.js} +1 -1
- package/core/built/admin/assets/use-tinybird-query-CSnGXzZh.js +1 -0
- package/core/built/admin/assets/use-tinybird-token-DTOKRR7e.js +1 -0
- package/core/built/admin/assets/{use-unsaved-changes-guard-DpYJcj1d.js → use-unsaved-changes-guard-CBzs-KPi.js} +1 -1
- package/core/built/admin/assets/{use-url-input-WnIqckzS.js → use-url-input-DFr8lCbS.js} +1 -1
- package/core/built/admin/assets/user-detail-modal-B_iUClI6.js +1 -0
- package/core/built/admin/assets/users-Cg-OeGzC.js +1 -0
- package/core/built/admin/assets/{virtual-list-D3MfPNOh.js → virtual-list-BW-padMe.js} +1 -1
- package/core/built/admin/assets/web-C1v33ehA.js +2 -0
- package/core/built/admin/assets/web-C4BKzn2T.js +1 -0
- package/core/built/admin/assets/{zapier-modal-BO6PS_Vd.js → zapier-modal-BBFfI6Ml.js} +1 -1
- package/core/built/admin/index.html +24 -30
- package/core/frontend/apps/private-blogging/lib/views/private.hbs +44 -43
- package/core/frontend/helpers/cancel_link.js +2 -2
- package/core/frontend/helpers/pagination.js +2 -2
- package/core/frontend/helpers/social_accounts.js +2 -2
- package/core/frontend/public/ghost.css +1 -1
- package/core/frontend/public/ghost.min.css +1 -1
- package/core/frontend/services/rendering/renderer.js +15 -0
- package/core/frontend/services/sitemap/user-map-generator.js +9 -2
- package/core/server/adapters/storage/LocalStorageBase.js +8 -5
- package/core/server/api/endpoints/db.js +19 -5
- package/core/server/api/endpoints/index.js +6 -2
- package/core/server/api/endpoints/mail.js +1 -1
- package/core/server/api/endpoints/member-metafields.js +89 -0
- package/core/server/api/endpoints/members.js +4 -4
- package/core/server/api/endpoints/posts.js +4 -3
- package/core/server/api/endpoints/tiers-checkout-config.js +59 -0
- package/core/server/api/endpoints/utils/serializers/input/members.js +4 -7
- package/core/server/api/endpoints/utils/serializers/output/config.js +1 -0
- package/core/server/api/endpoints/utils/serializers/output/index.js +6 -2
- package/core/server/api/endpoints/utils/serializers/output/{member-custom-fields.js → member-metafields.js} +2 -4
- package/core/server/api/endpoints/utils/serializers/output/members.js +2 -4
- package/core/server/api/endpoints/utils/serializers/output/tiers-checkout-config.js +12 -0
- package/core/server/api/endpoints/utils/validators/input/invitations.js +0 -5
- package/core/server/api/endpoints/utils/validators/input/pages.js +1 -1
- package/core/server/api/endpoints/utils/validators/input/posts.js +1 -1
- package/core/server/data/exporter/table-lists.js +3 -0
- package/core/server/data/importer/content-files.js +57 -0
- package/core/server/data/importer/handlers/image.js +2 -2
- package/core/server/data/importer/import-archive.js +131 -0
- package/core/server/data/importer/import-manager.js +18 -180
- package/core/server/data/importer/importers/content-file-importer.js +66 -4
- package/core/server/data/importer/importers/data/data-importer.js +3 -3
- package/core/server/data/migrations/utils/migrations.js +1 -1
- package/core/server/data/migrations/versions/6.61/2026-08-19-14-30-20-add-written-by-to-members-custom-field-values.js +42 -0
- package/core/server/data/migrations/versions/6.61/2026-08-19-14-38-53-add-members-custom-field-bindings-table.js +26 -0
- package/core/server/data/migrations/versions/6.61/2026-08-19-14-48-06-add-tier-checkout-field-tables.js +35 -0
- package/core/server/data/schema/schema.js +90 -0
- package/core/server/data/seeders/importers/automation-action-revisions-importer.js +3 -3
- package/core/server/data/seeders/importers/automation-actions-importer.js +3 -3
- package/core/server/data/seeders/importers/automation-run-steps-importer.js +3 -3
- package/core/server/data/seeders/importers/automation-runs-importer.js +5 -5
- package/core/server/data/seeders/importers/automations-importer.js +3 -3
- package/core/server/data/seeders/importers/comment-reports-importer.js +1 -1
- package/core/server/data/seeders/importers/comments-importer.js +1 -1
- package/core/server/data/seeders/importers/email-batches-importer.js +1 -1
- package/core/server/data/seeders/importers/email-recipients-importer.js +1 -1
- package/core/server/data/seeders/importers/emails-importer.js +1 -1
- package/core/server/data/seeders/importers/labels-importer.js +1 -1
- package/core/server/data/seeders/importers/members-click-events-importer.js +1 -1
- package/core/server/data/seeders/importers/members-created-events-importer.js +1 -1
- package/core/server/data/seeders/importers/members-feedback-importer.js +1 -1
- package/core/server/data/seeders/importers/members-importer.js +1 -1
- package/core/server/data/seeders/importers/members-login-events-importer.js +1 -1
- package/core/server/data/seeders/importers/members-products-importer.js +1 -1
- package/core/server/data/seeders/importers/members-status-events-importer.js +1 -1
- package/core/server/data/seeders/importers/members-stripe-customers-importer.js +1 -1
- package/core/server/data/seeders/importers/members-stripe-customers-subscriptions-importer.js +1 -1
- package/core/server/data/seeders/importers/members-subscribe-events-importer.js +1 -1
- package/core/server/data/seeders/importers/members-subscription-created-events-importer.js +1 -1
- package/core/server/data/seeders/importers/offer-redemptions-importer.js +1 -1
- package/core/server/data/seeders/importers/offers-importer.js +1 -1
- package/core/server/data/seeders/importers/posts-importer.js +1 -1
- package/core/server/data/seeders/importers/table-importer.js +2 -2
- package/core/server/data/seeders/importers/tags-importer.js +1 -1
- package/core/server/data/seeders/importers/users-importer.js +1 -1
- package/core/server/data/seeders/importers/web-mentions-importer.js +1 -1
- package/core/server/data/seeders/utils/random.js +3 -3
- package/core/server/ghost-server.js +3 -2
- package/core/server/lib/db-types/boolean.js +18 -0
- package/core/server/lib/image/image-size.js +24 -0
- package/core/server/lib/package-json/parse.js +2 -2
- package/core/server/lib/promise-pool.js +31 -0
- package/core/server/models/member.js +6 -17
- package/core/server/models/post.js +2 -2
- package/core/server/models/relations/authors.js +2 -16
- package/core/server/models/user.js +9 -2
- package/core/server/services/auth/passwordreset.js +13 -0
- package/core/server/services/automations/database-automations-repository.js +26 -26
- package/core/server/services/automations/poll.js +1 -0
- package/core/server/services/comments/comments-controller.js +30 -9
- package/core/server/services/comments/comments-service.js +55 -5
- package/core/server/services/content-import/csv/formula.js +12 -0
- package/core/server/services/content-import/csv/index.js +6 -1
- package/core/server/services/content-import/csv/parse.js +27 -6
- package/core/server/services/content-import/csv/serialize.js +13 -0
- package/core/server/services/content-import/import/assets.js +74 -0
- package/core/server/services/content-import/import/completion-email.js +164 -0
- package/core/server/services/content-import/import/errors-file.js +36 -0
- package/core/server/services/content-import/import/importer.js +204 -54
- package/core/server/services/content-import/import/local-media-url.js +63 -0
- package/core/server/services/content-import/import/media.js +254 -0
- package/core/server/services/content-import/import/post-data.js +1 -1
- package/core/server/services/content-import/import/post-link.js +11 -0
- package/core/server/services/content-import/import/post-repository.js +100 -0
- package/core/server/services/content-import/import/reader.js +9 -2
- package/core/server/services/content-import/import/relations.js +149 -0
- package/core/server/services/content-import/import/report.js +34 -0
- package/core/server/services/content-import/import/row.js +11 -2
- package/core/server/services/content-import/import/schema.js +30 -0
- package/core/server/services/content-import/import/source.js +125 -0
- package/core/server/services/content-import/import/staged-file.js +29 -0
- package/core/server/services/content-import/import/store.js +25 -2
- package/core/server/services/content-import/index.js +58 -34
- package/core/server/services/content-import/jobs/content-csv-import-job.js +20 -0
- package/core/server/services/custom-redirects/redirect-config-parser.js +2 -2
- package/core/server/services/custom-redirects/redirects-service.js +1 -1
- package/core/server/services/custom-redirects/validation.js +1 -1
- package/core/server/services/email-address/email-address-parser.js +30 -38
- package/core/server/services/email-address/email-address-service.js +37 -4
- package/core/server/services/email-analytics/email-analytics-service.js +8 -1
- package/core/server/services/email-analytics/index.js +2 -7
- package/core/server/services/email-analytics/lib/queries.js +7 -1
- package/core/server/services/email-service/batch-sending-service.js +0 -4
- package/core/server/services/email-service/email-renderer.js +14 -4
- package/core/server/services/email-service/email-service-wrapper.js +0 -9
- package/core/server/services/email-service/mailgun-email-provider.js +20 -9
- package/core/server/services/gift-links/schema.js +3 -3
- package/core/server/services/gifts/CONTEXT.md +1 -1
- package/core/server/services/gifts/constants.js +1 -1
- package/core/server/services/gifts/email-templates/gift-delivery.hbs +3 -3
- package/core/server/services/gifts/gift-bookshelf-repository.js +2 -2
- package/core/server/services/gifts/gift-delivery-bookshelf-repository.js +18 -9
- package/core/server/services/gifts/gift-delivery-schema.js +5 -5
- package/core/server/services/gifts/gift-delivery-service.js +4 -0
- package/core/server/services/gifts/gift-email-service.js +0 -1
- package/core/server/services/gifts/gift-schema.js +10 -10
- package/core/server/services/gifts/gift-service.js +4 -2
- package/core/server/services/gifts/index.js +3 -3
- package/core/server/services/gifts/jobs/index.js +4 -4
- package/core/server/services/indexnow-ping/indexnow-ping-service.js +2 -2
- package/core/server/services/invitations/accept.js +4 -2
- package/core/server/services/jobs/job-service.js +0 -9
- package/core/server/services/jobs-service/register-job-handlers.js +25 -15
- package/core/server/services/lib/mailgun-client.js +44 -8
- package/core/server/services/mail/ghost-mailer.js +4 -4
- package/core/server/services/media-inliner/external-media-inliner-job.js +12 -0
- package/core/server/services/media-inliner/external-media-inliner.js +103 -29
- package/core/server/services/media-inliner/service.js +13 -41
- package/core/server/services/media-inliner/types.js +2 -0
- package/core/server/services/member-welcome-emails/service.js +7 -7
- package/core/server/services/members/api.js +1 -0
- package/core/server/services/members/import-export/csv/parse.js +0 -7
- package/core/server/services/members/import-export/export/exporter.js +4 -6
- package/core/server/services/members/import-export/import/completion-email.js +2 -5
- package/core/server/services/members/import-export/import/importer.js +21 -5
- package/core/server/services/members/import-export/import/row.js +0 -3
- package/core/server/services/members/import-export/import/stripe-utils.js +6 -1
- package/core/server/services/members/import-export/index.js +10 -12
- package/core/server/services/members/jobs/clean-expired-comped-job.js +7 -0
- package/core/server/services/members/jobs/clean-expired-comped-task.js +145 -0
- package/core/server/services/members/jobs/index.js +48 -24
- package/core/server/services/members/members-api/controllers/member-controller.js +11 -0
- package/core/server/services/members/members-api/controllers/router-controller.js +3 -6
- package/core/server/services/members/members-api/members-api.js +9 -6
- package/core/server/services/members/members-api/repositories/event-repository.js +0 -4
- package/core/server/services/members/members-api/services/member-bread-service.js +53 -63
- package/core/server/services/members/members-api/services/payments-service.js +47 -1
- package/core/server/services/members/service.js +1 -5
- package/core/server/services/members-custom-fields/actions.js +27 -1
- package/core/server/services/members-custom-fields/bindings-service.js +120 -0
- package/core/server/services/members-custom-fields/codec.js +3 -4
- package/core/server/services/members-custom-fields/definitions-service.js +165 -22
- package/core/server/services/members-custom-fields/filter.js +75 -93
- package/core/server/services/members-custom-fields/index.js +11 -4
- package/core/server/services/members-custom-fields/key.js +2 -1
- package/core/server/services/members-custom-fields/models.js +1 -1
- package/core/server/services/members-custom-fields/queries.js +5 -0
- package/core/server/services/members-custom-fields/schema.js +37 -6
- package/core/server/services/members-custom-fields/serializers.js +3 -5
- package/core/server/services/members-custom-fields/values-service.js +65 -31
- package/core/server/services/mentions/mention-controller.js +23 -21
- package/core/server/services/mentions/process-webmention-job.js +16 -0
- package/core/server/services/mentions/service.js +8 -4
- package/core/server/services/public-config/config.js +7 -0
- package/core/server/services/route-settings/validation-errors.js +1 -1
- package/core/server/services/route-settings/yaml-parser.js +1 -1
- package/core/server/services/settings/settings-service.js +1 -8
- package/core/server/services/settings-helpers/settings-helpers.js +4 -6
- package/core/server/services/slack-ping/slack-ping-service.js +1 -1
- package/core/server/services/staff/staff-service-emails.js +2 -2
- package/core/server/services/stripe/config.js +14 -8
- package/core/server/services/stripe/service.js +10 -1
- package/core/server/services/stripe/services/checkout/completed-session.js +68 -0
- package/core/server/services/stripe/services/checkout/session-options.js +100 -0
- package/core/server/services/stripe/services/webhook/checkout-session-event-service.js +71 -1
- package/core/server/services/stripe/stripe-api.js +31 -0
- package/core/server/services/stripe/stripe-service.js +26 -9
- package/core/server/services/stripe/webhook-controller.js +10 -0
- package/core/server/services/tier-checkout-config/codec.js +99 -0
- package/core/server/services/tier-checkout-config/index.js +26 -0
- package/core/server/services/tier-checkout-config/models.js +63 -0
- package/core/server/services/tier-checkout-config/queries.js +80 -0
- package/core/server/services/tier-checkout-config/schema.js +33 -0
- package/core/server/services/tier-checkout-config/serializers.js +128 -0
- package/core/server/services/tier-checkout-config/service.js +304 -0
- package/core/server/services/tiers/service.js +21 -0
- package/core/server/services/update-check/index.js +13 -13
- package/core/server/services/update-check/jobs/update-check-job.js +7 -0
- package/core/server/services/update-check/update-check-service.js +2 -2
- package/core/server/web/admin/controller.js +1 -1
- package/core/server/web/api/endpoints/admin/routes.js +34 -20
- package/core/server/web/api/middleware/upload.js +2 -2
- package/core/server/web/parent/app.js +5 -0
- package/core/server/web/parent/middleware/filter-query-parameters.js +94 -0
- package/core/server/web/parent/middleware/index.js +1 -0
- package/core/server/web/query-parameter-policy/index.js +9 -0
- package/core/server/web/query-parameter-policy/policy.json +58 -0
- package/core/server/web/query-parameter-policy/schema.js +38 -0
- package/core/shared/config/defaults.json +3 -0
- package/core/shared/config/env/config.testing-mysql.json +4 -1
- package/core/shared/config/env/config.testing.json +3 -0
- package/core/shared/config/overrides.json +4 -2
- package/core/shared/labs.js +9 -15
- package/package.json +25 -25
- package/pnpm-lock.yaml +741 -108
- package/pnpm-workspace.yaml +12 -9
- package/tsconfig.json +2 -3
- package/components/tryghost-kg-default-nodes-2.2.0.tgz +0 -0
- package/core/built/admin/assets/Combination-gnJO3vIw.js +0 -41
- package/core/built/admin/assets/account-migration-8qTEI1i1.js +0 -1
- package/core/built/admin/assets/activitypub/inbox-DQ5CwLma.js +0 -1284
- package/core/built/admin/assets/activitypub/label-B72sfvEU.js +0 -16
- package/core/built/admin/assets/activitypub/layout-CXajhRsL.js +0 -1
- package/core/built/admin/assets/activitypub/settings-71gCm3SO.js +0 -315
- package/core/built/admin/assets/add-integration-modal--YpTq3c0.js +0 -1
- package/core/built/admin/assets/add-newsletter-modal-C8Ix2hol.js +0 -1
- package/core/built/admin/assets/add-recommendation-modal-CScuNFz4.js +0 -1
- package/core/built/admin/assets/alert-dialog-wEbVxANM.js +0 -1
- package/core/built/admin/assets/analytics-Cnji45H5.js +0 -1
- package/core/built/admin/assets/app-utils-D2Wp7ZSS.js +0 -1
- package/core/built/admin/assets/audience-DTLMXAFH.js +0 -1
- package/core/built/admin/assets/automations-BWpRCZ_f.js +0 -1
- package/core/built/admin/assets/bluesky-sharing-DHOpQxff.js +0 -1
- package/core/built/admin/assets/chart-helpers-B3OqQ6Ez.js +0 -1
- package/core/built/admin/assets/color-picker-Bmo1r9Fc.js +0 -3
- package/core/built/admin/assets/color-picker-field-DywowPhF.js +0 -1
- package/core/built/admin/assets/combobox-C4QCh6Yw.js +0 -1
- package/core/built/admin/assets/command-DcLbSifC.js +0 -1
- package/core/built/admin/assets/comments-Bxy_3WyE.js +0 -1
- package/core/built/admin/assets/confirmation-context-CLFHhyyH.js +0 -1
- package/core/built/admin/assets/custom-field-type-option-D0PXoSnP.js +0 -10
- package/core/built/admin/assets/custom-integration-modal-DCIHKN33.js +0 -1
- package/core/built/admin/assets/design-and-theme-modal-CZ-PkW7o.js +0 -23
- package/core/built/admin/assets/dialog-Db7c164G.js +0 -1
- package/core/built/admin/assets/dirty-confirm-dialog-CmARpZQS.js +0 -1
- package/core/built/admin/assets/dist-QLZuGq7o2.js +0 -1
- package/core/built/admin/assets/domain-D0SAtc-T.js +0 -1
- package/core/built/admin/assets/dropzone-CaPOeDRZ.js +0 -5
- package/core/built/admin/assets/edit-profile-By7v5XW5.js +0 -1
- package/core/built/admin/assets/editor-COXn4Atu.js +0 -8
- package/core/built/admin/assets/explore-BsNYrubV.js +0 -1
- package/core/built/admin/assets/feed-8RR1O1bK.js +0 -1
- package/core/built/admin/assets/filter-sources-4C0kF1NU.js +0 -1
- package/core/built/admin/assets/filters-CV5jCb9n.js +0 -1
- package/core/built/admin/assets/filters-Dmr0PoqU.js +0 -1
- package/core/built/admin/assets/first-promoter-modal-OCzSaL3B.js +0 -1
- package/core/built/admin/assets/gift-link-modal-0mzjn5oF.js +0 -1
- package/core/built/admin/assets/growth-55vWlGT_.js +0 -1
- package/core/built/admin/assets/growth-DA5AluYw.js +0 -3
- package/core/built/admin/assets/history-modal-G1s5hHz7.js +0 -1
- package/core/built/admin/assets/html-field-BegxsWpw.js +0 -1
- package/core/built/admin/assets/index-CDP9tIOk.js +0 -4
- package/core/built/admin/assets/index-Dqf2x-ry.css +0 -1
- package/core/built/admin/assets/index.es-BMUCj9TR.js +0 -1
- package/core/built/admin/assets/input-UHrKuvBT.js +0 -1
- package/core/built/admin/assets/invite-user-modal-BrAkxEO3.js +0 -1
- package/core/built/admin/assets/koenig-loader-DwVY_V-H.js +0 -1
- package/core/built/admin/assets/kpi-card-LERqWBoY.js +0 -1
- package/core/built/admin/assets/kpi-helpers-DB3ouhYF.js +0 -1
- package/core/built/admin/assets/kpi-tabs-BGvtMD8S.js +0 -1
- package/core/built/admin/assets/label-picker-BBWykUeA.js +0 -1
- package/core/built/admin/assets/layout-Di5K7doM.js +0 -1
- package/core/built/admin/assets/loading-indicator-SFTqyYVE.js +0 -1
- package/core/built/admin/assets/member-custom-fields-gOv6Hoh3.js +0 -1
- package/core/built/admin/assets/member-detail-D7mZDDp3.js +0 -1
- package/core/built/admin/assets/member-route-0Aa52Ntz.js +0 -1
- package/core/built/admin/assets/members-DvZG9t69.js +0 -2
- package/core/built/admin/assets/moderation-DzzPLDyl.js +0 -1
- package/core/built/admin/assets/navigation-modal-CI8Bx5vn.js +0 -1
- package/core/built/admin/assets/newsletter-Bg3cq5U-.js +0 -1
- package/core/built/admin/assets/newsletter-detail-modal-DQU3r4g2.js +0 -1
- package/core/built/admin/assets/newsletters-DMDfZbMS.js +0 -1
- package/core/built/admin/assets/note-i8k6pjaw.js +0 -1
- package/core/built/admin/assets/notifications-psA_2Ph4.js +0 -1
- package/core/built/admin/assets/offers-container-modal-BWfjSdyg.js +0 -1
- package/core/built/admin/assets/onboarding-DGQuV7Ew.js +0 -1
- package/core/built/admin/assets/overview-C-6xOLaf.js +0 -1
- package/core/built/admin/assets/overview-DaLhpRrC.js +0 -1
- package/core/built/admin/assets/pages-B6vr9F_D.js +0 -1
- package/core/built/admin/assets/popover-CFgqFDvC.js +0 -1
- package/core/built/admin/assets/portal-frame-DjBF-uy2.js +0 -1
- package/core/built/admin/assets/portal-modal-Cf0pBAk8.js +0 -1
- package/core/built/admin/assets/post-analytics-Buz1COoY.js +0 -1
- package/core/built/admin/assets/post-analytics-header-B-P0eFnT.js +0 -1
- package/core/built/admin/assets/posts-KY3aUhVV.js +0 -1
- package/core/built/admin/assets/preferences-Cq1aOiLy.js +0 -5
- package/core/built/admin/assets/profile-D6dHYf2o.js +0 -1
- package/core/built/admin/assets/select-D_RQaXgt.js +0 -1
- package/core/built/admin/assets/settings-B5hJKZ5C.js +0 -1
- package/core/built/admin/assets/settings-DIo_F-MF.js +0 -4
- package/core/built/admin/assets/share-modal-CLBfj377.js +0 -1
- package/core/built/admin/assets/shim-ClF_m5ux.js +0 -1
- package/core/built/admin/assets/slack-modal-Bt9I_tEo.js +0 -1
- package/core/built/admin/assets/sort-button-DMBXF7tP.js +0 -1
- package/core/built/admin/assets/stats-B_YkF7bp.js +0 -1
- package/core/built/admin/assets/stats-view-BOCp9RG8.js +0 -1
- package/core/built/admin/assets/stripe-connect-modal-BeSukI8q.js +0 -1
- package/core/built/admin/assets/tag-detail-2a3BfzTW.js +0 -2
- package/core/built/admin/assets/tags-Dp2_8UzL.js +0 -1
- package/core/built/admin/assets/testimonials-modal-C9nrypNo.js +0 -1
- package/core/built/admin/assets/tier-detail-modal-CxRwgTPR.js +0 -1
- package/core/built/admin/assets/use-check-theme-limit-error-C9-QhGdV.js +0 -1
- package/core/built/admin/assets/use-filter-params-CSpAvgD2.js +0 -1
- package/core/built/admin/assets/use-growth-stats-BOmoPDHJ.js +0 -1
- package/core/built/admin/assets/use-post-referrers-CbXLFIex.js +0 -1
- package/core/built/admin/assets/use-reply-chain-data-JYFKSO83.js +0 -1
- package/core/built/admin/assets/use-settings-navigation-BBzro2-V.js +0 -1
- package/core/built/admin/assets/use-tinybird-query-D2Aeoty5.js +0 -39
- package/core/built/admin/assets/use-tinybird-token-DBuwMmln.js +0 -1
- package/core/built/admin/assets/user-detail-modal-59mBJqxN.js +0 -1
- package/core/built/admin/assets/users-5msE1WMs.js +0 -1
- package/core/built/admin/assets/web-DUuX7nE2.js +0 -2
- package/core/built/admin/assets/web-N8ucs2aG.js +0 -1
- package/core/server/api/endpoints/member-custom-fields.js +0 -100
- package/core/server/services/jobs/worker-model-event-bridge.js +0 -149
- package/core/server/services/members/jobs/clean-expired-comped.js +0 -132
- package/core/server/services/update-check/run-update-check.js +0 -63
- /package/core/server/lib/{db-date.js → db-types/date.js} +0 -0
|
@@ -37,25 +37,35 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.default = registerJobHandlers;
|
|
40
|
-
const errors_1 = __importDefault(require("@tryghost/errors"));
|
|
41
|
-
const index_1 = require("./index");
|
|
42
40
|
const clean_tokens_job_1 = __importDefault(require("../members/jobs/clean-tokens-job"));
|
|
43
|
-
const
|
|
44
|
-
const gifts = __importStar(require("../gifts"));
|
|
41
|
+
const clean_expired_comped_job_1 = __importDefault(require("../members/jobs/clean-expired-comped-job"));
|
|
45
42
|
const clean_gifts_job_1 = __importDefault(require("../gifts/jobs/clean-gifts-job"));
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
43
|
+
const external_media_inliner_job_1 = __importDefault(require("../media-inliner/external-media-inliner-job"));
|
|
44
|
+
const content_csv_import_job_1 = __importDefault(require("../content-import/jobs/content-csv-import-job"));
|
|
45
|
+
const contentImport = __importStar(require("../content-import"));
|
|
46
|
+
const update_check_job_1 = __importDefault(require("../update-check/jobs/update-check-job"));
|
|
47
|
+
const process_webmention_job_1 = __importDefault(require("../mentions/process-webmention-job"));
|
|
48
|
+
const updateCheck = require('../update-check');
|
|
49
|
+
function registerJobHandlers({ jobsService, memberJobs, giftService, mediaInliner, mentionsController, }) {
|
|
50
50
|
jobsService.handle(clean_tokens_job_1.default, async () => {
|
|
51
|
-
await
|
|
51
|
+
await memberJobs.cleanTokens();
|
|
52
|
+
});
|
|
53
|
+
jobsService.handle(clean_expired_comped_job_1.default, async () => {
|
|
54
|
+
await memberJobs.cleanExpiredComped();
|
|
52
55
|
});
|
|
53
56
|
jobsService.handle(clean_gifts_job_1.default, async () => {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
await giftService.cleanup();
|
|
58
|
+
});
|
|
59
|
+
jobsService.handle(external_media_inliner_job_1.default, async (job) => {
|
|
60
|
+
await mediaInliner.inline(job.domains);
|
|
61
|
+
});
|
|
62
|
+
jobsService.handle(content_csv_import_job_1.default, async (job) => {
|
|
63
|
+
await contentImport.handleJob(job);
|
|
64
|
+
});
|
|
65
|
+
jobsService.handle(update_check_job_1.default, async () => {
|
|
66
|
+
await updateCheck({ rethrowErrors: true });
|
|
67
|
+
});
|
|
68
|
+
jobsService.handle(process_webmention_job_1.default, async (job) => {
|
|
69
|
+
await mentionsController.processWebmention(job);
|
|
60
70
|
});
|
|
61
71
|
}
|
|
@@ -165,8 +165,8 @@ module.exports = class MailgunClient {
|
|
|
165
165
|
* @param {Object} mailgunOptions
|
|
166
166
|
* @param {Function} batchHandler
|
|
167
167
|
* @param {Object} options
|
|
168
|
-
* @param {number} [options.maxEvents]
|
|
169
|
-
* @returns {Promise<void>}
|
|
168
|
+
* @param {number} [options.maxEvents] Per-domain soft maximum. We stop fetching a domain after we reached the maximum AND received at least one event after begin (not equal) to prevent deadlocks.
|
|
169
|
+
* @returns {Promise<{safeCursor?: Date} | void>}
|
|
170
170
|
*/
|
|
171
171
|
async fetchEvents(mailgunOptions, batchHandler, { maxEvents = Infinity } = {}) {
|
|
172
172
|
const mailgunInstance = this.getInstance();
|
|
@@ -184,12 +184,33 @@ module.exports = class MailgunClient {
|
|
|
184
184
|
// Determine which domains to fetch from
|
|
185
185
|
const domains = this.#getDomainsToFetch(mailgunConfig);
|
|
186
186
|
|
|
187
|
+
const cappedDomainCursors = [];
|
|
188
|
+
|
|
187
189
|
// Fetch events from each domain
|
|
188
190
|
for (const domain of domains) {
|
|
189
|
-
await this.#fetchEventsFromDomain(
|
|
190
|
-
|
|
191
|
-
|
|
191
|
+
const result = await this.#fetchEventsFromDomain(
|
|
192
|
+
domain,
|
|
193
|
+
mailgunInstance,
|
|
194
|
+
mailgunOptions,
|
|
195
|
+
batchHandler,
|
|
196
|
+
{ maxEvents },
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
if (
|
|
200
|
+
result.capped &&
|
|
201
|
+
result.lastEventTimestamp &&
|
|
202
|
+
Number.isFinite(result.lastEventTimestamp.getTime())
|
|
203
|
+
) {
|
|
204
|
+
cappedDomainCursors.push(result.lastEventTimestamp);
|
|
205
|
+
}
|
|
192
206
|
}
|
|
207
|
+
|
|
208
|
+
const safeCursor = cappedDomainCursors.reduce(
|
|
209
|
+
(earliest, cursor) => (!earliest || cursor < earliest ? cursor : earliest),
|
|
210
|
+
undefined,
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
return { safeCursor };
|
|
193
214
|
}
|
|
194
215
|
|
|
195
216
|
/**
|
|
@@ -220,7 +241,7 @@ module.exports = class MailgunClient {
|
|
|
220
241
|
* @param {Function} batchHandler
|
|
221
242
|
* @param {Object} options
|
|
222
243
|
* @param {number} options.maxEvents
|
|
223
|
-
* @returns {Promise<
|
|
244
|
+
* @returns {Promise<{capped: boolean, eventCount: number, lastEventTimestamp?: Date}>}
|
|
224
245
|
*/
|
|
225
246
|
async #fetchEventsFromDomain(
|
|
226
247
|
domain,
|
|
@@ -235,6 +256,9 @@ module.exports = class MailgunClient {
|
|
|
235
256
|
|
|
236
257
|
let batchCount = 0;
|
|
237
258
|
let totalBatchTime = 0;
|
|
259
|
+
let eventCount = 0;
|
|
260
|
+
let lastEventTimestamp;
|
|
261
|
+
let capped = false;
|
|
238
262
|
|
|
239
263
|
try {
|
|
240
264
|
let page = await this.getEventsFromMailgun(mailgunInstance, domain, mailgunOptions);
|
|
@@ -248,7 +272,6 @@ module.exports = class MailgunClient {
|
|
|
248
272
|
`[MailgunClient fetchEventsFromDomain ${domain}]: finished fetching first page with ${events.length} events`,
|
|
249
273
|
);
|
|
250
274
|
|
|
251
|
-
let eventCount = 0;
|
|
252
275
|
const beginTimestamp = mailgunOptions.begin
|
|
253
276
|
? Math.ceil(mailgunOptions.begin * 1000)
|
|
254
277
|
: undefined; // ceil here if we have rounding errors
|
|
@@ -263,6 +286,13 @@ module.exports = class MailgunClient {
|
|
|
263
286
|
totalBatchTime += batchDuration;
|
|
264
287
|
|
|
265
288
|
eventCount += events.length;
|
|
289
|
+
const batchLastEventTimestamp = events[events.length - 1].timestamp;
|
|
290
|
+
if (
|
|
291
|
+
batchLastEventTimestamp &&
|
|
292
|
+
(!lastEventTimestamp || batchLastEventTimestamp > lastEventTimestamp)
|
|
293
|
+
) {
|
|
294
|
+
lastEventTimestamp = batchLastEventTimestamp;
|
|
295
|
+
}
|
|
266
296
|
|
|
267
297
|
if (
|
|
268
298
|
eventCount >= maxEvents &&
|
|
@@ -270,6 +300,7 @@ module.exports = class MailgunClient {
|
|
|
270
300
|
!events[events.length - 1].timestamp ||
|
|
271
301
|
events[events.length - 1].timestamp.getTime() > beginTimestamp)
|
|
272
302
|
) {
|
|
303
|
+
capped = true;
|
|
273
304
|
break;
|
|
274
305
|
}
|
|
275
306
|
|
|
@@ -294,10 +325,15 @@ module.exports = class MailgunClient {
|
|
|
294
325
|
const overallEndTime = Date.now();
|
|
295
326
|
const totalDuration = overallEndTime - overallStartTime;
|
|
296
327
|
const averageBatchTime = batchCount > 0 ? totalBatchTime / batchCount : 0;
|
|
328
|
+
const cursor = Number.isFinite(lastEventTimestamp?.getTime())
|
|
329
|
+
? lastEventTimestamp.toISOString()
|
|
330
|
+
: 'none';
|
|
297
331
|
|
|
298
332
|
logging.info(
|
|
299
|
-
`[MailgunClient fetchEventsFromDomain ${domain}]: Processed ${batchCount} batches in ${(totalDuration / 1000).toFixed(2)}s. Average batch time: ${(averageBatchTime / 1000).toFixed(2)}s`,
|
|
333
|
+
`[MailgunClient fetchEventsFromDomain ${domain}]: Processed ${eventCount} events in ${batchCount} batches in ${(totalDuration / 1000).toFixed(2)}s. Average batch time: ${(averageBatchTime / 1000).toFixed(2)}s. Status: ${capped ? 'capped' : 'exhausted'}. Cursor: ${cursor}`,
|
|
300
334
|
);
|
|
335
|
+
|
|
336
|
+
return { capped, eventCount, lastEventTimestamp };
|
|
301
337
|
} catch (error) {
|
|
302
338
|
logging.error(`[MailgunClient fetchEventsFromDomain ${domain}]: Error fetching events`);
|
|
303
339
|
logging.error(error);
|
|
@@ -17,7 +17,7 @@ const messages = {
|
|
|
17
17
|
reason: ' Reason: {reason}.',
|
|
18
18
|
messageSent: 'Message sent. Double check inbox and spam folder!',
|
|
19
19
|
};
|
|
20
|
-
const
|
|
20
|
+
const emailAddressParser = require('../email-address/email-address-parser');
|
|
21
21
|
const DEFAULT_TAGS = ['ghost-email', 'transactional-email'];
|
|
22
22
|
const MAX_MAILGUN_TAGS = 10;
|
|
23
23
|
|
|
@@ -54,8 +54,8 @@ function getFromAddress(requestedFromAddress, requestedReplyToAddress) {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
return {
|
|
57
|
-
from:
|
|
58
|
-
replyTo: addresses.replyTo ?
|
|
57
|
+
from: emailAddressParser.stringify(addresses.from),
|
|
58
|
+
replyTo: addresses.replyTo ? emailAddressParser.stringify(addresses.replyTo) : null,
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -100,7 +100,7 @@ function createMessage(message) {
|
|
|
100
100
|
*/
|
|
101
101
|
function createMailError({ message, err, ignoreDefaultMessage } = { message: '' }) {
|
|
102
102
|
const helpMessage = tpl(messages.checkEmailConfigInstructions, {
|
|
103
|
-
url: 'https://ghost.org/
|
|
103
|
+
url: 'https://docs.ghost.org/config/#mail',
|
|
104
104
|
});
|
|
105
105
|
const defaultErrorMessage = tpl(messages.failedSendingEmailError);
|
|
106
106
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const job_1 = require("../jobs-service/job");
|
|
4
|
+
class ExternalMediaInlinerJob extends job_1.Job {
|
|
5
|
+
static type = 'external-media-inliner';
|
|
6
|
+
domains;
|
|
7
|
+
constructor({ domains }) {
|
|
8
|
+
super();
|
|
9
|
+
this.domains = domains;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
exports.default = ExternalMediaInlinerJob;
|
|
@@ -177,6 +177,96 @@ class ExternalMediaInliner {
|
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Download and store one external media URL without deciding where it came from
|
|
182
|
+
* or where its replacement belongs. Callers own discovery and replacement.
|
|
183
|
+
*
|
|
184
|
+
* @param {string} sourceUrl
|
|
185
|
+
* @returns {Promise<import('./types').ExternalMediaImportResult>}
|
|
186
|
+
*/
|
|
187
|
+
async importUrl(sourceUrl) {
|
|
188
|
+
let response;
|
|
189
|
+
try {
|
|
190
|
+
response = await this.getRemoteMedia(sourceUrl);
|
|
191
|
+
} catch (error) {
|
|
192
|
+
return {
|
|
193
|
+
status: 'failed',
|
|
194
|
+
sourceUrl,
|
|
195
|
+
stage: 'download',
|
|
196
|
+
reason: 'The media file could not be downloaded.',
|
|
197
|
+
error,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (!response) {
|
|
202
|
+
return {
|
|
203
|
+
status: 'failed',
|
|
204
|
+
sourceUrl,
|
|
205
|
+
stage: 'download',
|
|
206
|
+
reason: 'The media file could not be downloaded.',
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
let media;
|
|
211
|
+
try {
|
|
212
|
+
media = await this.extractFileDataFromResponse(sourceUrl, response);
|
|
213
|
+
} catch (error) {
|
|
214
|
+
return {
|
|
215
|
+
status: 'failed',
|
|
216
|
+
sourceUrl,
|
|
217
|
+
stage: 'extract',
|
|
218
|
+
reason: 'The downloaded media file could not be read.',
|
|
219
|
+
error,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
const storedUrl = await this.storeMediaLocally(media);
|
|
225
|
+
if (!storedUrl) {
|
|
226
|
+
return {
|
|
227
|
+
status: 'failed',
|
|
228
|
+
sourceUrl,
|
|
229
|
+
stage: 'unsupported',
|
|
230
|
+
reason: 'No configured storage accepts this media file.',
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
status: 'stored',
|
|
236
|
+
sourceUrl,
|
|
237
|
+
storedUrl,
|
|
238
|
+
};
|
|
239
|
+
} catch (error) {
|
|
240
|
+
return {
|
|
241
|
+
status: 'failed',
|
|
242
|
+
sourceUrl,
|
|
243
|
+
stage: 'storage',
|
|
244
|
+
reason: 'The media file could not be stored in Ghost.',
|
|
245
|
+
error,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Convert an import result into the replacement URL expected by the existing
|
|
252
|
+
* content and field processing. Expected download and unsupported-file failures
|
|
253
|
+
* return null so the original URL remains unchanged. Results containing an
|
|
254
|
+
* underlying processing error are rethrown so the established per-resource
|
|
255
|
+
* catch boundary continues to log and isolate them.
|
|
256
|
+
*
|
|
257
|
+
* @param {import('./types').ExternalMediaImportResult} result
|
|
258
|
+
* @returns {string|null}
|
|
259
|
+
*/
|
|
260
|
+
#replacementUrlFromImportResult(result) {
|
|
261
|
+
if (result.status === 'stored') {
|
|
262
|
+
return result.storedUrl;
|
|
263
|
+
}
|
|
264
|
+
if ('error' in result) {
|
|
265
|
+
throw result.error;
|
|
266
|
+
}
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
|
|
180
270
|
static findMatches(content, domain) {
|
|
181
271
|
// NOTE: the src could end with a quote, bracket, apostrophe, double-backslash, or encoded quote.
|
|
182
272
|
// Backlashes are added to content as an escape character
|
|
@@ -208,22 +298,14 @@ class ExternalMediaInliner {
|
|
|
208
298
|
const matches = this.constructor.findMatches(content, domain);
|
|
209
299
|
|
|
210
300
|
for (const src of matches) {
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
if (
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const inlinedSrc = await this.storeMediaLocally(media);
|
|
220
|
-
|
|
221
|
-
if (inlinedSrc) {
|
|
222
|
-
// NOTE: does not account for duplicate images in content
|
|
223
|
-
// in those cases would be processed twice
|
|
224
|
-
content = content.replace(src, inlinedSrc);
|
|
225
|
-
logging.info(`Inlined media: ${src} -> ${inlinedSrc}`);
|
|
226
|
-
}
|
|
301
|
+
const result = await this.importUrl(src);
|
|
302
|
+
const replacementUrl = this.#replacementUrlFromImportResult(result);
|
|
303
|
+
|
|
304
|
+
if (replacementUrl) {
|
|
305
|
+
// NOTE: does not account for duplicate images in content
|
|
306
|
+
// in those cases would be processed twice
|
|
307
|
+
content = content.replace(src, replacementUrl);
|
|
308
|
+
logging.info(`Inlined media: ${src} -> ${replacementUrl}`);
|
|
227
309
|
}
|
|
228
310
|
}
|
|
229
311
|
}
|
|
@@ -246,20 +328,12 @@ class ExternalMediaInliner {
|
|
|
246
328
|
const src = resourceModel.get(field);
|
|
247
329
|
|
|
248
330
|
if (src && src.startsWith(domain)) {
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
let media;
|
|
252
|
-
if (response) {
|
|
253
|
-
media = await this.extractFileDataFromResponse(src, response);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
if (media) {
|
|
257
|
-
const inlinedSrc = await this.storeMediaLocally(media);
|
|
331
|
+
const result = await this.importUrl(src);
|
|
332
|
+
const replacementUrl = this.#replacementUrlFromImportResult(result);
|
|
258
333
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}
|
|
334
|
+
if (replacementUrl) {
|
|
335
|
+
updatedFields[field] = replacementUrl;
|
|
336
|
+
logging.info(`Added media to inline: ${src} -> ${replacementUrl}`);
|
|
263
337
|
}
|
|
264
338
|
}
|
|
265
339
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
const errors = require('@tryghost/errors');
|
|
2
|
+
|
|
3
|
+
let instance;
|
|
4
|
+
|
|
1
5
|
module.exports = {
|
|
2
6
|
async init() {
|
|
3
|
-
const debug = require('@tryghost/debug')('mediaInliner');
|
|
4
7
|
const MediaInliner = require('./external-media-inliner');
|
|
5
|
-
const logging = require('@tryghost/logging');
|
|
6
8
|
const models = require('../../models');
|
|
7
|
-
const jobsService = require('../jobs');
|
|
8
9
|
const adapterManager = require('../../services/adapter-manager').default;
|
|
9
10
|
|
|
10
11
|
const mediaStorage = adapterManager.getAdapter('storage:media');
|
|
@@ -31,45 +32,16 @@ module.exports = {
|
|
|
31
32
|
},
|
|
32
33
|
});
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (!domains || !domains.length) {
|
|
37
|
-
// default domains to inline from if none are provided
|
|
38
|
-
domains = ['https://s3.amazonaws.com/revue', 'https://substackcdn.com'];
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
debug('[Inliner] Starting media inlining job for domains: ', domains);
|
|
35
|
+
instance = mediaInliner;
|
|
36
|
+
},
|
|
42
37
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const startedAt = Date.now();
|
|
50
|
-
logging.info('[Background Job] external-media-inliner started');
|
|
51
|
-
try {
|
|
52
|
-
const result = await mediaInliner.inline(data.domains);
|
|
53
|
-
logging.info(
|
|
54
|
-
`[Background Job] external-media-inliner completed in ${Date.now() - startedAt}ms`,
|
|
55
|
-
);
|
|
56
|
-
return result;
|
|
57
|
-
} catch (err) {
|
|
58
|
-
logging.error(
|
|
59
|
-
err,
|
|
60
|
-
`[Background Job] external-media-inliner failed after ${Date.now() - startedAt}ms`,
|
|
61
|
-
);
|
|
62
|
-
throw err;
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
data: { domains },
|
|
66
|
-
offloaded: false,
|
|
67
|
-
});
|
|
38
|
+
getInstance() {
|
|
39
|
+
if (!instance) {
|
|
40
|
+
throw new errors.IncorrectUsageError({
|
|
41
|
+
message: 'Media inliner used before init(). Call init() from boot first.',
|
|
42
|
+
});
|
|
43
|
+
}
|
|
68
44
|
|
|
69
|
-
|
|
70
|
-
status: 'success',
|
|
71
|
-
};
|
|
72
|
-
},
|
|
73
|
-
};
|
|
45
|
+
return instance;
|
|
74
46
|
},
|
|
75
47
|
};
|
|
@@ -7,7 +7,7 @@ const MagicLink = require('../lib/magic-link/magic-link');
|
|
|
7
7
|
const sentry = require('../../../shared/sentry');
|
|
8
8
|
const emailAddressService = require('../email-address');
|
|
9
9
|
const settingsHelpers = require('../settings-helpers');
|
|
10
|
-
const
|
|
10
|
+
const emailAddressParser = require('../email-address/email-address-parser');
|
|
11
11
|
const mail = require('../mail');
|
|
12
12
|
const MailgunClient = require('../lib/mailgun-client');
|
|
13
13
|
const config = require('../../../shared/config');
|
|
@@ -152,7 +152,7 @@ class MemberWelcomeEmailService {
|
|
|
152
152
|
},
|
|
153
153
|
});
|
|
154
154
|
|
|
155
|
-
const from =
|
|
155
|
+
const from = emailAddressParser.stringify(fromAddresses.from);
|
|
156
156
|
const replyToSetting = newsletter.get('sender_reply_to');
|
|
157
157
|
let replyTo = null;
|
|
158
158
|
|
|
@@ -173,7 +173,7 @@ class MemberWelcomeEmailService {
|
|
|
173
173
|
});
|
|
174
174
|
|
|
175
175
|
if (addresses.replyTo) {
|
|
176
|
-
replyTo =
|
|
176
|
+
replyTo = emailAddressParser.stringify(addresses.replyTo);
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
|
|
@@ -199,10 +199,10 @@ class MemberWelcomeEmailService {
|
|
|
199
199
|
async #getEffectiveSenderOptions(automatedSender = {}) {
|
|
200
200
|
const defaultOptions = await this.#getSenderOptions();
|
|
201
201
|
const defaultFrom =
|
|
202
|
-
|
|
202
|
+
emailAddressParser.parse(defaultOptions.from || '') ||
|
|
203
203
|
emailAddressService.service.defaultFromEmail;
|
|
204
204
|
const defaultReplyTo = defaultOptions.replyTo
|
|
205
|
-
?
|
|
205
|
+
? emailAddressParser.parse(defaultOptions.replyTo)
|
|
206
206
|
: undefined;
|
|
207
207
|
|
|
208
208
|
const senderName = trimValue(automatedSender.senderName) || defaultFrom?.name || undefined;
|
|
@@ -218,10 +218,10 @@ class MemberWelcomeEmailService {
|
|
|
218
218
|
});
|
|
219
219
|
|
|
220
220
|
return {
|
|
221
|
-
from:
|
|
221
|
+
from: emailAddressParser.stringify(addresses.from),
|
|
222
222
|
...(addresses.replyTo
|
|
223
223
|
? {
|
|
224
|
-
replyTo:
|
|
224
|
+
replyTo: emailAddressParser.stringify(addresses.replyTo),
|
|
225
225
|
}
|
|
226
226
|
: {}),
|
|
227
227
|
};
|
|
@@ -274,6 +274,7 @@ function createApiInstance(config) {
|
|
|
274
274
|
// two together in boot order. Custom fields is initialised in initCore, the
|
|
275
275
|
// members API is built in initServices, so this is always the live instance.
|
|
276
276
|
customFieldValues: customFieldsService.values,
|
|
277
|
+
customFieldDefinitions: customFieldsService.definitions,
|
|
277
278
|
});
|
|
278
279
|
|
|
279
280
|
return membersApiInstance;
|
|
@@ -43,19 +43,12 @@ function parse(path, headerMapping) {
|
|
|
43
43
|
// hasOwn, not `in`: a prototype-named header would otherwise match an
|
|
44
44
|
// inherited method on the mapping and take a function as its mapped name.
|
|
45
45
|
if (headerMapping && Object.hasOwn(headerMapping, header)) {
|
|
46
|
-
// An empty target is the caller naming a column to leave out, which
|
|
47
|
-
// it has to name: an unnamed column carries through below, and for a
|
|
48
|
-
// custom_fields.* column carrying through means being imported. So
|
|
49
|
-
// omitting a column from the mapping is the opposite of excluding it.
|
|
50
46
|
if (!headerMapping[header]) {
|
|
51
47
|
continue;
|
|
52
48
|
}
|
|
53
49
|
row[headerMapping[header]] = value;
|
|
54
50
|
}
|
|
55
51
|
else if (isSafeColumnName(header)) {
|
|
56
|
-
// Carry any unmapped column through untouched, so the import is not
|
|
57
|
-
// constrained to a known vocabulary: a custom_fields.* column
|
|
58
|
-
// survives parsing even though nothing consumes it yet.
|
|
59
52
|
row[header] = value;
|
|
60
53
|
}
|
|
61
54
|
}
|
|
@@ -54,14 +54,14 @@ class MembersCSVExporter {
|
|
|
54
54
|
const hasFilter = options.limit !== 'all' || options.filter || options.search;
|
|
55
55
|
const ids = hasFilter ? await this._members.findFilteredIds(options) : null;
|
|
56
56
|
if (ids) {
|
|
57
|
-
logging.info(
|
|
57
|
+
logging.info({ event: { name: 'members-export.filtered' }, matched: ids.length }, 'Found members matching the export filter');
|
|
58
58
|
}
|
|
59
59
|
const reference = await this.fetchReferenceData();
|
|
60
60
|
const membersQuery = this._knex('members').select(...MEMBER_COLUMNS);
|
|
61
61
|
if (ids) {
|
|
62
62
|
membersQuery.whereIn('id', ids);
|
|
63
63
|
}
|
|
64
|
-
logging.info('
|
|
64
|
+
logging.info({ event: { name: 'members-export.started' } }, 'Starting streaming export of members');
|
|
65
65
|
const batchingTransform = this.createBatchingTransform();
|
|
66
66
|
const processingTransform = this.createProcessingTransform(reference);
|
|
67
67
|
(0, node_stream_1.pipeline)(membersQuery.stream(), batchingTransform, processingTransform, (err) => {
|
|
@@ -69,9 +69,7 @@ class MembersCSVExporter {
|
|
|
69
69
|
logging.error({ event: { name: 'members-export.stream.error' }, err }, 'Members export stream failed');
|
|
70
70
|
}
|
|
71
71
|
else {
|
|
72
|
-
logging.info('
|
|
73
|
-
(Date.now() - start) / 1000 +
|
|
74
|
-
's');
|
|
72
|
+
logging.info({ event: { name: 'members-export.completed' }, durationMs: Date.now() - start }, 'Members export completed');
|
|
75
73
|
}
|
|
76
74
|
});
|
|
77
75
|
return processingTransform;
|
|
@@ -95,7 +93,7 @@ class MembersCSVExporter {
|
|
|
95
93
|
acc[label.id] = label.name;
|
|
96
94
|
return acc;
|
|
97
95
|
}, {}));
|
|
98
|
-
logging.info(
|
|
96
|
+
logging.info({ event: { name: 'members-export.reference_data_read' }, durationMs: Date.now() - start }, 'Read the products and labels an export names');
|
|
99
97
|
const activeCustomFields = await this._customFields.activeDefinitions();
|
|
100
98
|
return { allProducts, allLabels, activeCustomFields };
|
|
101
99
|
}
|
|
@@ -55,8 +55,6 @@ function humaniseError(row) {
|
|
|
55
55
|
function stringifyLabels(labels) {
|
|
56
56
|
return labels.map((label) => (typeof label === 'string' ? label : label.name)).join(',');
|
|
57
57
|
}
|
|
58
|
-
// The custom_fields.* cells a submitted row carried, echoed untouched so a manager can
|
|
59
|
-
// fix a failed row and re-upload the values they mapped.
|
|
60
58
|
function customFieldCells(row) {
|
|
61
59
|
return Object.fromEntries(Object.entries(row).filter(([column]) => (0, csv_2.isCustomFieldColumn)(column)));
|
|
62
60
|
}
|
|
@@ -82,9 +80,8 @@ function toErrorReportRow(row) {
|
|
|
82
80
|
}
|
|
83
81
|
// The error report attached to the completion email: the failed rows as CSV, called only
|
|
84
82
|
// when there are rows to list. It shares the serialiser with the export but not the
|
|
85
|
-
// shaping -- the export writes db members, this echoes submitted rows. Member columns
|
|
86
|
-
//
|
|
87
|
-
// before the last error column, and each row's custom cells merged on.
|
|
83
|
+
// shaping -- the export writes db members, this echoes submitted rows. Member columns
|
|
84
|
+
// come from the shaper's keys, so the type stays the single source.
|
|
88
85
|
function buildErrorReport(errors) {
|
|
89
86
|
const memberColumns = Object.keys(toErrorReportRow(errors[0])).filter((column) => column !== 'error');
|
|
90
87
|
const customColumns = [...new Set(errors.flatMap((row) => Object.keys(customFieldCells(row))))];
|
|
@@ -17,6 +17,7 @@ const messages = {
|
|
|
17
17
|
giftCannotCombineWithImportTier: 'Cannot specify both gift_id and import_tier.',
|
|
18
18
|
giftCannotCombineWithComplimentary: 'Cannot specify both gift_id and complimentary_plan.',
|
|
19
19
|
giftReassignFailed: 'Failed to reassign gift to member.',
|
|
20
|
+
customFieldWriteFailed: 'Failed to save the custom field values for this member.',
|
|
20
21
|
};
|
|
21
22
|
// Columns whose presence makes a row slow to import (they reach out to Stripe), so
|
|
22
23
|
// a file carrying any of them is deferred regardless of size. The routing decision
|
|
@@ -90,7 +91,7 @@ class MembersCSVImporter {
|
|
|
90
91
|
// a request without a user actually reaches the deferred path.
|
|
91
92
|
const emailRecipient = requestUserEmail ?? (await this._email.getDefaultRecipient());
|
|
92
93
|
const spooled = await this._spool.write(rows);
|
|
93
|
-
logging.info(
|
|
94
|
+
logging.info({ event: { name: 'members.import.queued' }, rows: rows.length }, 'Members import queued');
|
|
94
95
|
this._addJob({
|
|
95
96
|
job: () => this.runImportJob(spooled, { labelName, extraLabels, emailRecipient }, verificationTrigger),
|
|
96
97
|
offloaded: false,
|
|
@@ -101,7 +102,7 @@ class MembersCSVImporter {
|
|
|
101
102
|
// in the job itself, and there is no retry behind it.
|
|
102
103
|
async runImportJob(spooled, { labelName, extraLabels, emailRecipient, }, verificationTrigger) {
|
|
103
104
|
const startedAt = Date.now();
|
|
104
|
-
logging.info(
|
|
105
|
+
logging.info({ event: { name: 'members.import.started' } }, 'Members import started');
|
|
105
106
|
// Null until the import produces one: parsing and mapping already happened inside
|
|
106
107
|
// the request, so anything failing from here is ours rather than the file's.
|
|
107
108
|
let result = null;
|
|
@@ -125,10 +126,15 @@ class MembersCSVImporter {
|
|
|
125
126
|
links: this._email.links,
|
|
126
127
|
})));
|
|
127
128
|
if (result) {
|
|
128
|
-
logging.info(
|
|
129
|
+
logging.info({
|
|
130
|
+
event: { name: 'members.import.completed' },
|
|
131
|
+
durationMs: Date.now() - startedAt,
|
|
132
|
+
imported: result.imported,
|
|
133
|
+
rejected: result.errors.length,
|
|
134
|
+
}, 'Members import completed');
|
|
129
135
|
}
|
|
130
136
|
else {
|
|
131
|
-
logging.info(
|
|
137
|
+
logging.info({ event: { name: 'members.import.failed' }, durationMs: Date.now() - startedAt }, 'Members import failed');
|
|
132
138
|
}
|
|
133
139
|
}
|
|
134
140
|
// Only the write itself may throw. Callers rely on that to tell an import that never
|
|
@@ -300,7 +306,17 @@ class MembersCSVImporter {
|
|
|
300
306
|
}
|
|
301
307
|
}
|
|
302
308
|
// On the row's transaction, so the values commit or roll back with the member.
|
|
303
|
-
|
|
309
|
+
try {
|
|
310
|
+
await this._customFields.applyWrite(member.id, customFieldPlan, trx);
|
|
311
|
+
}
|
|
312
|
+
catch (writeError) {
|
|
313
|
+
// planWrite passed every value before the transaction opened, so a failure
|
|
314
|
+
// here is ours and not the row's. Operators get the original, which a driver
|
|
315
|
+
// will have written a query into; the publisher gets a sentence instead, in a
|
|
316
|
+
// file they open next to a spreadsheet.
|
|
317
|
+
this._report(writeError);
|
|
318
|
+
throw new errors.DataImportError({ message: tpl(messages.customFieldWriteFailed) });
|
|
319
|
+
}
|
|
304
320
|
await trx.commit();
|
|
305
321
|
imported += 1;
|
|
306
322
|
}
|
|
@@ -16,9 +16,6 @@ const optionalCell = zod_1.z
|
|
|
16
16
|
// not defaulted), so a CSV lacking these columns leaves the member's state untouched.
|
|
17
17
|
const isSubscribed = (cell) => cell.toLowerCase() !== 'false';
|
|
18
18
|
const isComplimentary = (cell) => cell.toLowerCase() === 'true';
|
|
19
|
-
// The member import vocabulary as a schema: the single source of truth that coerces the
|
|
20
|
-
// raw string Row into typed member fields. Unknown columns (custom_fields.*, and anything
|
|
21
|
-
// else a source carries) pass through via .loose() so nothing is lost before the kernel.
|
|
22
19
|
exports.memberImportRowSchema = zod_1.z
|
|
23
20
|
.object({
|
|
24
21
|
id: optionalCell,
|