ghost 6.58.0 → 6.59.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-adapter-base-scheduling-0.2.2.tgz +0 -0
- package/components/tryghost-adapter-base-sso-0.1.2.tgz +0 -0
- package/components/tryghost-i18n-0.0.0.tgz +0 -0
- package/components/tryghost-kg-default-nodes-2.2.0.tgz +0 -0
- package/components/tryghost-kg-html-to-lexical-1.4.0.tgz +0 -0
- package/components/tryghost-kg-lexical-html-renderer-1.5.0.tgz +0 -0
- package/content/themes/casper/.github/workflows/test.yml +11 -1
- package/content/themes/casper/AGENTS.md +25 -0
- package/content/themes/casper/assets/built/screen.css +1 -1
- package/content/themes/casper/assets/css/screen.css +15 -1
- package/content/themes/casper/gulpfile.js +3 -0
- package/content/themes/casper/package.json +8 -8
- package/content/themes/source/.gitattributes +5 -0
- package/content/themes/source/.github/workflows/test.yml +39 -0
- package/content/themes/source/AGENTS.md +25 -0
- package/content/themes/source/README.md +33 -4
- package/content/themes/source/assets/built/screen.css +1 -1
- package/content/themes/source/assets/css/screen.css +15 -0
- package/content/themes/source/gulpfile.js +6 -3
- package/content/themes/source/package.json +13 -10
- package/content/themes/source/pnpm-workspace.yaml +3 -0
- package/core/boot.js +11 -4
- package/core/built/admin/assets/Combination-BCm9FL4o.js +41 -0
- package/core/built/admin/assets/about-BdSCpjXG.js +1 -0
- package/core/built/admin/assets/account-migration-BpKAqo8k.js +1 -0
- package/core/built/admin/assets/activitypub/{account-migration-BpDeIF3G.js → account-migration-DVnYS1XG.js} +8 -8
- package/core/built/admin/assets/activitypub/activitypub.js +2 -2
- package/core/built/admin/assets/activitypub/{bluesky-sharing-aPMtrS3L.js → bluesky-sharing-DLVeE9ii.js} +31 -31
- package/core/built/admin/assets/activitypub/{content-formatters-BORfuA9G.js → content-formatters-DCsVic-t.js} +2 -2
- package/core/built/admin/assets/activitypub/{dist-ByFmDSNP2.js → dist-ByFmDSNP.js} +1 -1
- package/core/built/admin/assets/activitypub/{domain-CPdiB27_.js → domain-rrvDLfnK.js} +8 -8
- package/core/built/admin/assets/activitypub/{edit-profile-BY2PawIe.js → edit-profile-VT-piVxq.js} +23 -23
- package/core/built/admin/assets/activitypub/{explore-Sp9bgAwq.js → explore-DZg3m48P.js} +14 -14
- package/core/built/admin/assets/activitypub/{feed-BjtF4ohL.js → feed-BJqQvDMI.js} +41 -41
- package/core/built/admin/assets/activitypub/{field-BDOYVTg6.js → field-BezS4-1J.js} +2 -2
- package/core/built/admin/assets/activitypub/inbox-Uf0GVrI_.js +1284 -0
- package/core/built/admin/assets/activitypub/{label-1jhN-qhD.js → label-CZ6if4zK.js} +2 -2
- package/core/built/admin/assets/activitypub/layout-DNuYT4Gu.js +1 -0
- package/core/built/admin/assets/activitypub/moderation-CP6mDVky.js +340 -0
- package/core/built/admin/assets/activitypub/{note-nr_m_5yb.js → note-BlFkye2g.js} +16 -16
- package/core/built/admin/assets/activitypub/{notifications-BdcyivbT.js → notifications-GGTrGEdu.js} +163 -126
- package/core/built/admin/assets/activitypub/{preferences-CNqy1x9C.js → preferences-CjZXrZqr.js} +1550 -1551
- package/core/built/admin/assets/activitypub/{profile-DlY4rhbO.js → profile-CcXYxydm.js} +248 -248
- package/core/built/admin/assets/activitypub/{routes-CEHdEZzS.js → routes-nHCKlZl-.js} +1971 -1747
- package/core/built/admin/assets/activitypub/settings-pDzybcb3.js +315 -0
- package/core/built/admin/assets/activitypub/{site-CtBUCPBz.js → site-BZF0F8LN.js} +101 -101
- package/core/built/admin/assets/activitypub/{step-1-D9jGWyAy.js → step-1-C-Ns1Tfg.js} +3 -3
- package/core/built/admin/assets/activitypub/{step-3-KpUqpGWi.js → step-3-rDMNGmLg.js} +8 -8
- package/core/built/admin/assets/activitypub/{tabs-DvlsxTCI.js → tabs-CDjh3_Jp.js} +198 -198
- package/core/built/admin/assets/activitypub/{topic-filter-CIN1E2jg.js → topic-filter-CmTc8hpN.js} +2 -2
- package/core/built/admin/assets/activitypub/{use-activity-pub-queries-CATFd6YV.js → use-activity-pub-queries-DUDEdSdR.js} +286 -243
- package/core/built/admin/assets/activitypub/{use-reply-chain-data-DJ0vhA8V.js → use-reply-chain-data-TunGtmJl.js} +14 -14
- package/core/built/admin/assets/add-integration-modal-CXRQi1x0.js +1 -0
- package/core/built/admin/assets/add-newsletter-modal-t447KRYz.js +1 -0
- package/core/built/admin/assets/add-recommendation-modal-D882MdnR.js +1 -0
- package/core/built/admin/assets/{analytics-BC2oR8ay.js → analytics-Dj-WlzPf.js} +1 -1
- package/core/built/admin/assets/announcement-bar-modal-CwHKKw-a.js +1 -0
- package/core/built/admin/assets/api-keys-Boy5pLbD.js +1 -0
- package/core/built/admin/assets/audience-Cc40y7zQ.js +1 -0
- package/core/built/admin/assets/automations-DNLK5Udr.js +1 -0
- package/core/built/admin/assets/bluesky-sharing-Cm4aFaOl.js +1 -0
- package/core/built/admin/assets/breadcrumb-CcFr_ltt.js +1 -0
- package/core/built/admin/assets/button-BshWPHvn.js +1 -0
- package/core/built/admin/assets/chart-helpers-BsbzoWhK.js +1 -0
- package/core/built/admin/assets/{checkbox-LmClhqcS.js → checkbox-1G6KfK2q.js} +1 -1
- package/core/built/admin/assets/chunk-KS7C4IRE-DCsL4i4D.js +4 -0
- package/core/built/admin/assets/{chunk.408.1a258af360eb8d905508.js → chunk.301.5673a0caf7c49bb00212.js} +3 -3
- package/core/built/admin/assets/{chunk.524.4c693c3a00ac91df34fa.js → chunk.524.86db0d945468c852374b.js} +4 -4
- package/core/built/admin/assets/{chunk.582.20b600e329e58d12d7c2.js → chunk.582.c2f6216d3c4af303e236.js} +6 -6
- package/core/built/admin/assets/{code-editor-view-DqTOUnbM.js → code-editor-view-BVjtWyst.js} +1 -1
- package/core/built/admin/assets/{code-editor-view-bA0lfxUL.js → code-editor-view-y_zVfmCa.js} +1 -1
- package/core/built/admin/assets/color-picker-XEMPgNJE.js +3 -0
- package/core/built/admin/assets/{color-picker-field-Cv9GOJQZ.js → color-picker-field-BhplXcy5.js} +1 -1
- package/core/built/admin/assets/combobox-CRMJrvcQ.js +1 -0
- package/core/built/admin/assets/command-151T2eRx.js +1 -0
- package/core/built/admin/assets/comments-Bjbl9toF.js +1 -0
- package/core/built/admin/assets/confirmation-context-QmFxSjWy.js +1 -0
- package/core/built/admin/assets/{content-api-modal-BOFWRUkC.js → content-api-modal-CpeABXne.js} +1 -1
- package/core/built/admin/assets/{copy-field-8biQvNMD.js → copy-field-CglgRZyG.js} +1 -1
- package/core/built/admin/assets/custom-field-type-option-DSyJw4T4.js +1 -0
- package/core/built/admin/assets/custom-integration-modal-DWB9bRVp.js +1 -0
- package/core/built/admin/assets/date-range-select-kaKtab5O.js +1 -0
- package/core/built/admin/assets/design-and-theme-modal-86s2AaZ-.js +23 -0
- package/core/built/admin/assets/dirty-confirm-dialog-CTFdXXy7.js +1 -0
- package/core/built/admin/assets/disabled-sources-indicator-BFyCxUOx.js +1 -0
- package/core/built/admin/assets/dist-C1-Oy3Db.js +1 -0
- package/core/built/admin/assets/dist-CJJqYPep.js +1 -0
- package/core/built/admin/assets/dist-CNlMj7ML.js +1 -0
- package/core/built/admin/assets/dist-Kll2twSG.js +1 -0
- package/core/built/admin/assets/domain-BOoMvBG3.js +1 -0
- package/core/built/admin/assets/{dropzone-B21Entqz.js → dropzone-B6KWzsrb.js} +2 -2
- package/core/built/admin/assets/edit-profile-BagrYOrN.js +1 -0
- package/core/built/admin/assets/editor-AaJjqbpk.js +8 -0
- package/core/built/admin/assets/embed-signup-form-modal-B8L95KwR.js +8 -0
- package/core/built/admin/assets/explore-xbykZTlO.js +1 -0
- package/core/built/admin/assets/feed-BAHSipWv.js +1 -0
- package/core/built/admin/assets/{field-BQqLTgHM.js → field-kJYZ2T3C.js} +1 -1
- package/core/built/admin/assets/{filter-sources-FgqoUIL7.js → filter-sources-QeqFGNRB.js} +1 -1
- package/core/built/admin/assets/{filters-D0uIMD8n.js → filters-By5KQeV6.js} +1 -1
- package/core/built/admin/assets/filters-DtYpAkDd.js +1 -0
- package/core/built/admin/assets/first-promoter-modal-8A02oyjA.js +1 -0
- package/core/built/admin/assets/{ghost-ba408063a5922b458b4a8f216cf23251.js → ghost-f110822e67868c9fdf9a6c23754b2559.js} +9 -8
- package/core/built/admin/assets/gift-link-modal-DbYCyuhF.js +1 -0
- package/core/built/admin/assets/global-data-context-gAMDH9u1.js +1 -0
- package/core/built/admin/assets/growth-BSOxJGey.js +3 -0
- package/core/built/admin/assets/growth-BZTjURq5.js +1 -0
- package/core/built/admin/assets/history-modal-CCgWtlZE.js +1 -0
- package/core/built/admin/assets/html-field-BQC7XyLA.js +1 -0
- package/core/built/admin/assets/iframe-buffering-DnOlWTD4.js +1 -0
- package/core/built/admin/assets/{images-3cQgwfTD.js → images-D5UKr2SG.js} +1 -1
- package/core/built/admin/assets/{inbox-Dz_JblyJ.js → inbox-DvN15_YH.js} +45 -39
- package/core/built/admin/assets/index-BJz9Zlsc.js +67 -0
- package/core/built/admin/assets/index-Dg8cdR8E.css +1 -0
- package/core/built/admin/assets/{input-group-DpogNUIt.js → input-group-BPAxETpQ.js} +1 -1
- package/core/built/admin/assets/invite-user-modal-jcUaaNkW.js +1 -0
- package/core/built/admin/assets/{koenig-lexical-2KuqRwZI.js → koenig-lexical-CfXTYeli.js} +1 -1
- package/core/built/admin/assets/koenig-loader-DktFM3Uh.js +1 -0
- package/core/built/admin/assets/{kpi-card-Ck3tT2Wg.js → kpi-card-fUHZlzBX.js} +1 -1
- package/core/built/admin/assets/{kpi-card-DDIxptaS.js → kpi-card-o0urVtNL.js} +1 -1
- package/core/built/admin/assets/kpi-helpers-2FTF4a3-.js +1 -0
- package/core/built/admin/assets/kpi-tabs-B8-_k2dQ.js +1 -0
- package/core/built/admin/assets/{label-DodDsMzn.js → label-CiRS3i_f.js} +1 -1
- package/core/built/admin/assets/label-picker-ao7tuPaK.js +1 -0
- package/core/built/admin/assets/layout-DC6QGDaw.js +1 -0
- package/core/built/admin/assets/member-custom-fields-BW756-EV.js +1 -0
- package/core/built/admin/assets/member-detail-CGGgUTSl.js +1 -0
- package/core/built/admin/assets/members-ZcPthwG0.js +11 -0
- package/core/built/admin/assets/moderation-24bElWs3.js +1 -0
- package/core/built/admin/assets/multi-select-combobox-CZPrgz-Z.js +1 -0
- package/core/built/admin/assets/navigation-modal-BiTP4U_J.js +1 -0
- package/core/built/admin/assets/newsletter-B3Ihwizp.js +1 -0
- package/core/built/admin/assets/newsletter-detail-modal-CYsu4qhl.js +1 -0
- package/core/built/admin/assets/newsletter-emails-CDPR4-SP.js +1 -0
- package/core/built/admin/assets/newsletters-bJiYLJWl.js +1 -0
- package/core/built/admin/assets/note-7f6urVVA.js +1 -0
- package/core/built/admin/assets/notifications-rosQCyXH.js +1 -0
- package/core/built/admin/assets/offers-container-modal-J2j6NpDL.js +1 -0
- package/core/built/admin/assets/onboarding-route-DTjcThKl.js +1 -0
- package/core/built/admin/assets/overview-BxvWzGaf.js +1 -0
- package/core/built/admin/assets/overview-D6JG0hkZ.js +1 -0
- package/core/built/admin/assets/{page-header-VLPJfj_d.js → page-header-l7vXmLIw.js} +1 -1
- package/core/built/admin/assets/pintura-modal-BGHmudVB.js +1 -0
- package/core/built/admin/assets/portal-frame-DVc4VHX_.js +1 -0
- package/core/built/admin/assets/portal-modal-BCOcdiRi.js +1 -0
- package/core/built/admin/assets/{post-analytics-Gv81eN_g.js → post-analytics-CTSZMK6I.js} +1 -1
- package/core/built/admin/assets/post-analytics-header-Dt4RQGq0.js +1 -0
- package/core/built/admin/assets/{post-analytics-provider-O2YjZMhN.js → post-analytics-provider-C4ydiboO.js} +1 -1
- package/core/built/admin/assets/post-share-modal-DjH_a_1L.js +1 -0
- package/core/built/admin/assets/preferences-BLo6VJUS.js +5 -0
- package/core/built/admin/assets/preview-modal-BOgjgsJL.js +1 -0
- package/core/built/admin/assets/profile-DG6iCdRH.js +1 -0
- package/core/built/admin/assets/radio-group-ChHD4EQJ.js +1 -0
- package/core/built/admin/assets/router-provider-Bexg1t-V.js +1 -0
- package/core/built/admin/assets/select-ChxknEcT.js +1 -0
- package/core/built/admin/assets/settings-B0VDAhgV.js +1 -0
- package/core/built/admin/assets/settings-Dwo3CeAD.js +4 -0
- package/core/built/admin/assets/settings-breadcrumbs-sLDNedBW.js +1 -0
- package/core/built/admin/assets/share-modal-BDb2nacg.js +1 -0
- package/core/built/admin/assets/skeleton-D7hwjdry.js +1 -0
- package/core/built/admin/assets/slack-modal-CwNAD69x.js +1 -0
- package/core/built/admin/assets/sort-button-DhqpxkrO.js +1 -0
- package/core/built/admin/assets/source-icon-BPsD5ees.js +1 -0
- package/core/built/admin/assets/stats-WdDJ8c_1.js +1 -0
- package/core/built/admin/assets/stats-view-J3PI2eT_.js +1 -0
- package/core/built/admin/assets/step-1-2q1SjerQ.js +1 -0
- package/core/built/admin/assets/step-2-D5mJB3Uu.js +1 -0
- package/core/built/admin/assets/step-3-BpAuYTGZ.js +2 -0
- package/core/built/admin/assets/{stripe-button-Bh4p6Wud.js → stripe-button-DDoUKN5a.js} +1 -1
- package/core/built/admin/assets/stripe-connect-modal-pMdF3t4S.js +1 -0
- package/core/built/admin/assets/switch-B_1yU3Aw.js +1 -0
- package/core/built/admin/assets/{table-DghJxZMH.js → table-CrqQtkR2.js} +1 -1
- package/core/built/admin/assets/tabs-BhzVdh5p.js +1 -0
- package/core/built/admin/assets/tag-detail-CwJhI4qg.js +2 -0
- package/core/built/admin/assets/tags-CvJ0PgrB.js +1 -0
- package/core/built/admin/assets/testimonials-modal-CV82IAnI.js +1 -0
- package/core/built/admin/assets/tier-detail-modal-pLzCOfeg.js +1 -0
- package/core/built/admin/assets/toggle-group-BW_T4yn6.js +1 -0
- package/core/built/admin/assets/topic-filter-DkXjDejV.js +1 -0
- package/core/built/admin/assets/transistor-modal-TMRvHZ6B.js +1 -0
- package/core/built/admin/assets/unsplash-modal-CV92RdPw.js +1 -0
- package/core/built/admin/assets/{use-analytics-data-IxJcSjc9.js → use-analytics-data-BM_uV9SU.js} +1 -1
- package/core/built/admin/assets/use-check-theme-limit-error-DkBnXMSY.js +1 -0
- package/core/built/admin/assets/use-confirm-unload-SzlkOIpm.js +1 -0
- package/core/built/admin/assets/use-feature-flag-cahOlMva.js +1 -0
- package/core/built/admin/assets/use-filter-params-BX5qf0Cg.js +1 -0
- package/core/built/admin/assets/{use-growth-stats-1NW-8eEZ.js → use-growth-stats-D0LlWBe6.js} +1 -1
- package/core/built/admin/assets/{use-hash-link-navigation-guard-B9j6HIrV.js → use-hash-link-navigation-guard-B_Y3FhYH.js} +1 -1
- package/core/built/admin/assets/use-limiter-D3DHSoAj.js +2 -0
- package/core/built/admin/assets/{use-pintura-config-BjFChXV1.js → use-pintura-config-D63Xu0SG.js} +1 -1
- package/core/built/admin/assets/use-pintura-editor-Z51aFnC8.js +1 -0
- package/core/built/admin/assets/{use-post-referrers-Dp1BnTAK.js → use-post-referrers-Cvymr3gt.js} +1 -1
- package/core/built/admin/assets/use-query-params-BfzuqbcD.js +1 -0
- package/core/built/admin/assets/use-reply-chain-data-Dc18Ujqo.js +1 -0
- package/core/built/admin/assets/use-setting-group-DYRIzqC0.js +1 -0
- package/core/built/admin/assets/use-settings-navigation-aDSLtmEl.js +1 -0
- package/core/built/admin/assets/{use-simple-pagination-DsEGSJhe.js → use-simple-pagination-B2TdNX67.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-query-Ci_DiPkX.js → use-tinybird-query-BvQIVuRc.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-token-CsUwc722.js → use-tinybird-token-COQTPIwL.js} +1 -1
- package/core/built/admin/assets/use-upgrade-route-BZf1g59m.js +1 -0
- package/core/built/admin/assets/{use-url-input-BEZJpVgN.js → use-url-input-PcqZadXa.js} +1 -1
- package/core/built/admin/assets/user-detail-modal-DeO3SAZq.js +1 -0
- package/core/built/admin/assets/{virtual-list-D5ugXyZY.js → virtual-list-D3MfPNOh.js} +1 -1
- package/core/built/admin/assets/web-CDdyG7z1.js +2 -0
- package/core/built/admin/assets/web-CwQ8_QUc.js +1 -0
- package/core/built/admin/assets/zapier-modal-a7t75iNI.js +1 -0
- package/core/built/admin/index.html +15 -12
- package/core/frontend/apps/private-blogging/lib/helpers/input_password.js +2 -1
- package/core/frontend/helpers/body_class.js +2 -1
- package/core/frontend/helpers/cancel_link.js +2 -1
- package/core/frontend/helpers/color_to_rgba.js +2 -1
- package/core/frontend/helpers/content_api_key.js +2 -1
- package/core/frontend/helpers/content_api_url.js +2 -1
- package/core/frontend/helpers/contrast_text_color.js +2 -1
- package/core/frontend/helpers/facebook_url.js +2 -1
- package/core/frontend/helpers/ghost_foot.js +2 -1
- package/core/frontend/helpers/ghost_head.js +2 -1
- package/core/frontend/helpers/has.js +4 -2
- package/core/frontend/helpers/link_class.js +2 -1
- package/core/frontend/helpers/meta_description.js +2 -1
- package/core/frontend/helpers/meta_title.js +2 -1
- package/core/frontend/helpers/navigation.js +113 -22
- package/core/frontend/helpers/page_url.js +2 -1
- package/core/frontend/helpers/post_class.js +2 -1
- package/core/frontend/helpers/reading_time.js +2 -1
- package/core/frontend/helpers/social_accounts.js +2 -1
- package/core/frontend/helpers/social_url.js +2 -1
- package/core/frontend/helpers/total_members.js +2 -1
- package/core/frontend/helpers/total_paid_members.js +2 -1
- package/core/frontend/helpers/tpl/navigation.hbs +1 -1
- package/core/frontend/helpers/twitter_url.js +2 -1
- package/core/frontend/public/cards.manifest.json +1 -1
- package/core/server/api/endpoints/posts.js +7 -4
- package/core/server/api/endpoints/utils/permissions.js +2 -1
- package/core/server/api/endpoints/utils/validators/input/settings.js +83 -1
- package/core/server/data/exporter/table-lists.js +1 -0
- package/core/server/data/importer/handlers/json.js +2 -1
- package/core/server/data/migrations/versions/5.19/2022-09-02-20-52-backfill-new-product-columns.js +2 -1
- package/core/server/data/migrations/versions/5.21/2022-10-26-04-50-member-subscription-created-batch-id.js +4 -2
- package/core/server/data/migrations/versions/5.22/2022-10-31-12-03-backfill-new-product-columns.js +2 -1
- package/core/server/data/migrations/versions/6.58/2026-08-11-21-22-48-rename-email-batches-provider-id.js +1 -1
- package/core/server/data/migrations/versions/6.59/2026-08-18-17-06-46-add-gift-delivery-fields-to-gifts.js +26 -0
- package/core/server/data/migrations/versions/6.59/2026-08-18-17-07-01-add-gift-deliveries.js +20 -0
- package/core/server/data/schema/commands.js +48 -24
- package/core/server/data/schema/schema.js +28 -7
- package/core/server/data/seeders/importers/automation-action-revisions-importer.js +3 -36
- package/core/server/data/seeders/importers/automation-actions-importer.js +5 -41
- package/core/server/data/seeders/importers/automation-run-steps-importer.js +3 -36
- package/core/server/data/seeders/importers/automation-runs-importer.js +7 -42
- package/core/server/data/seeders/importers/automations-importer.js +3 -36
- package/core/server/data/seeders/importers/comment-reports-importer.js +5 -5
- package/core/server/data/seeders/importers/comments-importer.js +5 -5
- package/core/server/data/seeders/importers/email-batches-importer.js +5 -4
- package/core/server/data/seeders/importers/email-recipients-importer.js +12 -20
- package/core/server/data/seeders/importers/emails-importer.js +6 -6
- package/core/server/data/seeders/importers/labels-importer.js +3 -3
- package/core/server/data/seeders/importers/members-click-events-importer.js +5 -5
- package/core/server/data/seeders/importers/members-created-events-importer.js +13 -12
- package/core/server/data/seeders/importers/members-feedback-importer.js +7 -7
- package/core/server/data/seeders/importers/members-importer.js +4 -4
- package/core/server/data/seeders/importers/members-login-events-importer.js +3 -3
- package/core/server/data/seeders/importers/members-products-importer.js +2 -2
- package/core/server/data/seeders/importers/members-status-events-importer.js +4 -3
- package/core/server/data/seeders/importers/members-stripe-customers-importer.js +2 -2
- package/core/server/data/seeders/importers/members-stripe-customers-subscriptions-importer.js +7 -7
- package/core/server/data/seeders/importers/members-subscribe-events-importer.js +3 -3
- package/core/server/data/seeders/importers/members-subscription-created-events-importer.js +3 -3
- package/core/server/data/seeders/importers/offer-redemptions-importer.js +7 -7
- package/core/server/data/seeders/importers/offers-importer.js +3 -3
- package/core/server/data/seeders/importers/posts-importer.js +4 -4
- package/core/server/data/seeders/importers/table-importer.js +2 -35
- package/core/server/data/seeders/importers/tags-importer.js +2 -2
- package/core/server/data/seeders/importers/users-importer.js +2 -2
- package/core/server/data/seeders/importers/web-mentions-importer.js +2 -2
- package/core/server/data/seeders/utils/random.js +7 -0
- package/core/server/ghost-server.js +19 -5
- package/core/server/lib/db-date.js +72 -3
- package/core/server/models/gift-delivery.js +23 -0
- package/core/server/models/gift.js +9 -0
- package/core/server/models/index.js +3 -2
- package/core/server/models/settings.js +43 -4
- package/core/server/services/automations/database-automations-repository.js +38 -31
- package/core/server/services/automations/poll.js +1 -1
- package/core/server/services/content-import/csv/index.js +8 -0
- package/core/server/services/content-import/csv/parse.js +60 -0
- package/core/server/services/content-import/import/importer.js +222 -0
- package/core/server/services/content-import/import/post-data.js +52 -0
- package/core/server/services/content-import/import/reader.js +15 -0
- package/core/server/services/content-import/import/row.js +50 -0
- package/core/server/services/content-import/import/store.js +71 -0
- package/core/server/services/content-import/index.js +67 -0
- package/core/server/services/email-analytics/automation-email-analytics-batch-processor.js +1 -1
- package/core/server/services/email-analytics/index.js +3 -1
- package/core/server/services/email-analytics/jobs/index.js +2 -2
- package/core/server/services/email-service/email-service-wrapper.js +1 -1
- package/core/server/services/email-suppression-list/email-suppression-list.js +4 -2
- package/core/server/services/gifts/CONTEXT.md +27 -3
- package/core/server/services/gifts/README.md +8 -4
- package/core/server/services/gifts/constants.js +2 -1
- package/core/server/services/gifts/email-templates/gift-delivery.hbs +20 -0
- package/core/server/services/gifts/email-templates/gift-delivery.js +39 -0
- package/core/server/services/gifts/email-templates/gift-purchase-confirmation.hbs +2 -2
- package/core/server/services/gifts/email-templates/gift-purchase-confirmation.js +19 -4
- package/core/server/services/gifts/events/send-gift-delivery-event.js +15 -0
- package/core/server/services/gifts/gift-bookshelf-repository.js +23 -3
- package/core/server/services/gifts/gift-checkout-adapter.js +5 -3
- package/core/server/services/gifts/gift-codec.js +8 -0
- package/core/server/services/gifts/gift-delivery-bookshelf-repository.js +107 -0
- package/core/server/services/gifts/gift-delivery-codec.js +12 -0
- package/core/server/services/gifts/gift-delivery-schema.js +15 -0
- package/core/server/services/gifts/gift-delivery-service.js +209 -0
- package/core/server/services/gifts/gift-email-renderer.js +13 -1
- package/core/server/services/gifts/gift-email-service.js +97 -17
- package/core/server/services/gifts/gift-schema.js +10 -6
- package/core/server/services/gifts/gift-service.js +259 -34
- package/core/server/services/gifts/gift.js +59 -5
- package/core/server/services/gifts/index.js +69 -3
- package/core/server/services/lib/mailgun-client.js +5 -2
- package/core/server/services/mail/ghost-mailer.js +9 -1
- package/core/server/services/member-welcome-emails/service.js +6 -1
- package/core/server/services/members/members-api/controllers/router-controller.js +26 -8
- package/core/server/services/stripe/services/webhook/checkout-session-event-service.js +100 -12
- package/core/server/services/stripe/stripe-api.js +3 -2
- package/core/server/services/stripe/webhook-controller.js +6 -3
- package/core/server/services/stripe/webhook-manager.js +2 -0
- package/core/shared/labs.js +1 -0
- package/package.json +9 -8
- package/pnpm-lock.yaml +26 -11
- package/pnpm-workspace.yaml +5 -6
- package/content/themes/source/yarn.lock +0 -6232
- package/core/built/admin/assets/Combination-BDvCdg1t.js +0 -41
- package/core/built/admin/assets/about-B_BvSyLF.js +0 -1
- package/core/built/admin/assets/account-migration-oJUGxnzT.js +0 -1
- package/core/built/admin/assets/activitypub/dist-Dqggkccr.js +0 -215
- package/core/built/admin/assets/activitypub/inbox-z1o2j1Tb.js +0 -1221
- package/core/built/admin/assets/activitypub/layout-BRMezuty.js +0 -1
- package/core/built/admin/assets/activitypub/moderation-CZudJi8N.js +0 -160
- package/core/built/admin/assets/activitypub/settings-CrSV1cux.js +0 -107
- package/core/built/admin/assets/add-integration-modal-BW23PDYs.js +0 -1
- package/core/built/admin/assets/add-newsletter-modal-M0ls8BKO.js +0 -1
- package/core/built/admin/assets/add-recommendation-modal-B0NeBMzl.js +0 -1
- package/core/built/admin/assets/announcement-bar-modal-pXBqjQE_.js +0 -1
- package/core/built/admin/assets/api-keys-Df6uv1dT.js +0 -1
- package/core/built/admin/assets/audience-BY8od8If.js +0 -1
- package/core/built/admin/assets/automations-DCs6Wady.js +0 -1
- package/core/built/admin/assets/bluesky-sharing-D0P42Cws.js +0 -1
- package/core/built/admin/assets/breadcrumb-MTj_cKLb.js +0 -1
- package/core/built/admin/assets/chart-helpers-DcO3k_vA.js +0 -1
- package/core/built/admin/assets/chunk-KS7C4IRE-DG9HTHQ3.js +0 -4
- package/core/built/admin/assets/color-picker-CBlJMv3y.js +0 -3
- package/core/built/admin/assets/combobox-BbPJzjCp.js +0 -1
- package/core/built/admin/assets/command-BrhXIlDr.js +0 -1
- package/core/built/admin/assets/comments-Cu4rD1aJ.js +0 -1
- package/core/built/admin/assets/confirmation-provider-BEdrnHns.js +0 -1
- package/core/built/admin/assets/custom-field-type-option-CNhAgHbJ.js +0 -1
- package/core/built/admin/assets/custom-integration-modal-CrGOI8IB.js +0 -1
- package/core/built/admin/assets/date-range-select-Bsh4gIek.js +0 -1
- package/core/built/admin/assets/design-and-theme-modal-DWZguwWX.js +0 -23
- package/core/built/admin/assets/dirty-confirm-dialog-DmdMpgyN.js +0 -1
- package/core/built/admin/assets/disabled-sources-indicator-BLnmyWgt.js +0 -1
- package/core/built/admin/assets/dist-DMFGvqqU.js +0 -1
- package/core/built/admin/assets/dist-DlCyLrOC.js +0 -1
- package/core/built/admin/assets/dist-j1xxxnY8.js +0 -1
- package/core/built/admin/assets/domain-Dp9TRrFp.js +0 -1
- package/core/built/admin/assets/edit-profile-DG6lpMsr.js +0 -1
- package/core/built/admin/assets/editor-CPQOrhrn.js +0 -8
- package/core/built/admin/assets/embed-signup-form-modal-CgWniDha.js +0 -9
- package/core/built/admin/assets/explore-C3rzONJv.js +0 -1
- package/core/built/admin/assets/feed-DpUwS1cU.js +0 -1
- package/core/built/admin/assets/filters-DZJT29GW.js +0 -1
- package/core/built/admin/assets/first-promoter-modal-DJhQQheG.js +0 -1
- package/core/built/admin/assets/gift-link-modal-sdFLjND9.js +0 -1
- package/core/built/admin/assets/global-data-provider-CixmTX00.js +0 -1
- package/core/built/admin/assets/growth-0YYkBEeS.js +0 -1
- package/core/built/admin/assets/growth-DpjAPeQT.js +0 -3
- package/core/built/admin/assets/history-modal-BWJ4-cJ0.js +0 -1
- package/core/built/admin/assets/html-field-C-oa5DEl.js +0 -1
- package/core/built/admin/assets/iframe-buffering-Ba9sDpi2.js +0 -1
- package/core/built/admin/assets/index-BOJzlYiz.js +0 -67
- package/core/built/admin/assets/index-Bid6LAyc.css +0 -1
- package/core/built/admin/assets/invite-user-modal-DCiQSiuD.js +0 -1
- package/core/built/admin/assets/koenig-editor-base-BApy8Q7C.js +0 -1
- package/core/built/admin/assets/kpi-helpers-ssbuy-hD.js +0 -1
- package/core/built/admin/assets/kpi-tabs-BqoIM6bI.js +0 -1
- package/core/built/admin/assets/label-picker-Cvvbmzb5.js +0 -1
- package/core/built/admin/assets/layout-DkkGyev2.js +0 -1
- package/core/built/admin/assets/member-custom-fields-CvGcpaiW.js +0 -1
- package/core/built/admin/assets/member-detail-CMCMZ47_.js +0 -1
- package/core/built/admin/assets/members-sRl6fNfq.js +0 -11
- package/core/built/admin/assets/moderation-KhKwGqI1.js +0 -1
- package/core/built/admin/assets/multi-select-combobox-nhCff0_K.js +0 -1
- package/core/built/admin/assets/navigation-modal-MDJExWDJ.js +0 -1
- package/core/built/admin/assets/newsletter-BIBHumfV.js +0 -1
- package/core/built/admin/assets/newsletter-detail-modal-CfjBMH3v.js +0 -1
- package/core/built/admin/assets/newsletter-emails-ClDL-7dA.js +0 -1
- package/core/built/admin/assets/newsletters-CBe-IPzK.js +0 -1
- package/core/built/admin/assets/note-HnNUOt4d.js +0 -1
- package/core/built/admin/assets/notifications-Cj5e-258.js +0 -1
- package/core/built/admin/assets/offers-container-modal-BHwHtixC.js +0 -1
- package/core/built/admin/assets/onboarding-route-D_4jp7Nq.js +0 -1
- package/core/built/admin/assets/overview-CezDdg-F.js +0 -1
- package/core/built/admin/assets/overview-D5mM51Lj.js +0 -1
- package/core/built/admin/assets/pintura-modal-BXXJ8Iaa.js +0 -1
- package/core/built/admin/assets/portal-frame-BLCvF8f-.js +0 -1
- package/core/built/admin/assets/portal-modal-DN80tAcy.js +0 -1
- package/core/built/admin/assets/post-analytics-header-_ydSY8qO.js +0 -1
- package/core/built/admin/assets/post-share-modal-CzYak_a5.js +0 -1
- package/core/built/admin/assets/preferences-gneLb6LV.js +0 -5
- package/core/built/admin/assets/preview-modal-Bkq-tgVm.js +0 -1
- package/core/built/admin/assets/profile-CzXmCNTC.js +0 -1
- package/core/built/admin/assets/radio-group-Dsq5ZEbV.js +0 -1
- package/core/built/admin/assets/router-provider-CkB_ArLF.js +0 -1
- package/core/built/admin/assets/select-xuZAuDyS.js +0 -1
- package/core/built/admin/assets/settings-Ct8pabhr.js +0 -1
- package/core/built/admin/assets/settings-KSWWQanQ.js +0 -4
- package/core/built/admin/assets/settings-breadcrumbs-DbPGu-Tu.js +0 -1
- package/core/built/admin/assets/share-modal-CdiUx9WM.js +0 -1
- package/core/built/admin/assets/slack-modal-BP--Z3jE.js +0 -1
- package/core/built/admin/assets/sort-button-CjfKdlsL.js +0 -1
- package/core/built/admin/assets/source-icon-DHj1h6Ts.js +0 -1
- package/core/built/admin/assets/stats-C615kulJ.js +0 -1
- package/core/built/admin/assets/stats-view-BSMRdYiN.js +0 -1
- package/core/built/admin/assets/step-1-pSdGYtIF.js +0 -1
- package/core/built/admin/assets/step-2-T9Uy4eBD.js +0 -1
- package/core/built/admin/assets/step-3-D4uXe_XG.js +0 -2
- package/core/built/admin/assets/stripe-connect-modal-C2vTvepi.js +0 -1
- package/core/built/admin/assets/switch-CE2NuMn-.js +0 -1
- package/core/built/admin/assets/tabs-l9c7FjzI.js +0 -1
- package/core/built/admin/assets/tag-detail-DBw_M2-p.js +0 -2
- package/core/built/admin/assets/tags-DoWsAG6l.js +0 -1
- package/core/built/admin/assets/testimonials-modal-BZKePE2T.js +0 -1
- package/core/built/admin/assets/tier-detail-modal-D65pVWsN.js +0 -1
- package/core/built/admin/assets/toggle-group-uJnl42w3.js +0 -1
- package/core/built/admin/assets/topic-filter-BM7hz0kW.js +0 -1
- package/core/built/admin/assets/transistor-modal-VZOGN3od.js +0 -1
- package/core/built/admin/assets/unsplash-modal-CKGfACxF.js +0 -1
- package/core/built/admin/assets/use-check-theme-limit-error-CGp3CGT5.js +0 -1
- package/core/built/admin/assets/use-feature-flag-CSq2Lwzt.js +0 -1
- package/core/built/admin/assets/use-filter-params-0hcJJuEJ.js +0 -1
- package/core/built/admin/assets/use-limiter-ffwpsZPp.js +0 -2
- package/core/built/admin/assets/use-pintura-editor-Cb41oJyL.js +0 -1
- package/core/built/admin/assets/use-query-params-DHhhu8wi.js +0 -1
- package/core/built/admin/assets/use-reply-chain-data-C0ZUTkfk.js +0 -1
- package/core/built/admin/assets/use-setting-group-Bx8M6FL2.js +0 -1
- package/core/built/admin/assets/use-settings-navigation-BOZcat_V.js +0 -1
- package/core/built/admin/assets/use-upgrade-route-B3_DUiuT.js +0 -1
- package/core/built/admin/assets/user-detail-modal-595z1N5r.js +0 -1
- package/core/built/admin/assets/web-CAta3lRE.js +0 -1
- package/core/built/admin/assets/web-Ds9_wWJK.js +0 -2
- package/core/built/admin/assets/zapier-modal-CcfDZY53.js +0 -1
- package/core/server/data/seeders/utils/database-date.js +0 -28
- package/core/server/services/automations/database-date.js +0 -59
- /package/core/built/admin/assets/{dist-CqO2lWS02.js → dist-CqO2lWS0.js} +0 -0
- /package/core/server/services/{automations → lib}/mailgun-message-id.js +0 -0
|
@@ -11,6 +11,9 @@ exports.giftCodec = zod_1.z.codec(gift_schema_1.DbGift, zod_1.z.instanceof(gift_
|
|
|
11
11
|
token: row.token,
|
|
12
12
|
buyerEmail: row.buyer_email,
|
|
13
13
|
buyerMemberId: row.buyer_member_id,
|
|
14
|
+
buyerName: row.buyer_name,
|
|
15
|
+
recipientName: row.recipient_name,
|
|
16
|
+
personalMessage: row.personal_message,
|
|
14
17
|
redeemerMemberId: row.redeemer_member_id,
|
|
15
18
|
tierId: row.tier_id,
|
|
16
19
|
cadence: row.cadence,
|
|
@@ -19,6 +22,7 @@ exports.giftCodec = zod_1.z.codec(gift_schema_1.DbGift, zod_1.z.instanceof(gift_
|
|
|
19
22
|
amount: row.amount,
|
|
20
23
|
stripeCheckoutSessionId: row.stripe_checkout_session_id,
|
|
21
24
|
stripePaymentIntentId: row.stripe_payment_intent_id,
|
|
25
|
+
checkoutStartedAt: row.checkout_started_at,
|
|
22
26
|
consumesAt: row.consumes_at,
|
|
23
27
|
expiresAt: row.expires_at,
|
|
24
28
|
status: row.status,
|
|
@@ -33,6 +37,9 @@ exports.giftCodec = zod_1.z.codec(gift_schema_1.DbGift, zod_1.z.instanceof(gift_
|
|
|
33
37
|
token: gift.token,
|
|
34
38
|
buyer_email: gift.buyerEmail,
|
|
35
39
|
buyer_member_id: gift.buyerMemberId,
|
|
40
|
+
buyer_name: gift.buyerName,
|
|
41
|
+
recipient_name: gift.recipientName,
|
|
42
|
+
personal_message: gift.personalMessage,
|
|
36
43
|
redeemer_member_id: gift.redeemerMemberId,
|
|
37
44
|
tier_id: gift.tierId,
|
|
38
45
|
cadence: gift.cadence,
|
|
@@ -41,6 +48,7 @@ exports.giftCodec = zod_1.z.codec(gift_schema_1.DbGift, zod_1.z.instanceof(gift_
|
|
|
41
48
|
amount: gift.amount,
|
|
42
49
|
stripe_checkout_session_id: gift.stripeCheckoutSessionId,
|
|
43
50
|
stripe_payment_intent_id: gift.stripePaymentIntentId,
|
|
51
|
+
checkout_started_at: gift.checkoutStartedAt,
|
|
44
52
|
consumes_at: gift.consumesAt,
|
|
45
53
|
expires_at: gift.expiresAt,
|
|
46
54
|
status: gift.status,
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GiftDeliveryBookshelfRepository = void 0;
|
|
4
|
+
const db_date_1 = require("../../lib/db-date");
|
|
5
|
+
const gift_delivery_codec_1 = require("./gift-delivery-codec");
|
|
6
|
+
// Deliveries that still need a send attempt: never started, or claimed by a
|
|
7
|
+
// process that has since died and left the claim stale
|
|
8
|
+
function recoverableDeliveries(query, staleBefore) {
|
|
9
|
+
query.andWhere((recoverable) => {
|
|
10
|
+
recoverable.where('gift_deliveries.status', 'pending')
|
|
11
|
+
.orWhere((stale) => {
|
|
12
|
+
stale.where('gift_deliveries.status', 'sending')
|
|
13
|
+
.where('gift_deliveries.started_at', '<=', (0, db_date_1.toDatabaseDate)(staleBefore));
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
class GiftDeliveryBookshelfRepository {
|
|
18
|
+
model;
|
|
19
|
+
knex;
|
|
20
|
+
constructor({ GiftDeliveryModel, knex }) {
|
|
21
|
+
this.model = GiftDeliveryModel;
|
|
22
|
+
this.knex = knex;
|
|
23
|
+
}
|
|
24
|
+
async getById(id, options = {}) {
|
|
25
|
+
const model = await this.model.findOne({ id }, { require: false, ...options });
|
|
26
|
+
return model ? (0, gift_delivery_codec_1.decodeGiftDeliveryRow)(model.toJSON()) : null;
|
|
27
|
+
}
|
|
28
|
+
async getByGiftId(giftId, options = {}) {
|
|
29
|
+
const model = await this.model.findOne({ gift_id: giftId }, { require: false, ...options });
|
|
30
|
+
return model ? (0, gift_delivery_codec_1.decodeGiftDeliveryRow)(model.toJSON()) : null;
|
|
31
|
+
}
|
|
32
|
+
async findRecoverableForPurchasedGifts(staleBefore, limit) {
|
|
33
|
+
const rows = await this.knex('gift_deliveries')
|
|
34
|
+
.select('gift_deliveries.*')
|
|
35
|
+
.join('gifts', 'gifts.id', 'gift_deliveries.gift_id')
|
|
36
|
+
.where('gifts.status', 'purchased')
|
|
37
|
+
.modify(recoverableDeliveries, staleBefore)
|
|
38
|
+
.limit(limit);
|
|
39
|
+
return rows.map(gift_delivery_codec_1.decodeGiftDeliveryRow);
|
|
40
|
+
}
|
|
41
|
+
async tryStartDelivery(id, now, staleBefore) {
|
|
42
|
+
const claimed = await this.knex('gift_deliveries')
|
|
43
|
+
.where({ id })
|
|
44
|
+
.whereExists((query) => {
|
|
45
|
+
query.select('gifts.id')
|
|
46
|
+
.from('gifts')
|
|
47
|
+
.whereRaw('gifts.id = gift_deliveries.gift_id')
|
|
48
|
+
.where('gifts.status', 'purchased');
|
|
49
|
+
})
|
|
50
|
+
.modify(recoverableDeliveries, staleBefore)
|
|
51
|
+
.update({
|
|
52
|
+
status: 'sending',
|
|
53
|
+
started_at: (0, db_date_1.toDatabaseDate)(now)
|
|
54
|
+
});
|
|
55
|
+
if (claimed !== 1) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
return this.getById(id);
|
|
59
|
+
}
|
|
60
|
+
async markSent(id, sentAt, providerMessageId) {
|
|
61
|
+
return this.updateState(id, 'sending', {
|
|
62
|
+
status: 'sent',
|
|
63
|
+
email_sent_at: (0, db_date_1.toDatabaseDate)(sentAt),
|
|
64
|
+
email_provider_message_id: providerMessageId,
|
|
65
|
+
started_at: null
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
// A delivery cancelled while its email was in flight keeps the acceptance
|
|
69
|
+
// details so the message can still be correlated with transport outcomes
|
|
70
|
+
async recordCancelledAcceptance(id, sentAt, providerMessageId) {
|
|
71
|
+
return this.updateState(id, 'cancelled', {
|
|
72
|
+
email_sent_at: (0, db_date_1.toDatabaseDate)(sentAt),
|
|
73
|
+
email_provider_message_id: providerMessageId
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
async markFailed(id) {
|
|
77
|
+
return this.updateState(id, 'sending', {
|
|
78
|
+
status: 'failed',
|
|
79
|
+
started_at: null
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
async markCancelled(id) {
|
|
83
|
+
return this.updateState(id, 'sending', {
|
|
84
|
+
status: 'cancelled',
|
|
85
|
+
started_at: null
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
async cancelPendingForGift(token, options = {}) {
|
|
89
|
+
const db = options.transacting ?? this.knex;
|
|
90
|
+
const gift = db('gifts').select('id').where({ token });
|
|
91
|
+
const updated = await db('gift_deliveries')
|
|
92
|
+
.whereIn('status', ['pending', 'sending'])
|
|
93
|
+
.whereIn('gift_id', gift)
|
|
94
|
+
.update({ status: 'cancelled', started_at: null });
|
|
95
|
+
return updated === 1;
|
|
96
|
+
}
|
|
97
|
+
async create(delivery, options = {}) {
|
|
98
|
+
await this.model.add((0, gift_delivery_codec_1.encodeGiftDelivery)(delivery), options);
|
|
99
|
+
}
|
|
100
|
+
async updateState(id, from, data) {
|
|
101
|
+
const updated = await this.knex('gift_deliveries')
|
|
102
|
+
.where({ id, status: from })
|
|
103
|
+
.update(data);
|
|
104
|
+
return updated === 1;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
exports.GiftDeliveryBookshelfRepository = GiftDeliveryBookshelfRepository;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.decodeGiftDeliveryRow = decodeGiftDeliveryRow;
|
|
4
|
+
exports.encodeGiftDelivery = encodeGiftDelivery;
|
|
5
|
+
const case_keys_1 = require("../../lib/case-keys");
|
|
6
|
+
const gift_delivery_schema_1 = require("./gift-delivery-schema");
|
|
7
|
+
function decodeGiftDeliveryRow(input) {
|
|
8
|
+
return (0, case_keys_1.camelKeys)(gift_delivery_schema_1.DbGiftDelivery.parse(input));
|
|
9
|
+
}
|
|
10
|
+
function encodeGiftDelivery(delivery) {
|
|
11
|
+
return gift_delivery_schema_1.DbGiftDelivery.parse((0, case_keys_1.snakeKeys)(delivery));
|
|
12
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DbGiftDelivery = exports.GiftDeliveryStatusSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const db_date_1 = require("../../lib/db-date");
|
|
6
|
+
exports.GiftDeliveryStatusSchema = zod_1.z.enum(['pending', 'sending', 'sent', 'failed', 'cancelled']);
|
|
7
|
+
exports.DbGiftDelivery = zod_1.z.object({
|
|
8
|
+
id: zod_1.z.string(),
|
|
9
|
+
gift_id: zod_1.z.string(),
|
|
10
|
+
recipient_email: zod_1.z.string().email(),
|
|
11
|
+
status: exports.GiftDeliveryStatusSchema.default('pending'),
|
|
12
|
+
started_at: db_date_1.DbDate.nullable().default(null),
|
|
13
|
+
email_sent_at: db_date_1.DbDate.nullable().default(null),
|
|
14
|
+
email_provider_message_id: zod_1.z.string().nullable().default(null)
|
|
15
|
+
});
|
|
@@ -0,0 +1,209 @@
|
|
|
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.GiftDeliveryService = void 0;
|
|
7
|
+
const bson_objectid_1 = __importDefault(require("bson-objectid"));
|
|
8
|
+
const logging_1 = __importDefault(require("@tryghost/logging"));
|
|
9
|
+
const send_gift_delivery_event_1 = require("./events/send-gift-delivery-event");
|
|
10
|
+
const constants_1 = require("./constants");
|
|
11
|
+
const DomainEvents = require('@tryghost/domain-events');
|
|
12
|
+
class GiftDeliveryService {
|
|
13
|
+
deps;
|
|
14
|
+
constructor(deps) {
|
|
15
|
+
this.deps = deps;
|
|
16
|
+
}
|
|
17
|
+
async createForCheckout({ giftId, recipientEmail }, options = {}) {
|
|
18
|
+
const delivery = {
|
|
19
|
+
id: new bson_objectid_1.default().toHexString(),
|
|
20
|
+
giftId,
|
|
21
|
+
recipientEmail,
|
|
22
|
+
status: 'pending',
|
|
23
|
+
startedAt: null,
|
|
24
|
+
emailSentAt: null,
|
|
25
|
+
emailProviderMessageId: null
|
|
26
|
+
};
|
|
27
|
+
await this.deps.giftDeliveryRepository.create(delivery, options);
|
|
28
|
+
return delivery.id;
|
|
29
|
+
}
|
|
30
|
+
async dispatchForGift(giftId) {
|
|
31
|
+
const delivery = await this.deps.giftDeliveryRepository.getByGiftId(giftId);
|
|
32
|
+
if (!delivery || delivery.status !== 'pending') {
|
|
33
|
+
return delivery?.recipientEmail ?? null;
|
|
34
|
+
}
|
|
35
|
+
DomainEvents.dispatch(send_gift_delivery_event_1.SendGiftDeliveryEvent.create({ deliveryId: delivery.id }));
|
|
36
|
+
return delivery.recipientEmail;
|
|
37
|
+
}
|
|
38
|
+
async recoverPending(limit = 1000) {
|
|
39
|
+
const staleBefore = new Date(Date.now() - constants_1.GIFT_DELIVERY_STALE_AFTER_MS);
|
|
40
|
+
const deliveries = await this.deps.giftDeliveryRepository.findRecoverableForPurchasedGifts(staleBefore, limit);
|
|
41
|
+
const result = {
|
|
42
|
+
sentCount: 0,
|
|
43
|
+
skippedCount: 0,
|
|
44
|
+
failedCount: 0
|
|
45
|
+
};
|
|
46
|
+
// Sequential on purpose: recovery can find many deliveries at once and each
|
|
47
|
+
// send holds a mail transport call, so fanning out through events would open
|
|
48
|
+
// them all concurrently
|
|
49
|
+
for (const delivery of deliveries) {
|
|
50
|
+
try {
|
|
51
|
+
const deliveryResult = await this.send(delivery.id);
|
|
52
|
+
result[`${deliveryResult}Count`] += 1;
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
result.failedCount += 1;
|
|
56
|
+
logging_1.default.error({
|
|
57
|
+
event: { name: 'gift_delivery.recovery_failed' },
|
|
58
|
+
err,
|
|
59
|
+
deliveryId: delivery.id
|
|
60
|
+
}, 'Failed to recover gift delivery');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
async cancelPendingForGift(token, options = {}) {
|
|
66
|
+
return this.deps.giftDeliveryRepository.cancelPendingForGift(token, options);
|
|
67
|
+
}
|
|
68
|
+
async send(id) {
|
|
69
|
+
const now = new Date();
|
|
70
|
+
const staleBefore = new Date(now.getTime() - constants_1.GIFT_DELIVERY_STALE_AFTER_MS);
|
|
71
|
+
const delivery = await this.deps.giftDeliveryRepository.tryStartDelivery(id, now, staleBefore);
|
|
72
|
+
if (!delivery) {
|
|
73
|
+
return 'skipped';
|
|
74
|
+
}
|
|
75
|
+
const gift = await this.deps.giftRepository.getById(delivery.giftId);
|
|
76
|
+
if (!gift) {
|
|
77
|
+
logging_1.default.error({
|
|
78
|
+
event: { name: 'gift_delivery.gift_missing' },
|
|
79
|
+
deliveryId: delivery.id,
|
|
80
|
+
giftId: delivery.giftId
|
|
81
|
+
}, 'Started gift delivery has no gift');
|
|
82
|
+
await this.deps.giftDeliveryRepository.markFailed(delivery.id);
|
|
83
|
+
return 'failed';
|
|
84
|
+
}
|
|
85
|
+
if (gift.status !== 'purchased') {
|
|
86
|
+
await this.deps.giftDeliveryRepository.markCancelled(delivery.id);
|
|
87
|
+
return 'skipped';
|
|
88
|
+
}
|
|
89
|
+
if (!gift.buyerEmail || !gift.buyerName) {
|
|
90
|
+
logging_1.default.error({
|
|
91
|
+
event: { name: 'gift_delivery.buyer_details_missing' },
|
|
92
|
+
deliveryId: delivery.id,
|
|
93
|
+
giftId: delivery.giftId
|
|
94
|
+
}, 'Gift delivery is missing required buyer details');
|
|
95
|
+
await this.deps.giftDeliveryRepository.markFailed(delivery.id);
|
|
96
|
+
return 'failed';
|
|
97
|
+
}
|
|
98
|
+
let tier;
|
|
99
|
+
try {
|
|
100
|
+
tier = await this.deps.tiersService.api.read(gift.tierId);
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
logging_1.default.error({
|
|
104
|
+
event: { name: 'gift_delivery.tier_read_failed' },
|
|
105
|
+
err,
|
|
106
|
+
deliveryId: delivery.id,
|
|
107
|
+
giftId: delivery.giftId,
|
|
108
|
+
tierId: gift.tierId
|
|
109
|
+
}, 'Failed to read tier for gift delivery');
|
|
110
|
+
await this.deps.giftDeliveryRepository.markFailed(delivery.id);
|
|
111
|
+
return 'failed';
|
|
112
|
+
}
|
|
113
|
+
if (!tier) {
|
|
114
|
+
logging_1.default.error({
|
|
115
|
+
event: { name: 'gift_delivery.tier_missing' },
|
|
116
|
+
deliveryId: delivery.id,
|
|
117
|
+
giftId: delivery.giftId,
|
|
118
|
+
tierId: gift.tierId
|
|
119
|
+
}, 'Tier not found for gift delivery');
|
|
120
|
+
await this.deps.giftDeliveryRepository.markFailed(delivery.id);
|
|
121
|
+
return 'failed';
|
|
122
|
+
}
|
|
123
|
+
let result;
|
|
124
|
+
try {
|
|
125
|
+
result = await this.deps.giftEmailService.sendGiftDelivery({
|
|
126
|
+
recipientEmail: delivery.recipientEmail,
|
|
127
|
+
recipientName: gift.recipientName,
|
|
128
|
+
buyerEmail: gift.buyerEmail,
|
|
129
|
+
buyerName: gift.buyerName,
|
|
130
|
+
personalMessage: gift.personalMessage,
|
|
131
|
+
token: gift.token,
|
|
132
|
+
tierName: tier.name,
|
|
133
|
+
benefits: tier.toJSON().benefits,
|
|
134
|
+
cadence: gift.cadence,
|
|
135
|
+
duration: gift.duration,
|
|
136
|
+
expiresAt: gift.expiresAt
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
logging_1.default.error({
|
|
141
|
+
event: { name: 'gift_delivery.acceptance_failed' },
|
|
142
|
+
// The bulk mailer rejects with {error, messageData}; the rendered
|
|
143
|
+
// message and recipient must stay out of the logs
|
|
144
|
+
err: isMailgunRejection(err) ? err.error : err,
|
|
145
|
+
deliveryId: delivery.id,
|
|
146
|
+
giftId: delivery.giftId
|
|
147
|
+
}, 'Mail transport did not accept gift delivery');
|
|
148
|
+
await this.deps.giftDeliveryRepository.markFailed(delivery.id);
|
|
149
|
+
return 'failed';
|
|
150
|
+
}
|
|
151
|
+
const sentAt = new Date();
|
|
152
|
+
let persisted;
|
|
153
|
+
let cancelledDuringSend = false;
|
|
154
|
+
try {
|
|
155
|
+
persisted = await this.deps.giftDeliveryRepository.markSent(delivery.id, sentAt, result.providerMessageId);
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
logging_1.default.warn({
|
|
159
|
+
event: { name: 'gift_delivery.acceptance_persistence.retrying' },
|
|
160
|
+
err,
|
|
161
|
+
deliveryId: delivery.id
|
|
162
|
+
}, 'Retrying accepted gift delivery persistence');
|
|
163
|
+
try {
|
|
164
|
+
persisted = await this.deps.giftDeliveryRepository.markSent(delivery.id, sentAt, result.providerMessageId);
|
|
165
|
+
}
|
|
166
|
+
catch (retryErr) {
|
|
167
|
+
logging_1.default.error({
|
|
168
|
+
event: { name: 'gift_delivery.acceptance_persistence.failed' },
|
|
169
|
+
err: retryErr,
|
|
170
|
+
deliveryId: delivery.id
|
|
171
|
+
}, 'Failed to persist accepted gift delivery');
|
|
172
|
+
try {
|
|
173
|
+
await this.deps.giftDeliveryRepository.markFailed(delivery.id);
|
|
174
|
+
}
|
|
175
|
+
catch (markFailedErr) {
|
|
176
|
+
logging_1.default.error({
|
|
177
|
+
event: { name: 'gift_delivery.acceptance_terminal_persistence.failed' },
|
|
178
|
+
err: markFailedErr,
|
|
179
|
+
deliveryId: delivery.id
|
|
180
|
+
}, 'Failed to move accepted gift delivery out of recovery');
|
|
181
|
+
}
|
|
182
|
+
return 'failed';
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (!persisted) {
|
|
186
|
+
cancelledDuringSend = await this.deps.giftDeliveryRepository.recordCancelledAcceptance(delivery.id, sentAt, result.providerMessageId);
|
|
187
|
+
}
|
|
188
|
+
if (persisted) {
|
|
189
|
+
return 'sent';
|
|
190
|
+
}
|
|
191
|
+
if (cancelledDuringSend) {
|
|
192
|
+
logging_1.default.info({
|
|
193
|
+
event: { name: 'gift_delivery.cancelled_during_send' },
|
|
194
|
+
deliveryId: delivery.id,
|
|
195
|
+
giftId: delivery.giftId
|
|
196
|
+
}, 'Gift delivery was cancelled while its email was in flight');
|
|
197
|
+
return 'skipped';
|
|
198
|
+
}
|
|
199
|
+
logging_1.default.error({
|
|
200
|
+
event: { name: 'gift_delivery.acceptance_persistence.failed' },
|
|
201
|
+
deliveryId: delivery.id
|
|
202
|
+
}, 'Failed to persist accepted gift delivery');
|
|
203
|
+
return 'failed';
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
exports.GiftDeliveryService = GiftDeliveryService;
|
|
207
|
+
function isMailgunRejection(err) {
|
|
208
|
+
return typeof err === 'object' && err !== null && 'error' in err && 'messageData' in err;
|
|
209
|
+
}
|
|
@@ -9,11 +9,13 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
9
9
|
const handlebars_1 = __importDefault(require("handlebars"));
|
|
10
10
|
const gift_purchase_confirmation_1 = require("./email-templates/gift-purchase-confirmation");
|
|
11
11
|
const gift_reminder_1 = require("./email-templates/gift-reminder");
|
|
12
|
+
const gift_delivery_1 = require("./email-templates/gift-delivery");
|
|
12
13
|
class GiftEmailRenderer {
|
|
13
14
|
handlebars;
|
|
14
15
|
t;
|
|
15
16
|
purchaseConfirmationTemplate = null;
|
|
16
17
|
reminderTemplate = null;
|
|
18
|
+
deliveryTemplate = null;
|
|
17
19
|
constructor({ t }) {
|
|
18
20
|
this.t = t;
|
|
19
21
|
this.handlebars = handlebars_1.default.create();
|
|
@@ -39,11 +41,21 @@ class GiftEmailRenderer {
|
|
|
39
41
|
text: (0, gift_reminder_1.renderText)(data, this.t)
|
|
40
42
|
};
|
|
41
43
|
}
|
|
44
|
+
async renderDelivery(data) {
|
|
45
|
+
if (!this.deliveryTemplate) {
|
|
46
|
+
const source = await node_fs_1.promises.readFile(node_path_1.default.join(__dirname, './email-templates/gift-delivery.hbs'), 'utf8');
|
|
47
|
+
this.deliveryTemplate = this.handlebars.compile(source);
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
html: this.deliveryTemplate(data),
|
|
51
|
+
text: (0, gift_delivery_1.renderText)(data, this.t)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
42
54
|
registerTemplateHelpers() {
|
|
43
55
|
this.handlebars.registerHelper('t', (key, options) => {
|
|
44
56
|
const hash = options?.hash || {};
|
|
45
57
|
const escapedHash = Object.entries(hash).reduce((acc, [name, value]) => {
|
|
46
|
-
acc[name] = this.htmlSafeInterpolationValue(value);
|
|
58
|
+
acc[name] = typeof value === 'number' ? value : this.htmlSafeInterpolationValue(value);
|
|
47
59
|
return acc;
|
|
48
60
|
}, {});
|
|
49
61
|
return new handlebars_1.default.SafeString(this.t(key, {
|
|
@@ -1,18 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.GiftEmailService = void 0;
|
|
4
7
|
const gift_email_renderer_1 = require("./gift-email-renderer");
|
|
8
|
+
const color_utils_1 = require("@tryghost/color-utils");
|
|
9
|
+
const errors_1 = __importDefault(require("@tryghost/errors"));
|
|
10
|
+
const mailgun_message_id_1 = require("../lib/mailgun-message-id");
|
|
5
11
|
const DEFAULT_DATE_LOCALE = 'en-gb';
|
|
12
|
+
const DEFAULT_ACCENT_COLOR = '#15212A';
|
|
6
13
|
class GiftEmailService {
|
|
7
|
-
|
|
14
|
+
transactionalMailer;
|
|
15
|
+
bulkMailer;
|
|
8
16
|
settingsCache;
|
|
9
17
|
urlUtils;
|
|
10
18
|
getFromAddress;
|
|
11
19
|
blogIcon;
|
|
12
20
|
renderer;
|
|
13
21
|
t;
|
|
14
|
-
constructor({
|
|
15
|
-
this.
|
|
22
|
+
constructor({ transactionalMailer, bulkMailer, settingsCache, urlUtils, getFromAddress, blogIcon, t }) {
|
|
23
|
+
this.transactionalMailer = transactionalMailer;
|
|
24
|
+
this.bulkMailer = bulkMailer;
|
|
16
25
|
this.settingsCache = settingsCache;
|
|
17
26
|
this.urlUtils = urlUtils;
|
|
18
27
|
this.getFromAddress = getFromAddress;
|
|
@@ -28,14 +37,22 @@ class GiftEmailService {
|
|
|
28
37
|
return '';
|
|
29
38
|
}
|
|
30
39
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
40
|
+
get accentColor() {
|
|
41
|
+
return this.settingsCache.get('accent_color') || DEFAULT_ACCENT_COLOR;
|
|
42
|
+
}
|
|
43
|
+
mixAccentColor(target, accentWeight, fallback) {
|
|
44
|
+
try {
|
|
45
|
+
return (0, color_utils_1.Color)(target).mix((0, color_utils_1.Color)(this.accentColor), accentWeight).hex().toLowerCase();
|
|
34
46
|
}
|
|
35
|
-
|
|
36
|
-
return
|
|
47
|
+
catch {
|
|
48
|
+
return fallback;
|
|
37
49
|
}
|
|
38
|
-
|
|
50
|
+
}
|
|
51
|
+
get accentTint() {
|
|
52
|
+
return this.mixAccentColor('#FFFFFF', 0.07, '#F4F5F6');
|
|
53
|
+
}
|
|
54
|
+
get accentShade() {
|
|
55
|
+
return this.mixAccentColor('#15212A', 0.72, '#738A94');
|
|
39
56
|
}
|
|
40
57
|
formatDate(date) {
|
|
41
58
|
const locale = this.settingsCache.get('locale') || DEFAULT_DATE_LOCALE;
|
|
@@ -45,12 +62,11 @@ class GiftEmailService {
|
|
|
45
62
|
year: 'numeric'
|
|
46
63
|
}).format(date);
|
|
47
64
|
}
|
|
48
|
-
async sendPurchaseConfirmation({ buyerEmail, token, tierName, cadence, duration, expiresAt }) {
|
|
65
|
+
async sendPurchaseConfirmation({ buyerEmail, token, tierName, cadence, duration, expiresAt, recipientEmail = null }) {
|
|
49
66
|
const siteDomain = this.siteDomain;
|
|
50
67
|
const siteUrl = this.urlUtils.getSiteUrl();
|
|
51
68
|
const siteTitle = this.settingsCache.get('title') ?? siteDomain;
|
|
52
69
|
const giftLink = `${siteUrl.replace(/\/$/, '')}/gift/${token}`;
|
|
53
|
-
const cadenceLabel = this.getCadenceLabel(cadence, duration);
|
|
54
70
|
const { html, text } = await this.renderer.renderPurchaseConfirmation({
|
|
55
71
|
siteTitle,
|
|
56
72
|
siteUrl,
|
|
@@ -60,18 +76,21 @@ class GiftEmailService {
|
|
|
60
76
|
toEmail: buyerEmail,
|
|
61
77
|
gift: {
|
|
62
78
|
tierName,
|
|
63
|
-
|
|
79
|
+
duration,
|
|
80
|
+
isMonthly: cadence === 'month',
|
|
64
81
|
link: giftLink,
|
|
65
|
-
expiresAt: this.formatDate(expiresAt)
|
|
82
|
+
expiresAt: this.formatDate(expiresAt),
|
|
83
|
+
recipientEmail
|
|
66
84
|
}
|
|
67
85
|
});
|
|
68
|
-
await this.
|
|
86
|
+
await this.transactionalMailer.send({
|
|
69
87
|
to: buyerEmail,
|
|
70
|
-
subject: this.t('Your gift is ready'),
|
|
88
|
+
subject: recipientEmail ? this.t('Your gift is on its way') : this.t('Your gift is ready'),
|
|
71
89
|
html,
|
|
72
90
|
text,
|
|
73
91
|
from: this.getFromAddress(),
|
|
74
|
-
forceTextContent: true
|
|
92
|
+
forceTextContent: true,
|
|
93
|
+
disableTracking: true
|
|
75
94
|
});
|
|
76
95
|
}
|
|
77
96
|
async sendReminder({ memberEmail, memberName, tierName, consumesAt }) {
|
|
@@ -94,7 +113,7 @@ class GiftEmailService {
|
|
|
94
113
|
manageSubscriptionUrl
|
|
95
114
|
}
|
|
96
115
|
});
|
|
97
|
-
await this.
|
|
116
|
+
await this.transactionalMailer.send({
|
|
98
117
|
to: memberEmail,
|
|
99
118
|
subject: this.t('Your gift subscription is ending soon'),
|
|
100
119
|
html,
|
|
@@ -103,5 +122,66 @@ class GiftEmailService {
|
|
|
103
122
|
forceTextContent: true
|
|
104
123
|
});
|
|
105
124
|
}
|
|
125
|
+
async sendGiftDelivery({ recipientEmail, recipientName, buyerEmail, buyerName, personalMessage, token, tierName, benefits, cadence, duration, expiresAt }) {
|
|
126
|
+
const siteDomain = this.siteDomain;
|
|
127
|
+
const siteUrl = this.urlUtils.getSiteUrl();
|
|
128
|
+
const siteTitle = this.settingsCache.get('title') ?? siteDomain;
|
|
129
|
+
const giftLink = `${siteUrl.replace(/\/$/, '')}/gift/${token}`;
|
|
130
|
+
const { html, text } = await this.renderer.renderDelivery({
|
|
131
|
+
siteTitle,
|
|
132
|
+
siteUrl,
|
|
133
|
+
siteIconUrl: this.blogIcon.getIconUrl({ absolute: true, fallbackToDefault: false }),
|
|
134
|
+
siteDomain,
|
|
135
|
+
accentColor: this.accentColor,
|
|
136
|
+
accentTint: this.accentTint,
|
|
137
|
+
accentShade: this.accentShade,
|
|
138
|
+
toEmail: recipientEmail,
|
|
139
|
+
buyerEmail,
|
|
140
|
+
buyerName,
|
|
141
|
+
recipientName,
|
|
142
|
+
personalMessage,
|
|
143
|
+
gift: {
|
|
144
|
+
tierName,
|
|
145
|
+
benefits,
|
|
146
|
+
duration,
|
|
147
|
+
isMonthly: cadence === 'month',
|
|
148
|
+
link: giftLink,
|
|
149
|
+
expiresAt: this.formatDate(expiresAt)
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
const subject = this.t('{buyerName} sent you a gift', {
|
|
153
|
+
buyerName,
|
|
154
|
+
interpolation: { escapeValue: false }
|
|
155
|
+
});
|
|
156
|
+
if (!this.bulkMailer.isConfigured()) {
|
|
157
|
+
await this.transactionalMailer.send({
|
|
158
|
+
to: recipientEmail,
|
|
159
|
+
subject,
|
|
160
|
+
html,
|
|
161
|
+
text,
|
|
162
|
+
from: this.getFromAddress(),
|
|
163
|
+
forceTextContent: true,
|
|
164
|
+
tags: ['gift-delivery'],
|
|
165
|
+
disableTracking: true
|
|
166
|
+
});
|
|
167
|
+
return { providerMessageId: null };
|
|
168
|
+
}
|
|
169
|
+
const response = await this.bulkMailer.send({
|
|
170
|
+
subject,
|
|
171
|
+
html,
|
|
172
|
+
plaintext: text,
|
|
173
|
+
from: this.getFromAddress(),
|
|
174
|
+
tags: ['gift-delivery'],
|
|
175
|
+
disable_tracking: true
|
|
176
|
+
}, { [recipientEmail]: {} }, []);
|
|
177
|
+
const providerMessageId = (0, mailgun_message_id_1.getMailgunMessageId)(response) ?? null;
|
|
178
|
+
if (!providerMessageId) {
|
|
179
|
+
throw new errors_1.default.EmailError({
|
|
180
|
+
message: 'Bulk Mailgun did not accept gift delivery',
|
|
181
|
+
code: 'EMAIL_NOT_ACCEPTED'
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
return { providerMessageId };
|
|
185
|
+
}
|
|
106
186
|
}
|
|
107
187
|
exports.GiftEmailService = GiftEmailService;
|
|
@@ -4,7 +4,7 @@ exports.DbGift = exports.GiftStatusSchema = exports.GiftCadenceSchema = void 0;
|
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const db_date_1 = require("../../lib/db-date");
|
|
6
6
|
exports.GiftCadenceSchema = zod_1.z.enum(['month', 'year']);
|
|
7
|
-
exports.GiftStatusSchema = zod_1.z.enum(['purchased', 'redeemed', 'consumed', 'expired', 'refunded']);
|
|
7
|
+
exports.GiftStatusSchema = zod_1.z.enum(['payment_pending', 'purchased', 'redeemed', 'consumed', 'expired', 'refunded']);
|
|
8
8
|
/**
|
|
9
9
|
* The persisted gift row. Bookshelf remains the persistence implementation, but
|
|
10
10
|
* values read through it are still runtime data and are validated before they
|
|
@@ -12,20 +12,24 @@ exports.GiftStatusSchema = zod_1.z.enum(['purchased', 'redeemed', 'consumed', 'e
|
|
|
12
12
|
*/
|
|
13
13
|
exports.DbGift = zod_1.z.object({
|
|
14
14
|
token: zod_1.z.string(),
|
|
15
|
-
buyer_email: zod_1.z.string(),
|
|
15
|
+
buyer_email: zod_1.z.string().nullable(),
|
|
16
16
|
buyer_member_id: zod_1.z.string().nullable(),
|
|
17
|
+
buyer_name: zod_1.z.string().nullable().default(null),
|
|
18
|
+
recipient_name: zod_1.z.string().nullable().default(null),
|
|
19
|
+
personal_message: zod_1.z.string().nullable().default(null),
|
|
17
20
|
redeemer_member_id: zod_1.z.string().nullable(),
|
|
18
21
|
tier_id: zod_1.z.string(),
|
|
19
22
|
cadence: exports.GiftCadenceSchema,
|
|
20
23
|
duration: zod_1.z.number().int().nonnegative(),
|
|
21
24
|
currency: zod_1.z.string(),
|
|
22
25
|
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(),
|
|
26
|
+
stripe_checkout_session_id: zod_1.z.string().nullable(),
|
|
27
|
+
stripe_payment_intent_id: zod_1.z.string().nullable(),
|
|
28
|
+
checkout_started_at: db_date_1.DbDate.nullable().default(null),
|
|
25
29
|
consumes_at: db_date_1.DbDate.nullable(),
|
|
26
|
-
expires_at: db_date_1.DbDate,
|
|
30
|
+
expires_at: db_date_1.DbDate.nullable(),
|
|
27
31
|
status: exports.GiftStatusSchema,
|
|
28
|
-
purchased_at: db_date_1.DbDate,
|
|
32
|
+
purchased_at: db_date_1.DbDate.nullable(),
|
|
29
33
|
redeemed_at: db_date_1.DbDate.nullable(),
|
|
30
34
|
consumed_at: db_date_1.DbDate.nullable(),
|
|
31
35
|
expired_at: db_date_1.DbDate.nullable(),
|