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
|
@@ -13,9 +13,7 @@ const messages = {
|
|
|
13
13
|
alreadyDisliked: 'This comment was disliked already',
|
|
14
14
|
replyToReply: 'Can not reply to a reply',
|
|
15
15
|
commentsNotEnabled: 'Comments are not enabled for this site.',
|
|
16
|
-
postIdRequired: 'A post ID is required to read comments.',
|
|
17
16
|
cannotCommentOnPost: 'You do not have permission to comment on this post.',
|
|
18
|
-
cannotReadComment: 'You do not have permission to read comments on this post',
|
|
19
17
|
cannotEditComment: 'You do not have permission to edit comments',
|
|
20
18
|
cannotPinReply: 'Replies cannot be pinned',
|
|
21
19
|
cannotPinDeletedComment: 'Deleted comments cannot be pinned',
|
|
@@ -41,7 +39,8 @@ const COMMENT_STATUSES_IN_REPLY_TO = [COMMENT_STATUS_PUBLISHED, COMMENT_STATUS_H
|
|
|
41
39
|
// new `comment.get(...)` call has one obvious place to register its column.
|
|
42
40
|
const REPLY_PARENT_REQUIRED_COLUMNS = ['parent_id', 'post_id'];
|
|
43
41
|
const IN_REPLY_TO_REQUIRED_COLUMNS = ['parent_id'];
|
|
44
|
-
const OWNERSHIP_REQUIRED_COLUMNS = ['member_id'
|
|
42
|
+
const OWNERSHIP_REQUIRED_COLUMNS = ['member_id'];
|
|
43
|
+
const VOTE_REQUIRED_COLUMNS = ['post_id'];
|
|
45
44
|
const REPORT_REQUIRED_COLUMNS = ['post_id', 'member_id', 'html', 'created_at'];
|
|
46
45
|
|
|
47
46
|
function getColumnList(columns) {
|
|
@@ -169,14 +168,11 @@ class CommentsService {
|
|
|
169
168
|
}
|
|
170
169
|
|
|
171
170
|
/** @private */
|
|
172
|
-
checkPostAccess(postModel, memberModel
|
|
173
|
-
const access = this.contentGating.checkPostAccess(
|
|
174
|
-
postModel.toJSON(),
|
|
175
|
-
memberModel ? memberModel.toJSON() : null,
|
|
176
|
-
);
|
|
171
|
+
checkPostAccess(postModel, memberModel) {
|
|
172
|
+
const access = this.contentGating.checkPostAccess(postModel.toJSON(), memberModel.toJSON());
|
|
177
173
|
if (access === this.contentGating.BLOCK_ACCESS) {
|
|
178
174
|
throw new errors.NoPermissionError({
|
|
179
|
-
message: tpl(
|
|
175
|
+
message: tpl(messages.cannotCommentOnPost),
|
|
180
176
|
});
|
|
181
177
|
}
|
|
182
178
|
}
|
|
@@ -194,32 +190,6 @@ class CommentsService {
|
|
|
194
190
|
});
|
|
195
191
|
}
|
|
196
192
|
|
|
197
|
-
async #checkPostAccessForRead(postId, options) {
|
|
198
|
-
if (options.isAdmin) {
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if (!postId) {
|
|
203
|
-
throw new errors.ValidationError({ message: tpl(messages.postIdRequired) });
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const postModel = await this.models.Post.findOne(
|
|
207
|
-
{ id: postId },
|
|
208
|
-
{ require: true, transacting: options.transacting, withRelated: ['tiers'] },
|
|
209
|
-
);
|
|
210
|
-
|
|
211
|
-
let memberModel = null;
|
|
212
|
-
const memberId = options.context?.member?.id;
|
|
213
|
-
if (memberId) {
|
|
214
|
-
memberModel = await this.models.Member.findOne(
|
|
215
|
-
{ id: memberId },
|
|
216
|
-
{ require: true, transacting: options.transacting, withRelated: ['products'] },
|
|
217
|
-
);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
this.checkPostAccess(postModel, memberModel, { action: 'read' });
|
|
221
|
-
}
|
|
222
|
-
|
|
223
193
|
/**
|
|
224
194
|
* Primary comment lookup: a single keyed fetch with the allowed statuses
|
|
225
195
|
* applied in the query (`WHERE id = ? AND status IN (...)`), optionally locked
|
|
@@ -385,9 +355,27 @@ class CommentsService {
|
|
|
385
355
|
this.checkCommentAccess(memberModel);
|
|
386
356
|
|
|
387
357
|
return await this.#withTransaction(options, async (transactionOptions) => {
|
|
388
|
-
await this.#getPublishedCommentForAction(
|
|
389
|
-
|
|
390
|
-
|
|
358
|
+
const comment = await this.#getPublishedCommentForAction(
|
|
359
|
+
commentId,
|
|
360
|
+
transactionOptions,
|
|
361
|
+
VOTE_REQUIRED_COLUMNS,
|
|
362
|
+
{
|
|
363
|
+
forUpdate: true,
|
|
364
|
+
},
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
const postModel = await this.models.Post.findOne(
|
|
368
|
+
{
|
|
369
|
+
id: comment.get('post_id'),
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
require: true,
|
|
373
|
+
...transactionOptions,
|
|
374
|
+
withRelated: ['tiers'],
|
|
375
|
+
},
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
this.checkPostAccess(postModel, memberModel);
|
|
391
379
|
|
|
392
380
|
const votes = await this.#getMemberCommentVotes(
|
|
393
381
|
{
|
|
@@ -551,7 +539,6 @@ class CommentsService {
|
|
|
551
539
|
*/
|
|
552
540
|
async getComments(options) {
|
|
553
541
|
this.checkEnabled();
|
|
554
|
-
await this.#checkPostAccessForRead(options.post_id, options);
|
|
555
542
|
const page = await this.models.Comment.findPage(
|
|
556
543
|
withPinnedSelect({ ...options, parentId: null, pinnedFirst: true }),
|
|
557
544
|
);
|
|
@@ -691,17 +678,6 @@ class CommentsService {
|
|
|
691
678
|
*/
|
|
692
679
|
async getReplies(id, options, { includeHidden = false } = {}) {
|
|
693
680
|
this.checkEnabled();
|
|
694
|
-
|
|
695
|
-
const parentComment = await this.#fetchCommentByID(id, options, {
|
|
696
|
-
requiredColumns: ['post_id'],
|
|
697
|
-
});
|
|
698
|
-
|
|
699
|
-
if (!parentComment) {
|
|
700
|
-
throw new errors.NotFoundError({ message: tpl(messages.commentNotFound) });
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
await this.#checkPostAccessForRead(parentComment.get('post_id'), options);
|
|
704
|
-
|
|
705
681
|
const page = await this.models.Comment.findPage(
|
|
706
682
|
withPinnedSelect({ ...options, parentId: id }, { includeHidden }),
|
|
707
683
|
);
|
|
@@ -783,9 +759,7 @@ class CommentsService {
|
|
|
783
759
|
async getCommentByID(id, options = {}) {
|
|
784
760
|
this.checkEnabled();
|
|
785
761
|
|
|
786
|
-
|
|
787
|
-
await this.#checkPostAccessForRead(model.get('post_id'), options);
|
|
788
|
-
return model;
|
|
762
|
+
return await this.#getReadableCommentByID(id, options);
|
|
789
763
|
}
|
|
790
764
|
|
|
791
765
|
/**
|
|
@@ -953,9 +927,6 @@ class CommentsService {
|
|
|
953
927
|
});
|
|
954
928
|
}
|
|
955
929
|
|
|
956
|
-
// Mutation responses include replies, so they require the same post access as reads.
|
|
957
|
-
await this.#checkPostAccessForRead(existingComment.get('post_id'), options);
|
|
958
|
-
|
|
959
930
|
const model = await this.models.Comment.edit(
|
|
960
931
|
{
|
|
961
932
|
status: COMMENT_STATUS_DELETED,
|
|
@@ -991,8 +962,6 @@ class CommentsService {
|
|
|
991
962
|
});
|
|
992
963
|
}
|
|
993
964
|
|
|
994
|
-
await this.#checkPostAccessForRead(existingComment.get('post_id'), options);
|
|
995
|
-
|
|
996
965
|
if (!comment) {
|
|
997
966
|
return existingComment;
|
|
998
967
|
}
|
|
@@ -5,25 +5,25 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.createImportFileStager = createImportFileStager;
|
|
7
7
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
8
|
-
const node_os_1 = __importDefault(require("node:os"));
|
|
9
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
// Stages uploads at the root of the imports storage adapter. The job reads the staged
|
|
10
|
+
// file from its path, so the adapter is the local store storage:imports defaults to.
|
|
11
|
+
function createImportFileStager(getStorage) {
|
|
12
12
|
return {
|
|
13
13
|
async stage({ filePath, fileName }) {
|
|
14
|
-
const
|
|
14
|
+
const storage = getStorage();
|
|
15
|
+
const name = `content-csv-import-${node_crypto_1.default.randomUUID()}`;
|
|
15
16
|
try {
|
|
16
|
-
await
|
|
17
|
-
|
|
17
|
+
const url = await storage.save({ name, path: filePath }, storage.storagePath);
|
|
18
|
+
return { path: node_path_1.default.join(storage.storagePath, storage.urlToPath(url)), name: fileName };
|
|
18
19
|
}
|
|
19
20
|
catch (error) {
|
|
20
|
-
await
|
|
21
|
+
await storage.delete(name).catch(() => { });
|
|
21
22
|
throw error;
|
|
22
23
|
}
|
|
23
|
-
return { path: stagedPath, name: fileName };
|
|
24
24
|
},
|
|
25
25
|
async remove(file) {
|
|
26
|
-
await
|
|
26
|
+
await getStorage().delete(node_path_1.default.basename(file.path));
|
|
27
27
|
},
|
|
28
28
|
};
|
|
29
29
|
}
|
|
@@ -78,7 +78,7 @@ function makeImporter() {
|
|
|
78
78
|
}),
|
|
79
79
|
email,
|
|
80
80
|
dispatchJob: (job) => (0, jobs_service_1.getInstance)().dispatch(job),
|
|
81
|
-
fileStager: (0, staged_file_1.createImportFileStager)(),
|
|
81
|
+
fileStager: (0, staged_file_1.createImportFileStager)(() => require('../adapter-manager').default.getAdapter('storage:imports')),
|
|
82
82
|
report,
|
|
83
83
|
store: new store_1.ImportRunStore(),
|
|
84
84
|
urlForPost: (post) => (0, post_link_1.urlForImportedPost)(post, {
|
|
@@ -16,7 +16,7 @@ const messages = {
|
|
|
16
16
|
* @param {import('@tryghost/adapter-base-redirects').RedirectConfig[]} redirects
|
|
17
17
|
*/
|
|
18
18
|
const validate = (redirects) => {
|
|
19
|
-
if (!
|
|
19
|
+
if (!Array.isArray(redirects)) {
|
|
20
20
|
throw new errors.ValidationError({
|
|
21
21
|
message: tpl(messages.redirectsWrongFormat),
|
|
22
22
|
help: tpl(messages.redirectsHelp),
|
|
@@ -4,12 +4,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.EmailAnalyticsServiceWrapper = void 0;
|
|
7
|
-
const errors_1 = __importDefault(require("@tryghost/errors"));
|
|
8
7
|
const logging_1 = __importDefault(require("@tryghost/logging"));
|
|
9
8
|
const email_analytics_service_1 = require("./email-analytics-service");
|
|
10
9
|
const fetch_mailgun_events_1 = require("./fetch-mailgun-events");
|
|
11
10
|
class EmailAnalyticsServiceWrapper {
|
|
12
11
|
#logName;
|
|
12
|
+
#jobType;
|
|
13
|
+
#completionEvent;
|
|
13
14
|
#config;
|
|
14
15
|
#metrics;
|
|
15
16
|
#service;
|
|
@@ -19,25 +20,10 @@ class EmailAnalyticsServiceWrapper {
|
|
|
19
20
|
get #logPrefix() {
|
|
20
21
|
return `[EmailAnalytics:${this.#logName}]`;
|
|
21
22
|
}
|
|
22
|
-
|
|
23
|
-
switch (this.#logName) {
|
|
24
|
-
case 'newsletters':
|
|
25
|
-
return 'email-analytics-fetch-latest';
|
|
26
|
-
case 'automations':
|
|
27
|
-
return 'email-analytics-automation-fetch-latest';
|
|
28
|
-
case 'gifts':
|
|
29
|
-
return 'email-analytics-gift-fetch-latest';
|
|
30
|
-
default:
|
|
31
|
-
return `email-analytics-${this.#logName}-fetch-latest`;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
constructor({ logName }) {
|
|
23
|
+
constructor({ logName, jobType, config, queries, mailgunTags, jobNames, cursorSeed, createEventProcessor, metrics, settingsCache, }) {
|
|
35
24
|
this.#logName = logName;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (this.#service) {
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
25
|
+
this.#jobType = jobType;
|
|
26
|
+
this.#completionEvent = `${jobType.replaceAll('-', '_')}.completed`;
|
|
41
27
|
this.#config = config;
|
|
42
28
|
this.#metrics = metrics;
|
|
43
29
|
this.#fetchOpenedEvents = Boolean(cursorSeed.eventColumns.opened);
|
|
@@ -51,32 +37,12 @@ class EmailAnalyticsServiceWrapper {
|
|
|
51
37
|
// Log the processing mode on initialization
|
|
52
38
|
const batchProcessingEnabled = this.#config.get('emailAnalytics:batchProcessing');
|
|
53
39
|
logging_1.default.info(`${this.#logPrefix} Initialized with ${batchProcessingEnabled ? 'BATCHED' : 'SEQUENTIAL'} processing mode`);
|
|
54
|
-
// We currently cannot trigger a non-offloaded job from the job manager
|
|
55
|
-
// So the email analytics jobs simply emits an event.
|
|
56
|
-
domainEvents.subscribe(event, async () => {
|
|
57
|
-
await this.startFetch();
|
|
58
|
-
});
|
|
59
40
|
}
|
|
60
41
|
get service() {
|
|
61
|
-
|
|
62
|
-
if (!result) {
|
|
63
|
-
throw new errors_1.default.InternalServerError({
|
|
64
|
-
message: 'EmailAnalyticsServiceWrapper is not initialized with service',
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
return result;
|
|
68
|
-
}
|
|
69
|
-
#getConfig() {
|
|
70
|
-
const result = this.#config;
|
|
71
|
-
if (!result) {
|
|
72
|
-
throw new errors_1.default.InternalServerError({
|
|
73
|
-
message: 'EmailAnalyticsServiceWrapper is not initialized with config',
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
return result;
|
|
42
|
+
return this.#service;
|
|
77
43
|
}
|
|
78
44
|
_logJobCompletion(jobType, fetchResult, totalDurationMs, lagSeconds = null) {
|
|
79
|
-
const config = this.#
|
|
45
|
+
const config = this.#config;
|
|
80
46
|
const { eventCount, apiPollingTimeMs, processingTimeMs, aggregationTimeMs, emailAggregationTimeMs, memberAggregationTimeMs, result, } = fetchResult;
|
|
81
47
|
if (eventCount === 0) {
|
|
82
48
|
return;
|
|
@@ -87,7 +53,7 @@ class EmailAnalyticsServiceWrapper {
|
|
|
87
53
|
const aggregationPercent = totalDurationMs > 0 ? Math.round((aggregationTimeMs / totalDurationMs) * 100) : 0;
|
|
88
54
|
const batchMode = config.get('emailAnalytics:batchProcessing') ? 'BATCHED' : 'SEQUENTIAL';
|
|
89
55
|
const logMessage = [
|
|
90
|
-
`[Background Job] ${this.#
|
|
56
|
+
`[Background Job] ${this.#jobType} processed ${jobType} | ${this.#logPrefix}`,
|
|
91
57
|
`${eventCount} events in ${(totalDurationMs / 1000).toFixed(1)}s (${throughput.toFixed(2)} events/s)`,
|
|
92
58
|
...(lagSeconds === null ? [] : [`Lag: ${(lagSeconds / 60).toFixed(1)}m`]),
|
|
93
59
|
`Mode: ${batchMode}`,
|
|
@@ -97,7 +63,7 @@ class EmailAnalyticsServiceWrapper {
|
|
|
97
63
|
logging_1.default.info({
|
|
98
64
|
system: {
|
|
99
65
|
event: 'job.completed',
|
|
100
|
-
job_type: this.#
|
|
66
|
+
job_type: this.#jobType,
|
|
101
67
|
task: jobType,
|
|
102
68
|
event_count: eventCount,
|
|
103
69
|
duration_ms: totalDurationMs,
|
|
@@ -112,13 +78,7 @@ class EmailAnalyticsServiceWrapper {
|
|
|
112
78
|
const metricName = this.#logName === 'newsletters'
|
|
113
79
|
? 'email-analytics-open-throughput'
|
|
114
80
|
: `email-${this.#logName}-analytics-open-throughput`;
|
|
115
|
-
|
|
116
|
-
if (!metrics) {
|
|
117
|
-
throw new errors_1.default.InternalServerError({
|
|
118
|
-
message: 'EmailAnalyticsServiceWrapper is not initialized with metrics',
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
metrics.metric(metricName, {
|
|
81
|
+
this.#metrics.metric(metricName, {
|
|
122
82
|
value: throughput,
|
|
123
83
|
events: eventCount,
|
|
124
84
|
duration: totalDurationMs,
|
|
@@ -159,12 +119,12 @@ class EmailAnalyticsServiceWrapper {
|
|
|
159
119
|
await this.service.restoreScheduled();
|
|
160
120
|
}
|
|
161
121
|
catch (e) {
|
|
162
|
-
logging_1.default.error(e, `[Background Job] ${this.#
|
|
122
|
+
logging_1.default.error(e, `[Background Job] ${this.#jobType} failed while restoring scheduled events after ${Date.now() - startedAt}ms`);
|
|
163
123
|
throw e;
|
|
164
124
|
}
|
|
165
125
|
}
|
|
166
126
|
if (this.#fetching) {
|
|
167
|
-
logging_1.default.info(`[Background Job] ${this.#
|
|
127
|
+
logging_1.default.info(`[Background Job] ${this.#jobType} skipped because a fetch is already running`);
|
|
168
128
|
return;
|
|
169
129
|
}
|
|
170
130
|
this.#fetching = true;
|
|
@@ -194,11 +154,19 @@ class EmailAnalyticsServiceWrapper {
|
|
|
194
154
|
this._restartFetch('scheduled backfill');
|
|
195
155
|
return;
|
|
196
156
|
}
|
|
197
|
-
|
|
157
|
+
// The message is unchanged so existing log queries keep matching; the
|
|
158
|
+
// structured fields are additive.
|
|
159
|
+
logging_1.default.info({
|
|
160
|
+
system: {
|
|
161
|
+
event: this.#completionEvent,
|
|
162
|
+
event_count: c1 + c2 + c3 + c4,
|
|
163
|
+
duration_ms: Date.now() - startedAt,
|
|
164
|
+
},
|
|
165
|
+
}, `[Background Job] ${this.#jobType} completed in ${Date.now() - startedAt}ms with ${c1 + c2 + c3 + c4} events | ${this.#logPrefix}`);
|
|
198
166
|
this.#fetching = false;
|
|
199
167
|
}
|
|
200
168
|
catch (e) {
|
|
201
|
-
logging_1.default.error(e, `[Background Job] ${this.#
|
|
169
|
+
logging_1.default.error(e, `[Background Job] ${this.#jobType} failed after ${Date.now() - startedAt}ms`);
|
|
202
170
|
// Log again only the error, otherwise we lose the stack trace
|
|
203
171
|
logging_1.default.error(e);
|
|
204
172
|
}
|
|
@@ -206,7 +174,7 @@ class EmailAnalyticsServiceWrapper {
|
|
|
206
174
|
}
|
|
207
175
|
_restartFetch(reason) {
|
|
208
176
|
this.#fetching = false;
|
|
209
|
-
logging_1.default.info(`[Background Job] ${this.#
|
|
177
|
+
logging_1.default.info(`[Background Job] ${this.#jobType} continuing due to ${reason}`);
|
|
210
178
|
this.startFetch();
|
|
211
179
|
}
|
|
212
180
|
}
|
|
@@ -3,7 +3,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.init =
|
|
6
|
+
exports.init = void 0;
|
|
7
|
+
exports.getNewsletters = getNewsletters;
|
|
8
|
+
exports.getAutomations = getAutomations;
|
|
9
|
+
exports.getGifts = getGifts;
|
|
10
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
7
11
|
const email_analytics_service_wrapper_1 = require("./email-analytics-service-wrapper");
|
|
8
12
|
// @ts-expect-error This module lacks type definitions.
|
|
9
13
|
const newsletter_email_analytics_batch_processor_1 = require("./newsletter-email-analytics-batch-processor");
|
|
@@ -21,16 +25,25 @@ const automation_email_analytics_batch_processor_1 = require("./automation-email
|
|
|
21
25
|
const gift_email_analytics_batch_processor_1 = require("./gift-email-analytics-batch-processor");
|
|
22
26
|
const start_gift_email_analytics_job_event_1 = require("./events/start-gift-email-analytics-job-event");
|
|
23
27
|
const constants_2 = require("../gifts/constants");
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
let newsletters;
|
|
29
|
+
let automations;
|
|
30
|
+
let gifts;
|
|
31
|
+
function getNewsletters() {
|
|
32
|
+
(0, strict_1.default)(newsletters, 'Newsletter email analytics should be initialized');
|
|
33
|
+
return newsletters;
|
|
34
|
+
}
|
|
35
|
+
function getAutomations() {
|
|
36
|
+
(0, strict_1.default)(automations, 'Automation email analytics should be initialized');
|
|
37
|
+
return automations;
|
|
38
|
+
}
|
|
39
|
+
function getGifts() {
|
|
40
|
+
(0, strict_1.default)(gifts, 'Gift email analytics should be initialized');
|
|
41
|
+
return gifts;
|
|
42
|
+
}
|
|
33
43
|
const init = ({ automationsApi, config, db, domainEvents, emailSuppressionList, giftDeliveryService, membersRepository, models: { Email, EmailRecipientFailure, EmailSpamComplaintEvent }, metrics, prometheusClient, settingsCache, }) => {
|
|
44
|
+
if (newsletters) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
34
47
|
const queries = new queries_1.Queries(db.knex);
|
|
35
48
|
const newsletterEmailEventProcessor = new email_event_processor_1.default({
|
|
36
49
|
domainEvents,
|
|
@@ -62,10 +75,10 @@ const init = ({ automationsApi, config, db, domainEvents, emailSuppressionList,
|
|
|
62
75
|
name: newsletter_email_analytics_batch_processor_1.AGGREGATE_MEMBER_STATS_METRIC_NAME,
|
|
63
76
|
help: 'Count of member stats aggregations',
|
|
64
77
|
});
|
|
65
|
-
|
|
78
|
+
newsletters = new email_analytics_service_wrapper_1.EmailAnalyticsServiceWrapper({
|
|
79
|
+
logName: 'newsletters',
|
|
80
|
+
jobType: 'email-analytics-fetch-latest',
|
|
66
81
|
config,
|
|
67
|
-
domainEvents,
|
|
68
|
-
event: start_email_analytics_job_event_1.StartEmailAnalyticsJobEvent,
|
|
69
82
|
queries,
|
|
70
83
|
mailgunTags: newsletterMailgunTags,
|
|
71
84
|
jobNames: {
|
|
@@ -91,10 +104,10 @@ const init = ({ automationsApi, config, db, domainEvents, emailSuppressionList,
|
|
|
91
104
|
queries,
|
|
92
105
|
}),
|
|
93
106
|
});
|
|
94
|
-
|
|
107
|
+
automations = new email_analytics_service_wrapper_1.EmailAnalyticsServiceWrapper({
|
|
108
|
+
logName: 'automations',
|
|
109
|
+
jobType: 'email-analytics-automation-fetch-latest',
|
|
95
110
|
config,
|
|
96
|
-
domainEvents,
|
|
97
|
-
event: start_automation_email_analytics_job_event_1.StartAutomationEmailAnalyticsJobEvent,
|
|
98
111
|
queries,
|
|
99
112
|
mailgunTags: automationMailgunTags,
|
|
100
113
|
jobNames: {
|
|
@@ -116,10 +129,10 @@ const init = ({ automationsApi, config, db, domainEvents, emailSuppressionList,
|
|
|
116
129
|
automationsApi,
|
|
117
130
|
}),
|
|
118
131
|
});
|
|
119
|
-
|
|
132
|
+
gifts = new email_analytics_service_wrapper_1.EmailAnalyticsServiceWrapper({
|
|
133
|
+
logName: 'gifts',
|
|
134
|
+
jobType: 'email-analytics-gift-fetch-latest',
|
|
120
135
|
config,
|
|
121
|
-
domainEvents,
|
|
122
|
-
event: start_gift_email_analytics_job_event_1.StartGiftEmailAnalyticsJobEvent,
|
|
123
136
|
queries,
|
|
124
137
|
mailgunTags: giftMailgunTags,
|
|
125
138
|
jobNames: {
|
|
@@ -139,5 +152,8 @@ const init = ({ automationsApi, config, db, domainEvents, emailSuppressionList,
|
|
|
139
152
|
settingsCache,
|
|
140
153
|
createEventProcessor: () => new gift_email_analytics_batch_processor_1.GiftEmailAnalyticsBatchProcessor({ giftDeliveryService }),
|
|
141
154
|
});
|
|
155
|
+
domainEvents.subscribe(start_email_analytics_job_event_1.StartEmailAnalyticsJobEvent, () => newsletters.startFetch());
|
|
156
|
+
domainEvents.subscribe(start_automation_email_analytics_job_event_1.StartAutomationEmailAnalyticsJobEvent, () => automations.startFetch());
|
|
157
|
+
domainEvents.subscribe(start_gift_email_analytics_job_event_1.StartGiftEmailAnalyticsJobEvent, () => gifts.startFetch());
|
|
142
158
|
};
|
|
143
159
|
exports.init = init;
|
|
@@ -47,10 +47,11 @@ function randomFiveMinuteCron() {
|
|
|
47
47
|
const minutes = Math.floor(Math.random() * 5); // 0-4
|
|
48
48
|
return `${seconds} ${minutes}/5 * * * *`;
|
|
49
49
|
}
|
|
50
|
+
function thirtyDaysAgo() {
|
|
51
|
+
return moment_1.default.utc().subtract(30, 'days').toDate();
|
|
52
|
+
}
|
|
50
53
|
class EmailAnalyticsJobScheduler {
|
|
51
|
-
#
|
|
52
|
-
#hasScheduledAutomationsJob = false;
|
|
53
|
-
#hasScheduledGiftDeliveriesJob = false;
|
|
54
|
+
#scheduledJobNames = new Set();
|
|
54
55
|
#models;
|
|
55
56
|
#config;
|
|
56
57
|
#jobManager;
|
|
@@ -64,76 +65,46 @@ class EmailAnalyticsJobScheduler {
|
|
|
64
65
|
this.#config.get('backgroundJobs:emailAnalytics'));
|
|
65
66
|
}
|
|
66
67
|
async scheduleRecurringNewslettersJob(skipNewsletterEmailCheck = false) {
|
|
67
|
-
if (this.#hasScheduledNewslettersJob) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
if (!this.#isConfigured()) {
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
68
|
// Don't register email analytics job if we have no emails,
|
|
74
69
|
// processor usage from many sites spinning up threads can be high.
|
|
75
70
|
// Mega service will re-run this scheduling task when an email is sent
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
:
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const at = randomFiveMinuteCron();
|
|
83
|
-
logging.info(`[Background Job] email-analytics-fetch-latest scheduled at ${at}`);
|
|
84
|
-
this.#jobManager.addJob({
|
|
85
|
-
at,
|
|
86
|
-
job: path.resolve(__dirname, 'fetch-latest/index.js'),
|
|
87
|
-
name: 'email-analytics-fetch-latest',
|
|
88
|
-
});
|
|
89
|
-
this.#hasScheduledNewslettersJob = true;
|
|
90
|
-
}
|
|
71
|
+
await this.#scheduleOnce({
|
|
72
|
+
name: 'email-analytics-fetch-latest',
|
|
73
|
+
workerPath: path.resolve(__dirname, 'fetch-latest/index.js'),
|
|
74
|
+
}, skipNewsletterEmailCheck, async () => Number(await this.#models.Email.where('created_at', '>', thirtyDaysAgo())
|
|
75
|
+
.where('status', '<>', 'failed')
|
|
76
|
+
.count()) > 0);
|
|
91
77
|
}
|
|
92
78
|
async scheduleRecurringAutomationsJob(skipAutomationEmailCheck = false) {
|
|
93
|
-
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
if (!this.#isConfigured()) {
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
const hasAutomatedEmailRecipient = skipAutomationEmailCheck ||
|
|
100
|
-
Boolean(await this.#models.AutomatedEmailRecipient.query()
|
|
101
|
-
.where('created_at', '>', moment_1.default.utc().subtract(30, 'days').toDate())
|
|
102
|
-
.whereNotNull('mailgun_message_id')
|
|
103
|
-
.first('id'));
|
|
104
|
-
if (!hasAutomatedEmailRecipient || this.#hasScheduledAutomationsJob) {
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
const at = randomFiveMinuteCron();
|
|
108
|
-
logging.info(`[Background Job] email-analytics-automation-fetch-latest scheduled at ${at}`);
|
|
109
|
-
this.#jobManager.addJob({
|
|
110
|
-
at,
|
|
111
|
-
job: path.resolve(__dirname, 'automation-fetch-latest/index.js'),
|
|
79
|
+
await this.#scheduleOnce({
|
|
112
80
|
name: 'email-analytics-automation-fetch-latest',
|
|
113
|
-
|
|
114
|
-
this.#
|
|
81
|
+
workerPath: path.resolve(__dirname, 'automation-fetch-latest/index.js'),
|
|
82
|
+
}, skipAutomationEmailCheck, async () => Boolean(await this.#models.AutomatedEmailRecipient.query()
|
|
83
|
+
.where('created_at', '>', thirtyDaysAgo())
|
|
84
|
+
.whereNotNull('mailgun_message_id')
|
|
85
|
+
.first('id')));
|
|
115
86
|
}
|
|
116
87
|
async scheduleRecurringGiftDeliveriesJob(skipGiftDeliveryCheck = false) {
|
|
117
|
-
|
|
88
|
+
await this.#scheduleOnce({
|
|
89
|
+
name: 'email-analytics-gift-fetch-latest',
|
|
90
|
+
workerPath: path.resolve(__dirname, 'gift-fetch-latest/index.js'),
|
|
91
|
+
}, skipGiftDeliveryCheck, async () => Boolean(await this.#models.GiftDelivery.query()
|
|
92
|
+
.where('email_sent_at', '>', thirtyDaysAgo())
|
|
93
|
+
.whereNotNull('email_provider_message_id')
|
|
94
|
+
.first('id')));
|
|
95
|
+
}
|
|
96
|
+
async #scheduleOnce(job, skipRecentSendsCheck, hasRecentSends) {
|
|
97
|
+
if (this.#scheduledJobNames.has(job.name) || !this.#isConfigured()) {
|
|
118
98
|
return;
|
|
119
99
|
}
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
(await this.#models.GiftDelivery.query()
|
|
123
|
-
.where('email_sent_at', '>', moment_1.default.utc().subtract(30, 'days').toDate())
|
|
124
|
-
.whereNotNull('email_provider_message_id')
|
|
125
|
-
.first('id')));
|
|
126
|
-
if (!hasGiftDelivery || this.#hasScheduledGiftDeliveriesJob) {
|
|
100
|
+
const shouldSchedule = skipRecentSendsCheck || (await hasRecentSends());
|
|
101
|
+
if (!shouldSchedule || this.#scheduledJobNames.has(job.name)) {
|
|
127
102
|
return;
|
|
128
103
|
}
|
|
129
104
|
const at = randomFiveMinuteCron();
|
|
130
|
-
logging.info(`[Background Job]
|
|
131
|
-
this.#jobManager.addJob({
|
|
132
|
-
|
|
133
|
-
job: path.resolve(__dirname, 'gift-fetch-latest/index.js'),
|
|
134
|
-
name: 'email-analytics-gift-fetch-latest',
|
|
135
|
-
});
|
|
136
|
-
this.#hasScheduledGiftDeliveriesJob = true;
|
|
105
|
+
logging.info(`[Background Job] ${job.name} scheduled at ${at}`);
|
|
106
|
+
this.#jobManager.addJob({ at, job: job.workerPath, name: job.name });
|
|
107
|
+
this.#scheduledJobNames.add(job.name);
|
|
137
108
|
}
|
|
138
109
|
}
|
|
139
110
|
exports.EmailAnalyticsJobScheduler = EmailAnalyticsJobScheduler;
|
|
@@ -4,6 +4,41 @@ Renders newsletter emails, splits them into recipient batches, and submits the
|
|
|
4
4
|
batches to the configured email provider. Domain terms live in
|
|
5
5
|
[CONTEXT.md](CONTEXT.md).
|
|
6
6
|
|
|
7
|
+
## Job lifecycle
|
|
8
|
+
|
|
9
|
+
New sends, retries, and boot recovery dispatch a data-only `SendEmailJob`
|
|
10
|
+
containing the email ID through the class-based jobs service. Boot injects
|
|
11
|
+
that service and registers `EmailService.handleSendEmailJob`, which delegates
|
|
12
|
+
to `BatchSendingService.emailJob`. The batch sender refetches the email and
|
|
13
|
+
acquires its status lock before preparing or submitting batches. See the
|
|
14
|
+
[jobs guide](../../../../../../docs/codebase/jobs.md#queues) for queue isolation
|
|
15
|
+
and concurrency limits.
|
|
16
|
+
|
|
17
|
+
`scheduleEmail()` waits for dispatch to complete, not for submission. The
|
|
18
|
+
current in-memory backend provides no durable acceptance guarantee and drops
|
|
19
|
+
dispatches after shutdown starts without rejecting them. Email status records
|
|
20
|
+
the sending outcome; a job handler returning does not imply submission, since
|
|
21
|
+
it may have skipped the send, recorded a failure, or stopped for shutdown.
|
|
22
|
+
|
|
23
|
+
Shutdown first stops workers from claiming new batches. The batch sender's
|
|
24
|
+
separate cleanup task waits for active batch preparation and submission even
|
|
25
|
+
if the jobs backend's shutdown wait times out. This tracking currently ends
|
|
26
|
+
before the final email status write. On boot, recovery reschedules eligible
|
|
27
|
+
interrupted sends within `bulkEmail:resumeMaxAgeMs` and skips batches already
|
|
28
|
+
submitted.
|
|
29
|
+
|
|
30
|
+
## Testing
|
|
31
|
+
|
|
32
|
+
Tests that publish or retry a newsletter should mock the email provider and
|
|
33
|
+
await `waitForEmailStatus(emailId)` from
|
|
34
|
+
[`test/utils/batch-email-utils.js`](../../../../test/utils/batch-email-utils.js)
|
|
35
|
+
before restoring mocks or deleting fixtures. The helper returns on either
|
|
36
|
+
`submitted` or `failed`, so assert the expected status explicitly.
|
|
37
|
+
|
|
38
|
+
A terminal email status describes the send's outcome; it does not prove that
|
|
39
|
+
every competing retry or job attempting the status lock has finished. Tests
|
|
40
|
+
which create competing attempts must also wait for those attempts to settle.
|
|
41
|
+
|
|
7
42
|
## Sending status
|
|
8
43
|
|
|
9
44
|
The sending status served by the Admin API's `emails/:id/status` endpoint is
|