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
|
@@ -717,7 +717,7 @@ module.exports = class MemberRepository {
|
|
|
717
717
|
}
|
|
718
718
|
|
|
719
719
|
// Fetch the member
|
|
720
|
-
|
|
720
|
+
const initialMember = await this._Member.findOne(
|
|
721
721
|
{
|
|
722
722
|
id: options.id,
|
|
723
723
|
},
|
|
@@ -1335,7 +1335,7 @@ module.exports = class MemberRepository {
|
|
|
1335
1335
|
logging.error(e);
|
|
1336
1336
|
}
|
|
1337
1337
|
|
|
1338
|
-
|
|
1338
|
+
const stripeCouponId = stripeSubscriptionData.discount?.coupon?.id;
|
|
1339
1339
|
|
|
1340
1340
|
// For trial offers, offer id is passed from metadata as there is no stripe coupon
|
|
1341
1341
|
let offerId = data.offerId || null;
|
|
@@ -1432,7 +1432,11 @@ module.exports = class MemberRepository {
|
|
|
1432
1432
|
|
|
1433
1433
|
return 'inactive';
|
|
1434
1434
|
};
|
|
1435
|
-
|
|
1435
|
+
const eventData = {};
|
|
1436
|
+
const isIncomplete = ['incomplete', 'incomplete_expired'].includes(
|
|
1437
|
+
stripeSubscriptionData.status,
|
|
1438
|
+
);
|
|
1439
|
+
let subscriptionToRecord = null;
|
|
1436
1440
|
|
|
1437
1441
|
// A cancellation (or reactivation) changes no `members` column, so the member
|
|
1438
1442
|
// model event would be suppressed by `wasChanged()`. Remember the pre-update
|
|
@@ -1482,10 +1486,34 @@ module.exports = class MemberRepository {
|
|
|
1482
1486
|
},
|
|
1483
1487
|
);
|
|
1484
1488
|
|
|
1489
|
+
if (
|
|
1490
|
+
stripeCustomerSubscriptionModel.get('status') === 'incomplete' &&
|
|
1491
|
+
this.isActiveSubscriptionStatus(stripeSubscriptionData.status)
|
|
1492
|
+
) {
|
|
1493
|
+
// Older Ghost versions recorded incomplete subscriptions as conversions. Avoid
|
|
1494
|
+
// recording them twice when a checkout already in progress completes.
|
|
1495
|
+
const createdEvent = await this._MemberPaidSubscriptionEvent.findOne(
|
|
1496
|
+
{
|
|
1497
|
+
member_id: memberModel.id,
|
|
1498
|
+
subscription_id: stripeCustomerSubscriptionModel.id,
|
|
1499
|
+
type: 'created',
|
|
1500
|
+
},
|
|
1501
|
+
options,
|
|
1502
|
+
);
|
|
1503
|
+
if (!createdEvent) {
|
|
1504
|
+
subscriptionToRecord = updatedStripeCustomerSubscriptionModel;
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1485
1508
|
// CASE: Record offer redemption when offer_id changes to a new non-null value
|
|
1486
1509
|
// This covers: null→new (free member upgrade), old→new (retention offer replacing expired signup offer)
|
|
1487
1510
|
// The OfferRedemptionEvent handler has a dedup check for repeated webhook deliveries
|
|
1488
|
-
if (
|
|
1511
|
+
if (
|
|
1512
|
+
!isIncomplete &&
|
|
1513
|
+
!subscriptionToRecord &&
|
|
1514
|
+
previousOfferId !== subscriptionData.offer_id &&
|
|
1515
|
+
subscriptionData.offer_id
|
|
1516
|
+
) {
|
|
1489
1517
|
const redemptionTimestamp =
|
|
1490
1518
|
subscriptionData.discount_start ||
|
|
1491
1519
|
updatedStripeCustomerSubscriptionModel.get('created_at');
|
|
@@ -1508,14 +1536,16 @@ module.exports = class MemberRepository {
|
|
|
1508
1536
|
}
|
|
1509
1537
|
|
|
1510
1538
|
if (
|
|
1511
|
-
|
|
1539
|
+
!isIncomplete &&
|
|
1540
|
+
!subscriptionToRecord &&
|
|
1541
|
+
(stripeCustomerSubscriptionModel.get('mrr') !==
|
|
1512
1542
|
updatedStripeCustomerSubscriptionModel.get('mrr') ||
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1543
|
+
stripeCustomerSubscriptionModel.get('plan_id') !==
|
|
1544
|
+
updatedStripeCustomerSubscriptionModel.get('plan_id') ||
|
|
1545
|
+
stripeCustomerSubscriptionModel.get('status') !==
|
|
1546
|
+
updatedStripeCustomerSubscriptionModel.get('status') ||
|
|
1547
|
+
stripeCustomerSubscriptionModel.get('cancel_at_period_end') !==
|
|
1548
|
+
updatedStripeCustomerSubscriptionModel.get('cancel_at_period_end'))
|
|
1519
1549
|
) {
|
|
1520
1550
|
const originalMrrDelta = stripeCustomerSubscriptionModel.get('mrr');
|
|
1521
1551
|
const updatedMrrDelta = updatedStripeCustomerSubscriptionModel.get('mrr');
|
|
@@ -1607,16 +1637,24 @@ module.exports = class MemberRepository {
|
|
|
1607
1637
|
subscriptionData,
|
|
1608
1638
|
options,
|
|
1609
1639
|
);
|
|
1640
|
+
if (!isIncomplete) {
|
|
1641
|
+
subscriptionToRecord = newStripeCustomerSubscriptionModel;
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
// A checkout attempt is not a paid conversion. Record the start only once
|
|
1646
|
+
// the subscription leaves incomplete, retaining attribution from Stripe metadata.
|
|
1647
|
+
if (subscriptionToRecord) {
|
|
1610
1648
|
await this._MemberPaidSubscriptionEvent.add(
|
|
1611
1649
|
{
|
|
1612
1650
|
member_id: memberModel.id,
|
|
1613
|
-
subscription_id:
|
|
1651
|
+
subscription_id: subscriptionToRecord.id,
|
|
1614
1652
|
type: 'created',
|
|
1615
1653
|
source: 'stripe',
|
|
1616
1654
|
from_plan: null,
|
|
1617
1655
|
to_plan: subscriptionPriceData.id,
|
|
1618
1656
|
currency: subscriptionPriceData.currency,
|
|
1619
|
-
mrr_delta:
|
|
1657
|
+
mrr_delta: subscriptionToRecord.get('mrr'),
|
|
1620
1658
|
...eventData,
|
|
1621
1659
|
},
|
|
1622
1660
|
options,
|
|
@@ -1653,7 +1691,7 @@ module.exports = class MemberRepository {
|
|
|
1653
1691
|
source,
|
|
1654
1692
|
tierId: ghostProduct?.get('id'),
|
|
1655
1693
|
memberId: memberModel.id,
|
|
1656
|
-
subscriptionId:
|
|
1694
|
+
subscriptionId: subscriptionToRecord.get('id'),
|
|
1657
1695
|
offerId: offerId,
|
|
1658
1696
|
attribution: attribution,
|
|
1659
1697
|
batchId: options.batch_id,
|
|
@@ -1665,17 +1703,17 @@ module.exports = class MemberRepository {
|
|
|
1665
1703
|
const offerRedemptionEvent = OfferRedemptionEvent.create({
|
|
1666
1704
|
memberId: memberModel.id,
|
|
1667
1705
|
offerId: offerId,
|
|
1668
|
-
subscriptionId:
|
|
1706
|
+
subscriptionId: subscriptionToRecord.get('id'),
|
|
1669
1707
|
});
|
|
1670
1708
|
this.dispatchEvent(offerRedemptionEvent, options);
|
|
1671
1709
|
}
|
|
1672
1710
|
|
|
1673
|
-
if (getStatus(
|
|
1711
|
+
if (getStatus(subscriptionToRecord) === 'active') {
|
|
1674
1712
|
const subscriptionActivatedEvent = SubscriptionActivatedEvent.create({
|
|
1675
1713
|
source,
|
|
1676
1714
|
tierId: ghostProduct?.get('id'),
|
|
1677
1715
|
memberId: memberModel.id,
|
|
1678
|
-
subscriptionId:
|
|
1716
|
+
subscriptionId: subscriptionToRecord.get('id'),
|
|
1679
1717
|
offerId: offerId,
|
|
1680
1718
|
attribution: attribution,
|
|
1681
1719
|
batchId: options.batch_id,
|
|
@@ -418,7 +418,7 @@ class ProductRepository {
|
|
|
418
418
|
|
|
419
419
|
const existingProduct = await this._Product.findOne({ id: productId }, options);
|
|
420
420
|
|
|
421
|
-
|
|
421
|
+
const productData = {
|
|
422
422
|
name: data.name,
|
|
423
423
|
visibility: data.visibility,
|
|
424
424
|
description: data.description,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const errors = require('@tryghost/errors');
|
|
2
|
-
const { ADMIN } = require('../../../members-metafields');
|
|
2
|
+
const { ADMIN, adminWriteOrigin } = require('../../../members-metafields');
|
|
3
3
|
const logging = require('@tryghost/logging');
|
|
4
4
|
const tpl = require('@tryghost/tpl');
|
|
5
5
|
const moment = require('moment');
|
|
@@ -641,16 +641,13 @@ module.exports = class MemberBREADService {
|
|
|
641
641
|
// The only route to this branch is the authenticated Admin API, so an anonymous
|
|
642
642
|
// request is a mistake somewhere upstream rather than a writer to invent a name
|
|
643
643
|
// for. Refusing keeps every stored writer resolvable.
|
|
644
|
-
const
|
|
645
|
-
if (!
|
|
644
|
+
const origin = adminWriteOrigin(options.context);
|
|
645
|
+
if (!origin) {
|
|
646
646
|
throw new errors.IncorrectUsageError({
|
|
647
647
|
message: tpl(messages.metafieldsWithoutWriter),
|
|
648
648
|
});
|
|
649
649
|
}
|
|
650
|
-
|
|
651
|
-
? { type: 'integration', id: context.integration.id }
|
|
652
|
-
: { type: 'user', id: context.user };
|
|
653
|
-
await this.metafieldValues.applyWrite(model.id, plannedMetafields, { writtenBy });
|
|
650
|
+
await this.metafieldValues.applyWrite(model.id, plannedMetafields, origin);
|
|
654
651
|
|
|
655
652
|
// Metafields aren't a member column or relation, so an edit touching
|
|
656
653
|
// only them leaves `model._changed` empty and the save fires nothing.
|
|
@@ -13,7 +13,7 @@ const settingsCache = require('../../../shared/settings-cache');
|
|
|
13
13
|
const config = require('../../../shared/config');
|
|
14
14
|
const models = require('../../models');
|
|
15
15
|
const { GhostMailer } = require('../mail');
|
|
16
|
-
const jobsService = require('../jobs');
|
|
16
|
+
const jobsService = require('../jobs-service');
|
|
17
17
|
const tiersService = require('../tiers');
|
|
18
18
|
const giftService = require('../gifts');
|
|
19
19
|
const VerificationTrigger = require('../verification-trigger');
|
|
@@ -83,7 +83,9 @@ const buildImporterDeps = ({ stripeAPIService }) => {
|
|
|
83
83
|
},
|
|
84
84
|
getGiftService: () => giftService.service,
|
|
85
85
|
sendEmail: ghostMailer.send.bind(ghostMailer),
|
|
86
|
-
|
|
86
|
+
// Resolved per dispatch rather than here: init() also runs where nothing has
|
|
87
|
+
// initialised the jobs service.
|
|
88
|
+
dispatchJob: (job) => jobsService.getInstance().dispatch(job),
|
|
87
89
|
knex: db.knex,
|
|
88
90
|
urlFor: urlUtils.urlFor.bind(urlUtils),
|
|
89
91
|
stripeAPIService,
|
|
@@ -233,6 +235,8 @@ module.exports = {
|
|
|
233
235
|
membersCSVImporter.importCSV(request, verificationTrigger);
|
|
234
236
|
module.exports.importInline = (request) =>
|
|
235
237
|
membersCSVImporter.importInline(request, verificationTrigger);
|
|
238
|
+
// The members-import job handler, registered by boot with the jobs service.
|
|
239
|
+
module.exports.handleImportJob = (job) => membersCSVImporter.handle(job, verificationTrigger);
|
|
236
240
|
|
|
237
241
|
// Constructed here rather than required statically: the exporter needs the
|
|
238
242
|
// metafields services, which boot builds before this one.
|
|
@@ -265,6 +269,7 @@ module.exports = {
|
|
|
265
269
|
|
|
266
270
|
importCSV: null,
|
|
267
271
|
importInline: null,
|
|
272
|
+
handleImportJob: null,
|
|
268
273
|
|
|
269
274
|
stats: membersStats,
|
|
270
275
|
export: null,
|
|
@@ -135,11 +135,11 @@ class MembersStats {
|
|
|
135
135
|
currentRangeDate = moment.tz(siteTimezone).subtract(days - 1, 'days');
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
const endDate = moment.tz(siteTimezone).add(1, 'hour');
|
|
139
139
|
const output = {};
|
|
140
140
|
|
|
141
141
|
while (currentRangeDate.isBefore(endDate)) {
|
|
142
|
-
|
|
142
|
+
const dateStr = currentRangeDate.format('YYYY-MM-DD');
|
|
143
143
|
runningTotal += resultObject[dateStr] || 0;
|
|
144
144
|
output[dateStr] = runningTotal;
|
|
145
145
|
|
|
@@ -23,7 +23,7 @@ class EventStorage {
|
|
|
23
23
|
*/
|
|
24
24
|
subscribe(domainEvents) {
|
|
25
25
|
domainEvents.subscribe(MemberCreatedEvent, async (event) => {
|
|
26
|
-
|
|
26
|
+
const attribution = event.data.attribution;
|
|
27
27
|
|
|
28
28
|
await this.models.MemberCreatedEvent.add({
|
|
29
29
|
member_id: event.data.memberId,
|
|
@@ -45,7 +45,7 @@ class EventStorage {
|
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
domainEvents.subscribe(SubscriptionCreatedEvent, async (event) => {
|
|
48
|
-
|
|
48
|
+
const attribution = event.data.attribution;
|
|
49
49
|
|
|
50
50
|
await this.models.SubscriptionCreatedEvent.add({
|
|
51
51
|
member_id: event.data.memberId,
|
|
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.actingContext = actingContext;
|
|
7
|
+
exports.adminWriteOrigin = adminWriteOrigin;
|
|
7
8
|
exports.recordMetafieldAction = recordMetafieldAction;
|
|
8
9
|
const logging_1 = __importDefault(require("@tryghost/logging"));
|
|
9
10
|
/**
|
|
@@ -16,14 +17,34 @@ const logging_1 = __importDefault(require("@tryghost/logging"));
|
|
|
16
17
|
*/
|
|
17
18
|
function actingContext(context) {
|
|
18
19
|
const frame = (context ?? {});
|
|
20
|
+
const viaApiKey = Boolean(frame.api_key);
|
|
19
21
|
if (frame.integration) {
|
|
20
|
-
return { actor: { id: frame.integration.id, type: 'integration' } };
|
|
22
|
+
return { actor: { id: frame.integration.id, type: 'integration', viaApiKey } };
|
|
21
23
|
}
|
|
22
24
|
if (frame.user) {
|
|
23
|
-
return { actor: { id: frame.user, type: 'user' } };
|
|
25
|
+
return { actor: { id: frame.user, type: 'user', viaApiKey } };
|
|
24
26
|
}
|
|
25
27
|
return { actor: null };
|
|
26
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Who made a write through the Admin API and where, read off the same context as
|
|
31
|
+
* `actingContext`, so the values a request writes and the history it records name the
|
|
32
|
+
* same writer. Null when nobody is acting, which no Admin API write should be.
|
|
33
|
+
*/
|
|
34
|
+
function adminWriteOrigin(context) {
|
|
35
|
+
const { actor } = actingContext(context);
|
|
36
|
+
if (!actor) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
if (actor.type === 'integration') {
|
|
40
|
+
return { writtenBy: { type: 'integration', id: actor.id }, source: 'admin_api' };
|
|
41
|
+
}
|
|
42
|
+
// A staff token authenticates as its user but is a call to the API, not a visit to Admin.
|
|
43
|
+
return {
|
|
44
|
+
writtenBy: { type: 'user', id: actor.id },
|
|
45
|
+
source: actor.viaApiKey ? 'admin_api' : 'admin',
|
|
46
|
+
};
|
|
47
|
+
}
|
|
27
48
|
// Field-definition changes map to activity-feed events. A field's timeline reads
|
|
28
49
|
// added -> edited -> archived -> restored, and a permanent delete (only from the
|
|
29
50
|
// archived state) ends it with deleted.
|
|
@@ -57,8 +57,8 @@ class MetafieldBindingsService {
|
|
|
57
57
|
*/
|
|
58
58
|
async writeCollected(memberId, productId, collected) {
|
|
59
59
|
return this.writeThrough(memberId, productId, collected, (binding) => ({
|
|
60
|
-
type: 'binding',
|
|
61
|
-
|
|
60
|
+
writtenBy: { type: 'binding', id: binding.bindingId },
|
|
61
|
+
source: 'checkout',
|
|
62
62
|
}));
|
|
63
63
|
}
|
|
64
64
|
/**
|
|
@@ -70,8 +70,8 @@ class MetafieldBindingsService {
|
|
|
70
70
|
*/
|
|
71
71
|
async writeSuppliedByMember(memberId, productId, supplied) {
|
|
72
72
|
return this.writeThrough(memberId, productId, supplied, () => ({
|
|
73
|
-
type: 'member',
|
|
74
|
-
|
|
73
|
+
writtenBy: { type: 'member', id: memberId },
|
|
74
|
+
source: 'checkout',
|
|
75
75
|
}));
|
|
76
76
|
}
|
|
77
77
|
/**
|
|
@@ -112,7 +112,7 @@ class MetafieldBindingsService {
|
|
|
112
112
|
// looking. A port exists because a publisher pointed it at a field, and that
|
|
113
113
|
// decision is what admits the value, whoever supplied it.
|
|
114
114
|
const planned = await this.values.planWrite({ [`${CUSTOM_NAMESPACE}.${into.key}`]: value }, access_1.INTERNAL);
|
|
115
|
-
await this.values.applyWrite(memberId, planned,
|
|
115
|
+
await this.values.applyWrite(memberId, planned, attribute(into));
|
|
116
116
|
}
|
|
117
117
|
async resolve(productId, port) {
|
|
118
118
|
const row = await this.knex(BINDINGS_TABLE)
|
|
@@ -15,11 +15,6 @@ const schema_1 = require("./schema");
|
|
|
15
15
|
const access_1 = require("./access");
|
|
16
16
|
const queries_1 = require("./queries");
|
|
17
17
|
const key_1 = require("./key");
|
|
18
|
-
// The same NQL -> knex bridge Bookshelf's filter plugin uses, applied directly to
|
|
19
|
-
// our raw-knex query: nql parses the `filter` string to a Mongo query, mongo-knex
|
|
20
|
-
// turns that into parametrised WHERE clauses. Neither needs a Bookshelf model.
|
|
21
|
-
const nql = require('@tryghost/nql');
|
|
22
|
-
const knexify = require('@tryghost/mongo-knex');
|
|
23
18
|
const TABLE = 'members_metafields';
|
|
24
19
|
// Column limits come from the canonical schema — the same source the migration and
|
|
25
20
|
// Bookshelf models read — so the service can never drift from the database. The
|
|
@@ -125,7 +120,7 @@ class MetafieldDefinitionsService {
|
|
|
125
120
|
return this.list((0, queries_1.definitions)(this.knex, {
|
|
126
121
|
audience,
|
|
127
122
|
status: filterReferencesStatus(parsed) ? queries_1.ANY_STATUS : queries_1.ACTIVE_ONLY,
|
|
128
|
-
filter: (query) => knexify(query, parsed, { tableName: TABLE }),
|
|
123
|
+
filter: (query) => (0, queries_1.knexify)(query, parsed, { tableName: TABLE }),
|
|
129
124
|
}));
|
|
130
125
|
}
|
|
131
126
|
return this.list((0, queries_1.definitions)(this.knex, { audience, status: queries_1.ACTIVE_ONLY }));
|
|
@@ -628,7 +623,7 @@ function isUniqueConstraintViolation(error) {
|
|
|
628
623
|
function parseFilter(filter) {
|
|
629
624
|
let mongoQuery;
|
|
630
625
|
try {
|
|
631
|
-
mongoQuery = nql(filter).toJSON();
|
|
626
|
+
mongoQuery = (0, queries_1.nql)(filter).toJSON();
|
|
632
627
|
}
|
|
633
628
|
catch (err) {
|
|
634
629
|
throw new errors_1.default.BadRequestError({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.bindings = exports.values = exports.definitions = exports.canWrite = exports.MEMBER_ACCESS = exports.MEMBERS = exports.INTERNAL = exports.ADMIN = exports.actingContext = void 0;
|
|
3
|
+
exports.bindings = exports.values = exports.definitions = exports.canWrite = exports.MEMBER_ACCESS = exports.MEMBERS = exports.INTERNAL = exports.ADMIN = exports.adminWriteOrigin = exports.actingContext = void 0;
|
|
4
4
|
exports.init = init;
|
|
5
5
|
const definitions_service_1 = require("./definitions-service");
|
|
6
6
|
const values_service_1 = require("./values-service");
|
|
@@ -9,6 +9,7 @@ const actions_1 = require("./actions");
|
|
|
9
9
|
const config_1 = require("./config");
|
|
10
10
|
var actions_2 = require("./actions");
|
|
11
11
|
Object.defineProperty(exports, "actingContext", { enumerable: true, get: function () { return actions_2.actingContext; } });
|
|
12
|
+
Object.defineProperty(exports, "adminWriteOrigin", { enumerable: true, get: function () { return actions_2.adminWriteOrigin; } });
|
|
12
13
|
// Which door a request came through, which is what decides how much of a member's
|
|
13
14
|
// answers it may see or change. Required wherever that is asked, so a new caller
|
|
14
15
|
// has to name itself rather than inherit an answer by default.
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ANY_STATUS = exports.ACTIVE_ONLY = void 0;
|
|
3
|
+
exports.ANY_STATUS = exports.ACTIVE_ONLY = exports.knexify = exports.nql = void 0;
|
|
4
4
|
exports.readableBy = readableBy;
|
|
5
5
|
exports.definitions = definitions;
|
|
6
6
|
exports.inFieldOrder = inFieldOrder;
|
|
7
7
|
const access_1 = require("./access");
|
|
8
8
|
const schema_1 = require("./schema");
|
|
9
9
|
const FIELDS_TABLE = 'members_metafields';
|
|
10
|
+
// The same NQL -> knex bridge Bookshelf's filter plugin uses, applied directly to our
|
|
11
|
+
// raw-knex queries: nql parses a `filter` string to a Mongo query, mongo-knex turns that
|
|
12
|
+
// into parametrised WHERE clauses. Neither needs a Bookshelf model. Typed once here, since
|
|
13
|
+
// neither package ships types.
|
|
14
|
+
exports.nql = require('@tryghost/nql');
|
|
15
|
+
exports.knexify = require('@tryghost/mongo-knex');
|
|
10
16
|
// Nothing in the database keeps an archived or hidden field out of a read: no
|
|
11
17
|
// constraint stops a value row referencing one. Both filters are therefore applied in
|
|
12
18
|
// code, and a query that forgets either is a silent bug.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DbBoundField = exports.DbMetafieldBinding = exports.DbMetafieldLeaf = exports.DbMetafieldValue = exports.WrittenBy = exports.DbMetafield = exports.FieldStatusSchema = exports.FIELD_STATUS = void 0;
|
|
3
|
+
exports.DbMetafieldChangeEventWithMember = exports.DbChangeEventMember = exports.DbMetafieldChangeEvent = exports.StoredFieldList = exports.MetafieldChangeEventFields = exports.DbBoundField = exports.DbMetafieldBinding = exports.DbMetafieldLeaf = exports.DbMetafieldValue = exports.WrittenBy = exports.DbMetafield = exports.FieldStatusSchema = exports.FIELD_STATUS = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const metafield_types_1 = require("@tryghost/metafield-types");
|
|
6
6
|
const date_1 = require("../../lib/db-types/date");
|
|
@@ -82,3 +82,51 @@ exports.DbBoundField = zod_1.z.object({
|
|
|
82
82
|
key: zod_1.z.string(),
|
|
83
83
|
type: metafield_types_1.FieldTypeSchema,
|
|
84
84
|
});
|
|
85
|
+
/** The fields an entry names. */
|
|
86
|
+
exports.MetafieldChangeEventFields = zod_1.z.array(metafield_types_1.MetafieldChangeEventFieldSchema);
|
|
87
|
+
/**
|
|
88
|
+
* The field list as the table holds it, JSON text, against the list itself. A codec rather
|
|
89
|
+
* than a parse on the way out, so the write stores what the read accepts. Zod validates
|
|
90
|
+
* JSON-compatible values (`z.json()`) but has no built-in for parsing JSON text, so decoding
|
|
91
|
+
* the text is this codec's job.
|
|
92
|
+
*/
|
|
93
|
+
exports.StoredFieldList = zod_1.z.codec(zod_1.z.string(), exports.MetafieldChangeEventFields, {
|
|
94
|
+
decode: (text, ctx) => {
|
|
95
|
+
try {
|
|
96
|
+
return JSON.parse(text);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
ctx.issues.push({
|
|
100
|
+
code: 'custom',
|
|
101
|
+
message: 'The stored field list is not JSON.',
|
|
102
|
+
input: text,
|
|
103
|
+
});
|
|
104
|
+
return zod_1.z.NEVER;
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
encode: (fields) => JSON.stringify(fields),
|
|
108
|
+
});
|
|
109
|
+
/** An entry as the table holds it. Writer and source are plain strings, as on the values table. */
|
|
110
|
+
exports.DbMetafieldChangeEvent = zod_1.z.object({
|
|
111
|
+
id: zod_1.z.string(),
|
|
112
|
+
member_id: zod_1.z.string(),
|
|
113
|
+
written_by_type: zod_1.z.string(),
|
|
114
|
+
written_by_id: zod_1.z.string().nullable(),
|
|
115
|
+
source: zod_1.z.string(),
|
|
116
|
+
metafields: exports.StoredFieldList,
|
|
117
|
+
created_at: date_1.DbDate,
|
|
118
|
+
});
|
|
119
|
+
/** The member columns an entry is shown with: only what a feed row needs. */
|
|
120
|
+
exports.DbChangeEventMember = zod_1.z.object({
|
|
121
|
+
id: zod_1.z.string(),
|
|
122
|
+
uuid: zod_1.z.string(),
|
|
123
|
+
name: zod_1.z.string().nullable(),
|
|
124
|
+
email: zod_1.z.string(),
|
|
125
|
+
});
|
|
126
|
+
/**
|
|
127
|
+
* An entry and its member read back together. Parses and nothing else: what to do with an
|
|
128
|
+
* entry that doesn't parse is the reading service's decision.
|
|
129
|
+
*/
|
|
130
|
+
exports.DbMetafieldChangeEventWithMember = zod_1.z
|
|
131
|
+
.object({ event: exports.DbMetafieldChangeEvent, member: exports.DbChangeEventMember })
|
|
132
|
+
.transform(({ event, member }) => ({ ...event, member }));
|
|
@@ -11,11 +11,14 @@ const zod_1 = require("zod");
|
|
|
11
11
|
const metafield_types_1 = require("@tryghost/metafield-types");
|
|
12
12
|
const identity_1 = require("@tryghost/metafield-types/identity");
|
|
13
13
|
const schema_1 = require("./schema");
|
|
14
|
+
const date_1 = require("../../lib/db-types/date");
|
|
14
15
|
const queries_1 = require("./queries");
|
|
15
16
|
const access_1 = require("./access");
|
|
16
17
|
const storage_1 = require("./storage");
|
|
17
18
|
const FIELDS_TABLE = 'members_metafields';
|
|
18
19
|
const VALUES_TABLE = 'members_metafield_values';
|
|
20
|
+
const CHANGE_EVENTS_TABLE = 'members_metafield_change_events';
|
|
21
|
+
const MEMBERS_TABLE = 'members';
|
|
19
22
|
/**
|
|
20
23
|
* From the canonical schema, the same source definitions-service reads, so no key a site
|
|
21
24
|
* could hold is refused and this cannot drift from the `members_metafields.key` column.
|
|
@@ -34,6 +37,24 @@ const UPSERT_CHUNK = 400;
|
|
|
34
37
|
const MAX_IDENTITY_LENGTH = MAX_KEY_LENGTH * 2 + 1;
|
|
35
38
|
const ValuesInput = zod_1.z.record(zod_1.z.string().max(MAX_IDENTITY_LENGTH), zod_1.z.unknown());
|
|
36
39
|
const wireProperty = (identity) => [identity_1.QUALIFIER, identity].join('.');
|
|
40
|
+
/**
|
|
41
|
+
* Every refusal in one error.
|
|
42
|
+
*
|
|
43
|
+
* The API renders a single error, so the first still fills `message` and `property` and
|
|
44
|
+
* a client that reads only those sees exactly what it saw before. The whole set rides in
|
|
45
|
+
* `errorDetails`, which the renderer already passes through as `details`, so a client
|
|
46
|
+
* that wants to mark every refused input can have them all from one attempt.
|
|
47
|
+
*/
|
|
48
|
+
function refusalError(refusals) {
|
|
49
|
+
const [first, ...rest] = refusals;
|
|
50
|
+
return new errors_1.default.ValidationError({
|
|
51
|
+
message: first.message,
|
|
52
|
+
property: first.property,
|
|
53
|
+
// Omitted when there is nothing more to say, so the common single refusal keeps the
|
|
54
|
+
// shape it has always had rather than growing a one-element list.
|
|
55
|
+
...(rest.length > 0 ? { errorDetails: refusals } : {}),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
37
58
|
/**
|
|
38
59
|
* What a member holds for each defined field. Separate from the definitions service
|
|
39
60
|
* because a value belongs to the member and a definition belongs to the site's settings,
|
|
@@ -166,21 +187,27 @@ class MetafieldValuesService {
|
|
|
166
187
|
}
|
|
167
188
|
const byIdentity = await this.allowedFieldsByIdentity(identities, audience);
|
|
168
189
|
const writes = [];
|
|
190
|
+
// Gathered rather than thrown as they are found. A write names several values, and
|
|
191
|
+
// a composite is several again, so refusing at the first leaves someone correcting
|
|
192
|
+
// one part per round trip to learn what was wrong with the rest.
|
|
193
|
+
const refusals = [];
|
|
169
194
|
for (const [identity, raw] of Object.entries(values)) {
|
|
170
195
|
const field = byIdentity.get(identity);
|
|
171
196
|
if (!field) {
|
|
172
|
-
|
|
197
|
+
refusals.push({
|
|
173
198
|
message: `Unknown custom field: ${identity}`,
|
|
174
199
|
property: wireProperty(identity),
|
|
175
200
|
});
|
|
201
|
+
continue;
|
|
176
202
|
}
|
|
177
203
|
// A different refusal from the unknown-field one above: this audience can
|
|
178
204
|
// already see the field, so naming it discloses nothing.
|
|
179
205
|
if (!(0, access_1.canWrite)(audience, field)) {
|
|
180
|
-
|
|
206
|
+
refusals.push({
|
|
181
207
|
message: `Cannot set custom field: ${identity}`,
|
|
182
208
|
property: wireProperty(identity),
|
|
183
209
|
});
|
|
210
|
+
continue;
|
|
184
211
|
}
|
|
185
212
|
// `null` clears any field, and `''` clears one with no parts. For a value
|
|
186
213
|
// with parts `''` names nothing, so it is left to fail validation rather than
|
|
@@ -195,14 +222,21 @@ class MetafieldValuesService {
|
|
|
195
222
|
// Which field failed rides in `property`.
|
|
196
223
|
const value = metafield_types_1.FIELD_TYPES[field.type].value.safeParse(raw);
|
|
197
224
|
if (!value.success) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
225
|
+
// Every issue, not the first: a value with parts fails once per part, and each
|
|
226
|
+
// names the part it belongs to, which is what lets a client mark them all.
|
|
227
|
+
for (const issue of value.error.issues) {
|
|
228
|
+
refusals.push({
|
|
229
|
+
message: issue.message,
|
|
230
|
+
property: [wireProperty(identity), ...issue.path].join('.'),
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
continue;
|
|
203
234
|
}
|
|
204
235
|
writes.push({ field, value: value.data });
|
|
205
236
|
}
|
|
237
|
+
if (refusals.length > 0) {
|
|
238
|
+
throw refusalError(refusals);
|
|
239
|
+
}
|
|
206
240
|
return writes;
|
|
207
241
|
}
|
|
208
242
|
/**
|
|
@@ -213,13 +247,19 @@ class MetafieldValuesService {
|
|
|
213
247
|
* them, and saying nothing about a path leaves it alone. There is no whole-value
|
|
214
248
|
* replace, so a caller that does not know about a field cannot erase it.
|
|
215
249
|
*
|
|
216
|
-
* `writtenBy`
|
|
217
|
-
* one cannot quietly inherit the identity of
|
|
250
|
+
* `writtenBy` and `source` are required and have no default: every writer has to name
|
|
251
|
+
* itself and where it wrote, so a new one cannot quietly inherit the identity of
|
|
252
|
+
* whichever was written first.
|
|
218
253
|
*
|
|
219
254
|
* Always transactional. Given an executor it joins that transaction, so the importer's
|
|
220
255
|
* failed value write takes its member with it; given none it opens its own.
|
|
256
|
+
*
|
|
257
|
+
* Every write also puts an entry on the member's activity feed, naming the fields it
|
|
258
|
+
* touched and who wrote them where, in the same transaction, so the feed cannot name a
|
|
259
|
+
* change that was not stored or miss one that was. A value has more than one author,
|
|
260
|
+
* and the feed is where a publisher finds out which of them changed it.
|
|
221
261
|
*/
|
|
222
|
-
async applyWrite(memberId, writes, { writtenBy, executor = this.knex }) {
|
|
262
|
+
async applyWrite(memberId, writes, { writtenBy, source, executor = this.knex }) {
|
|
223
263
|
if (writes.length === 0) {
|
|
224
264
|
return;
|
|
225
265
|
}
|
|
@@ -282,6 +322,22 @@ class MetafieldValuesService {
|
|
|
282
322
|
// it currently holds rather than who wrote its first value.
|
|
283
323
|
.merge(['value_text', 'written_by_type', 'written_by_id', 'updated_at']);
|
|
284
324
|
}
|
|
325
|
+
const fields = writes.map(({ field }) => ({
|
|
326
|
+
namespace: field.namespace,
|
|
327
|
+
key: field.key,
|
|
328
|
+
name: field.name,
|
|
329
|
+
}));
|
|
330
|
+
await trx(CHANGE_EVENTS_TABLE).insert({
|
|
331
|
+
id: new bson_objectid_1.default().toHexString(),
|
|
332
|
+
member_id: memberId,
|
|
333
|
+
written_by_type: writtenBy.type,
|
|
334
|
+
written_by_id: writtenBy.id,
|
|
335
|
+
source,
|
|
336
|
+
metafields: schema_1.StoredFieldList.encode(fields),
|
|
337
|
+
// A string rather than the Date the value rows take: SQLite stores a bound Date
|
|
338
|
+
// as a number, which sorts before every string the feed pages through time with.
|
|
339
|
+
created_at: (0, date_1.toDatabaseDate)(now),
|
|
340
|
+
});
|
|
285
341
|
};
|
|
286
342
|
// knex's marker for a transactor: join it rather than nesting a savepoint under it.
|
|
287
343
|
if (executor.isTransaction) {
|
|
@@ -291,5 +347,51 @@ class MetafieldValuesService {
|
|
|
291
347
|
await executor.transaction(apply);
|
|
292
348
|
}
|
|
293
349
|
}
|
|
350
|
+
/**
|
|
351
|
+
* Entries from members' activity feeds, newest first, each with its member, as one page
|
|
352
|
+
* of the members events endpoint reads them.
|
|
353
|
+
*
|
|
354
|
+
* `filter` is a parsed NQL filter over this table's own columns: mapping the feed's
|
|
355
|
+
* names onto them is the feed's business, and applying them is this table's.
|
|
356
|
+
*/
|
|
357
|
+
async browseChangeEvents({ limit, filter, }) {
|
|
358
|
+
const filtered = () => {
|
|
359
|
+
const query = this.knex(CHANGE_EVENTS_TABLE);
|
|
360
|
+
return filter ? (0, queries_1.knexify)(query, filter, { tableName: CHANGE_EVENTS_TABLE }) : query;
|
|
361
|
+
};
|
|
362
|
+
// Joined rather than looked up afterwards: an entry cannot outlive its member, whose
|
|
363
|
+
// delete cascades to it, so every entry has one to join.
|
|
364
|
+
const newestFirst = filtered()
|
|
365
|
+
.join(MEMBERS_TABLE, `${MEMBERS_TABLE}.id`, `${CHANGE_EVENTS_TABLE}.member_id`)
|
|
366
|
+
.orderBy([
|
|
367
|
+
{ column: `${CHANGE_EVENTS_TABLE}.created_at`, order: 'desc' },
|
|
368
|
+
{ column: `${CHANGE_EVENTS_TABLE}.id`, order: 'desc' },
|
|
369
|
+
]);
|
|
370
|
+
const page = limit === undefined ? newestFirst : newestFirst.limit(limit);
|
|
371
|
+
const [rows, counted] = await Promise.all([
|
|
372
|
+
page.select(`${CHANGE_EVENTS_TABLE}.id`, `${CHANGE_EVENTS_TABLE}.member_id`, `${CHANGE_EVENTS_TABLE}.written_by_type`, `${CHANGE_EVENTS_TABLE}.written_by_id`, `${CHANGE_EVENTS_TABLE}.source`, `${CHANGE_EVENTS_TABLE}.metafields`, `${CHANGE_EVENTS_TABLE}.created_at`, { member_uuid: `${MEMBERS_TABLE}.uuid` }, { member_name: `${MEMBERS_TABLE}.name` }, { member_email: `${MEMBERS_TABLE}.email` }),
|
|
373
|
+
filtered().count({ total: '*' }).first(),
|
|
374
|
+
]);
|
|
375
|
+
// What to do with an entry whose field list can't be read is decided here, not in the
|
|
376
|
+
// parser: it is still shown, naming no fields. The feed pages through time a page per
|
|
377
|
+
// event type, so dropping an entry would push a valid one off the end of its page for
|
|
378
|
+
// good. Anything else a row can't be read for, the table's constraints rule out, so it
|
|
379
|
+
// throws rather than being hidden.
|
|
380
|
+
const events = rows.map(({ member_uuid: uuid, member_name: name, member_email: email, ...event }) => {
|
|
381
|
+
const fields = schema_1.StoredFieldList.safeParse(event.metafields);
|
|
382
|
+
if (!fields.success) {
|
|
383
|
+
logging_1.default.warn({
|
|
384
|
+
event: { name: 'members.metafields.change_event_unreadable' },
|
|
385
|
+
err: fields.error,
|
|
386
|
+
changeEventId: event.id,
|
|
387
|
+
}, 'Reading an unreadable metafield change entry as naming no fields');
|
|
388
|
+
}
|
|
389
|
+
return schema_1.DbMetafieldChangeEventWithMember.parse({
|
|
390
|
+
event: fields.success ? event : { ...event, metafields: schema_1.StoredFieldList.encode([]) },
|
|
391
|
+
member: { id: event.member_id, uuid, name, email },
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
return { events, total: Number(counted?.total ?? 0) };
|
|
395
|
+
}
|
|
294
396
|
}
|
|
295
397
|
exports.MetafieldValuesService = MetafieldValuesService;
|