ghost 6.64.0 → 6.65.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/bin/create-migration.js +15 -0
- package/components/tryghost-adapter-base-scheduling-0.2.4.tgz +0 -0
- package/components/tryghost-api-framework-0.0.0.tgz +0 -0
- package/components/tryghost-i18n-0.0.0.tgz +0 -0
- package/components/tryghost-kg-default-cards-10.3.6.tgz +0 -0
- package/components/tryghost-kg-lexical-html-renderer-1.5.1.tgz +0 -0
- package/components/tryghost-metafield-types-0.0.0.tgz +0 -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 +15 -2
- package/content/themes/casper/assets/js/init.js +37 -9
- package/content/themes/casper/package.json +8 -8
- 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 +14 -1
- package/content/themes/source/assets/js/main.js +24 -4
- package/content/themes/source/package.json +7 -7
- package/core/boot.js +22 -16
- package/core/bridge.js +1 -1
- package/core/built/admin/assets/Combination-DQ6f9A5P.js +41 -0
- package/core/built/admin/assets/{PolarAngleAxis-a81ieQUO.js → PolarAngleAxis-BBaU7PRV.js} +1 -1
- package/core/built/admin/assets/RadialBarChart-Bp20JPnA.js +1 -0
- package/core/built/admin/assets/{about-39CAfroa.js → about-Dp43j18M.js} +1 -1
- package/core/built/admin/assets/accordion-h62UzFhj.js +1 -0
- package/core/built/admin/assets/account-migration-CBTbtX6Q.js +1 -0
- package/core/built/admin/assets/action-list-DC9QpX-l.js +1 -0
- package/core/built/admin/assets/activitypub/{account-migration-KA6Jjf9L.js → account-migration-B1DUZS4Q.js} +8 -8
- package/core/built/admin/assets/activitypub/activitypub.js +24 -11
- package/core/built/admin/assets/activitypub/avatar-CRGud8yf.js +153 -0
- package/core/built/admin/assets/activitypub/{avatar-flipboard-csQaNeYK.js → avatar-flipboard-BZNFAXfm.js} +3 -3
- package/core/built/admin/assets/activitypub/{bluesky-sharing-C7nTDMvw.js → bluesky-sharing-Cg_v-LwS.js} +30 -31
- package/core/built/admin/assets/activitypub/{chunk-KS7C4IRE-Ck3O4oo2.js → chunk-KS7C4IRE-CTUL98Lc.js} +2498 -1885
- package/core/built/admin/assets/activitypub/{content-formatters-CLcjaBwV.js → content-formatters-D7GWP7nH.js} +23 -24
- package/core/built/admin/assets/activitypub/{copy-DcOh8kXw.js → copy-CgzTvoeR.js} +2 -2
- package/core/built/admin/assets/activitypub/{domain-fZ-X8i4-.js → domain-C3G_nkHU.js} +46 -46
- package/core/built/admin/assets/activitypub/{edit-profile-BqMLQV88.js → edit-profile-C8cCY-AK.js} +927 -928
- package/core/built/admin/assets/activitypub/{explore-B4yPoP8E.js → explore-NlDT4inq.js} +11 -11
- package/core/built/admin/assets/activitypub/{feed-BRC_mvAe.js → feed-DF22jCTV.js} +10 -10
- package/core/built/admin/assets/activitypub/{field-COacNgOt.js → field-95-iuTte.js} +4 -4
- package/core/built/admin/assets/activitypub/{hash-C7u-irQQ.js → hash-R7LMLnhY.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-CbTTdDQ3.js → inbox-DUJUiV6x.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-BW-yvUoN.js → inbox-DerExBzA.js} +416 -414
- package/core/built/admin/assets/activitypub/{label-kPMmYI7l.js → label-EwCn1QYc.js} +4 -4
- package/core/built/admin/assets/activitypub/layout-Dy6NnW1G.js +1 -0
- package/core/built/admin/assets/activitypub/{moderation-CuidJzc0.js → moderation-BSkfiVYo.js} +73 -75
- package/core/built/admin/assets/activitypub/{note-CAMRRSqD.js → note-DOnAJTXK.js} +9 -9
- package/core/built/admin/assets/activitypub/{notifications-BmNlXGgp.js → notifications-DZc6XTjG.js} +27 -27
- package/core/built/admin/assets/activitypub/{onboarding-Dmun9jr_.js → onboarding-8vEIwYTH.js} +3 -3
- package/core/built/admin/assets/activitypub/{onboarding-Bqg7fh_L.js → onboarding-DYVqbNmp.js} +2 -2
- package/core/built/admin/assets/activitypub/{preferences--oH19wf9.js → preferences-DDok6F2N.js} +1418 -1390
- package/core/built/admin/assets/activitypub/{profile-DH_tQx2D.js → profile-DjVa2hUk.js} +254 -255
- package/core/built/admin/assets/activitypub/{reply-Cap9PxSH.js → reply-DtXBpHxu.js} +2 -2
- package/core/built/admin/assets/activitypub/routes-Cw302e1c.js +4946 -0
- package/core/built/admin/assets/activitypub/{separator-CAxxu8VV.js → separator-FpRpVDd3.js} +8 -9
- package/core/built/admin/assets/activitypub/settings-yK-puALn.js +314 -0
- package/core/built/admin/assets/activitypub/{site-BYps63oh.js → site-CHw0R2PA.js} +2 -2
- package/core/built/admin/assets/activitypub/{step-1-CKjR2KAm.js → step-1-737bnvxk.js} +7 -7
- package/core/built/admin/assets/activitypub/{step-2-B6PKfOUQ.js → step-2-DBaIsAsM.js} +7 -7
- package/core/built/admin/assets/activitypub/{step-3-C-qBbcUo.js → step-3-BBmy9m9V.js} +23 -23
- package/core/built/admin/assets/activitypub/tabs-LQ2xirdm.js +1101 -0
- package/core/built/admin/assets/activitypub/{topic-filter-DsUOuu0k.js → topic-filter-75Nu0CKj.js} +4 -4
- package/core/built/admin/assets/activitypub/{use-activity-pub-queries-CX9snsLs.js → use-activity-pub-queries-CYI2QJMs.js} +58 -58
- package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-DsJTGDSS.js → use-navigate-with-base-path-B4jZ8hyg.js} +3924 -1403
- package/core/built/admin/assets/activitypub/{use-reply-chain-data-AJi9zkzm.js → use-reply-chain-data-BHmFxc3U.js} +6 -6
- package/core/built/admin/assets/activitypub/users-CSjOwpT9.js +886 -0
- package/core/built/admin/assets/activitypub/{x-DR8AT3-4.js → x-B9ptWCds.js} +2 -2
- package/core/built/admin/assets/{add-integration-modal-B12oRifD.js → add-integration-modal-BwePhq95.js} +1 -1
- package/core/built/admin/assets/add-newsletter-modal-Zv0I7Msm.js +1 -0
- package/core/built/admin/assets/{add-recommendation-modal-CJf92edY.js → add-recommendation-modal-cdLv0wHy.js} +1 -1
- package/core/built/admin/assets/analytics-DY8_SaP7.js +1 -0
- package/core/built/admin/assets/announcement-bar-modal-C-2yWwag.js +1 -0
- package/core/built/admin/assets/api-keys-BHKATUqQ.js +1 -0
- package/core/built/admin/assets/{audience-6GXevic-.js → audience-wRcSN0jV.js} +1 -1
- package/core/built/admin/assets/{automated-emails-DJQ4uNZo.js → automated-emails-CzUa3v4d.js} +1 -1
- package/core/built/admin/assets/automations-DupSsSOr.js +1 -0
- package/core/built/admin/assets/badge-CI1Z0xyG.js +1 -0
- package/core/built/admin/assets/bluesky-sharing-B8PC1SBj.js +1 -0
- package/core/built/admin/assets/box-LUuYRT-1.js +1 -0
- package/core/built/admin/assets/brand-icon-CrmbX86T.js +1 -0
- package/core/built/admin/assets/breadcrumb-DDDpzLyo.js +1 -0
- package/core/built/admin/assets/button-D414TI3B.js +1 -0
- package/core/built/admin/assets/calendar-Cp_opo3i.js +1 -0
- package/core/built/admin/assets/card-C2dfd5u0.js +1 -0
- package/core/built/admin/assets/chart-B_UNLXse.js +63 -0
- package/core/built/admin/assets/chart-helpers-Czu_9QpJ.js +1 -0
- package/core/built/admin/assets/checkbox-CFFqf0Nh.js +1 -0
- package/core/built/admin/assets/{chunk.368.22ce1b732c0b40abd3f1.js → chunk.201.6961b57ed7a281ac193e.js} +23 -19
- package/core/built/admin/assets/chunk.524.0b850e72ec49a0fa3882.js +25 -0
- package/core/built/admin/assets/chunk.582.db2ae9e968256e8d58ac.js +27 -0
- package/core/built/admin/assets/code-editor-view-Bd_nnHKk.js +1 -0
- package/core/built/admin/assets/code-editor-view-BeU3Yf9M.js +1 -0
- package/core/built/admin/assets/color-picker-Bzm1V7qg.js +3 -0
- package/core/built/admin/assets/color-picker-field-DP0moHz9.js +1 -0
- package/core/built/admin/assets/combobox-6_ruuTuj.js +1 -0
- package/core/built/admin/assets/command-B5wfm6pd.js +1 -0
- package/core/built/admin/assets/comments-BB26dUSl.js +1 -0
- package/core/built/admin/assets/confirmation-context-znVJD4pX.js +1 -0
- package/core/built/admin/assets/container-CHHif8km.js +1 -0
- package/core/built/admin/assets/{content-api-modal-0ZjuLcQ8.js → content-api-modal-DOHwl0yZ.js} +1 -1
- package/core/built/admin/assets/context-menu-BpY46Dlb.js +1 -0
- package/core/built/admin/assets/copy-field-DRBx6Ady.js +1 -0
- package/core/built/admin/assets/create-ghost-browse-value-source-eIXo5Qws.js +1 -0
- package/core/built/admin/assets/custom-field-type-option-DrSlETOI.js +1 -0
- package/core/built/admin/assets/custom-integration-modal-Dgo425Ec.js +1 -0
- package/core/built/admin/assets/data-list-DM8yqpXi.js +1 -0
- package/core/built/admin/assets/date-range-select-DdC23c13.js +1 -0
- package/core/built/admin/assets/design-and-theme-modal-CbSu9IT4.js +2 -0
- package/core/built/admin/assets/detail-page-CN4NdhpV.js +1 -0
- package/core/built/admin/assets/dirty-confirm-dialog-BT_Vq9TD.js +1 -0
- package/core/built/admin/assets/{disabled-sources-indicator-CLZhMv8J.js → disabled-sources-indicator-C5xtW0Q2.js} +1 -1
- package/core/built/admin/assets/dist-B40T-RCQ.js +1 -0
- package/core/built/admin/assets/dist-CrlaEoaK.js +1 -0
- package/core/built/admin/assets/domain-BPReCZa8.js +1 -0
- package/core/built/admin/assets/dropzone-B2auMkf2.js +5 -0
- package/core/built/admin/assets/edit-profile-B7XTBzKe.js +1 -0
- package/core/built/admin/assets/editor-BcaC9dqm.js +8 -0
- package/core/built/admin/assets/editor-screen-FuMpItC8.js +27 -0
- package/core/built/admin/assets/email-sending-status-provider-9d81o1C2.js +1 -0
- package/core/built/admin/assets/embed-signup-form-modal-CbVbMsxl.js +8 -0
- package/core/built/admin/assets/empty-indicator-CSQts7GV.js +1 -0
- package/core/built/admin/assets/explore-DbSXFTJN.js +1 -0
- package/core/built/admin/assets/feature-image-placeholder-DFXA4vfJ.js +1 -0
- package/core/built/admin/assets/feed-W7RlF-Gp.js +1 -0
- package/core/built/admin/assets/field-BXEh3ptU.js +1 -0
- package/core/built/admin/assets/filter-sources-DHK2IoCk.js +1 -0
- package/core/built/admin/assets/filters-0SYNTqyQ.js +1 -0
- package/core/built/admin/assets/filters-DTtZKQyq.js +1 -0
- package/core/built/admin/assets/{first-promoter-modal-wr73Dldc.js → first-promoter-modal-DcoN_OXu.js} +1 -1
- package/core/built/admin/assets/{ghost-a1e6e172c6917e8ef8fa6b0007756d9b.js → ghost-25495154132af7305cb7b646fd83f48f.js} +562 -514
- package/core/built/admin/assets/gift-link-modal-BX-Q1TOk.js +1 -0
- package/core/built/admin/assets/grid-BEUIDQX7.js +1 -0
- package/core/built/admin/assets/growth-G6PLO4aF.js +1 -0
- package/core/built/admin/assets/growth-tkD-fBOY.js +3 -0
- package/core/built/admin/assets/heading-ih-fOVqF.js +1 -0
- package/core/built/admin/assets/history-modal-Ca-gStoq.js +1 -0
- package/core/built/admin/assets/html-field-B3CrKV9Y.js +1 -0
- package/core/built/admin/assets/images-H-BIm8OQ.js +1 -0
- package/core/built/admin/assets/{inbox-DgSZ6w2p.js → inbox-DBbDVMPz.js} +14 -14
- package/core/built/admin/assets/index-DOF2jDq0.js +4 -0
- package/core/built/admin/assets/index-JrrNchW1.css +2 -0
- package/core/built/admin/assets/input-group-CvH-3d3F.js +1 -0
- package/core/built/admin/assets/input-surface-CL23Mlh8.js +1 -0
- package/core/built/admin/assets/integration-header-C7GXGis_.js +1 -0
- package/core/built/admin/assets/invite-user-modal-EJ2_6RpV.js +1 -0
- package/core/built/admin/assets/isPlainObject-BzzgUf1E.js +1 -0
- package/core/built/admin/assets/{koenig-lexical-CXVgQFUk.js → koenig-lexical-Dkwee5FG.js} +1 -1
- package/core/built/admin/assets/koenig-loader-BzXCXHoA.js +2 -0
- package/core/built/admin/assets/kpi-card-iCDUTgOR.js +1 -0
- package/core/built/admin/assets/kpi-card-jTnpLKGr.js +1 -0
- package/core/built/admin/assets/kpi-helpers-Criu0YAf.js +1 -0
- package/core/built/admin/assets/kpi-tabs-DbOmmyOo.js +1 -0
- package/core/built/admin/assets/label-Du0yqMqL.js +1 -0
- package/core/built/admin/assets/label-picker-Bq1XXr1D.js +1 -0
- package/core/built/admin/assets/{labels-KMkFoRG6.js → labels-0x2NfLtC.js} +1 -1
- package/core/built/admin/assets/layout-Ckwzsnsu.js +1 -0
- package/core/built/admin/assets/lib-CCz2mjm8.js +2 -0
- package/core/built/admin/assets/link-helpers-o9GgBTni.js +1 -0
- package/core/built/admin/assets/{links-FwNCVyVe.js → links-Ctujreuh.js} +1 -1
- package/core/built/admin/assets/list-page-CH_wnDC3.js +1 -0
- package/core/built/admin/assets/member-activity-CsAh6MxK.js +1 -0
- package/core/built/admin/assets/member-activity-feed-0ytsQbtY.js +1 -0
- package/core/built/admin/assets/member-custom-fields-CTFGnnYv.js +1 -0
- package/core/built/admin/assets/member-detail-QU3vB1ML.js +1 -0
- package/core/built/admin/assets/members-CtMVO_AB.js +4 -0
- package/core/built/admin/assets/modal-page-8Ipfl51B.js +1 -0
- package/core/built/admin/assets/moderation-B8VtA-W0.js +1 -0
- package/core/built/admin/assets/multi-select-combobox-_9wEYmGv.js +1 -0
- package/core/built/admin/assets/navigation-modal-CGcjJvkc.js +1 -0
- package/core/built/admin/assets/newsletter-BpHjjqmq.js +1 -0
- package/core/built/admin/assets/newsletter-detail-modal-DzTbGOyo.js +1 -0
- package/core/built/admin/assets/newsletter-emails-BMKH4Osp.js +1 -0
- package/core/built/admin/assets/newsletters-BxC84_cL.js +1 -0
- package/core/built/admin/assets/note-BPtEzzZS.js +1 -0
- package/core/built/admin/assets/notifications-ng3ulEdp.js +1 -0
- package/core/built/admin/assets/offers-container-modal-CwqnRa3T.js +1 -0
- package/core/built/admin/assets/onboarding-route-CplZzd7F.js +1 -0
- package/core/built/admin/assets/overview-B0yD9OoX.js +1 -0
- package/core/built/admin/assets/overview-C4eJzQqi.js +1 -0
- package/core/built/admin/assets/page-header-CdCpekG9.js +1 -0
- package/core/built/admin/assets/pagemenu-CKQD9hoP.js +1 -0
- package/core/built/admin/assets/pages-route-CKsnxIvw.js +1 -0
- package/core/built/admin/assets/{pintura-modal-BljM7hf8.js → pintura-modal-C5jzeyGk.js} +1 -1
- package/core/built/admin/assets/portal-frame-DYo0bh0E.js +1 -0
- package/core/built/admin/assets/portal-modal-C0d8xjKa.js +1 -0
- package/core/built/admin/assets/post-analytics-C5C7IyAm.js +1 -0
- package/core/built/admin/assets/post-analytics-header-DKWHwoK2.js +1 -0
- package/core/built/admin/assets/{post-analytics-provider-CgreZDjS.js → post-analytics-provider-BqCPPxmW.js} +1 -1
- package/core/built/admin/assets/post-share-modal-wxmqbAv_.js +1 -0
- package/core/built/admin/assets/posts-list-screen-B978ZO0R.js +2 -0
- package/core/built/admin/assets/posts-route-CDqxKazl.js +1 -0
- package/core/built/admin/assets/preferences-CnejTRc4.js +5 -0
- package/core/built/admin/assets/preview-chrome-C7yZWVLZ.js +1 -0
- package/core/built/admin/assets/preview-modal-CFUxUGHp.js +1 -0
- package/core/built/admin/assets/profile-ClRGiOqA.js +1 -0
- package/core/built/admin/assets/radio-group-CTmLo0h9.js +1 -0
- package/core/built/admin/assets/router-provider-DDZvgS3V.js +1 -0
- package/core/built/admin/assets/select-D_4xWFYv.js +1 -0
- package/core/built/admin/assets/settings-BRl7-7a9.js +1 -0
- package/core/built/admin/assets/settings-BoQcPa-y.js +4 -0
- package/core/built/admin/assets/settings-breadcrumbs-C9Yxl81q.js +1 -0
- package/core/built/admin/assets/{dist-BXHrDV19.js → shade-scope-Cccj-c7I-D0XwkdKQ.js} +5 -3
- package/core/built/admin/assets/share-modal-DBqZobuR.js +1 -0
- package/core/built/admin/assets/simple-pagination-D5t_ox6x.js +1 -0
- package/core/built/admin/assets/skeleton-CPcTiTSX.js +1 -0
- package/core/built/admin/assets/{slack-modal-D7W_fNON.js → slack-modal-BF9ex8cd.js} +1 -1
- package/core/built/admin/assets/sort-button-C0EXGjnS.js +1 -0
- package/core/built/admin/assets/{source-icon-CQboPZQ3.js → source-icon-BzzftCQ2.js} +1 -1
- package/core/built/admin/assets/stats-view-CzpGoHzF.js +1 -0
- package/core/built/admin/assets/{step-1-BKhjy0PV.js → step-1-CBUYUPPF.js} +1 -1
- package/core/built/admin/assets/{step-2-PxA1OM7R.js → step-2-CEIXUwQa.js} +1 -1
- package/core/built/admin/assets/{step-3-BJbAiq16.js → step-3-C1HA5i1N.js} +2 -2
- package/core/built/admin/assets/stripe-button-DMFsLs80.js +1 -0
- package/core/built/admin/assets/stripe-connect-modal-CEvxCUTv.js +1 -0
- package/core/built/admin/assets/switch-DoOJF2yg.js +1 -0
- package/core/built/admin/assets/table-BDYbUHJz.js +1 -0
- package/core/built/admin/assets/tabs-BZyz4ZCH.js +1 -0
- package/core/built/admin/assets/tag-detail-CFzaz_9E.js +2 -0
- package/core/built/admin/assets/tag-picker-BKUCZuRc.js +1 -0
- package/core/built/admin/assets/tags-Bs1FiD7m.js +1 -0
- package/core/built/admin/assets/{testimonials-modal-Dkt6ODLW.js → testimonials-modal-CVm0_7E-.js} +1 -1
- package/core/built/admin/assets/text-BLI0C8Mc.js +1 -0
- package/core/built/admin/assets/textarea-C5qolByg.js +1 -0
- package/core/built/admin/assets/tier-detail-modal-Cmwzi-e7.js +1 -0
- package/core/built/admin/assets/toggle-group-BTL1QsWY.js +1 -0
- package/core/built/admin/assets/{topic-filter-DVOe3D-b.js → topic-filter-BMjhquv7.js} +1 -1
- package/core/built/admin/assets/transistor-modal-hMv53jF8.js +1 -0
- package/core/built/admin/assets/trend-badge-1w_HEzkG.js +1 -0
- package/core/built/admin/assets/{unsplash-modal-DXt7Brum.js → unsplash-modal-jzC8lEB2.js} +1 -1
- package/core/built/admin/assets/use-analytics-data-B3lGOxsC.js +1 -0
- package/core/built/admin/assets/use-availability-DnKqOyqd.js +1 -0
- package/core/built/admin/assets/{use-check-theme-limit-error-JD-J5aaZ.js → use-check-theme-limit-error-C1tc2P4N.js} +2 -2
- package/core/built/admin/assets/use-filter-params-DU-aiaTb.js +1 -0
- package/core/built/admin/assets/{use-filterable-api-CEMSAYPg.js → use-filterable-api-CxCrnPxG.js} +1 -1
- package/core/built/admin/assets/use-growth-stats-E13rMQUE.js +1 -0
- package/core/built/admin/assets/use-limiter-DlL7au90.js +19 -0
- package/core/built/admin/assets/{use-navigate-with-base-path-88EQpw2k.js → use-navigate-with-base-path-BzgNwMAx.js} +1 -1
- package/core/built/admin/assets/{use-pintura-config-BmU45Ruy.js → use-pintura-config-CtEV1JQY.js} +1 -1
- package/core/built/admin/assets/{use-pintura-editor-Bz2bZ4iT.js → use-pintura-editor-CaxE64kD.js} +1 -1
- package/core/built/admin/assets/use-reply-chain-data-BST2OgCM.js +1 -0
- package/core/built/admin/assets/use-sending-eta-DR9ueMH8.js +1 -0
- package/core/built/admin/assets/use-setting-group-ldtX1Vsk.js +1 -0
- package/core/built/admin/assets/use-settings-navigation-CzGU4sus.js +1 -0
- package/core/built/admin/assets/use-staff-users-DoeXaPsb.js +1 -0
- package/core/built/admin/assets/use-tinybird-query-BaBNCaj0.js +1 -0
- package/core/built/admin/assets/{use-tinybird-token-Lk1RSn0t.js → use-tinybird-token-CnZ19NDf.js} +1 -1
- package/core/built/admin/assets/{use-url-input-DpmbDppC.js → use-url-input-C5KHJw79.js} +1 -1
- package/core/built/admin/assets/user-detail-modal-Dm0KfPeR.js +1 -0
- package/core/built/admin/assets/{vendor-2dd263291938d407802568d3357ec69e.js → vendor-97e91ff8dd1457c668f9ef103ee1a77b.js} +2748 -2977
- package/core/built/admin/assets/virtual-list-tAuPUICw.js +1 -0
- package/core/built/admin/assets/web-BQlKcW2x.js +2 -0
- package/core/built/admin/assets/web-NTT5DkEz.js +1 -0
- package/core/built/admin/assets/{zapier-modal-BafLUO4R.js → zapier-modal-DsHG9Oev.js} +1 -1
- package/core/built/admin/index.html +22 -24
- package/core/frontend/apps/private-blogging/index.js +2 -2
- package/core/frontend/apps/private-blogging/lib/middleware.js +1 -1
- package/core/frontend/apps/private-blogging/lib/router.js +1 -1
- package/core/frontend/helpers/cancel_link.js +3 -3
- package/core/frontend/helpers/content.js +2 -2
- package/core/frontend/helpers/content_api_url.js +1 -1
- package/core/frontend/helpers/date.js +1 -1
- package/core/frontend/helpers/foreach.js +5 -5
- package/core/frontend/helpers/get.js +7 -4
- package/core/frontend/helpers/ghost_head.js +2 -2
- package/core/frontend/helpers/has.js +1 -3
- package/core/frontend/helpers/link.js +7 -7
- package/core/frontend/helpers/link_class.js +2 -2
- package/core/frontend/helpers/navigation.js +1 -2
- package/core/frontend/helpers/recommendations.js +5 -6
- package/core/frontend/helpers/tiers.js +1 -1
- package/core/frontend/helpers/total_members.js +1 -1
- package/core/frontend/helpers/total_paid_members.js +1 -1
- package/core/frontend/meta/author-url.js +1 -1
- package/core/frontend/meta/modified-date.js +1 -1
- package/core/frontend/meta/published-date.js +1 -1
- package/core/frontend/meta/schema.js +3 -8
- package/core/frontend/meta/structured-data.js +1 -2
- package/core/frontend/services/assets-minification/minifier.js +1 -1
- package/core/frontend/services/data/entry-lookup.js +1 -1
- package/core/frontend/services/data/fetch-data.js +2 -2
- package/core/frontend/services/rendering/format-response.js +1 -1
- package/core/frontend/services/rendering/renderer.js +3 -0
- package/core/frontend/services/rendering/templates.js +1 -1
- package/core/frontend/services/routing/controllers/static.js +1 -1
- package/core/frontend/services/routing/parent-router.js +1 -1
- package/core/frontend/services/routing/registry.js +1 -1
- package/core/frontend/services/sitemap/base-site-map-generator.js +68 -97
- package/core/frontend/services/sitemap/site-map-index-generator.js +16 -21
- package/core/frontend/services/sitemap/site-map-manager.js +82 -43
- package/core/frontend/services/sitemap/sitemap-xml.js +72 -0
- package/core/frontend/services/theme-engine/handlebars/utils.js +8 -8
- package/core/frontend/services/theme-engine/i18n/i18n.js +4 -5
- package/core/frontend/services/theme-engine/middleware/update-global-template-options.js +1 -1
- package/core/frontend/services/theme-engine/preview.js +1 -1
- package/core/frontend/utils/member-count.js +2 -2
- package/core/frontend/web/routers/serve-public-file.js +1 -1
- package/core/server/adapters/scheduling/README.md +4 -0
- package/core/server/adapters/scheduling/get-scheduler-idempotency-key.js +21 -0
- package/core/server/adapters/storage/LocalStorageBase.js +2 -1
- package/core/server/api/endpoints/automated-emails.js +8 -2
- package/core/server/api/endpoints/db.js +1 -1
- package/core/server/api/endpoints/emails.js +3 -3
- package/core/server/api/endpoints/mail.js +1 -1
- package/core/server/api/endpoints/member-signin-urls.js +1 -1
- package/core/server/api/endpoints/members.js +3 -3
- package/core/server/api/endpoints/oembed.js +1 -1
- package/core/server/api/endpoints/posts.js +1 -1
- package/core/server/api/endpoints/settings.js +2 -2
- package/core/server/api/endpoints/themes.js +5 -5
- package/core/server/api/endpoints/utils/serializers/input/pages.js +2 -2
- package/core/server/api/endpoints/utils/serializers/input/posts.js +2 -2
- package/core/server/api/endpoints/utils/serializers/input/tags.js +1 -1
- package/core/server/api/endpoints/utils/serializers/input/utils/slug-filter-order.js +4 -4
- package/core/server/api/endpoints/utils/serializers/output/mappers/comments.js +8 -5
- package/core/server/api/endpoints/utils/serializers/output/mappers/posts.js +1 -1
- package/core/server/api/endpoints/utils/serializers/output/pages.js +4 -4
- package/core/server/api/endpoints/utils/serializers/output/posts.js +4 -4
- package/core/server/api/endpoints/utils/serializers/output/search-index.js +11 -11
- package/core/server/api/endpoints/utils/serializers/output/settings.js +1 -1
- package/core/server/api/endpoints/utils/serializers/output/utils/extra-attrs.js +3 -3
- package/core/server/api/endpoints/utils/validators/input/automated_emails.js +23 -33
- package/core/server/api/endpoints/utils/validators/input/settings.js +3 -3
- package/core/server/data/exporter/exporter.js +1 -2
- package/core/server/data/exporter/table-lists.js +1 -0
- package/core/server/data/importer/handlers/image.js +1 -2
- package/core/server/data/importer/handlers/json.js +1 -3
- package/core/server/data/importer/handlers/markdown.js +4 -8
- package/core/server/data/importer/import-manager.js +1 -1
- package/core/server/data/importer/importers/content-file-importer.js +4 -8
- package/core/server/data/importer/importers/data/base.js +10 -10
- package/core/server/data/importer/importers/data/custom-theme-settings-importer.js +1 -1
- package/core/server/data/importer/importers/data/data-importer.js +3 -4
- package/core/server/data/importer/importers/data/posts-importer.js +10 -10
- package/core/server/data/importer/importers/data/products-importer.js +3 -3
- package/core/server/data/importer/importers/data/settings-importer.js +2 -2
- package/core/server/data/importer/importers/data/stripe-prices-importer.js +1 -1
- package/core/server/data/importer/importers/data/stripe-products-importer.js +1 -1
- package/core/server/data/importer/importers/data/tags-importer.js +1 -1
- package/core/server/data/importer/importers/data/users-importer.js +2 -2
- package/core/server/data/importer/importers/json-to-html.js +12 -9
- package/core/server/data/migrations/versions/5.0/2022-05-11-13-12-rename-settings.js +1 -1
- package/core/server/data/migrations/versions/6.65/2026-09-16-15-35-26-add-members-metafield-change-events.js +17 -0
- package/core/server/data/schema/fixtures/fixture-manager.js +3 -3
- package/core/server/data/schema/index.js +1 -1
- package/core/server/data/schema/schema.js +26 -0
- package/core/server/data/schema/validator.js +96 -123
- package/core/server/data/seeders/importers/email-recipients-importer.js +2 -2
- package/core/server/data/seeders/importers/members-login-events-importer.js +1 -1
- package/core/server/data/seeders/importers/members-newsletters-importer.js +1 -1
- package/core/server/data/seeders/importers/members-paid-subscription-events-importer.js +1 -1
- package/core/server/data/seeders/importers/members-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 +2 -2
- package/core/server/data/seeders/importers/members-subscription-created-events-importer.js +1 -1
- package/core/server/data/tinybird/pipes/mv_hits.pipe +25 -20
- package/core/server/data/tinybird/scripts/docker-analytics-manager.js +2 -2
- package/core/server/ghost-server.js +1 -19
- package/core/server/lib/image/image-size.js +1 -3
- package/core/server/lib/request-external.js +53 -117
- package/core/server/models/base/plugins/actions.js +2 -3
- package/core/server/models/base/plugins/crud.js +1 -2
- package/core/server/models/base/plugins/events.js +4 -4
- package/core/server/models/base/plugins/raw-knex.js +3 -3
- package/core/server/models/base/plugins/sanitize.js +1 -1
- package/core/server/models/benefit.js +1 -1
- package/core/server/models/collection.js +1 -1
- package/core/server/models/comment.js +3 -3
- package/core/server/models/invite.js +2 -5
- package/core/server/models/label.js +3 -6
- package/core/server/models/member.js +7 -10
- package/core/server/models/newsletter.js +1 -1
- package/core/server/models/permission.js +3 -6
- package/core/server/models/post.js +20 -23
- package/core/server/models/product.js +4 -1
- package/core/server/models/relations/authors.js +9 -12
- package/core/server/models/role.js +2 -5
- package/core/server/models/settings.js +3 -3
- package/core/server/models/tag.js +2 -5
- package/core/server/models/user.js +8 -10
- package/core/server/models/webhook.js +2 -4
- package/core/server/notify.js +2 -2
- package/core/server/services/adapter-manager/utils.js +6 -1
- package/core/server/services/api-version-compatibility/legacy-api-path-match.js +2 -2
- package/core/server/services/auth/api-key/admin.js +2 -2
- package/core/server/services/auth/api-key/content.js +1 -1
- package/core/server/services/auth/passwordreset.js +1 -1
- package/core/server/services/auth/setup.js +1 -3
- package/core/server/services/automations/README.md +19 -0
- package/core/server/services/automations/poll.js +3 -1
- package/core/server/services/automations/service.js +2 -2
- package/core/server/services/automations/welcome-email-automation-poll.js +199 -293
- package/core/server/services/comments/comments-controller.js +9 -30
- package/core/server/services/comments/comments-service-email-renderer.js +1 -1
- package/core/server/services/comments/comments-service-emails.js +1 -1
- package/core/server/services/comments/comments-service.js +27 -58
- package/core/server/services/comments/comments-stats-service.js +1 -1
- package/core/server/services/content-import/import/staged-file.js +9 -9
- package/core/server/services/content-import/index.js +1 -1
- package/core/server/services/custom-redirects/validation.js +1 -1
- package/core/server/services/email-analytics/email-analytics-service-wrapper.js +23 -55
- package/core/server/services/email-analytics/index.js +35 -19
- package/core/server/services/email-analytics/jobs/email-analytics-job-scheduler.js +31 -60
- package/core/server/services/email-service/README.md +35 -0
- package/core/server/services/email-service/batch-sending-service.js +13 -14
- package/core/server/services/email-service/email-renderer.js +50 -14
- package/core/server/services/email-service/email-service-wrapper.js +3 -2
- package/core/server/services/email-service/email-service.js +33 -8
- package/core/server/services/email-service/email-templates/partials/feedback-button.hbs +2 -1
- package/core/server/services/email-service/email-templates/partials/styles.hbs +0 -6
- package/core/server/services/email-service/email-templates/template.hbs +1 -1
- package/core/server/services/email-service/jobs/send-email-job.js +12 -0
- package/core/server/services/email-service/mailgun-email-provider.js +7 -2
- package/core/server/services/email-service/sending-service.js +1 -1
- package/core/server/services/gifts/gift-service.js +12 -3
- package/core/server/services/gifts/index.js +0 -2
- package/core/server/services/gifts/jobs/index.js +12 -24
- package/core/server/services/gifts/jobs/send-gift-reminders-job.js +6 -46
- package/core/server/services/invitations/accept.js +2 -2
- package/core/server/services/jobs/job-service.js +0 -28
- package/core/server/services/jobs-service/jobs-service.js +5 -0
- package/core/server/services/jobs-service/register-job-handlers.js +17 -1
- package/core/server/services/lib/mailgun-client.js +16 -1
- package/core/server/services/lib/mailgun-recipient-message-id.js +49 -0
- package/core/server/services/limits.js +1 -1
- package/core/server/services/mail/ghost-mailer.js +1 -1
- package/core/server/services/member-attribution/member-attribution-service.js +1 -1
- package/core/server/services/member-attribution/referrer-translator.js +1 -1
- package/core/server/services/members/account-service.js +3 -3
- package/core/server/services/members/api.js +1 -1
- package/core/server/services/members/import-export/import/importer.js +25 -16
- package/core/server/services/members/import-export/import/spool.js +11 -15
- package/core/server/services/members/import-export/index.js +6 -4
- package/core/server/services/members/jobs/index.js +1 -1
- package/core/server/services/members/jobs/members-import-job.js +18 -0
- package/core/server/services/members/member-avatar.js +18 -0
- package/core/server/services/members/members-api/controllers/member-controller.js +2 -2
- package/core/server/services/members/members-api/members-api.js +4 -4
- package/core/server/services/members/members-api/repositories/event-repository.js +36 -1
- package/core/server/services/members/members-api/repositories/member-repository.js +55 -17
- package/core/server/services/members/members-api/repositories/product-repository.js +1 -1
- package/core/server/services/members/members-api/services/member-bread-service.js +4 -7
- package/core/server/services/members/service.js +7 -2
- package/core/server/services/members/stats/members-stats.js +2 -2
- package/core/server/services/members-events/event-storage.js +2 -2
- package/core/server/services/members-metafields/actions.js +23 -2
- package/core/server/services/members-metafields/bindings-service.js +5 -5
- package/core/server/services/members-metafields/definitions-service.js +2 -7
- package/core/server/services/members-metafields/index.js +2 -1
- package/core/server/services/members-metafields/queries.js +7 -1
- package/core/server/services/members-metafields/schema.js +49 -1
- package/core/server/services/members-metafields/values-service.js +112 -10
- package/core/server/services/mentions/mention-sending-service.js +2 -2
- package/core/server/services/mentions/mention.js +3 -4
- package/core/server/services/mentions/mentions-api.js +4 -1
- package/core/server/services/mentions-jobs/job-service.js +0 -28
- package/core/server/services/milestones/milestones-service.js +1 -1
- package/core/server/services/newsletters/newsletters-service.js +1 -1
- package/core/server/services/notifications/notifications.js +3 -3
- package/core/server/services/oembed/oembed-service.js +79 -3
- package/core/server/services/permissions/can-this.js +1 -2
- package/core/server/services/post-scheduling/post-scheduling.js +8 -1
- package/core/server/services/posts/post-scheduling.js +7 -0
- package/core/server/services/settings/settings-bread-service.js +3 -3
- package/core/server/services/settings/settings-utils.js +1 -1
- package/core/server/services/settings-helpers/settings-helpers.js +1 -1
- package/core/server/services/staff/staff-service-emails.js +9 -9
- package/core/server/services/stats/posts-stats-service.js +10 -10
- package/core/server/services/stats/referrers-stats-service.js +1 -1
- package/core/server/services/stats/subscription-stats-service.js +3 -3
- package/core/server/services/stats/utils/tinybird.js +8 -1
- package/core/server/services/stripe/stripe-api.js +3 -3
- package/core/server/services/stripe/stripe-migrations.js +6 -6
- package/core/server/services/themes/storage.js +21 -26
- package/core/server/services/themes/theme-storage.js +2 -2
- package/core/server/services/tiers/tier.js +15 -15
- package/core/server/services/tinybird-sync/index.js +3 -0
- package/core/server/services/tinybird-sync/tinybird-sync-service.js +7 -2
- package/core/server/services/update-check/update-check-service.js +3 -3
- package/core/server/services/webhooks/serialize.js +1 -1
- package/core/server/web/api/app.js +0 -5
- package/core/server/web/api/middleware/upload.js +29 -2
- package/core/server/web/parent/middleware/index.js +1 -1
- package/core/server/web/parent/middleware/log-request.js +5 -0
- package/core/server/web/parent/middleware/queue-request.js +104 -59
- package/core/server/web/shared/middleware/api/spam-prevention.js +2 -2
- package/core/shared/config/defaults.json +6 -1
- package/core/shared/custom-theme-settings-cache/custom-theme-settings-service.js +1 -1
- package/core/shared/labs.js +3 -2
- package/core/shared/settings-cache/cache-manager.js +1 -1
- package/nodemon.json +1 -1
- package/package.json +6 -7
- package/pnpm-lock.yaml +126 -728
- package/pnpm-workspace.yaml +4 -7
- package/tsconfig.json +1 -1
- package/components/tryghost-adapter-base-scheduling-0.2.3.tgz +0 -0
- package/core/built/admin/assets/Combination-DWfJPcz9.js +0 -41
- package/core/built/admin/assets/RadialBarChart-CHRBhiBN.js +0 -1
- package/core/built/admin/assets/_baseAssignValue-DcPokYW8.js +0 -1
- package/core/built/admin/assets/accordion-CJmRaqK7.js +0 -1
- package/core/built/admin/assets/account-migration-DYf7skbb.js +0 -1
- package/core/built/admin/assets/action-list-Crnh-_UI.js +0 -1
- package/core/built/admin/assets/activitypub/avatar-DVJF5bxP.js +0 -261
- package/core/built/admin/assets/activitypub/layout-BL4_jtuV.js +0 -1
- package/core/built/admin/assets/activitypub/routes-DdyY9uFF.js +0 -7181
- package/core/built/admin/assets/activitypub/settings-B6EEcVkW.js +0 -315
- package/core/built/admin/assets/activitypub/tabs-DLhZtVde.js +0 -1083
- package/core/built/admin/assets/activitypub/users-inweyKL5.js +0 -1499
- package/core/built/admin/assets/add-newsletter-modal-CTvXiIqY.js +0 -1
- package/core/built/admin/assets/analytics-PHR1Fggi.js +0 -1
- package/core/built/admin/assets/announcement-bar-modal-Bql0CXdp.js +0 -1
- package/core/built/admin/assets/api-keys-CubH-7_H.js +0 -1
- package/core/built/admin/assets/automations-DcZSLFDU.js +0 -1
- package/core/built/admin/assets/badge-qP06OT7C.js +0 -1
- package/core/built/admin/assets/bluesky-sharing-CJw4d4Mf.js +0 -1
- package/core/built/admin/assets/box-CZ_LSVSy.js +0 -1
- package/core/built/admin/assets/brand-icon-D3ffDYaC.js +0 -1
- package/core/built/admin/assets/breadcrumb-Chn1Ip_q.js +0 -1
- package/core/built/admin/assets/button-DvIh2Bq-.js +0 -1
- package/core/built/admin/assets/calendar-1QGoStFM.js +0 -1
- package/core/built/admin/assets/card-FHQ4D10L.js +0 -1
- package/core/built/admin/assets/chart-helpers-CNpA7re_.js +0 -1
- package/core/built/admin/assets/chart-jVz9Nf9t.js +0 -63
- package/core/built/admin/assets/checkbox-DKht9xMl.js +0 -1
- package/core/built/admin/assets/chunk.524.cc9be98e44890cedc8c8.js +0 -25
- package/core/built/admin/assets/chunk.582.e6fe742b3217207b11a6.js +0 -27
- package/core/built/admin/assets/code-editor-view-BLQIV85-.js +0 -1
- package/core/built/admin/assets/code-editor-view-Cg5eJxcr.js +0 -1
- package/core/built/admin/assets/color-picker-DQoghc0t.js +0 -3
- package/core/built/admin/assets/color-picker-field-BbeK5RRs.js +0 -1
- package/core/built/admin/assets/combobox-weO4FBbv.js +0 -1
- package/core/built/admin/assets/command-DlLrCm9V.js +0 -1
- package/core/built/admin/assets/comments-BjTitLmP.js +0 -1
- package/core/built/admin/assets/confirmation-context-IyqoXVk-.js +0 -1
- package/core/built/admin/assets/container-DAimjOF_.js +0 -1
- package/core/built/admin/assets/context-menu-BtioYFrg.js +0 -1
- package/core/built/admin/assets/copy-field-C6ujqJlP.js +0 -1
- package/core/built/admin/assets/create-ghost-browse-value-source-DAZMWZNq.js +0 -1
- package/core/built/admin/assets/custom-integration-modal-Dm8QI_J4.js +0 -1
- package/core/built/admin/assets/data-list-C0CKkeWW.js +0 -1
- package/core/built/admin/assets/date-range-select-DrjHQdgz.js +0 -1
- package/core/built/admin/assets/design-and-theme-modal-CXVOzBWu.js +0 -2
- package/core/built/admin/assets/detail-page-QpLIL-EO.js +0 -1
- package/core/built/admin/assets/dirty-confirm-dialog-DWwxREGh.js +0 -1
- package/core/built/admin/assets/dist-Bf-HQtVL.js +0 -1
- package/core/built/admin/assets/dist-CUwte9v_.js +0 -1
- package/core/built/admin/assets/dist-FEQQAwTe.js +0 -1
- package/core/built/admin/assets/domain-DBrBic6q.js +0 -1
- package/core/built/admin/assets/dropzone-D61QVcEY.js +0 -5
- package/core/built/admin/assets/edit-profile-jCqJBxqw.js +0 -1
- package/core/built/admin/assets/editor-8Fm4UBEd.js +0 -8
- package/core/built/admin/assets/editor-screen-BSX_uC2B.js +0 -27
- package/core/built/admin/assets/email-sending-status-provider-DnNhlIvt.js +0 -1
- package/core/built/admin/assets/embed-signup-form-modal-DqtBs1WE.js +0 -8
- package/core/built/admin/assets/empty-indicator-L4Ql5Db7.js +0 -1
- package/core/built/admin/assets/en-Cx75dJv5.js +0 -1
- package/core/built/admin/assets/explore-DXhDCtKw.js +0 -1
- package/core/built/admin/assets/feature-image-placeholder-e2q6TcH6.js +0 -1
- package/core/built/admin/assets/feed-mmt4noNL.js +0 -1
- package/core/built/admin/assets/field-p7KnP98B.js +0 -1
- package/core/built/admin/assets/filter-bar-CiIKBg3D.js +0 -1
- package/core/built/admin/assets/filter-sources-I-dRaQxl.js +0 -1
- package/core/built/admin/assets/filters-1injvyxJ.js +0 -1
- package/core/built/admin/assets/filters-DnpO6GWX.js +0 -1
- package/core/built/admin/assets/gift-link-modal-C3H7YFXO.js +0 -1
- package/core/built/admin/assets/grid-5vplBYri.js +0 -1
- package/core/built/admin/assets/growth-B1og1kQp.js +0 -1
- package/core/built/admin/assets/growth-Bf6OHZlK.js +0 -3
- package/core/built/admin/assets/heading-BB21QAbb.js +0 -1
- package/core/built/admin/assets/history-modal-t_qEnxPx.js +0 -1
- package/core/built/admin/assets/html-field-COLQbV4r.js +0 -1
- package/core/built/admin/assets/images-4mvoc3lB.js +0 -1
- package/core/built/admin/assets/index-B3HZU2i8.css +0 -2
- package/core/built/admin/assets/index-DDd-zhNL.js +0 -4
- package/core/built/admin/assets/inline-B8H6NzIw.js +0 -1
- package/core/built/admin/assets/input-group-CLIN2ykI.js +0 -1
- package/core/built/admin/assets/input-surface-wSO5CRy5.js +0 -1
- package/core/built/admin/assets/integration-header-CKGrXNVP.js +0 -1
- package/core/built/admin/assets/invite-user-modal-BaN2mdhI.js +0 -1
- package/core/built/admin/assets/koenig-loader-B1XPPY5C.js +0 -2
- package/core/built/admin/assets/kpi-card-CRLPVlFW.js +0 -1
- package/core/built/admin/assets/kpi-card-CRPbW8NZ.js +0 -1
- package/core/built/admin/assets/kpi-helpers-D7zT96bp.js +0 -1
- package/core/built/admin/assets/kpi-tabs-DE_yh9Go.js +0 -1
- package/core/built/admin/assets/label-CgeBxy4o.js +0 -1
- package/core/built/admin/assets/label-picker-DGDp4Vwr.js +0 -1
- package/core/built/admin/assets/layout-DWnOMly4.js +0 -1
- package/core/built/admin/assets/lib-_QYaUU89.js +0 -2
- package/core/built/admin/assets/link-helpers-8J7ixADV.js +0 -1
- package/core/built/admin/assets/list-page-DLMP0PxC.js +0 -1
- package/core/built/admin/assets/member-activity-ClMWFyvN.js +0 -1
- package/core/built/admin/assets/member-activity-feed-BQY5Rwaj.js +0 -1
- package/core/built/admin/assets/member-custom-fields-Dd_ie1zG.js +0 -1
- package/core/built/admin/assets/member-detail-DjwkGVne.js +0 -1
- package/core/built/admin/assets/members--N9lhk1N.js +0 -4
- package/core/built/admin/assets/modal-page-Dz-9TP4E.js +0 -1
- package/core/built/admin/assets/moderation-DfYBKq9s.js +0 -1
- package/core/built/admin/assets/multi-select-combobox-BcBEzUW_.js +0 -1
- package/core/built/admin/assets/navigation-modal-Cd-scywg.js +0 -1
- package/core/built/admin/assets/newsletter-detail-modal-CFRY6vg_.js +0 -1
- package/core/built/admin/assets/newsletter-emails-4ntck1AC.js +0 -1
- package/core/built/admin/assets/newsletter-o6eCJtab.js +0 -1
- package/core/built/admin/assets/newsletters-Bmlv0edG.js +0 -1
- package/core/built/admin/assets/note-CWs3LF-0.js +0 -1
- package/core/built/admin/assets/notifications-BjSvTJqh.js +0 -1
- package/core/built/admin/assets/offers-container-modal-Bc9A196w.js +0 -1
- package/core/built/admin/assets/onboarding-route-Di1LJYEE.js +0 -1
- package/core/built/admin/assets/overview-DE7NHZQN.js +0 -1
- package/core/built/admin/assets/overview-DEF-ZY6x.js +0 -1
- package/core/built/admin/assets/page-header-B-G_g42C.js +0 -1
- package/core/built/admin/assets/pagemenu-CnkTDO71.js +0 -1
- package/core/built/admin/assets/pages-route-VT7r7YhG.js +0 -1
- package/core/built/admin/assets/portal-frame-D9V8iq5x.js +0 -1
- package/core/built/admin/assets/portal-modal-B5OOR9ZD.js +0 -1
- package/core/built/admin/assets/post-analytics-header-J0WciLx6.js +0 -1
- package/core/built/admin/assets/post-analytics-rGb-PgD-.js +0 -1
- package/core/built/admin/assets/post-share-modal-D0BbTo4W.js +0 -1
- package/core/built/admin/assets/posts-list-screen-CO5jAWoU.js +0 -2
- package/core/built/admin/assets/posts-route-DV-CXWK9.js +0 -1
- package/core/built/admin/assets/preferences-Tac29oG6.js +0 -5
- package/core/built/admin/assets/preview-chrome-C2lQgZBZ.js +0 -1
- package/core/built/admin/assets/preview-modal-CjRbBr8U.js +0 -1
- package/core/built/admin/assets/profile-yiM1f_Og.js +0 -1
- package/core/built/admin/assets/radio-group-CKROWsw4.js +0 -1
- package/core/built/admin/assets/router-provider-CU87qxXw.js +0 -1
- package/core/built/admin/assets/select-CKaC4ggu.js +0 -1
- package/core/built/admin/assets/settings-81kxUa28.js +0 -1
- package/core/built/admin/assets/settings-BVXFqBVK.js +0 -4
- package/core/built/admin/assets/settings-breadcrumbs-czGRx-iu.js +0 -1
- package/core/built/admin/assets/shade-app-CcS3f8MZ-D5BnfHck.js +0 -3
- package/core/built/admin/assets/share-modal-BL-16PiB.js +0 -1
- package/core/built/admin/assets/skeleton-DwCva0-b.js +0 -1
- package/core/built/admin/assets/sort-button-Dquc83jY.js +0 -1
- package/core/built/admin/assets/stack-BURz77VS.js +0 -1
- package/core/built/admin/assets/stats-view-NDja_g96.js +0 -1
- package/core/built/admin/assets/stripe-button-C3cfCAou.js +0 -1
- package/core/built/admin/assets/stripe-connect-modal-FyE3Ey2c.js +0 -1
- package/core/built/admin/assets/switch-zfUUy9Jh.js +0 -1
- package/core/built/admin/assets/table-dE4GTIRT.js +0 -1
- package/core/built/admin/assets/tabs-B3XK93sR.js +0 -1
- package/core/built/admin/assets/tag-detail-D4P-tZdM.js +0 -2
- package/core/built/admin/assets/tag-picker-BNSZ6Qxw.js +0 -1
- package/core/built/admin/assets/tags-BsZcaJ6h.js +0 -1
- package/core/built/admin/assets/text-DC-ot6u2.js +0 -1
- package/core/built/admin/assets/textarea-B4FffFmE.js +0 -1
- package/core/built/admin/assets/tier-detail-modal-a77Z2VMH.js +0 -1
- package/core/built/admin/assets/toggle-group-D2p4okmJ.js +0 -1
- package/core/built/admin/assets/transistor-modal-C8RMkhOD.js +0 -1
- package/core/built/admin/assets/trend-badge-B9v7kMF8.js +0 -1
- package/core/built/admin/assets/types-uYQ1HgqL.js +0 -1
- package/core/built/admin/assets/use-analytics-data-ChHqiZXv.js +0 -1
- package/core/built/admin/assets/use-availability-hPdeG76y.js +0 -1
- package/core/built/admin/assets/use-filter-params-BSFigDIn.js +0 -1
- package/core/built/admin/assets/use-growth-stats-CqqdnESN.js +0 -1
- package/core/built/admin/assets/use-limiter-DBGjYqu7.js +0 -19
- package/core/built/admin/assets/use-reply-chain-data-DmD2yX5D.js +0 -1
- package/core/built/admin/assets/use-sending-eta-CQeG6OP_.js +0 -1
- package/core/built/admin/assets/use-setting-group-Dza6bQ7W.js +0 -1
- package/core/built/admin/assets/use-settings-navigation-BIjI5mL3.js +0 -1
- package/core/built/admin/assets/use-simple-pagination-DwVxe-Lo.js +0 -1
- package/core/built/admin/assets/use-staff-users-i_Mq9OGR.js +0 -1
- package/core/built/admin/assets/use-tinybird-query-DXoF74NM.js +0 -1
- package/core/built/admin/assets/user-detail-modal-BU4QCHE7.js +0 -1
- package/core/built/admin/assets/virtual-list-DScLNuU8.js +0 -1
- package/core/built/admin/assets/web-BZS-t4H0.js +0 -1
- package/core/built/admin/assets/web-DxAUxnK3.js +0 -2
- package/core/frontend/services/sitemap/utils.js +0 -17
- package/core/server/services/automations/get-scheduler-idempotency-key.js +0 -13
- package/core/server/web/api/testmode/index.js +0 -1
- package/core/server/web/api/testmode/jobs/cpu-hog.js +0 -15
- package/core/server/web/api/testmode/jobs/graceful-job.js +0 -50
- package/core/server/web/api/testmode/jobs/say-hello.js +0 -17
- package/core/server/web/api/testmode/routes.js +0 -120
|
@@ -2,6 +2,7 @@ const logging = require('@tryghost/logging');
|
|
|
2
2
|
const ObjectID = require('bson-objectid').default;
|
|
3
3
|
const errors = require('@tryghost/errors');
|
|
4
4
|
const tpl = require('@tryghost/tpl');
|
|
5
|
+
const SendEmailJob = require('./jobs/send-email-job').default;
|
|
5
6
|
const messages = {
|
|
6
7
|
emailErrorPartialFailure:
|
|
7
8
|
'An error occurred, and your newsletter was only partially sent. Please retry sending the remaining emails.',
|
|
@@ -17,7 +18,7 @@ const SHUTDOWN_CODE = 'BULK_EMAIL_SHUTDOWN_IN_PROGRESS';
|
|
|
17
18
|
* @typedef {import('./email-renderer')} EmailRenderer
|
|
18
19
|
* @typedef {import('./domain-warming-service').DomainWarmingService} DomainWarmingService
|
|
19
20
|
* @typedef {import('./email-renderer').MemberLike} MemberLike
|
|
20
|
-
* @typedef {
|
|
21
|
+
* @typedef {import('../jobs-service/jobs-service').JobsService} JobsService
|
|
21
22
|
* @typedef {object} Email
|
|
22
23
|
* @typedef {object} Newsletter
|
|
23
24
|
* @typedef {object} Post
|
|
@@ -188,23 +189,20 @@ class BatchSendingService {
|
|
|
188
189
|
}
|
|
189
190
|
|
|
190
191
|
/**
|
|
191
|
-
*
|
|
192
|
+
* Dispatches the job that sends the email; the job itself only proceeds if the email
|
|
193
|
+
* is pending or failed.
|
|
194
|
+
* Resolves when dispatch completes, not when the email is sent.
|
|
192
195
|
* @param {Email} email
|
|
193
|
-
* @returns {void}
|
|
196
|
+
* @returns {Promise<void>}
|
|
194
197
|
*/
|
|
195
|
-
scheduleEmail(email) {
|
|
198
|
+
async scheduleEmail(email) {
|
|
199
|
+
await this.#jobsService.dispatch(new SendEmailJob({ emailId: email.id }));
|
|
196
200
|
logging.info(`[Background Job] batch-sending-service-job queued for email ${email.id}`);
|
|
197
|
-
return this.#jobsService.addJob({
|
|
198
|
-
name: 'batch-sending-service-job',
|
|
199
|
-
job: this.emailJob.bind(this),
|
|
200
|
-
data: { emailId: email.id },
|
|
201
|
-
offloaded: false,
|
|
202
|
-
});
|
|
203
201
|
}
|
|
204
202
|
|
|
205
203
|
/**
|
|
206
|
-
*
|
|
207
|
-
* @param {{emailId: string}} data
|
|
204
|
+
* Sends an email after refetching it and acquiring its status lock.
|
|
205
|
+
* @param {{emailId: string}} data Identifier of the email to refetch and lock.
|
|
208
206
|
*/
|
|
209
207
|
async emailJob({ emailId }) {
|
|
210
208
|
logging.info(`[Background Job] batch-sending-service-job started for email ${emailId}`);
|
|
@@ -270,6 +268,7 @@ class BatchSendingService {
|
|
|
270
268
|
{ ...this.#getAfterRetryConfig(), description: `email ${emailId} -> submitted` },
|
|
271
269
|
);
|
|
272
270
|
logging.info(
|
|
271
|
+
{ system: { event: 'send_email.submitted', email_id: emailId } },
|
|
273
272
|
`[Background Job] batch-sending-service-job completed for email ${emailId} in ${Date.now() - startTime}ms`,
|
|
274
273
|
);
|
|
275
274
|
} catch (e) {
|
|
@@ -820,7 +819,7 @@ class BatchSendingService {
|
|
|
820
819
|
let succeeded = false;
|
|
821
820
|
|
|
822
821
|
try {
|
|
823
|
-
|
|
822
|
+
const members = await this.retryDb(
|
|
824
823
|
async () => {
|
|
825
824
|
const m = await this.getBatchMembers(batch.id);
|
|
826
825
|
|
|
@@ -958,7 +957,7 @@ class BatchSendingService {
|
|
|
958
957
|
* @returns {Promise<MemberLike[]>}
|
|
959
958
|
*/
|
|
960
959
|
async getBatchMembers(batchId) {
|
|
961
|
-
|
|
960
|
+
const models = await this.#models.EmailRecipient.findAll({
|
|
962
961
|
filter: `batch_id:'${batchId}'`,
|
|
963
962
|
withRelated: ['member', 'member.stripeSubscriptions', 'member.products'],
|
|
964
963
|
});
|
|
@@ -65,6 +65,19 @@ function escapeHtml(unsafe) {
|
|
|
65
65
|
.replace(/'/g, ''');
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* @param {string} html
|
|
70
|
+
* @returns {string}
|
|
71
|
+
*/
|
|
72
|
+
function fixOutlookChars(html) {
|
|
73
|
+
return html
|
|
74
|
+
.replace(/'/g, ''')
|
|
75
|
+
.replace(/→/g, '→')
|
|
76
|
+
.replace(/–/g, '–')
|
|
77
|
+
.replace(/“/g, '“')
|
|
78
|
+
.replace(/”/g, '”');
|
|
79
|
+
}
|
|
80
|
+
|
|
68
81
|
/**
|
|
69
82
|
* @param {string} locale
|
|
70
83
|
* @returns {boolean}
|
|
@@ -754,23 +767,52 @@ class EmailRenderer {
|
|
|
754
767
|
// Convert DOM back to HTML
|
|
755
768
|
html = $.html(); // () Fix for vscode syntax highlighter
|
|
756
769
|
|
|
770
|
+
// Personalize the entire name row after CSS inlining so an absent name also
|
|
771
|
+
// removes its label and markup, including in clients that discard stylesheets.
|
|
772
|
+
const nameRow = $('.subscription-details .subscription-name');
|
|
773
|
+
const nameHtml = nameRow.length ? $.html(nameRow) : '';
|
|
774
|
+
const nameText = nameRow.text();
|
|
775
|
+
const plaintextHtml = nameHtml
|
|
776
|
+
? html.replace(nameHtml, '<p>%%{subscription_name_text}%%</p>')
|
|
777
|
+
: html;
|
|
778
|
+
if (nameHtml) {
|
|
779
|
+
html = html.replace(nameHtml, '%%{subscription_name_html}%%');
|
|
780
|
+
}
|
|
781
|
+
|
|
757
782
|
// Replacement strings
|
|
758
783
|
const replacementDefinitions = this.buildReplacementDefinitions({
|
|
759
784
|
html,
|
|
760
785
|
newsletterUuid: newsletter.get('uuid'),
|
|
761
786
|
});
|
|
762
787
|
|
|
788
|
+
if (nameHtml) {
|
|
789
|
+
const outlookNameHtml = fixOutlookChars(nameHtml);
|
|
790
|
+
replacementDefinitions.push(
|
|
791
|
+
{
|
|
792
|
+
id: 'subscription_name_html',
|
|
793
|
+
token: /%%\{subscription_name_html\}%%/g,
|
|
794
|
+
getValue: (member) =>
|
|
795
|
+
member.name?.trim()
|
|
796
|
+
? outlookNameHtml.replace('%%{name}%%', () => escapeHtml(member.name))
|
|
797
|
+
: '',
|
|
798
|
+
trusted: true, // Member name is already HTML-escaped
|
|
799
|
+
},
|
|
800
|
+
{
|
|
801
|
+
id: 'subscription_name_text',
|
|
802
|
+
token: /%%\{subscription_name_text\}%%/g,
|
|
803
|
+
getValue: (member) =>
|
|
804
|
+
member.name?.trim() ? nameText.replace('%%{name}%%', () => member.name) : '',
|
|
805
|
+
},
|
|
806
|
+
);
|
|
807
|
+
}
|
|
808
|
+
|
|
763
809
|
// TODO: normalizeReplacementStrings (replace unsupported replacement strings)
|
|
764
810
|
|
|
765
811
|
// Convert HTML to plaintext
|
|
766
|
-
const plaintext = htmlToPlaintext.email(
|
|
812
|
+
const plaintext = htmlToPlaintext.email(plaintextHtml);
|
|
767
813
|
|
|
768
814
|
// Fix any unsupported chars in Outlook
|
|
769
|
-
html = html
|
|
770
|
-
html = html.replace(/→/g, '→');
|
|
771
|
-
html = html.replace(/–/g, '–');
|
|
772
|
-
html = html.replace(/“/g, '“');
|
|
773
|
-
html = html.replace(/”/g, '”');
|
|
815
|
+
html = fixOutlookChars(html);
|
|
774
816
|
|
|
775
817
|
return {
|
|
776
818
|
html,
|
|
@@ -813,7 +855,7 @@ class EmailRenderer {
|
|
|
813
855
|
isMemberTrialing(member) {
|
|
814
856
|
// Do we have an active subscription?
|
|
815
857
|
if (member.status === 'paid') {
|
|
816
|
-
|
|
858
|
+
const activeSubscription = member.subscriptions.find((subscription) => {
|
|
817
859
|
return subscription.status === 'trialing';
|
|
818
860
|
});
|
|
819
861
|
|
|
@@ -965,12 +1007,6 @@ class EmailRenderer {
|
|
|
965
1007
|
return member.name;
|
|
966
1008
|
},
|
|
967
1009
|
},
|
|
968
|
-
{
|
|
969
|
-
id: 'name_class',
|
|
970
|
-
getValue: (member) => {
|
|
971
|
-
return member.name ? '' : 'hidden';
|
|
972
|
-
},
|
|
973
|
-
},
|
|
974
1010
|
{
|
|
975
1011
|
id: 'email',
|
|
976
1012
|
getValue: (member) => {
|
|
@@ -1183,7 +1219,7 @@ class EmailRenderer {
|
|
|
1183
1219
|
*/
|
|
1184
1220
|
#getEmailPreheader(postModel, audience, html) {
|
|
1185
1221
|
let plaintext = postModel.get('plaintext');
|
|
1186
|
-
|
|
1222
|
+
const customExcerpt = postModel.get('custom_excerpt');
|
|
1187
1223
|
if (customExcerpt) {
|
|
1188
1224
|
return customExcerpt;
|
|
1189
1225
|
} else {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const assert = require('node:assert/strict');
|
|
1
2
|
const debug = require('@tryghost/debug')('i18n');
|
|
2
3
|
const url = require('../../api/endpoints/utils/serializers/output/utils/url');
|
|
3
4
|
const events = require('../../lib/common/events');
|
|
@@ -14,10 +15,11 @@ class EmailServiceWrapper {
|
|
|
14
15
|
return jsonModel.url;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
init({ ghostServer } = {}) {
|
|
18
|
+
init({ ghostServer, jobsService } = {}) {
|
|
18
19
|
if (this.service) {
|
|
19
20
|
return;
|
|
20
21
|
}
|
|
22
|
+
assert(jobsService, 'Email service requires the jobs service');
|
|
21
23
|
|
|
22
24
|
const EmailService = require('./email-service');
|
|
23
25
|
const EmailController = require('./email-controller');
|
|
@@ -36,7 +38,6 @@ class EmailServiceWrapper {
|
|
|
36
38
|
const configService = require('../../../shared/config');
|
|
37
39
|
const settingsCache = require('../../../shared/settings-cache');
|
|
38
40
|
const settingsHelpers = require('../settings-helpers');
|
|
39
|
-
const jobsService = require('../jobs');
|
|
40
41
|
const membersService = require('../members');
|
|
41
42
|
const db = require('../../data/db');
|
|
42
43
|
const sentry = require('../../../shared/sentry');
|
|
@@ -204,7 +204,7 @@ class EmailService {
|
|
|
204
204
|
});
|
|
205
205
|
|
|
206
206
|
try {
|
|
207
|
-
this.#batchSendingService.scheduleEmail(email);
|
|
207
|
+
await this.#batchSendingService.scheduleEmail(email);
|
|
208
208
|
} catch (e) {
|
|
209
209
|
await email.save(
|
|
210
210
|
{
|
|
@@ -334,7 +334,7 @@ class EmailService {
|
|
|
334
334
|
logging.warn(`Email resume: scheduling ${email.id} for re-send ${JSON.stringify(breadcrumb)}`);
|
|
335
335
|
|
|
336
336
|
// Skip checkLimits — this email already passed limits when first sent.
|
|
337
|
-
this.#batchSendingService.scheduleEmail(email);
|
|
337
|
+
await this.#batchSendingService.scheduleEmail(email);
|
|
338
338
|
}
|
|
339
339
|
|
|
340
340
|
async #buildResumeBreadcrumb(email) {
|
|
@@ -392,10 +392,22 @@ class EmailService {
|
|
|
392
392
|
// so we have a immediate response when retrying an email (schedule can take a while to kick off sometimes)
|
|
393
393
|
await email.save({ status: 'pending' }, { patch: true });
|
|
394
394
|
|
|
395
|
-
|
|
395
|
+
try {
|
|
396
|
+
await this.#batchSendingService.scheduleEmail(email);
|
|
397
|
+
} catch (e) {
|
|
398
|
+
await email.save({ status: 'failed' }, { patch: true });
|
|
399
|
+
throw e;
|
|
400
|
+
}
|
|
396
401
|
return email;
|
|
397
402
|
}
|
|
398
403
|
|
|
404
|
+
/**
|
|
405
|
+
* @param {import('./jobs/send-email-job').default} job
|
|
406
|
+
*/
|
|
407
|
+
async handleSendEmailJob(job) {
|
|
408
|
+
await this.#batchSendingService.emailJob({ emailId: job.emailId });
|
|
409
|
+
}
|
|
410
|
+
|
|
399
411
|
/**
|
|
400
412
|
* @params {string|null} [audienceStatus] - the audience's free/paid status
|
|
401
413
|
* ('status:free' / 'status:-free'), see EmailRenderer#describeSegment
|
|
@@ -476,11 +488,24 @@ class EmailService {
|
|
|
476
488
|
* @return {string}
|
|
477
489
|
*/
|
|
478
490
|
replaceDefinitions(htmlOrPlaintext, replacements, member) {
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
htmlOrPlaintext = htmlOrPlaintext.replace(replacement.token, replacement.getValue(member));
|
|
491
|
+
if (!replacements.length) {
|
|
492
|
+
return htmlOrPlaintext;
|
|
482
493
|
}
|
|
483
|
-
|
|
494
|
+
const values = replacements.map(({ token, getValue }) => ({ token, value: getValue(member) }));
|
|
495
|
+
const tokens = new RegExp(
|
|
496
|
+
replacements.map(({ token }) => `(?:${token.source})`).join('|'),
|
|
497
|
+
'g',
|
|
498
|
+
);
|
|
499
|
+
|
|
500
|
+
// Only match the original body, never placeholders inside inserted member data.
|
|
501
|
+
return htmlOrPlaintext.replace(tokens, (match) => {
|
|
502
|
+
for (const { token, value } of values) {
|
|
503
|
+
if (match.search(token) !== -1) {
|
|
504
|
+
return value;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
return match;
|
|
508
|
+
});
|
|
484
509
|
}
|
|
485
510
|
|
|
486
511
|
/**
|
|
@@ -497,7 +522,7 @@ class EmailService {
|
|
|
497
522
|
const exampleMember = await this.getExampleMember(null, audience.status);
|
|
498
523
|
|
|
499
524
|
const subject = this.#emailRenderer.getSubject(post);
|
|
500
|
-
|
|
525
|
+
const { html, plaintext, replacements } = await this.#emailRenderer.renderBody(
|
|
501
526
|
post,
|
|
502
527
|
newsletter,
|
|
503
528
|
renderSegment,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
{{!-- Keep the native table-cell display; inline display values make Firefox wrap sibling cells. --}}
|
|
2
|
+
<td dir="ltr" valign="top" align="center" style="box-sizing: border-box; vertical-align: top; font-family: inherit; text-align: center; padding: 0 4px 4px; cursor: pointer; width: {{#if cellWidth}}{{cellWidth}}{{else}}30%{{/if}};">
|
|
2
3
|
<a href="{{href}}" target="_blank">
|
|
3
4
|
<img src={{iconUrl}} border="0" width={{width}} height={{height}} alt="{{buttonText}}" {{#if iconClass}}class="{{iconClass}}"{{/if}}>
|
|
4
5
|
<p class="feedback-button-text">{{t buttonText}}</p>
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%">
|
|
60
60
|
<tr>
|
|
61
61
|
<td class="subscription-details">
|
|
62
|
-
<p class="
|
|
62
|
+
<p class="subscription-name">{{t 'Name'}}: %%{name}%%</p>
|
|
63
63
|
<p>{{t 'Email'}}: <a href="#">%%{email}%%</a></p>
|
|
64
64
|
<p>{{t 'Member since'}}: %%{created_at}%%</p>
|
|
65
65
|
</td>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const job_1 = require("../../jobs-service/job");
|
|
4
|
+
class SendEmailJob extends job_1.Job {
|
|
5
|
+
static type = 'send-email';
|
|
6
|
+
emailId;
|
|
7
|
+
constructor({ emailId }) {
|
|
8
|
+
super();
|
|
9
|
+
this.emailId = emailId;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
exports.default = SendEmailJob;
|
|
@@ -30,7 +30,7 @@ const { escapeExpression } = require('handlebars');
|
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* @typedef {object} EmailProviderSuccessResponse
|
|
33
|
-
* @prop {string} id
|
|
33
|
+
* @prop {string|null} id Null when the batch carries a per-recipient Message-Id
|
|
34
34
|
*/
|
|
35
35
|
|
|
36
36
|
class MailgunEmailProvider {
|
|
@@ -134,6 +134,11 @@ class MailgunEmailProvider {
|
|
|
134
134
|
messageData.deliveryTime = options.deliveryTime;
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
// only newsletters opt in; automation and gift emails rely on the id Mailgun returns
|
|
138
|
+
if (this.#config.get('bulkEmail:perRecipientMessageId') === true) {
|
|
139
|
+
messageData.perRecipientMessageId = true;
|
|
140
|
+
}
|
|
141
|
+
|
|
137
142
|
// create recipient data for Mailgun using replacement definitions
|
|
138
143
|
const htmlEscapedIds = new Set(
|
|
139
144
|
replacementDefinitions.filter((def) => !def.trusted).map((def) => def.id),
|
|
@@ -163,7 +168,7 @@ class MailgunEmailProvider {
|
|
|
163
168
|
|
|
164
169
|
// Return mailgun provider id, trim <> from response
|
|
165
170
|
return {
|
|
166
|
-
id: response.id.trim().replace(/^<|>$/g, ''),
|
|
171
|
+
id: response.id === null ? null : response.id.trim().replace(/^<|>$/g, ''),
|
|
167
172
|
};
|
|
168
173
|
} catch (e) {
|
|
169
174
|
let ghostError;
|
|
@@ -917,15 +917,13 @@ class GiftService {
|
|
|
917
917
|
}, '[Background Job] clean-gifts finished its cleanup phases');
|
|
918
918
|
}
|
|
919
919
|
async processReminders() {
|
|
920
|
+
const startedAt = Date.now();
|
|
920
921
|
const now = new Date();
|
|
921
922
|
const toRemind = await this.deps.giftRepository.findPendingReminder({
|
|
922
923
|
now,
|
|
923
924
|
reminderLeadMs: GIFT_REMINDER_LEAD_MS,
|
|
924
925
|
reminderFloorMs: GIFT_REMINDER_FLOOR_MS,
|
|
925
926
|
});
|
|
926
|
-
if (toRemind.length === 0) {
|
|
927
|
-
return { remindedCount: 0, skippedCount: 0, failedCount: 0 };
|
|
928
|
-
}
|
|
929
927
|
let remindedCount = 0;
|
|
930
928
|
let skippedCount = 0;
|
|
931
929
|
let failedCount = 0;
|
|
@@ -944,6 +942,17 @@ class GiftService {
|
|
|
944
942
|
failedCount += 1;
|
|
945
943
|
}
|
|
946
944
|
}
|
|
945
|
+
// The jobs service's lifecycle log carries no counts, so the poll reports
|
|
946
|
+
// its own summary here for the daily job and the exact scheduler alike.
|
|
947
|
+
logging_1.default.info({
|
|
948
|
+
system: {
|
|
949
|
+
event: 'send_gift_reminders.completed',
|
|
950
|
+
reminded_count: remindedCount,
|
|
951
|
+
skipped_count: skippedCount,
|
|
952
|
+
failed_count: failedCount,
|
|
953
|
+
duration_ms: Date.now() - startedAt,
|
|
954
|
+
},
|
|
955
|
+
}, `[Background Job] send-gift-reminders processed reminders: ${remindedCount} sent, ${skippedCount} not due, ${failedCount} rejected`);
|
|
947
956
|
return { remindedCount, skippedCount, failedCount };
|
|
948
957
|
}
|
|
949
958
|
async sendReminderForGift(token) {
|
|
@@ -28,7 +28,6 @@ function init(options) {
|
|
|
28
28
|
const { SubscriptionActivatedEvent } = require('../../../shared/events');
|
|
29
29
|
const StartGiftReminderFlushEvent = require('./events/start-gift-reminder-flush-event');
|
|
30
30
|
const { StartGiftDeliveryFlushEvent } = require('./events/start-gift-delivery-flush-event');
|
|
31
|
-
const jobs = require('./jobs');
|
|
32
31
|
const emailAnalyticsJobs = require('../email-analytics/jobs');
|
|
33
32
|
const { GhostMailer } = require('../mail');
|
|
34
33
|
const MailgunClient = require('../lib/mailgun-client');
|
|
@@ -155,7 +154,6 @@ function init(options) {
|
|
|
155
154
|
logging.error(err, `Failed to process gift delivery ${event.data.deliveryId}`);
|
|
156
155
|
}
|
|
157
156
|
});
|
|
158
|
-
jobs.scheduleGiftReminderJob();
|
|
159
157
|
}
|
|
160
158
|
// Re-arms future deliveries and retries sends interrupted by a previous
|
|
161
159
|
// shutdown. Runs after all services initialise because recovery sends need
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
1
|
const logging = require('@tryghost/logging');
|
|
3
|
-
const jobManager = require('../../jobs');
|
|
4
2
|
const CleanGiftsJob = require('./clean-gifts-job').default;
|
|
3
|
+
const SendGiftRemindersJob = require('./send-gift-reminders-job').default;
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
const hasScheduled = {
|
|
7
6
|
cleanup: false,
|
|
8
7
|
reminders: false,
|
|
9
8
|
};
|
|
@@ -23,25 +22,6 @@ function randomOffPeakDailyCron() {
|
|
|
23
22
|
return `${s} ${m} ${h} * * *`;
|
|
24
23
|
}
|
|
25
24
|
|
|
26
|
-
function scheduleJob(key, name, jobFile) {
|
|
27
|
-
if (alreadyScheduledOrTest(key)) {
|
|
28
|
-
return hasScheduled[key];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const at = randomOffPeakDailyCron();
|
|
32
|
-
|
|
33
|
-
logging.info(`[Background Job] ${name} scheduled at ${at}`);
|
|
34
|
-
jobManager.addJob({
|
|
35
|
-
at,
|
|
36
|
-
job: path.resolve(__dirname, jobFile),
|
|
37
|
-
name,
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
hasScheduled[key] = true;
|
|
41
|
-
|
|
42
|
-
return true;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
25
|
module.exports = {
|
|
46
26
|
async scheduleGiftCleanupJob(jobsService) {
|
|
47
27
|
if (alreadyScheduledOrTest('cleanup')) {
|
|
@@ -55,7 +35,15 @@ module.exports = {
|
|
|
55
35
|
hasScheduled.cleanup = true;
|
|
56
36
|
},
|
|
57
37
|
|
|
58
|
-
scheduleGiftReminderJob() {
|
|
59
|
-
|
|
38
|
+
async scheduleGiftReminderJob(jobsService) {
|
|
39
|
+
if (alreadyScheduledOrTest('reminders')) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const cron = randomOffPeakDailyCron();
|
|
44
|
+
logging.info(`[Background Job] send-gift-reminders scheduled at ${cron}`);
|
|
45
|
+
await jobsService.scheduleRecurring(new SendGiftRemindersJob(), { cron });
|
|
46
|
+
|
|
47
|
+
hasScheduled.reminders = true;
|
|
60
48
|
},
|
|
61
49
|
};
|
|
@@ -1,47 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
// service dependency inside this worker
|
|
7
|
-
|
|
8
|
-
// Exit early when cancelled to prevent stalling shutdown. No cleanup needed
|
|
9
|
-
// when cancelling as everything is idempotent - any gift that hasn't yet had
|
|
10
|
-
// its reminder recorded will be picked up on the next run
|
|
11
|
-
function cancel() {
|
|
12
|
-
if (parentPort) {
|
|
13
|
-
parentPort.postMessage('cancelled before completion');
|
|
14
|
-
parentPort.postMessage('cancelled');
|
|
15
|
-
} else {
|
|
16
|
-
setTimeout(() => {
|
|
17
|
-
process.exit(0);
|
|
18
|
-
}, 1000);
|
|
19
|
-
}
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const job_1 = require("../../jobs-service/job");
|
|
4
|
+
class SendGiftRemindersJob extends job_1.Job {
|
|
5
|
+
static type = 'send-gift-reminders';
|
|
20
6
|
}
|
|
21
|
-
|
|
22
|
-
if (parentPort) {
|
|
23
|
-
parentPort.once('message', (message) => {
|
|
24
|
-
if (message === 'cancel') {
|
|
25
|
-
return cancel();
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
(async () => {
|
|
31
|
-
if (parentPort) {
|
|
32
|
-
// Bounce to the main thread via the JobManager's event bridge - the
|
|
33
|
-
// gifts service subscribes to this event and runs the work in the
|
|
34
|
-
// already-initialised main process
|
|
35
|
-
parentPort.postMessage({
|
|
36
|
-
event: {
|
|
37
|
-
type: StartGiftReminderFlushEvent.name,
|
|
38
|
-
},
|
|
39
|
-
});
|
|
40
|
-
parentPort.postMessage('dispatched to main process');
|
|
41
|
-
parentPort.postMessage('done');
|
|
42
|
-
} else {
|
|
43
|
-
setTimeout(() => {
|
|
44
|
-
process.exit(0);
|
|
45
|
-
}, 1000);
|
|
46
|
-
}
|
|
47
|
-
})();
|
|
7
|
+
exports.default = SendGiftRemindersJob;
|
|
@@ -20,7 +20,7 @@ async function accept(invitation) {
|
|
|
20
20
|
return models.Base.transaction(async (transacting) => {
|
|
21
21
|
const options = { context: { internal: true }, transacting, forUpdate: true };
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
const invite = await models.Invite.findOne({ token: inviteToken, status: 'sent' }, options);
|
|
24
24
|
|
|
25
25
|
if (!invite) {
|
|
26
26
|
throw new errors.NotFoundError({ message: tpl(messages.inviteNotFound) });
|
|
@@ -32,7 +32,7 @@ async function accept(invitation) {
|
|
|
32
32
|
|
|
33
33
|
const email = invite.get('email');
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
const user = await models.User.findOne({ email }, options);
|
|
36
36
|
if (user) {
|
|
37
37
|
throw new errors.ValidationError({
|
|
38
38
|
message: tpl(messages.inviteEmailAlreadyExist.message),
|
|
@@ -21,33 +21,6 @@ const workerMessageHandler = ({ name, message }) => {
|
|
|
21
21
|
}
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
const initTestMode = () => {
|
|
25
|
-
// Output job queue length every 5 seconds
|
|
26
|
-
setInterval(() => {
|
|
27
|
-
logging.warn(
|
|
28
|
-
`${jobManager.inlineQueue.length()} jobs in the queue. Idle: ${jobManager.inlineQueue.idle()}`,
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
const runningScheduledjobs = Object.keys(jobManager.bree.workers);
|
|
32
|
-
if (Object.keys(jobManager.bree.workers).length) {
|
|
33
|
-
logging.warn(
|
|
34
|
-
`${Object.keys(jobManager.bree.workers).length} jobs running: ${runningScheduledjobs}`,
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const scheduledJobs = Object.keys(jobManager.bree.intervals);
|
|
39
|
-
if (Object.keys(jobManager.bree.intervals).length) {
|
|
40
|
-
logging.warn(
|
|
41
|
-
`${Object.keys(jobManager.bree.intervals).length} scheduled jobs: ${scheduledJobs}`,
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (runningScheduledjobs.length === 0 && scheduledJobs.length === 0) {
|
|
46
|
-
logging.warn('No scheduled or running jobs');
|
|
47
|
-
}
|
|
48
|
-
}, 5000);
|
|
49
|
-
};
|
|
50
|
-
|
|
51
24
|
const jobManager = new JobManager({
|
|
52
25
|
errorHandler,
|
|
53
26
|
workerMessageHandler,
|
|
@@ -58,4 +31,3 @@ const jobManager = new JobManager({
|
|
|
58
31
|
});
|
|
59
32
|
|
|
60
33
|
module.exports = jobManager;
|
|
61
|
-
module.exports.initTestMode = initTestMode;
|
|
@@ -68,6 +68,11 @@ class JobsService {
|
|
|
68
68
|
const queue = this.#queueByType.get(type);
|
|
69
69
|
return queue === undefined ? undefined : { queue };
|
|
70
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Resolves when the backend's enqueue call completes, without waiting for the
|
|
73
|
+
* handler to run. Enqueue errors reject this promise. Resolution does not
|
|
74
|
+
* guarantee execution: the backend controls what happens to work during shutdown.
|
|
75
|
+
*/
|
|
71
76
|
async dispatch(job) {
|
|
72
77
|
const envelope = this.#buildEnvelope(job);
|
|
73
78
|
await this.#backend.enqueue(envelope, this.#routingFor(envelope.type));
|
|
@@ -40,12 +40,15 @@ exports.default = registerJobHandlers;
|
|
|
40
40
|
const clean_tokens_job_1 = __importDefault(require("../members/jobs/clean-tokens-job"));
|
|
41
41
|
const clean_expired_comped_job_1 = __importDefault(require("../members/jobs/clean-expired-comped-job"));
|
|
42
42
|
const clean_gifts_job_1 = __importDefault(require("../gifts/jobs/clean-gifts-job"));
|
|
43
|
+
const send_gift_reminders_job_1 = __importDefault(require("../gifts/jobs/send-gift-reminders-job"));
|
|
43
44
|
const external_media_inliner_job_1 = __importDefault(require("../media-inliner/external-media-inliner-job"));
|
|
44
45
|
const content_csv_import_job_1 = __importDefault(require("../content-import/jobs/content-csv-import-job"));
|
|
45
46
|
const contentImport = __importStar(require("../content-import"));
|
|
47
|
+
const members_import_job_1 = __importDefault(require("../members/jobs/members-import-job"));
|
|
46
48
|
const update_check_job_1 = __importDefault(require("../update-check/jobs/update-check-job"));
|
|
47
49
|
const process_webmention_job_1 = __importDefault(require("../mentions/process-webmention-job"));
|
|
48
50
|
const send_webmentions_job_1 = __importDefault(require("../mentions/send-webmentions-job"));
|
|
51
|
+
const send_email_job_1 = __importDefault(require("../email-service/jobs/send-email-job"));
|
|
49
52
|
const updateCheck = require('../update-check');
|
|
50
53
|
// Webmention processing fetches external pages and is triggered by
|
|
51
54
|
// unauthenticated requests, so webmention jobs run in their own lane where a
|
|
@@ -54,7 +57,11 @@ const updateCheck = require('../update-check');
|
|
|
54
57
|
// this shared declaration so none can declare the queue with a different
|
|
55
58
|
// concurrency.
|
|
56
59
|
const WEBMENTIONS_QUEUE = { queue: 'webmentions', concurrency: 3 };
|
|
57
|
-
|
|
60
|
+
// Keep newsletter sends independent of imports and other shared work. Two sends
|
|
61
|
+
// can progress at once, each with its own two batch workers, so a long send or
|
|
62
|
+
// retry does not hold up every other newsletter.
|
|
63
|
+
const EMAIL_QUEUE = { queue: 'email', concurrency: 2 };
|
|
64
|
+
function registerJobHandlers({ jobsService, memberJobs, giftService, mediaInliner, mentionsController, mentionsSendingService, membersService, emailService, }) {
|
|
58
65
|
jobsService.handle(clean_tokens_job_1.default, async () => {
|
|
59
66
|
await memberJobs.cleanTokens();
|
|
60
67
|
});
|
|
@@ -64,12 +71,18 @@ function registerJobHandlers({ jobsService, memberJobs, giftService, mediaInline
|
|
|
64
71
|
jobsService.handle(clean_gifts_job_1.default, async () => {
|
|
65
72
|
await giftService.cleanup();
|
|
66
73
|
});
|
|
74
|
+
jobsService.handle(send_gift_reminders_job_1.default, async () => {
|
|
75
|
+
await giftService.processReminders();
|
|
76
|
+
});
|
|
67
77
|
jobsService.handle(external_media_inliner_job_1.default, async (job) => {
|
|
68
78
|
await mediaInliner.inline(job.domains);
|
|
69
79
|
});
|
|
70
80
|
jobsService.handle(content_csv_import_job_1.default, async (job) => {
|
|
71
81
|
await contentImport.handleJob(job);
|
|
72
82
|
});
|
|
83
|
+
jobsService.handle(members_import_job_1.default, async (job) => {
|
|
84
|
+
await membersService.handleImportJob(job);
|
|
85
|
+
});
|
|
73
86
|
jobsService.handle(update_check_job_1.default, async () => {
|
|
74
87
|
await updateCheck({ rethrowErrors: true });
|
|
75
88
|
});
|
|
@@ -79,4 +92,7 @@ function registerJobHandlers({ jobsService, memberJobs, giftService, mediaInline
|
|
|
79
92
|
jobsService.handle(send_webmentions_job_1.default, async (job) => {
|
|
80
93
|
await mentionsSendingService.sendWebmentions(job);
|
|
81
94
|
}, WEBMENTIONS_QUEUE);
|
|
95
|
+
jobsService.handle(send_email_job_1.default, async (job) => {
|
|
96
|
+
await emailService.handleSendEmailJob(job);
|
|
97
|
+
}, EMAIL_QUEUE);
|
|
82
98
|
}
|