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
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const logging = require('@tryghost/logging');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Stripe and Bookshelf adapter used only by the gifts module.
|
|
5
|
+
*
|
|
6
|
+
* The module interface deals in stable member/customer IDs. Stripe customer
|
|
7
|
+
* objects and Bookshelf models stay on this side of the seam.
|
|
8
|
+
*/
|
|
9
|
+
module.exports = class GiftCheckoutAdapter {
|
|
10
|
+
constructor({StripeCustomerModel, getStripeApi}) {
|
|
11
|
+
this.StripeCustomerModel = StripeCustomerModel;
|
|
12
|
+
this.getStripeApi = getStripeApi;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async getCustomerId({memberId, email, name}) {
|
|
16
|
+
if (!memberId) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const rows = await this.StripeCustomerModel.where({
|
|
21
|
+
member_id: memberId
|
|
22
|
+
}).query().select('customer_id');
|
|
23
|
+
|
|
24
|
+
for (const row of rows) {
|
|
25
|
+
try {
|
|
26
|
+
const customer = await this.getStripeApi().getCustomer(row.customer_id);
|
|
27
|
+
if (!customer.deleted) {
|
|
28
|
+
return customer.id;
|
|
29
|
+
}
|
|
30
|
+
} catch (err) {
|
|
31
|
+
logging.warn(err);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const customer = await this.getStripeApi().createCustomer({
|
|
36
|
+
email,
|
|
37
|
+
name
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
await this.StripeCustomerModel.add({
|
|
41
|
+
member_id: memberId,
|
|
42
|
+
customer_id: customer.id,
|
|
43
|
+
email: customer.email,
|
|
44
|
+
name: customer.name
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return customer.id;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async createSession(data) {
|
|
51
|
+
const {customerId, ...sessionData} = data;
|
|
52
|
+
const session = await this.getStripeApi().createGiftCheckoutSession({
|
|
53
|
+
...sessionData,
|
|
54
|
+
customer: customerId ? {id: customerId} : null
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return session.url;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.resolveGiftDuration = resolveGiftDuration;
|
|
7
|
+
exports.validateGiftCheckoutOffer = validateGiftCheckoutOffer;
|
|
8
|
+
const errors_1 = __importDefault(require("@tryghost/errors"));
|
|
9
|
+
// Mirrored by the Portal catalogue in apps/portal/src/utils/gift-subscriptions.ts
|
|
10
|
+
// until later customization work makes durations server-provided
|
|
11
|
+
const GIFT_DURATION_CATALOGUE = new Map([
|
|
12
|
+
[1, { cadence: 'month', billingDuration: 1, portalPlan: 'monthly', priceProperty: 'monthlyPrice', multiplier: 1 }],
|
|
13
|
+
[3, { cadence: 'month', billingDuration: 3, portalPlan: 'monthly', priceProperty: 'monthlyPrice', multiplier: 3 }],
|
|
14
|
+
[6, { cadence: 'month', billingDuration: 6, portalPlan: 'monthly', priceProperty: 'monthlyPrice', multiplier: 6 }],
|
|
15
|
+
[12, { cadence: 'year', billingDuration: 1, portalPlan: 'yearly', priceProperty: 'yearlyPrice', multiplier: 1 }]
|
|
16
|
+
]);
|
|
17
|
+
function invalidGiftOffer(context) {
|
|
18
|
+
return new errors_1.default.BadRequestError({
|
|
19
|
+
message: 'Bad Request.',
|
|
20
|
+
context
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function resolveGiftDuration({ duration, cadence }) {
|
|
24
|
+
let totalMonths = duration;
|
|
25
|
+
if (totalMonths === undefined) {
|
|
26
|
+
if (cadence === 'month') {
|
|
27
|
+
totalMonths = 1;
|
|
28
|
+
}
|
|
29
|
+
else if (cadence === 'year') {
|
|
30
|
+
totalMonths = 12;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
throw invalidGiftOffer('Expected a supported gift duration or legacy cadence');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (typeof totalMonths !== 'number' || !Number.isInteger(totalMonths)) {
|
|
37
|
+
throw invalidGiftOffer(`Unsupported gift duration "${totalMonths}"`);
|
|
38
|
+
}
|
|
39
|
+
const offer = GIFT_DURATION_CATALOGUE.get(totalMonths);
|
|
40
|
+
if (!offer) {
|
|
41
|
+
throw invalidGiftOffer(`Unsupported gift duration "${totalMonths}"`);
|
|
42
|
+
}
|
|
43
|
+
if (cadence !== undefined && cadence !== offer.cadence) {
|
|
44
|
+
throw invalidGiftOffer(`Gift duration "${totalMonths}" conflicts with cadence "${cadence}"`);
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
...offer,
|
|
48
|
+
totalMonths,
|
|
49
|
+
isCadenceOnly: duration === undefined
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function validateGiftCheckoutOffer({ tier, portalPlans, offer }) {
|
|
53
|
+
if (tier.status !== 'active' || tier.visibility !== 'public' || tier.type !== 'paid') {
|
|
54
|
+
throw invalidGiftOffer('The requested tier is not available for gift purchases');
|
|
55
|
+
}
|
|
56
|
+
// legacy cadence-only requests predate the Portal plan gate, keep them working
|
|
57
|
+
if (!offer.isCadenceOnly && (!Array.isArray(portalPlans) || !portalPlans.includes(offer.portalPlan))) {
|
|
58
|
+
throw invalidGiftOffer(`The ${offer.portalPlan} Portal plan is not available`);
|
|
59
|
+
}
|
|
60
|
+
const unitAmount = tier[offer.priceProperty];
|
|
61
|
+
if (typeof unitAmount !== 'number' || !Number.isSafeInteger(unitAmount) || unitAmount <= 0 || typeof tier.currency !== 'string' || !tier.currency) {
|
|
62
|
+
throw invalidGiftOffer('The requested tier does not have a valid gift price');
|
|
63
|
+
}
|
|
64
|
+
const amount = unitAmount * offer.multiplier;
|
|
65
|
+
if (!Number.isSafeInteger(amount)) {
|
|
66
|
+
throw invalidGiftOffer('The requested gift amount is invalid');
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
cadence: offer.cadence,
|
|
70
|
+
duration: offer.billingDuration,
|
|
71
|
+
totalMonths: offer.totalMonths,
|
|
72
|
+
amount
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import errors from '@tryghost/errors';
|
|
2
|
+
import type {GiftCadence} from './gift-schema';
|
|
3
|
+
|
|
4
|
+
export type {GiftCadence} from './gift-schema';
|
|
5
|
+
|
|
6
|
+
export interface GiftCheckoutTier {
|
|
7
|
+
status: string;
|
|
8
|
+
visibility: string;
|
|
9
|
+
type: string;
|
|
10
|
+
currency: string | null;
|
|
11
|
+
monthlyPrice: number | null;
|
|
12
|
+
yearlyPrice: number | null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface GiftDurationDefinition {
|
|
16
|
+
cadence: GiftCadence;
|
|
17
|
+
billingDuration: number;
|
|
18
|
+
portalPlan: 'monthly' | 'yearly';
|
|
19
|
+
priceProperty: 'monthlyPrice' | 'yearlyPrice';
|
|
20
|
+
multiplier: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ResolvedGiftDuration extends GiftDurationDefinition {
|
|
24
|
+
totalMonths: number;
|
|
25
|
+
isCadenceOnly: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface GiftCheckoutPlan {
|
|
29
|
+
cadence: GiftCadence;
|
|
30
|
+
duration: number;
|
|
31
|
+
totalMonths: number;
|
|
32
|
+
amount: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Mirrored by the Portal catalogue in apps/portal/src/utils/gift-subscriptions.ts
|
|
36
|
+
// until later customization work makes durations server-provided
|
|
37
|
+
const GIFT_DURATION_CATALOGUE = new Map<number, GiftDurationDefinition>([
|
|
38
|
+
[1, {cadence: 'month', billingDuration: 1, portalPlan: 'monthly', priceProperty: 'monthlyPrice', multiplier: 1}],
|
|
39
|
+
[3, {cadence: 'month', billingDuration: 3, portalPlan: 'monthly', priceProperty: 'monthlyPrice', multiplier: 3}],
|
|
40
|
+
[6, {cadence: 'month', billingDuration: 6, portalPlan: 'monthly', priceProperty: 'monthlyPrice', multiplier: 6}],
|
|
41
|
+
[12, {cadence: 'year', billingDuration: 1, portalPlan: 'yearly', priceProperty: 'yearlyPrice', multiplier: 1}]
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
function invalidGiftOffer(context: string) {
|
|
45
|
+
return new errors.BadRequestError({
|
|
46
|
+
message: 'Bad Request.',
|
|
47
|
+
context
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function resolveGiftDuration({
|
|
52
|
+
duration,
|
|
53
|
+
cadence
|
|
54
|
+
}: {
|
|
55
|
+
duration?: number;
|
|
56
|
+
cadence?: string;
|
|
57
|
+
}): ResolvedGiftDuration {
|
|
58
|
+
let totalMonths = duration;
|
|
59
|
+
|
|
60
|
+
if (totalMonths === undefined) {
|
|
61
|
+
if (cadence === 'month') {
|
|
62
|
+
totalMonths = 1;
|
|
63
|
+
} else if (cadence === 'year') {
|
|
64
|
+
totalMonths = 12;
|
|
65
|
+
} else {
|
|
66
|
+
throw invalidGiftOffer('Expected a supported gift duration or legacy cadence');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (typeof totalMonths !== 'number' || !Number.isInteger(totalMonths)) {
|
|
71
|
+
throw invalidGiftOffer(`Unsupported gift duration "${totalMonths}"`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const offer = GIFT_DURATION_CATALOGUE.get(totalMonths);
|
|
75
|
+
|
|
76
|
+
if (!offer) {
|
|
77
|
+
throw invalidGiftOffer(`Unsupported gift duration "${totalMonths}"`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (cadence !== undefined && cadence !== offer.cadence) {
|
|
81
|
+
throw invalidGiftOffer(`Gift duration "${totalMonths}" conflicts with cadence "${cadence}"`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
...offer,
|
|
86
|
+
totalMonths,
|
|
87
|
+
isCadenceOnly: duration === undefined
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function validateGiftCheckoutOffer({
|
|
92
|
+
tier,
|
|
93
|
+
portalPlans,
|
|
94
|
+
offer
|
|
95
|
+
}: {
|
|
96
|
+
tier: GiftCheckoutTier;
|
|
97
|
+
portalPlans: unknown;
|
|
98
|
+
offer: ResolvedGiftDuration;
|
|
99
|
+
}): GiftCheckoutPlan {
|
|
100
|
+
if (tier.status !== 'active' || tier.visibility !== 'public' || tier.type !== 'paid') {
|
|
101
|
+
throw invalidGiftOffer('The requested tier is not available for gift purchases');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// legacy cadence-only requests predate the Portal plan gate, keep them working
|
|
105
|
+
if (!offer.isCadenceOnly && (!Array.isArray(portalPlans) || !portalPlans.includes(offer.portalPlan))) {
|
|
106
|
+
throw invalidGiftOffer(`The ${offer.portalPlan} Portal plan is not available`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const unitAmount = tier[offer.priceProperty];
|
|
110
|
+
|
|
111
|
+
if (typeof unitAmount !== 'number' || !Number.isSafeInteger(unitAmount) || unitAmount <= 0 || typeof tier.currency !== 'string' || !tier.currency) {
|
|
112
|
+
throw invalidGiftOffer('The requested tier does not have a valid gift price');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const amount = unitAmount * offer.multiplier;
|
|
116
|
+
|
|
117
|
+
if (!Number.isSafeInteger(amount)) {
|
|
118
|
+
throw invalidGiftOffer('The requested gift amount is invalid');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
cadence: offer.cadence,
|
|
123
|
+
duration: offer.billingDuration,
|
|
124
|
+
totalMonths: offer.totalMonths,
|
|
125
|
+
amount
|
|
126
|
+
};
|
|
127
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.giftCodec = void 0;
|
|
4
|
+
exports.decodeGiftRow = decodeGiftRow;
|
|
5
|
+
exports.encodeGift = encodeGift;
|
|
6
|
+
const zod_1 = require("zod");
|
|
7
|
+
const gift_1 = require("./gift");
|
|
8
|
+
const gift_schema_1 = require("./gift-schema");
|
|
9
|
+
exports.giftCodec = zod_1.z.codec(gift_schema_1.DbGift, zod_1.z.instanceof(gift_1.Gift), {
|
|
10
|
+
decode: row => new gift_1.Gift({
|
|
11
|
+
token: row.token,
|
|
12
|
+
buyerEmail: row.buyer_email,
|
|
13
|
+
buyerMemberId: row.buyer_member_id,
|
|
14
|
+
redeemerMemberId: row.redeemer_member_id,
|
|
15
|
+
tierId: row.tier_id,
|
|
16
|
+
cadence: row.cadence,
|
|
17
|
+
duration: row.duration,
|
|
18
|
+
currency: row.currency,
|
|
19
|
+
amount: row.amount,
|
|
20
|
+
stripeCheckoutSessionId: row.stripe_checkout_session_id,
|
|
21
|
+
stripePaymentIntentId: row.stripe_payment_intent_id,
|
|
22
|
+
consumesAt: row.consumes_at,
|
|
23
|
+
expiresAt: row.expires_at,
|
|
24
|
+
status: row.status,
|
|
25
|
+
purchasedAt: row.purchased_at,
|
|
26
|
+
redeemedAt: row.redeemed_at,
|
|
27
|
+
consumedAt: row.consumed_at,
|
|
28
|
+
expiredAt: row.expired_at,
|
|
29
|
+
refundedAt: row.refunded_at,
|
|
30
|
+
consumesSoonReminderSentAt: row.consumes_soon_reminder_sent_at
|
|
31
|
+
}),
|
|
32
|
+
encode: gift => ({
|
|
33
|
+
token: gift.token,
|
|
34
|
+
buyer_email: gift.buyerEmail,
|
|
35
|
+
buyer_member_id: gift.buyerMemberId,
|
|
36
|
+
redeemer_member_id: gift.redeemerMemberId,
|
|
37
|
+
tier_id: gift.tierId,
|
|
38
|
+
cadence: gift.cadence,
|
|
39
|
+
duration: gift.duration,
|
|
40
|
+
currency: gift.currency,
|
|
41
|
+
amount: gift.amount,
|
|
42
|
+
stripe_checkout_session_id: gift.stripeCheckoutSessionId,
|
|
43
|
+
stripe_payment_intent_id: gift.stripePaymentIntentId,
|
|
44
|
+
consumes_at: gift.consumesAt,
|
|
45
|
+
expires_at: gift.expiresAt,
|
|
46
|
+
status: gift.status,
|
|
47
|
+
purchased_at: gift.purchasedAt,
|
|
48
|
+
redeemed_at: gift.redeemedAt,
|
|
49
|
+
consumed_at: gift.consumedAt,
|
|
50
|
+
expired_at: gift.expiredAt,
|
|
51
|
+
refunded_at: gift.refundedAt,
|
|
52
|
+
consumes_soon_reminder_sent_at: gift.consumesSoonReminderSentAt
|
|
53
|
+
})
|
|
54
|
+
});
|
|
55
|
+
function decodeGiftRow(input) {
|
|
56
|
+
return exports.giftCodec.parse(input);
|
|
57
|
+
}
|
|
58
|
+
function encodeGift(gift) {
|
|
59
|
+
const row = zod_1.z.encode(exports.giftCodec, gift);
|
|
60
|
+
// DbDate encodes to Date at runtime, but z.encode types the row as DbGift input,
|
|
61
|
+
// whose date fields also accept strings and numbers. Parse to narrow it to GiftRow.
|
|
62
|
+
return gift_schema_1.DbGift.parse(row);
|
|
63
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import {z} from 'zod';
|
|
2
|
+
import {Gift} from './gift';
|
|
3
|
+
import {DbGift} from './gift-schema';
|
|
4
|
+
|
|
5
|
+
export const giftCodec = z.codec(DbGift, z.instanceof(Gift), {
|
|
6
|
+
decode: row => new Gift({
|
|
7
|
+
token: row.token,
|
|
8
|
+
buyerEmail: row.buyer_email,
|
|
9
|
+
buyerMemberId: row.buyer_member_id,
|
|
10
|
+
redeemerMemberId: row.redeemer_member_id,
|
|
11
|
+
tierId: row.tier_id,
|
|
12
|
+
cadence: row.cadence,
|
|
13
|
+
duration: row.duration,
|
|
14
|
+
currency: row.currency,
|
|
15
|
+
amount: row.amount,
|
|
16
|
+
stripeCheckoutSessionId: row.stripe_checkout_session_id,
|
|
17
|
+
stripePaymentIntentId: row.stripe_payment_intent_id,
|
|
18
|
+
consumesAt: row.consumes_at,
|
|
19
|
+
expiresAt: row.expires_at,
|
|
20
|
+
status: row.status,
|
|
21
|
+
purchasedAt: row.purchased_at,
|
|
22
|
+
redeemedAt: row.redeemed_at,
|
|
23
|
+
consumedAt: row.consumed_at,
|
|
24
|
+
expiredAt: row.expired_at,
|
|
25
|
+
refundedAt: row.refunded_at,
|
|
26
|
+
consumesSoonReminderSentAt: row.consumes_soon_reminder_sent_at
|
|
27
|
+
}),
|
|
28
|
+
encode: gift => ({
|
|
29
|
+
token: gift.token,
|
|
30
|
+
buyer_email: gift.buyerEmail,
|
|
31
|
+
buyer_member_id: gift.buyerMemberId,
|
|
32
|
+
redeemer_member_id: gift.redeemerMemberId,
|
|
33
|
+
tier_id: gift.tierId,
|
|
34
|
+
cadence: gift.cadence,
|
|
35
|
+
duration: gift.duration,
|
|
36
|
+
currency: gift.currency,
|
|
37
|
+
amount: gift.amount,
|
|
38
|
+
stripe_checkout_session_id: gift.stripeCheckoutSessionId,
|
|
39
|
+
stripe_payment_intent_id: gift.stripePaymentIntentId,
|
|
40
|
+
consumes_at: gift.consumesAt,
|
|
41
|
+
expires_at: gift.expiresAt,
|
|
42
|
+
status: gift.status,
|
|
43
|
+
purchased_at: gift.purchasedAt,
|
|
44
|
+
redeemed_at: gift.redeemedAt,
|
|
45
|
+
consumed_at: gift.consumedAt,
|
|
46
|
+
expired_at: gift.expiredAt,
|
|
47
|
+
refunded_at: gift.refundedAt,
|
|
48
|
+
consumes_soon_reminder_sent_at: gift.consumesSoonReminderSentAt
|
|
49
|
+
})
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
export function decodeGiftRow(input: unknown): Gift {
|
|
53
|
+
return giftCodec.parse(input);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function encodeGift(gift: Gift): z.output<typeof DbGift> {
|
|
57
|
+
const row = z.encode(giftCodec, gift);
|
|
58
|
+
|
|
59
|
+
// DbDate encodes to Date at runtime, but z.encode types the row as DbGift input,
|
|
60
|
+
// whose date fields also accept strings and numbers. Parse to narrow it to GiftRow.
|
|
61
|
+
return DbGift.parse(row);
|
|
62
|
+
}
|
|
@@ -7,16 +7,13 @@ exports.GiftController = void 0;
|
|
|
7
7
|
const errors_1 = __importDefault(require("@tryghost/errors"));
|
|
8
8
|
class GiftController {
|
|
9
9
|
service;
|
|
10
|
-
|
|
11
|
-
constructor({ service, tiersService }) {
|
|
10
|
+
constructor({ service }) {
|
|
12
11
|
this.service = service;
|
|
13
|
-
this.tiersService = tiersService;
|
|
14
12
|
}
|
|
15
13
|
async getRedeemable(frame) {
|
|
16
14
|
const token = frame.data.token;
|
|
17
15
|
const memberStatus = frame.options?.context?.member?.status ?? null;
|
|
18
|
-
|
|
19
|
-
return this.serializeGift(gift);
|
|
16
|
+
return this.service.getRedeemable({ token, memberStatus });
|
|
20
17
|
}
|
|
21
18
|
async redeem(frame) {
|
|
22
19
|
const token = frame.data.token;
|
|
@@ -26,32 +23,10 @@ class GiftController {
|
|
|
26
23
|
message: 'Member authentication required.'
|
|
27
24
|
});
|
|
28
25
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const tier = await this.tiersService.api.read(gift.tierId);
|
|
34
|
-
if (!tier) {
|
|
35
|
-
throw new errors_1.default.InternalServerError({
|
|
36
|
-
message: `Tier ${gift.tierId} not found for gift: ${gift.token}`
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
const tierJSON = tier.toJSON();
|
|
40
|
-
return {
|
|
41
|
-
token: gift.token,
|
|
42
|
-
cadence: gift.cadence,
|
|
43
|
-
duration: gift.duration,
|
|
44
|
-
currency: gift.currency,
|
|
45
|
-
amount: gift.amount,
|
|
46
|
-
expires_at: gift.expiresAt,
|
|
47
|
-
consumes_at: gift.consumesAt,
|
|
48
|
-
tier: {
|
|
49
|
-
id: tierJSON.id,
|
|
50
|
-
name: tierJSON.name,
|
|
51
|
-
description: tierJSON.description,
|
|
52
|
-
benefits: tierJSON.benefits
|
|
53
|
-
}
|
|
54
|
-
};
|
|
26
|
+
return this.service.redeem({
|
|
27
|
+
token,
|
|
28
|
+
memberId: member.id
|
|
29
|
+
});
|
|
55
30
|
}
|
|
56
31
|
}
|
|
57
32
|
exports.GiftController = GiftController;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import errors from '@tryghost/errors';
|
|
2
|
-
import {Gift, type GiftCadence} from './gift';
|
|
3
2
|
import {GiftService} from './gift-service';
|
|
4
3
|
|
|
5
4
|
type Frame = {
|
|
@@ -16,57 +15,22 @@ type Frame = {
|
|
|
16
15
|
};
|
|
17
16
|
};
|
|
18
17
|
|
|
19
|
-
type SerializedTier = {
|
|
20
|
-
id: string;
|
|
21
|
-
name: string;
|
|
22
|
-
description: string | null;
|
|
23
|
-
benefits: string[];
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
type Tier = {
|
|
27
|
-
name: string;
|
|
28
|
-
toJSON(): SerializedTier;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
type GiftDTO = {
|
|
32
|
-
token: string;
|
|
33
|
-
cadence: GiftCadence;
|
|
34
|
-
duration: number;
|
|
35
|
-
currency: string;
|
|
36
|
-
amount: number;
|
|
37
|
-
expires_at: Date;
|
|
38
|
-
consumes_at: Date | null;
|
|
39
|
-
tier: SerializedTier;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
type TiersService = {
|
|
43
|
-
api: {
|
|
44
|
-
read(idString: string): Promise<Tier | null>;
|
|
45
|
-
};
|
|
46
|
-
};
|
|
47
|
-
|
|
48
18
|
export class GiftController {
|
|
49
19
|
private readonly service: GiftService;
|
|
50
|
-
private readonly tiersService: TiersService;
|
|
51
20
|
|
|
52
21
|
constructor({
|
|
53
|
-
service
|
|
54
|
-
tiersService
|
|
22
|
+
service
|
|
55
23
|
}: {
|
|
56
24
|
service: GiftService;
|
|
57
|
-
tiersService: TiersService;
|
|
58
25
|
}) {
|
|
59
26
|
this.service = service;
|
|
60
|
-
this.tiersService = tiersService;
|
|
61
27
|
}
|
|
62
28
|
|
|
63
29
|
async getRedeemable(frame: Frame) {
|
|
64
30
|
const token = frame.data.token;
|
|
65
31
|
const memberStatus = frame.options?.context?.member?.status ?? null;
|
|
66
32
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return this.serializeGift(gift);
|
|
33
|
+
return this.service.getRedeemable({token, memberStatus});
|
|
70
34
|
}
|
|
71
35
|
|
|
72
36
|
async redeem(frame: Frame) {
|
|
@@ -79,36 +43,9 @@ export class GiftController {
|
|
|
79
43
|
});
|
|
80
44
|
}
|
|
81
45
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
private async serializeGift(gift: Gift): Promise<GiftDTO> {
|
|
88
|
-
const tier = await this.tiersService.api.read(gift.tierId);
|
|
89
|
-
|
|
90
|
-
if (!tier) {
|
|
91
|
-
throw new errors.InternalServerError({
|
|
92
|
-
message: `Tier ${gift.tierId} not found for gift: ${gift.token}`
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const tierJSON = tier.toJSON();
|
|
97
|
-
|
|
98
|
-
return {
|
|
99
|
-
token: gift.token,
|
|
100
|
-
cadence: gift.cadence,
|
|
101
|
-
duration: gift.duration,
|
|
102
|
-
currency: gift.currency,
|
|
103
|
-
amount: gift.amount,
|
|
104
|
-
expires_at: gift.expiresAt,
|
|
105
|
-
consumes_at: gift.consumesAt,
|
|
106
|
-
tier: {
|
|
107
|
-
id: tierJSON.id,
|
|
108
|
-
name: tierJSON.name,
|
|
109
|
-
description: tierJSON.description,
|
|
110
|
-
benefits: tierJSON.benefits
|
|
111
|
-
}
|
|
112
|
-
};
|
|
46
|
+
return this.service.redeem({
|
|
47
|
+
token,
|
|
48
|
+
memberId: member.id
|
|
49
|
+
});
|
|
113
50
|
}
|
|
114
51
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {GiftEmailRenderer, Translate} from './gift-email-renderer';
|
|
2
|
+
import type {GiftCadence} from './gift-schema';
|
|
2
3
|
|
|
3
4
|
const DEFAULT_DATE_LOCALE = 'en-gb';
|
|
4
5
|
|
|
@@ -29,7 +30,7 @@ interface PurchaseConfirmationData {
|
|
|
29
30
|
buyerEmail: string;
|
|
30
31
|
token: string;
|
|
31
32
|
tierName: string;
|
|
32
|
-
cadence:
|
|
33
|
+
cadence: GiftCadence;
|
|
33
34
|
duration: number;
|
|
34
35
|
expiresAt: Date;
|
|
35
36
|
}
|
|
@@ -69,7 +70,7 @@ export class GiftEmailService {
|
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
private getCadenceLabel(cadence:
|
|
73
|
+
private getCadenceLabel(cadence: GiftCadence, duration: number): string {
|
|
73
74
|
if (duration === 1) {
|
|
74
75
|
return cadence === 'year' ? this.t('one-year') : this.t('one-month');
|
|
75
76
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DbGift = exports.GiftStatusSchema = exports.GiftCadenceSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const db_date_1 = require("../../lib/db-date");
|
|
6
|
+
exports.GiftCadenceSchema = zod_1.z.enum(['month', 'year']);
|
|
7
|
+
exports.GiftStatusSchema = zod_1.z.enum(['purchased', 'redeemed', 'consumed', 'expired', 'refunded']);
|
|
8
|
+
/**
|
|
9
|
+
* The persisted gift row. Bookshelf remains the persistence implementation, but
|
|
10
|
+
* values read through it are still runtime data and are validated before they
|
|
11
|
+
* become trusted gift-domain values.
|
|
12
|
+
*/
|
|
13
|
+
exports.DbGift = zod_1.z.object({
|
|
14
|
+
token: zod_1.z.string(),
|
|
15
|
+
buyer_email: zod_1.z.string(),
|
|
16
|
+
buyer_member_id: zod_1.z.string().nullable(),
|
|
17
|
+
redeemer_member_id: zod_1.z.string().nullable(),
|
|
18
|
+
tier_id: zod_1.z.string(),
|
|
19
|
+
cadence: exports.GiftCadenceSchema,
|
|
20
|
+
duration: zod_1.z.number().int().nonnegative(),
|
|
21
|
+
currency: zod_1.z.string(),
|
|
22
|
+
amount: zod_1.z.number().int().nonnegative(),
|
|
23
|
+
stripe_checkout_session_id: zod_1.z.string(),
|
|
24
|
+
stripe_payment_intent_id: zod_1.z.string(),
|
|
25
|
+
consumes_at: db_date_1.DbDate.nullable(),
|
|
26
|
+
expires_at: db_date_1.DbDate,
|
|
27
|
+
status: exports.GiftStatusSchema,
|
|
28
|
+
purchased_at: db_date_1.DbDate,
|
|
29
|
+
redeemed_at: db_date_1.DbDate.nullable(),
|
|
30
|
+
consumed_at: db_date_1.DbDate.nullable(),
|
|
31
|
+
expired_at: db_date_1.DbDate.nullable(),
|
|
32
|
+
refunded_at: db_date_1.DbDate.nullable(),
|
|
33
|
+
consumes_soon_reminder_sent_at: db_date_1.DbDate.nullable().default(null)
|
|
34
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {z} from 'zod';
|
|
2
|
+
import {DbDate} from '../../lib/db-date';
|
|
3
|
+
import type {CamelKeys} from '../../lib/case-keys';
|
|
4
|
+
|
|
5
|
+
export const GiftCadenceSchema = z.enum(['month', 'year']);
|
|
6
|
+
export const GiftStatusSchema = z.enum(['purchased', 'redeemed', 'consumed', 'expired', 'refunded']);
|
|
7
|
+
export type GiftCadence = z.infer<typeof GiftCadenceSchema>;
|
|
8
|
+
export type GiftStatus = z.infer<typeof GiftStatusSchema>;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The persisted gift row. Bookshelf remains the persistence implementation, but
|
|
12
|
+
* values read through it are still runtime data and are validated before they
|
|
13
|
+
* become trusted gift-domain values.
|
|
14
|
+
*/
|
|
15
|
+
export const DbGift = z.object({
|
|
16
|
+
token: z.string(),
|
|
17
|
+
buyer_email: z.string(),
|
|
18
|
+
buyer_member_id: z.string().nullable(),
|
|
19
|
+
redeemer_member_id: z.string().nullable(),
|
|
20
|
+
tier_id: z.string(),
|
|
21
|
+
cadence: GiftCadenceSchema,
|
|
22
|
+
duration: z.number().int().nonnegative(),
|
|
23
|
+
currency: z.string(),
|
|
24
|
+
amount: z.number().int().nonnegative(),
|
|
25
|
+
stripe_checkout_session_id: z.string(),
|
|
26
|
+
stripe_payment_intent_id: z.string(),
|
|
27
|
+
consumes_at: DbDate.nullable(),
|
|
28
|
+
expires_at: DbDate,
|
|
29
|
+
status: GiftStatusSchema,
|
|
30
|
+
purchased_at: DbDate,
|
|
31
|
+
redeemed_at: DbDate.nullable(),
|
|
32
|
+
consumed_at: DbDate.nullable(),
|
|
33
|
+
expired_at: DbDate.nullable(),
|
|
34
|
+
refunded_at: DbDate.nullable(),
|
|
35
|
+
consumes_soon_reminder_sent_at: DbDate.nullable().default(null)
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export type GiftRow = z.output<typeof DbGift>;
|
|
39
|
+
export type GiftData = CamelKeys<GiftRow>;
|