ghost 6.55.0 → 6.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
- package/components/tryghost-kg-default-cards-10.3.3.tgz +0 -0
- package/components/tryghost-kg-default-nodes-2.1.4.tgz +0 -0
- package/components/tryghost-kg-default-transforms-1.3.3.tgz +0 -0
- package/components/tryghost-kg-html-to-lexical-1.3.3.tgz +0 -0
- package/components/tryghost-kg-lexical-html-renderer-1.4.3.tgz +0 -0
- package/components/tryghost-nql-string-0.0.0.tgz +0 -0
- package/core/built/admin/assets/{Combination-CJG7Flp-.js → Combination-BCm9FL4o.js} +1 -1
- package/core/built/admin/assets/{PolarAngleAxis-BGjf7sBu.js → PolarAngleAxis-C4qtHy1B.js} +1 -1
- package/core/built/admin/assets/{RadialBarChart-zeFf9mBz.js → RadialBarChart-Bh09Stsj.js} +1 -1
- package/core/built/admin/assets/{account-migration-sjHLx5dy.js → account-migration-D3O_yVck.js} +1 -1
- package/core/built/admin/assets/activitypub/{account-migration-DoYXtnZ8.js → account-migration-D8FbP-Z-.js} +7 -7
- package/core/built/admin/assets/activitypub/activitypub.js +6 -6
- package/core/built/admin/assets/activitypub/{avatar-B3DEDB1l.js → avatar-11EA1VSs.js} +2 -2
- package/core/built/admin/assets/activitypub/{avatar-flipboard-Cqrg0XMx.js → avatar-flipboard-jfbLG-xe.js} +2 -2
- package/core/built/admin/assets/activitypub/{bluesky-sharing-Bxeh6MKW.js → bluesky-sharing-f1jTQe2P.js} +16 -16
- package/core/built/admin/assets/activitypub/{content-formatters-BpQARh28.js → content-formatters-nmTOPY9M.js} +3 -3
- package/core/built/admin/assets/activitypub/{copy-D9UWIAan.js → copy-BCwo9ep3.js} +2 -2
- package/core/built/admin/assets/activitypub/{dist-D8sFfdpC.js → dist-BEfX-0aW.js} +12 -12
- package/core/built/admin/assets/activitypub/{domain-CWueFW6q.js → domain-AaDlZlrA.js} +9 -9
- package/core/built/admin/assets/activitypub/{edit-profile-BGxPU7N5.js → edit-profile-DPi8Tl5M.js} +945 -945
- package/core/built/admin/assets/activitypub/{explore-DBK-u0Ch.js → explore-CxTzjILQ.js} +8 -8
- package/core/built/admin/assets/activitypub/{feed-Bcir5RQZ.js → feed-DPv5x2MX.js} +33 -33
- package/core/built/admin/assets/activitypub/{field-BV-CIT3d.js → field-DbgA8lat.js} +3 -3
- package/core/built/admin/assets/activitypub/{hash-BC9Q0N4c.js → hash-BWuZoYkA.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-DQ4ZnC1m.js → inbox-35VDQ2K2.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-yC0nfvzl.js → inbox-DSrF_i1O.js} +85 -85
- package/core/built/admin/assets/activitypub/{label-DTRS9TDk.js → label-CPjFBbo7.js} +3 -3
- package/core/built/admin/assets/activitypub/layout-CTgrezN7.js +1 -0
- package/core/built/admin/assets/activitypub/{moderation-Dwl9t0pb.js → moderation-BkDC1XVZ.js} +11 -11
- package/core/built/admin/assets/activitypub/{note-B-zKsZ7r.js → note-xEyH4xT-.js} +7 -7
- package/core/built/admin/assets/activitypub/{notifications-DjJZD54t.js → notifications-DClaL3ui.js} +9 -9
- package/core/built/admin/assets/activitypub/{preferences-CoU8vs6d.js → preferences-DpnOa-4Z.js} +20 -20
- package/core/built/admin/assets/activitypub/{profile-BAsaJekx.js → profile-Dvi9mz-6.js} +237 -237
- package/core/built/admin/assets/activitypub/{reply-CpdMeAO5.js → reply-Cs9ySd6Z.js} +2 -2
- package/core/built/admin/assets/activitypub/{routes-CLH3OVTP.js → routes-DFBeTnfn.js} +1139 -1150
- package/core/built/admin/assets/activitypub/{separator-BI1FLsYM.js → separator-C5u_VjzY.js} +3 -3
- package/core/built/admin/assets/activitypub/{settings-DA6pNJ-Q.js → settings-CV2fFu6D.js} +13 -13
- package/core/built/admin/assets/activitypub/{site-CQD42Pw5.js → site-_TgDd_EM.js} +81 -81
- package/core/built/admin/assets/activitypub/{step-1-DIDg4aHb.js → step-1-BfnNL2oF.js} +5 -5
- package/core/built/admin/assets/activitypub/{step-2-BfGe8BYt.js → step-2-DLndZkO-.js} +6 -6
- package/core/built/admin/assets/activitypub/{step-3-mE9spAqw.js → step-3-A-PZTtRE.js} +10 -10
- package/core/built/admin/assets/activitypub/{tabs-Cg0saow1.js → tabs-BUbz7tea.js} +317 -315
- package/core/built/admin/assets/activitypub/{topic-filter-DLHBZT93.js → topic-filter-BdX_xYYT.js} +3 -3
- package/core/built/admin/assets/activitypub/{use-activity-pub-queries-VzbN99u_.js → use-activity-pub-queries-EwuRemHh.js} +2 -2
- package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-CAwXTmqH.js → use-navigate-with-base-path-cy4ACBc4.js} +92 -90
- package/core/built/admin/assets/activitypub/{use-reply-chain-data-Br4aOWOh.js → use-reply-chain-data-CyFcGfan.js} +5 -5
- package/core/built/admin/assets/activitypub/{x-C8tAnXly.js → x-7zk8mV2p.js} +2 -2
- package/core/built/admin/assets/{audience-DjEWNhp3.js → audience-CkBb2LlO.js} +1 -1
- package/core/built/admin/assets/{automated-emails-DRy7qY-J.js → automated-emails-6t2bIRWf.js} +1 -1
- package/core/built/admin/assets/{automations-Bx3VcFIL.js → automations-cMllU545.js} +1 -1
- package/core/built/admin/assets/{bluesky-sharing-ClMd0-Ft.js → bluesky-sharing-DPGpyD-G.js} +1 -1
- package/core/built/admin/assets/{box-BWbhXh21.js → box-By_EgGtm.js} +1 -1
- package/core/built/admin/assets/{breadcrumb-BD0U2ygg.js → breadcrumb-DIfcF5ow.js} +1 -1
- package/core/built/admin/assets/build-CsiH26uS.js +1 -0
- package/core/built/admin/assets/{card-DGoDJzrn.js → card-aUHX0hyR.js} +1 -1
- package/core/built/admin/assets/{chart-BuRT2Y6p.js → chart-BnXyfH11.js} +1 -1
- package/core/built/admin/assets/{chart-helpers-fjQKRVyY.js → chart-helpers-DV0FkdcD.js} +1 -1
- package/core/built/admin/assets/{checkbox-CWa28lgO.js → checkbox-BIl077a0.js} +1 -1
- package/core/built/admin/assets/{chunk.481.6c7395a49484bb4583aa.js → chunk.332.05991a101d67f732dec3.js} +43 -42
- package/core/built/admin/assets/chunk.524.87b52f4fada529df2c4c.js +25 -0
- package/core/built/admin/assets/chunk.582.340bcca873e806cbd4bb.js +27 -0
- package/core/built/admin/assets/{code-editor-view-CCr0rBWS.js → code-editor-view-C0aWNwdl.js} +1 -1
- package/core/built/admin/assets/comments-qwhIjH3c.js +1 -0
- package/core/built/admin/assets/{container-BYz2Ed39.js → container-LiZf3XdY.js} +1 -1
- package/core/built/admin/assets/{data-list-16rkJYYi.js → data-list-CupF7bjM.js} +1 -1
- package/core/built/admin/assets/{date-range-select-BFp58EMk.js → date-range-select-dXc-GsVe.js} +1 -1
- package/core/built/admin/assets/{disabled-sources-indicator-CUxMfiPa.js → disabled-sources-indicator-DYAXW-6V.js} +1 -1
- package/core/built/admin/assets/dist-BUKmD51u.js +1 -0
- package/core/built/admin/assets/{dist-2Tnz2Yfq.js → dist-CNlMj7ML.js} +1 -1
- package/core/built/admin/assets/{dist-DGzPJ_bO.js → dist-Cb5RSHSg.js} +1 -1
- package/core/built/admin/assets/{dist-P_ohUlTi.js → dist-CqO2lWS0.js} +1 -1
- package/core/built/admin/assets/domain-B5ogXUBa.js +1 -0
- package/core/built/admin/assets/{dropzone-DlK-K2DQ.js → dropzone-BnFG7dDP.js} +1 -1
- package/core/built/admin/assets/{edit-profile-BKT-REJw.js → edit-profile-B07jCZy9.js} +1 -1
- package/core/built/admin/assets/editor-DDYSOXGN.js +8 -0
- package/core/built/admin/assets/{empty-indicator-oX1u1Pkn.js → empty-indicator-WB1WgT9h.js} +1 -1
- package/core/built/admin/assets/{explore-ClUpJZOp.js → explore-Ca1y8HW_.js} +1 -1
- package/core/built/admin/assets/{feed-DF92dUMP.js → feed-x4iFMBP4.js} +1 -1
- package/core/built/admin/assets/{field-Do7lieBA.js → field-ChCAabZg.js} +1 -1
- package/core/built/admin/assets/filter-sources-LdtH1e-T.js +1 -0
- package/core/built/admin/assets/filters-CLehPo_D.js +1 -0
- package/core/built/admin/assets/filters-CjVKRP8U.js +1 -0
- package/core/built/admin/assets/{ghost-c4057474943effc568c5659ccc4909d7.js → ghost-7f01131115186d1af84ead76e79646dd.js} +149 -129
- package/core/built/admin/assets/{ghost-a9c788f6f0b1ea6cf0f14ca4b13f4496.css → ghost-d38c30b866e59f2e06a357b5eb844fe5.css} +1 -1
- package/core/built/admin/assets/{ghost-dark-db36d795d21c61426837df40af270966.css → ghost-dark-1bffd5099ba7a749303133efacf8480b.css} +1 -1
- package/core/built/admin/assets/{gift-link-modal-f9VIMNDv.js → gift-link-modal-Buqh2vXh.js} +1 -1
- package/core/built/admin/assets/{growth-B3I6gNLm.js → growth-DajOBdN0.js} +1 -1
- package/core/built/admin/assets/{growth-CVw1My4S.js → growth-Dy2UOSay.js} +2 -2
- package/core/built/admin/assets/{inbox-CFBSu4Ed.js → inbox-BUOLDoji.js} +12 -12
- package/core/built/admin/assets/index-08n05-rc.js +67 -0
- package/core/built/admin/assets/index-DgUzgFvS.css +1 -0
- package/core/built/admin/assets/{inline-EMAm3okk.js → inline-Cw-gigDW.js} +1 -1
- package/core/built/admin/assets/input-group-uSK3XT7b.js +1 -0
- package/core/built/admin/assets/{input-surface-BpAjZXY5.js → input-surface-BdOe4KzO.js} +1 -1
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +9535 -9527
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +124 -124
- package/core/built/admin/assets/{koenig-lexical-f8vHRYvz.js → koenig-lexical-BFc4yFed.js} +86 -86
- package/core/built/admin/assets/{kpi-card-DSap9rAy.js → kpi-card-BN-E6WYX.js} +1 -1
- package/core/built/admin/assets/{kpi-card-DU0rn9Cj.js → kpi-card-CCt-qeWD.js} +1 -1
- package/core/built/admin/assets/{kpi-helpers-Dz33AV6h.js → kpi-helpers-DlxTbTs4.js} +1 -1
- package/core/built/admin/assets/{kpi-tabs-BtTTrKvJ.js → kpi-tabs-BlcUttpn.js} +1 -1
- package/core/built/admin/assets/{label-DS8Mi6G5.js → label-AVrxwmsz.js} +1 -1
- package/core/built/admin/assets/{label-picker-1DvSuLA1.js → label-picker-DVnpu_IF.js} +1 -1
- package/core/built/admin/assets/labels-DAIqKed7.js +1 -0
- package/core/built/admin/assets/layout-B0YLh7R9.js +1 -0
- package/core/built/admin/assets/link-helpers-D9LsDOkG.js +1 -0
- package/core/built/admin/assets/{list-page-B6ANtzkd.js → list-page-CdsnQR9N.js} +1 -1
- package/core/built/admin/assets/member-custom-fields-C8NpZYza.js +1 -0
- package/core/built/admin/assets/member-detail-CIVAdKXf.js +1 -0
- package/core/built/admin/assets/{members-CQpVFJu4.js → members-oLPiSkXD.js} +2 -2
- package/core/built/admin/assets/modals-CB7v-k3T.js +31 -0
- package/core/built/admin/assets/{moderation-DySbgyUS.js → moderation-CQ3urTFE.js} +1 -1
- package/core/built/admin/assets/newsletter-rbeDX9Wk.js +1 -0
- package/core/built/admin/assets/newsletters-vt3Ni1Ov.js +1 -0
- package/core/built/admin/assets/{note-C8kxrz6w.js → note-Dk877BTv.js} +1 -1
- package/core/built/admin/assets/{notifications-CgtSfX7t.js → notifications-CPlLjPxO.js} +1 -1
- package/core/built/admin/assets/{onboarding-route-4qtziA9d.js → onboarding-route-DdStOtjy.js} +1 -1
- package/core/built/admin/assets/{overview-Bf2sbznq.js → overview--4V-kx9c.js} +1 -1
- package/core/built/admin/assets/overview-DSLv30oz.js +1 -0
- package/core/built/admin/assets/{page-header-B3Wb9Jqv.js → page-header-pp4dWmOS.js} +1 -1
- package/core/built/admin/assets/{post-analytics-We-ko5Cb.js → post-analytics-BMJrI81R.js} +1 -1
- package/core/built/admin/assets/post-analytics-header-pU2m9ZuV.js +1 -0
- package/core/built/admin/assets/{post-analytics-provider-B6UynTGb.js → post-analytics-provider-Btml5vJI.js} +1 -1
- package/core/built/admin/assets/{post-share-modal-DFfO93_0.js → post-share-modal-AcUZkbfL.js} +1 -1
- package/core/built/admin/assets/preferences-Ru4CRPHC.js +5 -0
- package/core/built/admin/assets/profile-BNb1oj7D.js +1 -0
- package/core/built/admin/assets/{router-provider-C1R4AZ2o.js → router-provider-BmEZf6c6.js} +1 -1
- package/core/built/admin/assets/select-B0HteHrp.js +1 -0
- package/core/built/admin/assets/settings-B1C9Q3wo.js +4 -0
- package/core/built/admin/assets/{settings-WXNeWyU1.js → settings-BGgk7oCI.js} +1 -1
- package/core/built/admin/assets/{shade-app-B3heOAfz-CGXQwWeO.js → shade-app-B3heOAfz-BhQXxNkT.js} +1 -1
- package/core/built/admin/assets/{share-modal-DsgW3wbg.js → share-modal-BeuydaBf.js} +1 -1
- package/core/built/admin/assets/social-urls-DA2lXrhW.js +8 -0
- package/core/built/admin/assets/{sort-button-Bb_FCl7e.js → sort-button-Df4t2AL8.js} +1 -1
- package/core/built/admin/assets/{source-icon-BeaRxUn9.js → source-icon-ajoaKb41.js} +1 -1
- package/core/built/admin/assets/{stack-xiN0eQ6Z.js → stack-CIfOAAVf.js} +1 -1
- package/core/built/admin/assets/{stats-Bjj_kmJ1.js → stats-C_lfxZAw.js} +1 -1
- package/core/built/admin/assets/{stats-view-BxPbW6K1.js → stats-view-Cxot1iD8.js} +1 -1
- package/core/built/admin/assets/{step-1-B1e3-1F8.js → step-1-ogAA4lDr.js} +1 -1
- package/core/built/admin/assets/{step-2-C5WaeBJo.js → step-2-C5Xini-7.js} +1 -1
- package/core/built/admin/assets/{step-3-B46iVPwu.js → step-3-CZv1FV81.js} +2 -2
- package/core/built/admin/assets/{switch-z5Y6Uzt6.js → switch-CyQcwMAv.js} +1 -1
- package/core/built/admin/assets/{table-CB-4_HBJ.js → table-DFj1wiYv.js} +1 -1
- package/core/built/admin/assets/{tabs-CCEgKYzq.js → tabs-D4y1gqUd.js} +1 -1
- package/core/built/admin/assets/{tags-Cg2VJGmG.js → tags-Dmft-Y4O.js} +1 -1
- package/core/built/admin/assets/{text-DwtjwGkh.js → text-DU9Hv33_.js} +1 -1
- package/core/built/admin/assets/textarea-d9qEkMOP.js +1 -0
- package/core/built/admin/assets/{toggle-group-b3IlxvhV.js → toggle-group-R2QMFvXW.js} +1 -1
- package/core/built/admin/assets/{topic-filter-DAFT87tQ.js → topic-filter-DGDxDalo.js} +1 -1
- package/core/built/admin/assets/{use-analytics-data-S0FTd43s.js → use-analytics-data-C76v4LIp.js} +1 -1
- package/core/built/admin/assets/use-filter-params-WrdhAISv.js +1 -0
- package/core/built/admin/assets/{use-growth-stats-CIaiHOVb.js → use-growth-stats-aNXJOmQr.js} +1 -1
- package/core/built/admin/assets/{use-post-referrers-Cq2vBPrA.js → use-post-referrers-CG0SZioj.js} +1 -1
- package/core/built/admin/assets/{use-reply-chain-data-DBDRtGu-.js → use-reply-chain-data-RLdPKRmS.js} +1 -1
- package/core/built/admin/assets/{use-simple-pagination-CjYNxdwe.js → use-simple-pagination-DgcF69KI.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-query-falHAtuK.js → use-tinybird-query-C1R_Kbmy.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-token-D7-8ND1y.js → use-tinybird-token-C9fPjVM_.js} +1 -1
- package/core/built/admin/assets/{virtual-list-CvnC9zF2.js → virtual-list-BmTUzeoF.js} +1 -1
- package/core/built/admin/assets/{web-B_5GZRvH.js → web-BqW0b0_C.js} +1 -1
- package/core/built/admin/assets/web-Cy5GKMWM.js +2 -0
- package/core/built/admin/index.html +16 -16
- package/core/frontend/services/data/fetch-data.js +10 -5
- package/core/frontend/services/routing/api-adapter.js +115 -0
- package/core/frontend/services/routing/api-adapter.ts +157 -0
- package/core/frontend/services/routing/collection-router.js +5 -3
- package/core/frontend/services/routing/controllers/index.js +1 -1
- package/core/frontend/services/routing/controllers/rss.js +26 -31
- package/core/frontend/services/routing/controllers/rss.ts +66 -0
- package/core/frontend/services/routing/controllers/static.js +8 -7
- package/core/frontend/services/routing/parent-router.js +17 -4
- package/core/frontend/services/routing/permalink-adapter.js +26 -0
- package/core/frontend/services/routing/permalink-adapter.ts +23 -0
- package/core/frontend/services/routing/registry.js +59 -33
- package/core/frontend/services/routing/router-manager.js +9 -13
- package/core/frontend/services/routing/static-routes-router.js +4 -4
- package/core/frontend/services/routing/taxonomy-router.js +6 -2
- package/core/server/data/migrations/versions/6.56/2026-08-03-19-27-51-add-automation-run-step-id-to-automated-email-recipients.js +8 -0
- package/core/server/data/migrations/versions/6.56/2026-08-04-14-42-39-restrict-notification-add-permission-to-administrators.js +12 -0
- package/core/server/data/schema/fixtures/fixtures.json +2 -2
- package/core/server/data/schema/schema.js +1 -0
- package/core/server/models/post.js +10 -1
- package/core/server/services/automations/automations-repository.ts +3 -1
- package/core/server/services/automations/database-automations-repository.js +33 -14
- package/core/server/services/automations/database-automations-repository.ts +41 -15
- package/core/server/services/automations/get-scheduler-idempotency-key.js +13 -0
- package/core/server/services/automations/get-scheduler-idempotency-key.ts +8 -0
- package/core/server/services/automations/poll.js +3 -1
- package/core/server/services/automations/poll.ts +4 -1
- package/core/server/services/automations/service.js +5 -1
- package/core/server/services/automations/service.ts +5 -1
- package/core/server/services/gift-links/CONTEXT.md +9 -0
- package/core/server/services/gifts/CONTEXT.md +55 -0
- package/core/server/services/gifts/README.md +28 -0
- package/core/server/services/gifts/gift-bookshelf-repository.js +65 -45
- package/core/server/services/gifts/gift-bookshelf-repository.ts +203 -74
- package/core/server/services/gifts/gift-checkout-adapter.js +59 -0
- package/core/server/services/gifts/gift-checkout-offer.js +74 -0
- package/core/server/services/gifts/gift-checkout-offer.ts +127 -0
- package/core/server/services/gifts/gift-codec.js +63 -0
- package/core/server/services/gifts/gift-codec.ts +62 -0
- package/core/server/services/gifts/gift-controller.js +6 -31
- package/core/server/services/gifts/gift-controller.ts +6 -69
- package/core/server/services/gifts/gift-email-service.ts +3 -2
- package/core/server/services/gifts/gift-schema.js +34 -0
- package/core/server/services/gifts/gift-schema.ts +39 -0
- package/core/server/services/gifts/gift-service-wrapper.js +13 -11
- package/core/server/services/gifts/gift-service.js +265 -52
- package/core/server/services/gifts/gift-service.ts +431 -97
- package/core/server/services/gifts/gift.ts +16 -39
- package/core/server/services/link-tracking/link-click-repository.js +6 -1
- package/core/server/services/link-tracking/link-click-tracking-service.js +12 -3
- package/core/server/services/member-welcome-emails/member-welcome-email-renderer.js +4 -3
- package/core/server/services/member-welcome-emails/service.js +7 -3
- package/core/server/services/members/api.js +1 -2
- package/core/server/services/members/import-export/import/importer.ts +3 -2
- package/core/server/services/members/import-export/index.js +10 -2
- package/core/server/services/members/import-export/index.ts +14 -4
- package/core/server/services/members/members-api/controllers/router-controller.js +38 -36
- package/core/server/services/members/members-api/members-api.js +13 -7
- package/core/server/services/members/members-api/repositories/event-repository.js +4 -94
- package/core/server/services/members/members-api/services/member-bread-service.js +3 -3
- package/core/server/services/members/members-api/services/payments-service.js +6 -68
- package/core/server/services/members-custom-fields/schema.js +8 -1
- package/core/server/services/members-custom-fields/schema.ts +8 -0
- package/core/server/services/members-custom-fields/values-service.js +118 -2
- package/core/server/services/members-custom-fields/values-service.ts +134 -4
- package/core/server/services/oembed/oembed-service.js +44 -3
- package/core/server/services/posts/posts-service.js +11 -0
- package/core/server/services/route-settings/dynamic-routing-service.js +1 -3
- package/core/server/services/route-settings/route-settings-parser.js +25 -25
- package/core/server/services/route-settings/route-settings-parser.ts +27 -27
- package/core/server/services/route-settings/validation-errors.js +68 -4
- package/core/server/services/route-settings/validation-errors.ts +79 -4
- package/core/server/services/stats/posts-stats-service.js +27 -23
- package/core/server/services/stripe/services/webhook/charge-refunded-event-service.js +1 -1
- package/core/server/services/stripe/services/webhook/checkout-session-event-service.js +16 -4
- package/core/server/services/stripe/stripe-api.js +10 -0
- package/core/server/web/gift-preview/controller.js +10 -25
- package/core/shared/labs.js +2 -2
- package/package.json +10 -9
- package/pnpm-lock.yaml +28 -18
- package/pnpm-workspace.yaml +5 -4
- package/components/tryghost-kg-default-nodes-2.1.3.tgz +0 -0
- package/core/built/admin/assets/activitypub/layout-qx-bUL1k.js +0 -1
- package/core/built/admin/assets/chunk.524.cffd399783a519e404a9.js +0 -25
- package/core/built/admin/assets/chunk.582.eebf0d0b2d202bb746a5.js +0 -27
- package/core/built/admin/assets/comments-DyUSMUdj.js +0 -1
- package/core/built/admin/assets/dist-CjY-f5Z1.js +0 -1
- package/core/built/admin/assets/domain-C0bV2UV8.js +0 -1
- package/core/built/admin/assets/editor-Dp865O-4.js +0 -8
- package/core/built/admin/assets/filter-sources-BV2rnpfA.js +0 -1
- package/core/built/admin/assets/filters-CUXJ2Nm9.js +0 -1
- package/core/built/admin/assets/filters-DfbIWc_l.js +0 -1
- package/core/built/admin/assets/index-9Cw8z3rA.css +0 -1
- package/core/built/admin/assets/index-BGG7NckO.js +0 -67
- package/core/built/admin/assets/input-group-BSgG_lVw.js +0 -1
- package/core/built/admin/assets/labels-CDSUt4J_.js +0 -1
- package/core/built/admin/assets/layout-yQEi2qDM.js +0 -1
- package/core/built/admin/assets/link-helpers-7EYqvvHs.js +0 -1
- package/core/built/admin/assets/member-custom-fields-yOMHyAg9.js +0 -1
- package/core/built/admin/assets/member-detail-BAq7gDaa.js +0 -1
- package/core/built/admin/assets/modals-ReORvWnu.js +0 -31
- package/core/built/admin/assets/newsletter-CJQvtHqN.js +0 -1
- package/core/built/admin/assets/newsletters-B3QQXfGT.js +0 -1
- package/core/built/admin/assets/nql-BsgFMd93.js +0 -1
- package/core/built/admin/assets/overview-CBfePxcr.js +0 -1
- package/core/built/admin/assets/post-analytics-header-Bmc9su6q.js +0 -1
- package/core/built/admin/assets/preferences-DJPfHEGu.js +0 -5
- package/core/built/admin/assets/profile-DqBQp-gT.js +0 -1
- package/core/built/admin/assets/select-C5_xEt5b.js +0 -1
- package/core/built/admin/assets/settings-BwiVIXau.js +0 -4
- package/core/built/admin/assets/social-urls-BQPFEk1_.js +0 -8
- package/core/built/admin/assets/textarea-Bjccvh_E.js +0 -1
- package/core/built/admin/assets/use-filter-params-Gpoq2E1L.js +0 -1
- package/core/built/admin/assets/web-jFYWCKDn.js +0 -2
- package/core/server/services/gifts/gift-repository.js +0 -2
- package/core/server/services/gifts/gift-repository.ts +0 -29
- package/core/server/services/route-settings/activation-bridge.js +0 -174
- package/core/server/services/route-settings/activation-bridge.ts +0 -250
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {GIFT_EXPIRY_DAYS} from './constants';
|
|
2
|
+
import type {GiftCadence, GiftData, GiftStatus} from './gift-schema';
|
|
2
3
|
|
|
3
|
-
export type GiftStatus
|
|
4
|
-
export type GiftCadence = 'month' | 'year';
|
|
4
|
+
export type {GiftCadence, GiftStatus} from './gift-schema';
|
|
5
5
|
|
|
6
6
|
export type RedeemableCheckFailureReason = 'redeemed' | 'consumed' | 'expired' | 'refunded' | 'paid-member';
|
|
7
7
|
export type RedeemableCheckResult =
|
|
@@ -13,43 +13,20 @@ export type ReassignableCheckResult =
|
|
|
13
13
|
| {reassignable: true}
|
|
14
14
|
| {reassignable: false; reason: ReassignableCheckFailureReason};
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
token
|
|
18
|
-
buyerEmail
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
status: GiftStatus;
|
|
31
|
-
purchasedAt: Date;
|
|
32
|
-
redeemedAt: Date | null;
|
|
33
|
-
consumedAt: Date | null;
|
|
34
|
-
expiredAt: Date | null;
|
|
35
|
-
refundedAt: Date | null;
|
|
36
|
-
consumesSoonReminderSentAt: Date | null;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface GiftFromPurchaseData {
|
|
40
|
-
token: string;
|
|
41
|
-
buyerEmail: string;
|
|
42
|
-
buyerMemberId: string | null;
|
|
43
|
-
tierId: string;
|
|
44
|
-
cadence: GiftCadence;
|
|
45
|
-
duration: number;
|
|
46
|
-
currency: string;
|
|
47
|
-
amount: number;
|
|
48
|
-
stripeCheckoutSessionId: string;
|
|
49
|
-
stripePaymentIntentId: string;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export class Gift {
|
|
16
|
+
export type GiftFromPurchaseData = Pick<GiftData,
|
|
17
|
+
| 'token'
|
|
18
|
+
| 'buyerEmail'
|
|
19
|
+
| 'buyerMemberId'
|
|
20
|
+
| 'tierId'
|
|
21
|
+
| 'cadence'
|
|
22
|
+
| 'duration'
|
|
23
|
+
| 'currency'
|
|
24
|
+
| 'amount'
|
|
25
|
+
| 'stripeCheckoutSessionId'
|
|
26
|
+
| 'stripePaymentIntentId'
|
|
27
|
+
>;
|
|
28
|
+
|
|
29
|
+
export class Gift implements GiftData {
|
|
53
30
|
token: string;
|
|
54
31
|
buyerEmail: string;
|
|
55
32
|
buyerMemberId: string | null;
|
|
@@ -32,7 +32,12 @@ module.exports = class LinkClickRepository {
|
|
|
32
32
|
|
|
33
33
|
// Memoize the findOne function
|
|
34
34
|
this.memoizedFindOne = _.memoize(async (uuid, options) => {
|
|
35
|
-
|
|
35
|
+
try {
|
|
36
|
+
return await this.#Member.findOne({uuid}, options);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
this.memoizedFindOne.cache.delete(uuid);
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
36
41
|
});
|
|
37
42
|
}
|
|
38
43
|
|
|
@@ -4,6 +4,7 @@ const PostLink = require('./post-link');
|
|
|
4
4
|
const ObjectID = require('bson-objectid').default;
|
|
5
5
|
const errors = require('@tryghost/errors');
|
|
6
6
|
const nql = require('@tryghost/nql');
|
|
7
|
+
const {escapeNqlString} = require('@tryghost/nql-string');
|
|
7
8
|
const _ = require('lodash');
|
|
8
9
|
const tpl = require('@tryghost/tpl');
|
|
9
10
|
const moment = require('moment');
|
|
@@ -174,7 +175,11 @@ class LinkClickTrackingService {
|
|
|
174
175
|
|
|
175
176
|
// manages transformation of current url to relative for comparision
|
|
176
177
|
const transformedOldUrl = this.#urlUtils.absoluteToTransformReady(redirectUrl.href);
|
|
177
|
-
|
|
178
|
+
// the url is re-serialised into NQL here, so it needs escaping again -
|
|
179
|
+
// quotes are legal in a URL and would otherwise break the filter.
|
|
180
|
+
// postId is String()d because nql parses an unquoted `post_id:123` to a
|
|
181
|
+
// number, and escapeNqlString operates on strings
|
|
182
|
+
const filterQuery = `post_id:${escapeNqlString(String(postId))}+to:${escapeNqlString(transformedOldUrl)}`;
|
|
178
183
|
|
|
179
184
|
const updatedFilterOptions = {
|
|
180
185
|
...filterOptions,
|
|
@@ -245,13 +250,15 @@ class LinkClickTrackingService {
|
|
|
245
250
|
* Add first-party tracking to a URL in an automation email
|
|
246
251
|
* @param {Readonly<URL>} url
|
|
247
252
|
* @param {string} automationActionRevisionId
|
|
253
|
+
* @param {string} automationRunStepId
|
|
248
254
|
* @param {string} memberUuid
|
|
249
255
|
* @return {Promise<URL>}
|
|
250
256
|
*/
|
|
251
|
-
async addAutomationTrackingToUrl(url, automationActionRevisionId, memberUuid) {
|
|
257
|
+
async addAutomationTrackingToUrl(url, automationActionRevisionId, automationRunStepId, memberUuid) {
|
|
252
258
|
const redirect = await this.#linkRedirectService.getOrAddAutomationRedirect(automationActionRevisionId, url);
|
|
253
259
|
const trackedUrl = new URL(redirect.from.href);
|
|
254
260
|
trackedUrl.searchParams.set('m', memberUuid);
|
|
261
|
+
trackedUrl.searchParams.set('step', automationRunStepId);
|
|
255
262
|
return trackedUrl;
|
|
256
263
|
}
|
|
257
264
|
|
|
@@ -269,7 +276,8 @@ class LinkClickTrackingService {
|
|
|
269
276
|
});
|
|
270
277
|
|
|
271
278
|
const automationActionRevisionId = event.data.link.automationActionRevisionId;
|
|
272
|
-
|
|
279
|
+
const automationRunStepId = event.data.url.searchParams.get('step');
|
|
280
|
+
if (!automationActionRevisionId || !automationRunStepId) {
|
|
273
281
|
await this.#linkClickRepository.save(click);
|
|
274
282
|
return;
|
|
275
283
|
}
|
|
@@ -282,6 +290,7 @@ class LinkClickTrackingService {
|
|
|
282
290
|
|
|
283
291
|
await this.#automationsApi.trackEmailClicked({
|
|
284
292
|
automationActionRevisionId,
|
|
293
|
+
automationRunStepId,
|
|
285
294
|
memberId,
|
|
286
295
|
clickedAt: event.timestamp
|
|
287
296
|
}, {transacting});
|
|
@@ -119,9 +119,10 @@ class MemberWelcomeEmailRenderer {
|
|
|
119
119
|
* @param {string} [options.unsubscribeUrl] - When set, the footer shows an "Unsubscribe from these emails" link instead of "Manage your preferences"
|
|
120
120
|
* @param {boolean} [options.trackClicks]
|
|
121
121
|
* @param {string | null} [options.automationActionRevisionId]
|
|
122
|
+
* @param {string | null} [options.automationRunStepId]
|
|
122
123
|
* @returns {Promise<{html: string, text: string, subject: string}>}
|
|
123
124
|
*/
|
|
124
|
-
async render({lexical, subject, designSettings, member, siteSettings, unsubscribeUrl, trackClicks = false, automationActionRevisionId = null}) {
|
|
125
|
+
async render({lexical, subject, designSettings, member, siteSettings, unsubscribeUrl, trackClicks = false, automationActionRevisionId = null, automationRunStepId = null}) {
|
|
125
126
|
designSettings = designSettings || {};
|
|
126
127
|
|
|
127
128
|
const design = emailDesign.getEmailDesign({
|
|
@@ -171,9 +172,9 @@ class MemberWelcomeEmailRenderer {
|
|
|
171
172
|
return originalPath;
|
|
172
173
|
}
|
|
173
174
|
const isTrackable = ['http:', 'https:'].includes(url.protocol);
|
|
174
|
-
if (trackClicks && automationActionRevisionId && member.uuid && isTrackable) {
|
|
175
|
+
if (trackClicks && automationActionRevisionId && automationRunStepId && member.uuid && isTrackable) {
|
|
175
176
|
await linkTracking.init();
|
|
176
|
-
return await linkTracking.service.addAutomationTrackingToUrl(url, automationActionRevisionId, member.uuid);
|
|
177
|
+
return await linkTracking.service.addAutomationTrackingToUrl(url, automationActionRevisionId, automationRunStepId, member.uuid);
|
|
177
178
|
}
|
|
178
179
|
return url;
|
|
179
180
|
}, {base: siteSettings.url});
|
|
@@ -370,10 +370,11 @@ class MemberWelcomeEmailService {
|
|
|
370
370
|
* @param {boolean} [options.trackOpens]
|
|
371
371
|
* @param {boolean} [options.trackClicks]
|
|
372
372
|
* @param {string | null} [options.automationActionRevisionId]
|
|
373
|
+
* @param {string | null} [options.automationRunStepId]
|
|
373
374
|
* @param {null | {url: string, oneClickUrl: string}} [options.unsubscribe] - When set, the footer links to an unsubscribe URL and the email carries one-click List-Unsubscribe headers
|
|
374
375
|
* @returns {Promise<unknown>}
|
|
375
376
|
*/
|
|
376
|
-
async #sendEmail({member, memberStatus, email, emailType, trackOpens, trackClicks = false, automationActionRevisionId = null, unsubscribe = null}) {
|
|
377
|
+
async #sendEmail({member, memberStatus, email, emailType, trackOpens, trackClicks = false, automationActionRevisionId = null, automationRunStepId = null, unsubscribe = null}) {
|
|
377
378
|
if (!member.email) {
|
|
378
379
|
throw new errors.IncorrectUsageError({
|
|
379
380
|
message: MESSAGES.MISSING_RECIPIENT_EMAIL
|
|
@@ -400,7 +401,8 @@ class MemberWelcomeEmailService {
|
|
|
400
401
|
siteSettings: this.#getSiteSettings(),
|
|
401
402
|
unsubscribeUrl: unsubscribe?.url,
|
|
402
403
|
trackClicks,
|
|
403
|
-
automationActionRevisionId
|
|
404
|
+
automationActionRevisionId,
|
|
405
|
+
automationRunStepId
|
|
404
406
|
});
|
|
405
407
|
|
|
406
408
|
const senderOptions = await this.#getEffectiveSenderOptions(
|
|
@@ -480,9 +482,10 @@ class MemberWelcomeEmailService {
|
|
|
480
482
|
* @param {boolean} options.trackOpens
|
|
481
483
|
* @param {boolean} options.trackClicks
|
|
482
484
|
* @param {string} options.automationActionRevisionId
|
|
485
|
+
* @param {string} options.automationRunStepId
|
|
483
486
|
* @returns {Promise<unknown>}
|
|
484
487
|
*/
|
|
485
|
-
async sendAutomationEmail({email, member, memberStatus, trackOpens, trackClicks, automationActionRevisionId}) {
|
|
488
|
+
async sendAutomationEmail({email, member, memberStatus, trackOpens, trackClicks, automationActionRevisionId, automationRunStepId}) {
|
|
486
489
|
const designSettings = email.designSettingId ?
|
|
487
490
|
await EmailDesignSetting.findOne({id: email.designSettingId}) :
|
|
488
491
|
null;
|
|
@@ -510,6 +513,7 @@ class MemberWelcomeEmailService {
|
|
|
510
513
|
trackOpens,
|
|
511
514
|
trackClicks,
|
|
512
515
|
automationActionRevisionId,
|
|
516
|
+
automationRunStepId,
|
|
513
517
|
unsubscribe
|
|
514
518
|
});
|
|
515
519
|
}
|
|
@@ -244,8 +244,7 @@ function createApiInstance(config) {
|
|
|
244
244
|
EmailSpamComplaintEvent: models.EmailSpamComplaintEvent,
|
|
245
245
|
Automation: models.Automation,
|
|
246
246
|
WelcomeEmailAutomationRun: models.WelcomeEmailAutomationRun,
|
|
247
|
-
AutomatedEmailRecipient: models.AutomatedEmailRecipient
|
|
248
|
-
Gift: models.Gift
|
|
247
|
+
AutomatedEmailRecipient: models.AutomatedEmailRecipient
|
|
249
248
|
},
|
|
250
249
|
stripeAPIService: stripeService.api,
|
|
251
250
|
tiersService: tiersService,
|
|
@@ -82,7 +82,7 @@ interface StripeSubscriptions {
|
|
|
82
82
|
|
|
83
83
|
// Gifts: reassigning a redeemed gift to the imported member.
|
|
84
84
|
export interface GiftService {
|
|
85
|
-
reassignRedeemer(giftId: string, memberId: string, options:
|
|
85
|
+
reassignRedeemer(giftId: string, memberId: string, options: {transacting?: Knex.Transaction}): Promise<void>;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
// The completion email concern: who it goes to, the links it carries, and sending
|
|
@@ -99,7 +99,8 @@ type CustomFieldPlan = unknown;
|
|
|
99
99
|
// The custom fields collaborator as the import needs it. activeFields is the field set a
|
|
100
100
|
// custom_fields.* column is read against, empty when the feature is off; planWrite
|
|
101
101
|
// validates a row's values (throwing so the row fails whole) and applyWrite persists
|
|
102
|
-
// them, both on the
|
|
102
|
+
// them, merging a composite's sub-fields into whatever is already stored, both on the
|
|
103
|
+
// row's transaction.
|
|
103
104
|
export interface CustomFieldsImport {
|
|
104
105
|
activeFields(): Promise<CsvField[]>;
|
|
105
106
|
planWrite(values: Record<string, unknown>): Promise<CustomFieldPlan[]>;
|
|
@@ -40,7 +40,11 @@ function makeImporter(deps) {
|
|
|
40
40
|
// Gifts is initialised at boot and always present at request time; the getter
|
|
41
41
|
// resolves it lazily so the ready service is picked up whenever a row uses it.
|
|
42
42
|
const gifts = {
|
|
43
|
-
reassignRedeemer: (giftId, memberId, options) => deps.getGiftService().reassignRedeemer(
|
|
43
|
+
reassignRedeemer: (giftId, memberId, options) => deps.getGiftService().reassignRedeemer({
|
|
44
|
+
giftId,
|
|
45
|
+
memberId,
|
|
46
|
+
transacting: options.transacting
|
|
47
|
+
})
|
|
44
48
|
};
|
|
45
49
|
// Gated by the same labs flag as the export, so the two halves round-trip or stay
|
|
46
50
|
// silent together: off, activeFields resolves empty and every custom_fields.* column
|
|
@@ -48,7 +52,11 @@ function makeImporter(deps) {
|
|
|
48
52
|
const customFields = {
|
|
49
53
|
activeFields: async () => (labs.isSet('membersCustomFields') ? deps.customFields.definitions.browse() : []),
|
|
50
54
|
planWrite: values => deps.customFields.values.planWrite(values),
|
|
51
|
-
|
|
55
|
+
// mergeComposites: a sub-field is a field, so a row writes the sub-fields it fills
|
|
56
|
+
// and leaves the rest alone, the same rule a blank `name` column follows. Without
|
|
57
|
+
// it a file naming one address column would replace the whole address, clearing
|
|
58
|
+
// five sub-fields it never mentioned.
|
|
59
|
+
applyWrite: (memberId, plan, executor) => deps.customFields.values.applyWrite(memberId, plan, executor, { mergeComposites: true })
|
|
52
60
|
};
|
|
53
61
|
return new importer_1.default({
|
|
54
62
|
knex: deps.knex,
|
|
@@ -18,7 +18,9 @@ interface ImporterServices {
|
|
|
18
18
|
getMembersRepository(): Promise<Omit<MembersRepository, 'getImportLabel'>>;
|
|
19
19
|
getDefaultTier(): Promise<Tier>;
|
|
20
20
|
getTierByName(name: string): Promise<Tier | null>;
|
|
21
|
-
getGiftService():
|
|
21
|
+
getGiftService(): {
|
|
22
|
+
reassignRedeemer(input: {giftId: string; memberId: string; transacting?: Knex.Transaction}): Promise<void>;
|
|
23
|
+
};
|
|
22
24
|
sendEmail: EmailNotifications['send'];
|
|
23
25
|
urlFor: EmailNotifications['urlFor'];
|
|
24
26
|
addJob(job: {job: () => Promise<void>; offloaded: boolean; name: string}): void;
|
|
@@ -31,7 +33,7 @@ interface ImporterServices {
|
|
|
31
33
|
definitions: {browse(): Promise<CsvField[]>};
|
|
32
34
|
values: {
|
|
33
35
|
planWrite(values: Record<string, unknown>): Promise<unknown[]>;
|
|
34
|
-
applyWrite(memberId: string, plan: unknown[], executor: Knex): Promise<void>;
|
|
36
|
+
applyWrite(memberId: string, plan: unknown[], executor: Knex, options?: {mergeComposites?: boolean}): Promise<void>;
|
|
35
37
|
};
|
|
36
38
|
};
|
|
37
39
|
}
|
|
@@ -72,7 +74,11 @@ export function makeImporter(deps: ImporterServices) {
|
|
|
72
74
|
// Gifts is initialised at boot and always present at request time; the getter
|
|
73
75
|
// resolves it lazily so the ready service is picked up whenever a row uses it.
|
|
74
76
|
const gifts: GiftService = {
|
|
75
|
-
reassignRedeemer: (giftId, memberId, options) => deps.getGiftService().reassignRedeemer(
|
|
77
|
+
reassignRedeemer: (giftId, memberId, options) => deps.getGiftService().reassignRedeemer({
|
|
78
|
+
giftId,
|
|
79
|
+
memberId,
|
|
80
|
+
transacting: options.transacting
|
|
81
|
+
})
|
|
76
82
|
};
|
|
77
83
|
|
|
78
84
|
// Gated by the same labs flag as the export, so the two halves round-trip or stay
|
|
@@ -81,7 +87,11 @@ export function makeImporter(deps: ImporterServices) {
|
|
|
81
87
|
const customFields: CustomFieldsImport = {
|
|
82
88
|
activeFields: async () => (labs.isSet('membersCustomFields') ? deps.customFields.definitions.browse() : []),
|
|
83
89
|
planWrite: values => deps.customFields.values.planWrite(values),
|
|
84
|
-
|
|
90
|
+
// mergeComposites: a sub-field is a field, so a row writes the sub-fields it fills
|
|
91
|
+
// and leaves the rest alone, the same rule a blank `name` column follows. Without
|
|
92
|
+
// it a file naming one address column would replace the whole address, clearing
|
|
93
|
+
// five sub-fields it never mentioned.
|
|
94
|
+
applyWrite: (memberId, plan, executor) => deps.customFields.values.applyWrite(memberId, plan, executor, {mergeComposites: true})
|
|
85
95
|
};
|
|
86
96
|
|
|
87
97
|
return new MembersCSVImporter({
|
|
@@ -525,7 +525,7 @@ module.exports = class RouterController {
|
|
|
525
525
|
* @param {string} options.cancelUrl URL to redirect to after cancelled checkout
|
|
526
526
|
* @param {string} [options.email] Email address of the customer
|
|
527
527
|
* @param {object} [options.member] Currently authenticated member OR member associated with the email address
|
|
528
|
-
* @param {
|
|
528
|
+
* @param {number|null} [options.giftTrialDays] Stable continuation decision from gift subscriptions
|
|
529
529
|
* @param {boolean} options.isAuthenticated
|
|
530
530
|
* @param {object} options.metadata Metadata to be passed to Stripe
|
|
531
531
|
* @returns
|
|
@@ -680,9 +680,11 @@ module.exports = class RouterController {
|
|
|
680
680
|
|
|
681
681
|
/**
|
|
682
682
|
* @param {object} options
|
|
683
|
-
* @param {
|
|
684
|
-
* @param {
|
|
685
|
-
* @param {string} options.
|
|
683
|
+
* @param {string} options.tierId
|
|
684
|
+
* @param {string} [options.offerId]
|
|
685
|
+
* @param {string} [options.cadence]
|
|
686
|
+
* @param {number} [options.duration]
|
|
687
|
+
* @param {string} [options.email]
|
|
686
688
|
* @param {string} options.successUrl
|
|
687
689
|
* @param {string} options.cancelUrl
|
|
688
690
|
* @param {object} options.metadata
|
|
@@ -698,10 +700,26 @@ module.exports = class RouterController {
|
|
|
698
700
|
}
|
|
699
701
|
|
|
700
702
|
try {
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
703
|
+
return await this._giftService.service.startCheckout({
|
|
704
|
+
tierId: options.tierId,
|
|
705
|
+
offerId: options.offerId,
|
|
706
|
+
cadence: options.cadence,
|
|
707
|
+
duration: options.duration,
|
|
708
|
+
metadata: options.metadata,
|
|
709
|
+
successUrl: options.successUrl,
|
|
710
|
+
cancelUrl: options.cancelUrl,
|
|
711
|
+
buyer: {
|
|
712
|
+
memberId: options.member?.id ?? null,
|
|
713
|
+
email: options.member?.get('email') ?? options.email ?? null,
|
|
714
|
+
name: options.member?.get('name') ?? null,
|
|
715
|
+
isAuthenticated: options.isAuthenticated
|
|
716
|
+
}
|
|
717
|
+
});
|
|
704
718
|
} catch (err) {
|
|
719
|
+
if (err instanceof BadRequestError || err instanceof NoPermissionError) {
|
|
720
|
+
throw err;
|
|
721
|
+
}
|
|
722
|
+
|
|
705
723
|
logging.error(err);
|
|
706
724
|
this._sentry?.captureException?.(err);
|
|
707
725
|
throw new BadRequestError({
|
|
@@ -788,7 +806,7 @@ module.exports = class RouterController {
|
|
|
788
806
|
let tier;
|
|
789
807
|
let cadence;
|
|
790
808
|
let offer;
|
|
791
|
-
let
|
|
809
|
+
let giftTrialDays;
|
|
792
810
|
|
|
793
811
|
if (req.body.continueFromGift) {
|
|
794
812
|
if (!isAuthenticated || !member) {
|
|
@@ -796,25 +814,16 @@ module.exports = class RouterController {
|
|
|
796
814
|
message: tpl(messages.signInRequired)
|
|
797
815
|
});
|
|
798
816
|
}
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
});
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
gift = await this._giftService.service.getActiveByMember(member.id);
|
|
807
|
-
if (!gift) {
|
|
808
|
-
throw new BadRequestError({
|
|
809
|
-
message: tpl(messages.badRequest),
|
|
810
|
-
context: 'No active gift subscription found for member'
|
|
811
|
-
});
|
|
812
|
-
}
|
|
817
|
+
const continuation = await this._giftService.service.preparePaidContinuation({
|
|
818
|
+
memberId: member.id,
|
|
819
|
+
memberStatus: member.get('status')
|
|
820
|
+
});
|
|
813
821
|
|
|
814
822
|
({tier, cadence} = await this._getSubscriptionCheckoutData({
|
|
815
|
-
tierId:
|
|
816
|
-
cadence:
|
|
823
|
+
tierId: continuation.tierId,
|
|
824
|
+
cadence: continuation.cadence
|
|
817
825
|
}));
|
|
826
|
+
giftTrialDays = continuation.trialDays;
|
|
818
827
|
} else {
|
|
819
828
|
({tier, cadence, offer} = await this._getSubscriptionCheckoutData(req.body));
|
|
820
829
|
}
|
|
@@ -825,7 +834,7 @@ module.exports = class RouterController {
|
|
|
825
834
|
tier,
|
|
826
835
|
cadence,
|
|
827
836
|
offer,
|
|
828
|
-
|
|
837
|
+
giftTrialDays
|
|
829
838
|
});
|
|
830
839
|
|
|
831
840
|
// Add welcome_page_url to the response if available and member is authenticated
|
|
@@ -842,19 +851,12 @@ module.exports = class RouterController {
|
|
|
842
851
|
});
|
|
843
852
|
}
|
|
844
853
|
|
|
845
|
-
if (req.body.offerId) {
|
|
846
|
-
throw new BadRequestError({
|
|
847
|
-
message: tpl(messages.badRequest),
|
|
848
|
-
context: 'Offers cannot be applied to gift subscriptions'
|
|
849
|
-
});
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
const data = await this._getSubscriptionCheckoutData(req.body);
|
|
853
|
-
|
|
854
854
|
response = await this._createGiftCheckoutSession({
|
|
855
855
|
...options,
|
|
856
|
-
|
|
857
|
-
|
|
856
|
+
tierId: req.body.tierId,
|
|
857
|
+
offerId: req.body.offerId,
|
|
858
|
+
cadence: req.body.cadence,
|
|
859
|
+
duration: req.body.duration,
|
|
858
860
|
successUrl: siteUrl,
|
|
859
861
|
cancelUrl: options.cancelUrl || siteUrl
|
|
860
862
|
});
|
|
@@ -67,8 +67,7 @@ module.exports = function MembersAPI({
|
|
|
67
67
|
MemberFeedback,
|
|
68
68
|
Automation,
|
|
69
69
|
WelcomeEmailAutomationRun,
|
|
70
|
-
AutomatedEmailRecipient
|
|
71
|
-
Gift
|
|
70
|
+
AutomatedEmailRecipient
|
|
72
71
|
},
|
|
73
72
|
tiersService,
|
|
74
73
|
stripeAPIService,
|
|
@@ -142,7 +141,7 @@ module.exports = function MembersAPI({
|
|
|
142
141
|
memberAttributionService,
|
|
143
142
|
MemberEmailChangeEvent,
|
|
144
143
|
AutomatedEmailRecipient,
|
|
145
|
-
|
|
144
|
+
giftSubscriptions: giftService
|
|
146
145
|
});
|
|
147
146
|
|
|
148
147
|
const nextPaymentCalculator = new NextPaymentCalculator();
|
|
@@ -192,8 +191,7 @@ module.exports = function MembersAPI({
|
|
|
192
191
|
Offer,
|
|
193
192
|
offersAPI,
|
|
194
193
|
stripeAPIService,
|
|
195
|
-
settingsCache
|
|
196
|
-
giftService
|
|
194
|
+
settingsCache
|
|
197
195
|
});
|
|
198
196
|
|
|
199
197
|
const memberController = new MemberController({
|
|
@@ -283,7 +281,10 @@ module.exports = function MembersAPI({
|
|
|
283
281
|
}
|
|
284
282
|
|
|
285
283
|
if (giftToken) {
|
|
286
|
-
await giftService.service.redeem(
|
|
284
|
+
await giftService.service.redeem({
|
|
285
|
+
token: giftToken,
|
|
286
|
+
memberId: member.id
|
|
287
|
+
});
|
|
287
288
|
}
|
|
288
289
|
|
|
289
290
|
await MemberLoginEvent.add({member_id: member.id});
|
|
@@ -316,7 +317,12 @@ module.exports = function MembersAPI({
|
|
|
316
317
|
{name, email, labels, newsletters, attribution, geolocation, status: 'gift'},
|
|
317
318
|
{transacting}
|
|
318
319
|
);
|
|
319
|
-
await giftService.service.redeem(
|
|
320
|
+
await giftService.service.redeem({
|
|
321
|
+
token: giftToken,
|
|
322
|
+
memberId: created.id,
|
|
323
|
+
transacting,
|
|
324
|
+
newMember: true
|
|
325
|
+
});
|
|
320
326
|
return created;
|
|
321
327
|
});
|
|
322
328
|
} else {
|
|
@@ -37,7 +37,7 @@ module.exports = class EventRepository {
|
|
|
37
37
|
urlService,
|
|
38
38
|
MemberEmailChangeEvent,
|
|
39
39
|
AutomatedEmailRecipient,
|
|
40
|
-
|
|
40
|
+
giftSubscriptions
|
|
41
41
|
}) {
|
|
42
42
|
this._DonationPaymentEvent = DonationPaymentEvent;
|
|
43
43
|
this._MemberSubscribeEvent = MemberSubscribeEvent;
|
|
@@ -57,7 +57,7 @@ module.exports = class EventRepository {
|
|
|
57
57
|
this._memberAttributionService = memberAttributionService;
|
|
58
58
|
this._MemberEmailChangeEvent = MemberEmailChangeEvent;
|
|
59
59
|
this._AutomatedEmailRecipient = AutomatedEmailRecipient;
|
|
60
|
-
this.
|
|
60
|
+
this._giftSubscriptions = giftSubscriptions;
|
|
61
61
|
this._knex = db.knex;
|
|
62
62
|
}
|
|
63
63
|
|
|
@@ -462,101 +462,11 @@ module.exports = class EventRepository {
|
|
|
462
462
|
}
|
|
463
463
|
|
|
464
464
|
async getGiftPurchaseEvents(options = {}, filter) {
|
|
465
|
-
options
|
|
466
|
-
...options,
|
|
467
|
-
withRelated: ['buyer', 'tier'],
|
|
468
|
-
filter: 'buyer_member_id:-null+custom:true',
|
|
469
|
-
useBasicCount: true,
|
|
470
|
-
mongoTransformer: chainTransformers(
|
|
471
|
-
// First set the filter manually
|
|
472
|
-
replaceCustomFilterTransformer(filter),
|
|
473
|
-
|
|
474
|
-
// Map the used keys in that filter
|
|
475
|
-
...mapKeys({
|
|
476
|
-
'data.created_at': 'purchased_at',
|
|
477
|
-
'data.member_id': 'buyer_member_id'
|
|
478
|
-
})
|
|
479
|
-
)
|
|
480
|
-
};
|
|
481
|
-
|
|
482
|
-
if (options.order) {
|
|
483
|
-
options.order = options.order.replace(/created_at/g, 'purchased_at');
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
const {data: models, meta} = await this._Gift.findPage(options);
|
|
487
|
-
|
|
488
|
-
const data = models.map((model) => {
|
|
489
|
-
const json = model.toJSON(options);
|
|
490
|
-
|
|
491
|
-
return {
|
|
492
|
-
type: 'gift_purchase_event',
|
|
493
|
-
data: {
|
|
494
|
-
id: json.id,
|
|
495
|
-
member: json.buyer || null,
|
|
496
|
-
member_id: json.buyer_member_id,
|
|
497
|
-
tier_name: json.tier?.name,
|
|
498
|
-
cadence: json.cadence,
|
|
499
|
-
duration: json.duration,
|
|
500
|
-
amount: json.amount,
|
|
501
|
-
currency: json.currency,
|
|
502
|
-
created_at: json.purchased_at
|
|
503
|
-
}
|
|
504
|
-
};
|
|
505
|
-
});
|
|
506
|
-
|
|
507
|
-
return {
|
|
508
|
-
data,
|
|
509
|
-
meta
|
|
510
|
-
};
|
|
465
|
+
return this._giftSubscriptions.service.browsePurchaseEvents(options, filter);
|
|
511
466
|
}
|
|
512
467
|
|
|
513
468
|
async getGiftRedemptionEvents(options = {}, filter) {
|
|
514
|
-
options
|
|
515
|
-
...options,
|
|
516
|
-
withRelated: ['redeemer', 'tier'],
|
|
517
|
-
filter: 'redeemer_member_id:-null+custom:true',
|
|
518
|
-
useBasicCount: true,
|
|
519
|
-
mongoTransformer: chainTransformers(
|
|
520
|
-
// First set the filter manually
|
|
521
|
-
replaceCustomFilterTransformer(filter),
|
|
522
|
-
|
|
523
|
-
// Map the used keys in that filter
|
|
524
|
-
...mapKeys({
|
|
525
|
-
'data.created_at': 'redeemed_at',
|
|
526
|
-
'data.member_id': 'redeemer_member_id'
|
|
527
|
-
})
|
|
528
|
-
)
|
|
529
|
-
};
|
|
530
|
-
|
|
531
|
-
if (options.order) {
|
|
532
|
-
options.order = options.order.replace(/created_at/g, 'redeemed_at');
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
const {data: models, meta} = await this._Gift.findPage(options);
|
|
536
|
-
|
|
537
|
-
const data = models.map((model) => {
|
|
538
|
-
const json = model.toJSON(options);
|
|
539
|
-
|
|
540
|
-
return {
|
|
541
|
-
type: 'gift_redemption_event',
|
|
542
|
-
data: {
|
|
543
|
-
id: json.id,
|
|
544
|
-
member: json.redeemer || null,
|
|
545
|
-
member_id: json.redeemer_member_id,
|
|
546
|
-
tier_name: json.tier?.name,
|
|
547
|
-
cadence: json.cadence,
|
|
548
|
-
duration: json.duration,
|
|
549
|
-
amount: json.amount,
|
|
550
|
-
currency: json.currency,
|
|
551
|
-
created_at: json.redeemed_at
|
|
552
|
-
}
|
|
553
|
-
};
|
|
554
|
-
});
|
|
555
|
-
|
|
556
|
-
return {
|
|
557
|
-
data,
|
|
558
|
-
meta
|
|
559
|
-
};
|
|
469
|
+
return this._giftSubscriptions.service.browseRedemptionEvents(options, filter);
|
|
560
470
|
}
|
|
561
471
|
|
|
562
472
|
async getGiftEndedEvents(options = {}, filter) {
|
|
@@ -34,7 +34,7 @@ const CUSTOM_FIELDS_EDITED_ACTION = 'custom_fields_edited';
|
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* @typedef {object} IGiftServiceWrapper
|
|
37
|
-
* @prop {{
|
|
37
|
+
* @prop {{getMemberPresentations: (memberIds: string[]) => Promise<Map<string, {cadence: 'month' | 'year', currency: string, amount: number}>>}} service
|
|
38
38
|
*/
|
|
39
39
|
|
|
40
40
|
module.exports = class MemberBREADService {
|
|
@@ -347,7 +347,7 @@ module.exports = class MemberBREADService {
|
|
|
347
347
|
* @private
|
|
348
348
|
* Fetches active redeemed gifts for any gift-status members in the input list.
|
|
349
349
|
* @param {import('bookshelf').Model[]} members - Bookshelf member models
|
|
350
|
-
* @returns {Promise<Map<string, {cadence: 'month' | 'year', currency: string, amount: number}>>} keyed by member.id →
|
|
350
|
+
* @returns {Promise<Map<string, {cadence: 'month' | 'year', currency: string, amount: number}>>} keyed by member.id → stable gift presentation
|
|
351
351
|
*/
|
|
352
352
|
async fetchActiveGiftsForMembers(members) {
|
|
353
353
|
const giftMemberIds = members
|
|
@@ -359,7 +359,7 @@ module.exports = class MemberBREADService {
|
|
|
359
359
|
}
|
|
360
360
|
|
|
361
361
|
try {
|
|
362
|
-
return await this.giftService.service.
|
|
362
|
+
return await this.giftService.service.getMemberPresentations(giftMemberIds);
|
|
363
363
|
} catch (e) {
|
|
364
364
|
logging.error(`Failed to load active gifts for members - ${giftMemberIds.join(', ')}.`);
|
|
365
365
|
logging.error(e);
|