ghost 6.37.1 → 6.38.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-i18n-0.0.0.tgz +0 -0
- package/core/boot.js +13 -9
- package/core/bridge.js +10 -6
- package/core/built/admin/assets/{PolarAngleAxis-Cavik65s.js → PolarAngleAxis-CB-FJBDa.js} +1 -1
- package/core/built/admin/assets/{_baseAssignValue-CnqN6ZMT.js → _baseAssignValue-BEAjPAfy.js} +1 -1
- package/core/built/admin/assets/{a-large-small-Wi4LtZX-.js → a-large-small-L1BJa6mG.js} +1 -1
- package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +1 -1
- package/core/built/admin/assets/admin-x-settings/{code-editor-view-Cf8T5Jfd.mjs → code-editor-view-bOZzJ3a1.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-BMWEh-vR.mjs → index-BRuMxaCz.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-Aj-hFSnY.mjs → index-CTAQj489.mjs} +5 -5
- package/core/built/admin/assets/admin-x-settings/{index-DjBJrXYn.mjs → index-WoKKBOe4.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{modals-8WpWZZdI.mjs → modals-BQ5M-RTA.mjs} +3 -3
- package/core/built/admin/assets/{arrow-right-BiAOKOto.js → arrow-right-B2j-ecEK.js} +1 -1
- package/core/built/admin/assets/{at-sign-z5gBMxDW.js → at-sign-CtAw8_lw.js} +1 -1
- package/core/built/admin/assets/{audience-DxNilLIv.js → audience-CvSOqHuo.js} +1 -1
- package/core/built/admin/assets/automations-BVUNuVOh.js +1 -0
- package/core/built/admin/assets/automations-Bi9RJq5E.js +1 -0
- package/core/built/admin/assets/{avatar-flipboard-5OzseZFV.js → avatar-flipboard-DocLTXLh.js} +1 -1
- package/core/built/admin/assets/{bluesky-sharing-D9YcSpW8.js → bluesky-sharing-DafX2lE1.js} +1 -1
- package/core/built/admin/assets/chart-BRQeD9eh.js +67 -0
- package/core/built/admin/assets/{chevron-up-DVt6fhV3.js → chevron-up-v4_QwmnL.js} +1 -1
- package/core/built/admin/assets/{chunk.524.fcd26ef812d73e2b0620.js → chunk.524.c3d844070c24d7984fd7.js} +4 -4
- package/core/built/admin/assets/{chunk.582.b61ffaad1c2fa4aba820.js → chunk.582.0e0ede1eac12926acceb.js} +6 -6
- package/core/built/admin/assets/{chunk.383.adb5a3fe32b6b98c43ff.js → chunk.995.4b32363ab39da3e7f5d7.js} +3 -3
- package/core/built/admin/assets/circle-alert-Dbz54fYb.js +1 -0
- package/core/built/admin/assets/{code-editor-view-CbQIqTrK.js → code-editor-view-D-pYCcdu.js} +1 -1
- package/core/built/admin/assets/comments-DSv09jRn.js +1 -0
- package/core/built/admin/assets/{content-helpers-jANWGIfA.js → content-helpers-DQdPPDGb.js} +1 -1
- package/core/built/admin/assets/{copy-BBLeLEnG.js → copy-BULQsPZ4.js} +1 -1
- package/core/built/admin/assets/{data-list-BveeOSGI.js → data-list-WxXC0iKE.js} +1 -1
- package/core/built/admin/assets/{deleted-feed-item-DupAEk4P.js → deleted-feed-item-ZBLfgRkK.js} +1 -1
- package/core/built/admin/assets/{dropzone-D3PSdAHN.js → dropzone-B8mAoWrn.js} +1 -1
- package/core/built/admin/assets/{edit-profile-NrCR9_bi.js → edit-profile-Bj3th8H0.js} +1 -1
- package/core/built/admin/assets/editor-BZV40eAE.css +1 -0
- package/core/built/admin/assets/editor-u20VuPzn.js +7 -0
- package/core/built/admin/assets/{empty-indicator-Dhi_KeYX.js → empty-indicator-P3IsUguf.js} +1 -1
- package/core/built/admin/assets/{en-BontI3SP.js → en-C9bUnuy7.js} +1 -1
- package/core/built/admin/assets/{feed-B3Kfiz5R.js → feed-B94mO944.js} +1 -1
- package/core/built/admin/assets/{filter-query-core-Dwh4Qulu.js → filter-query-core-CM0KKIr2.js} +1 -1
- package/core/built/admin/assets/filters-D52FTE0z.js +1 -0
- package/core/built/admin/assets/gh-chart-Xa6Nvzwz.js +1 -0
- package/core/built/admin/assets/{ghost-c25434d5bbf5a5429883d4c3590f1946.js → ghost-82f1c4ffe0e1cd058ce4b66b5253b620.js} +159 -201
- package/core/built/admin/assets/{ghost-dark-52df27573e5f2a75e6cf4b1d06eea9e4.css → ghost-dark-9897cf5102772faedf566e5e54d4fbee.css} +1 -1
- package/core/built/admin/assets/{ghost-fcf460028e04192f392f16e173f2e3a8.css → ghost-e11f1814be0f38e98bb77b49eba11178.css} +1 -1
- package/core/built/admin/assets/{growth-DRH8zUIJ.js → growth-D3TW3JIB.js} +1 -1
- package/core/built/admin/assets/{hash-UvX5iOa9.js → hash-CstXS-iB.js} +1 -1
- package/core/built/admin/assets/{header-G1a38jnf.js → header-StkTITw-.js} +1 -1
- package/core/built/admin/assets/{inbox-By7HZskI.js → inbox-D1RM3kVF.js} +1 -1
- package/core/built/admin/assets/index-BEkJ9xtY.css +1 -0
- package/core/built/admin/assets/{index-CS_ZmI8F.js → index-BJx5Uas5.js} +1 -1
- package/core/built/admin/assets/index-BKzg6OE7.js +1 -0
- package/core/built/admin/assets/{index-D7JyKf1m.js → index-BM-MBdD0.js} +1 -1
- package/core/built/admin/assets/index-BRzvVuXt.js +2 -0
- package/core/built/admin/assets/index-B_3xqeM9.js +1 -0
- package/core/built/admin/assets/{index-ydyrczVl.js → index-C-OdR2wJ.js} +1 -1
- package/core/built/admin/assets/{index-X-MG1ikg.js → index-C59QlJqT.js} +1 -1
- package/core/built/admin/assets/{index-CJo0Qh4V.js → index-CCq_1tCo.js} +1 -1
- package/core/built/admin/assets/{index-qHUvPjQB.js → index-CYyEyBYw.js} +11 -11
- package/core/built/admin/assets/{index-CX4ztLSk.js → index-DHbiBjyq.js} +1 -1
- package/core/built/admin/assets/{index-BP6hBbPK.js → index-DTVgSMyL.js} +2 -2
- package/core/built/admin/assets/{index-vZMbI8e8.js → index-DiUjEgRw.js} +1 -1
- package/core/built/admin/assets/{index-YM4ZplGj.js → index-DugxKzA6.js} +1 -1
- package/core/built/admin/assets/{index-D8InZCr2.js → index-DzVHlhO1.js} +1 -1
- package/core/built/admin/assets/{index-D4b7U6K-.js → index-LsaD9Cq7.js} +1 -1
- package/core/built/admin/assets/{index-DRUPF8nN.js → index-x0hNfyqU.js} +1 -1
- package/core/built/admin/assets/{inline-Bc9zHR7e.js → inline-B4cWemrJ.js} +1 -1
- package/core/built/admin/assets/{koenig-lexical-B-ZnqRE1.js → koenig-lexical-BOdeH3QJ.js} +1 -1
- package/core/built/admin/assets/{kpi-card-zyi5Onb0.js → kpi-card-P7zSpcx0.js} +1 -1
- package/core/built/admin/assets/{kpi-card-CzlPIEPE.js → kpi-card-plhYOEtA.js} +1 -1
- package/core/built/admin/assets/{kpi-tabs-Dsa5ldXK.js → kpi-tabs-DZw2Qktb.js} +1 -1
- package/core/built/admin/assets/{kpis-Cw5ZRQhk.js → kpis-CDHM0xhW.js} +1 -1
- package/core/built/admin/assets/{label-BvxmsD1M.js → label-Ckacdx14.js} +1 -1
- package/core/built/admin/assets/{links-CiS5BPKS.js → links-BtXE5d0Y.js} +1 -1
- package/core/built/admin/assets/{list-filter-zmkB7rg4.js → list-filter-DLjjiktn.js} +1 -1
- package/core/built/admin/assets/{list-header-Cp779e3H.js → list-header-Cc7Ia3nr.js} +1 -1
- package/core/built/admin/assets/loader-circle-DLJd-dNm.js +1 -0
- package/core/built/admin/assets/{mail-i2qs8CIG.js → mail-B97w28e2.js} +1 -1
- package/core/built/admin/assets/{main-layout-KCvKOX8F.js → main-layout-bwWvzc9B.js} +1 -1
- package/core/built/admin/assets/{members-w601v5JJ.js → members-CokS8MI2.js} +5 -5
- package/core/built/admin/assets/{message-square-text-bG9IoWIt.js → message-square-text-D3rsjDFR.js} +1 -1
- package/core/built/admin/assets/minus-BzXQVmMw.js +1 -0
- package/core/built/admin/assets/{modals-lNzhiL0o.js → modals-D0lip-iy.js} +2 -2
- package/core/built/admin/assets/{moderation-CyfNRSWj.js → moderation-Dv7AadCV.js} +1 -1
- package/core/built/admin/assets/newsletter-BazC5rIj.js +1 -0
- package/core/built/admin/assets/{newsletters-B8xSXSr_.js → newsletters-m6HQgU5J.js} +1 -1
- package/core/built/admin/assets/{note-dNfKEpxw.js → note-CCGiEKz-.js} +1 -1
- package/core/built/admin/assets/{onboarding-route-Drbw0QtT.js → onboarding-route-Cbt7qWNE.js} +1 -1
- package/core/built/admin/assets/overview-TN50tzUh.js +1 -0
- package/core/built/admin/assets/{pagemenu-VJXMoMB4.js → pagemenu-BXrHWtsU.js} +1 -1
- package/core/built/admin/assets/{pencil-DHNmlLjO.js → pencil-BL1r8KVy.js} +1 -1
- package/core/built/admin/assets/{post-analytics-PsqLsS2e.js → post-analytics-BqEXi6Ps.js} +1 -1
- package/core/built/admin/assets/{post-analytics-context-QLZsZCFg.js → post-analytics-context-DekYNlb3.js} +1 -1
- package/core/built/admin/assets/{post-analytics-header-PecXsIT-.js → post-analytics-header-AkNbn6uc.js} +1 -1
- package/core/built/admin/assets/{post-share-modal-B4Rvo7W1.js → post-share-modal-CXDGVKoQ.js} +1 -1
- package/core/built/admin/assets/posts/{app-utils-DIc5TmxO.mjs → app-utils-DxA7edI_.mjs} +2 -2
- package/core/built/admin/assets/posts/{automations-BZpT35kJ.mjs → automations-Cwu_gHl4.mjs} +64 -67
- package/core/built/admin/assets/posts/automations-D81hV_2c.mjs +13 -0
- package/core/built/admin/assets/posts/{avatar-K63cvqSS.mjs → avatar-IkjEzEjI.mjs} +19 -19
- package/core/built/admin/assets/posts/{button-ufGBCcsV.mjs → button-KaJZKcou.mjs} +21 -21
- package/core/built/admin/assets/posts/{check-IcEnuTpD.mjs → check-Cf74RvZ1.mjs} +21 -21
- package/core/built/admin/assets/posts/{comments-CyJU9Ums.mjs → comments-pdKhYbYG.mjs} +54 -52
- package/core/built/admin/assets/posts/createLucideIcon-Cj7h3r9g.mjs +320 -0
- package/core/built/admin/assets/posts/{dialog-D6_lBvtT.mjs → dialog-Ci3W8fjx.mjs} +71 -76
- package/core/built/admin/assets/posts/{dropdown-menu-BSSmAory.mjs → dropdown-menu-DZ9XCHSw.mjs} +5 -5
- package/core/built/admin/assets/posts/editor-Bb-xle0o.mjs +6084 -0
- package/core/built/admin/assets/posts/ellipsis-ChhZo9aT.mjs +10 -0
- package/core/built/admin/assets/posts/{empty-indicator-DvGCvRAX.mjs → empty-indicator-DnFs7hDy.mjs} +2 -2
- package/core/built/admin/assets/posts/{filters-BLBaHhJz.mjs → filters-CO_FUPX-.mjs} +42 -40
- package/core/built/admin/assets/posts/get-site-timezone-C21yKZT_.mjs +87 -0
- package/core/built/admin/assets/posts/{growth-Bz1ChJNB.mjs → growth-CtX9Efm2.mjs} +12 -12
- package/core/built/admin/assets/posts/heading-DZ_KtDTL.mjs +138 -0
- package/core/built/admin/assets/posts/{hooks-BEngBys9.mjs → hooks-DVhBDVlA.mjs} +3 -2
- package/core/built/admin/assets/posts/{index-BAF0YXsp.mjs → index-B4db8w_H.mjs} +181 -172
- package/core/built/admin/assets/posts/{inline-C2YFQAdU.mjs → inline-BfB-SNk0.mjs} +2 -2
- package/core/built/admin/assets/posts/{input-surface-TqJlNSp_.mjs → input-surface-CNwZJloi.mjs} +2 -2
- package/core/built/admin/assets/posts/{kpis-DAX5_0-Q.mjs → kpis-BvY_5Y1u.mjs} +8792 -8803
- package/core/built/admin/assets/posts/{links-A1YTsBbF.mjs → links-Ck-h7SKg.mjs} +57 -57
- package/core/built/admin/assets/posts/{loading-indicator-BZsjmp8g.mjs → loading-indicator-B_rU9Lie.mjs} +3 -3
- package/core/built/admin/assets/posts/mail-DiZ3oKEW.mjs +9 -0
- package/core/built/admin/assets/posts/{main-layout-D_HHTP_n.mjs → main-layout-aYo6qOBZ.mjs} +2 -2
- package/core/built/admin/assets/posts/{newsletter-BRzgjOIa.mjs → newsletter-BQc53TF2.mjs} +31 -30
- package/core/built/admin/assets/posts/{overview-C41VFjn7.mjs → overview-blz9Ak9q.mjs} +42 -41
- package/core/built/admin/assets/posts/plus-YVjtYaK3.mjs +15 -0
- package/core/built/admin/assets/posts/{post-analytics-DGgMkFaf.mjs → post-analytics-BQbWGoeu.mjs} +6 -6
- package/core/built/admin/assets/posts/{post-analytics-context-B1r3HBrp.mjs → post-analytics-context-Be7THgnd.mjs} +7 -7
- package/core/built/admin/assets/posts/{post-analytics-header-DTWSO0Aq.mjs → post-analytics-header-iDFy7Bao.mjs} +2762 -2764
- package/core/built/admin/assets/posts/{post-share-modal-ohHwk0Yt.mjs → post-share-modal-akP16idN.mjs} +50 -48
- package/core/built/admin/assets/posts/posts.js +2 -1
- package/core/built/admin/assets/posts/{settings-CZYL6Jhr.mjs → settings-BdQhfHxY.mjs} +2 -2
- package/core/built/admin/assets/posts/{sheet-D7YesQJE.mjs → sheet-3dBlKeXf.mjs} +14 -13
- package/core/built/admin/assets/posts/{skeleton-S1k58YKa.mjs → skeleton-DMgSvYqr.mjs} +11 -11
- package/core/built/admin/assets/posts/{source-icon-DnpCy5N4.mjs → source-icon-Bp5Qxq1E.mjs} +4 -4
- package/core/built/admin/assets/posts/{stats-CbOpowXb.mjs → stats-DJx9qc_u.mjs} +4 -4
- package/core/built/admin/assets/posts/{table-Cw3Wxf1C.mjs → table-BpVwuzeH.mjs} +2 -2
- package/core/built/admin/assets/posts/{data-list-BnZARP88.mjs → tabs-CbZCpxfI.mjs} +4885 -5284
- package/core/built/admin/assets/posts/{tags-BoMt6Ce3.mjs → tags-CNtvDS6f.mjs} +33 -33
- package/core/built/admin/assets/posts/{tags-CNdsvTtZ.mjs → tags-DFOoFZ_3.mjs} +2 -2
- package/core/built/admin/assets/posts/{tooltip-CTcyINxz.mjs → tooltip-Cc_09RDU.mjs} +106 -117
- package/core/built/admin/assets/posts/value-CwGM7M-1.mjs +410 -0
- package/core/built/admin/assets/posts/{virtual-list-window-Bs88yrut.mjs → virtual-list-window-D6nRoL0y.mjs} +5 -5
- package/core/built/admin/assets/posts/{web-Bsd8eTP6.mjs → web-yfPssEt8.mjs} +1042 -1040
- package/core/built/admin/assets/posts/x-DrUGcpfp.mjs +9 -0
- package/core/built/admin/assets/posts/zap-DAN0ur-o.mjs +24 -0
- package/core/built/admin/assets/posts-D-jyPPE6.js +1 -0
- package/core/built/admin/assets/power-Cc5ncYVl.js +1 -0
- package/core/built/admin/assets/{referrers-kcBT5smx.js → referrers-qnXsKsuv.js} +1 -1
- package/core/built/admin/assets/{repeat-Cn2sKd-L.js → repeat-BGIBynZH.js} +1 -1
- package/core/built/admin/assets/{reply-D7E-ZMd5.js → reply-DOKdAsL_.js} +1 -1
- package/core/built/admin/assets/{rocket-DnH0XAGW.js → rocket-CAN2NB82.js} +1 -1
- package/core/built/admin/assets/{select-f0MMMQWu.js → select-8S9kYGSM.js} +1 -1
- package/core/built/admin/assets/{settings-BAQNP3gU.js → settings-DeD-bmZc.js} +4 -4
- package/core/built/admin/assets/{settings-ORttG1jT.js → settings-e4IB9sx6.js} +1 -1
- package/core/built/admin/assets/{share-modal-CXU4tGY8.js → share-modal-CBavk20I.js} +1 -1
- package/core/built/admin/assets/{sort-button-BXMrChFu.js → sort-button-DlSKBzUq.js} +1 -1
- package/core/built/admin/assets/{source-icon-cjy79jGT.js → source-icon-27VIJSXr.js} +1 -1
- package/core/built/admin/assets/{sprout-DZ4tZ78G.js → sprout-D66nqc79.js} +1 -1
- package/core/built/admin/assets/{square-vOzqJf7x.js → square-DccaXQWe.js} +1 -1
- package/core/built/admin/assets/stats/{audience-5NeJICtP.mjs → audience-B84ClTDM.mjs} +3 -3
- package/core/built/admin/assets/stats/{content-helpers-DFSTl3uB.mjs → content-helpers-CBAddUg7.mjs} +4 -4
- package/core/built/admin/assets/stats/{index-C4T7u699.mjs → index-Bz5oVpED.mjs} +5 -5
- package/core/built/admin/assets/stats/{index-B8bp6qe2.mjs → index-Cs8i1uR4.mjs} +74 -75
- package/core/built/admin/assets/stats/{index-wvi17m79.mjs → index-DEsP_Tbd.mjs} +8 -8
- package/core/built/admin/assets/stats/{index-CFfAYHdD.mjs → index-Ds-pM47x.mjs} +5 -5
- package/core/built/admin/assets/stats/{index-Chqk7VlR.mjs → index-Wzk1IcAz.mjs} +6 -6
- package/core/built/admin/assets/stats/{kpi-tabs-Df9yOI2P.mjs → kpi-tabs-Cs4l8B_M.mjs} +7 -7
- package/core/built/admin/assets/stats/{sort-button-Doi8OgMU.mjs → sort-button-rR-GA9Hu.mjs} +3 -3
- package/core/built/admin/assets/stats/{stats-FXeCefyd.mjs → stats-RFpEJpEq.mjs} +34 -34
- package/core/built/admin/assets/stats/stats.js +1 -1
- package/core/built/admin/assets/stats/{use-growth-stats-93C_3EZk.mjs → use-growth-stats-DwgFzbq9.mjs} +3 -3
- package/core/built/admin/assets/{stats-CIGz2Win.js → stats-B4OybenW.js} +1 -1
- package/core/built/admin/assets/{stats-view-BTKRxYTQ.js → stats-view-BuExwFXa.js} +1 -1
- package/core/built/admin/assets/{step-1-BSMHbT-q.js → step-1-D_5LOQrC.js} +1 -1
- package/core/built/admin/assets/{step-2-_YC0LCZv.js → step-2-BZLBj8cj.js} +1 -1
- package/core/built/admin/assets/{step-3-DWMqP2bO.js → step-3-s5uNbcOH.js} +1 -1
- package/core/built/admin/assets/{table-mHKjXQba.js → table-pGS7EeJe.js} +1 -1
- package/core/built/admin/assets/{tabs-B93IVRBz.js → tabs-CjdKssUe.js} +1 -1
- package/core/built/admin/assets/{tags-DCb-x2ZB.js → tags-B0vRk0A6.js} +1 -1
- package/core/built/admin/assets/{tags-kUHJ1MZo.js → tags-Dso4-FA_.js} +1 -1
- package/core/built/admin/assets/{textarea-DswCtM7_.js → textarea-DzFnun0_.js} +1 -1
- package/core/built/admin/assets/{tiers-DueraQKd.js → tiers-4Vkr_D-L.js} +1 -1
- package/core/built/admin/assets/{toggle-group-ChTo8MPD.js → toggle-group-hDsr47IB.js} +1 -1
- package/core/built/admin/assets/{topic-filter-DrW3fYWo.js → topic-filter-C7TpPe1G.js} +1 -1
- package/core/built/admin/assets/{trash-B2ESGftj.js → trash-DCVuk7ZX.js} +1 -1
- package/core/built/admin/assets/{underline-vo-bPRac.js → underline-CjK8la2G.js} +1 -1
- package/core/built/admin/assets/{upload-DXQRkBBq.js → upload-CPYhmtWZ.js} +1 -1
- package/core/built/admin/assets/{use-growth-stats-CoB8-lsd.js → use-growth-stats-CaFSgp_R.js} +1 -1
- package/core/built/admin/assets/{use-simple-pagination-Y_Wv2N6g.js → use-simple-pagination-D0vJBMvF.js} +1 -1
- package/core/built/admin/assets/{user-round-check-Dp9a2x-e.js → user-round-check-CzQQeFd8.js} +1 -1
- package/core/built/admin/assets/{user-round-x-DwCTGlxc.js → user-round-x-BDFm8yQj.js} +1 -1
- package/core/built/admin/assets/value-BGBiTkF3.js +1 -0
- package/core/built/admin/assets/{vendor-17f3b979ba6f3898d1a8c5249cc22ff1.js → vendor-ce3fc901ccbcf37f36eb791b4e454138.js} +29 -31
- package/core/built/admin/assets/{virtual-list-window-BF7IvGMC.js → virtual-list-window-EKtWZ5cP.js} +1 -1
- package/core/built/admin/assets/{wallet-cards-IEmcxJvG.js → wallet-cards-C1NOBbWE.js} +1 -1
- package/core/built/admin/assets/web-DCA7HR1S.js +1 -0
- package/core/built/admin/index.html +8 -8
- package/core/frontend/helpers/social_accounts.js +91 -0
- package/core/frontend/services/sitemap/handler.js +14 -2
- package/core/frontend/services/sitemap/site-map-manager.js +133 -9
- package/core/frontend/web/middleware/handle-image-sizes.js +4 -0
- package/core/server/api/endpoints/automations.js +38 -45
- package/core/server/models/member.js +30 -25
- package/core/server/services/automations/automations-api.js +58 -0
- package/core/server/services/automations/automations-api.ts +71 -0
- package/core/server/services/automations/automations-repository.js +2 -0
- package/core/server/services/automations/automations-repository.ts +63 -0
- package/core/server/services/automations/fake-database-automations-repository.js +210 -0
- package/core/server/services/automations/fake-database-automations-repository.ts +273 -0
- package/core/server/services/automations/temporary-fake-database.js +5 -4
- package/core/server/services/gifts/email-templates/gift-purchase-confirmation.hbs +10 -32
- package/core/server/services/gifts/email-templates/gift-purchase-confirmation.js +6 -8
- package/core/server/services/gifts/email-templates/gift-purchase-confirmation.ts +6 -8
- package/core/server/services/gifts/email-templates/gift-reminder.hbs +12 -37
- package/core/server/services/gifts/email-templates/gift-reminder.js +5 -9
- package/core/server/services/gifts/email-templates/gift-reminder.ts +5 -9
- package/core/server/services/gifts/gift-service.js +1 -1
- package/core/server/services/gifts/gift-service.ts +2 -2
- package/core/server/services/member-welcome-emails/service.js +0 -1
- package/core/server/services/members/members-api/controllers/router-controller.js +52 -7
- package/core/server/services/members/members-api/services/member-bread-service.js +0 -8
- package/core/server/services/route-settings/route-settings.js +18 -2
- package/core/server/services/staff/email-templates/gift.hbs +1 -1
- package/core/server/services/staff/email-templates/gift.txt.js +1 -1
- package/core/server/services/staff/email-templates/new-gift-subscription.hbs +1 -1
- package/core/server/services/staff/email-templates/new-gift-subscription.txt.js +1 -1
- package/core/server/services/staff/staff-service-emails.js +1 -1
- package/core/server/services/url/index.js +16 -1
- package/core/server/services/url/lazy-find-resource.js +49 -0
- package/core/server/services/url/lazy-find-resource.ts +62 -0
- package/core/server/services/url/lazy-url-service.js +220 -0
- package/core/server/services/url/lazy-url-service.ts +265 -0
- package/core/server/web/api/endpoints/admin/routes.js +1 -0
- package/core/server/web/gift-preview/Inter.ttf +0 -0
- package/core/server/web/gift-preview/controller.js +56 -6
- package/core/server/web/gift-preview/gift-card-noise.png +0 -0
- package/core/server/web/gift-preview/gift-card-orb.png +0 -0
- package/core/server/web/gift-preview/image.js +170 -43
- package/core/shared/config/defaults.json +1 -0
- package/core/shared/one-at-a-time.js +28 -33
- package/core/shared/one-at-a-time.ts +58 -0
- package/package.json +18 -16
- package/pnpm-lock.yaml +989 -356
- package/core/built/admin/assets/automations-D1uMjd06.js +0 -1
- package/core/built/admin/assets/chart-_EXssQtm.js +0 -67
- package/core/built/admin/assets/comments-E00h1SbR.js +0 -1
- package/core/built/admin/assets/filters-CC7XfjOS.js +0 -1
- package/core/built/admin/assets/gh-chart-B9_ywrJJ.js +0 -1
- package/core/built/admin/assets/index-AsV-aY0z.css +0 -1
- package/core/built/admin/assets/index-B0y_ynZw.js +0 -1
- package/core/built/admin/assets/index-Bs6wWbLA.js +0 -2
- package/core/built/admin/assets/index-Cb4641XW.js +0 -1
- package/core/built/admin/assets/loader-circle-B5UPqSok.js +0 -1
- package/core/built/admin/assets/minus-DWx9VUtr.js +0 -1
- package/core/built/admin/assets/newsletter-fkQye9OK.js +0 -1
- package/core/built/admin/assets/overview-CZ-VrnXy.js +0 -1
- package/core/built/admin/assets/posts/createLucideIcon-DcUfTBt_.mjs +0 -454
- package/core/built/admin/assets/posts/get-site-timezone-DlXmHA3y.mjs +0 -93
- package/core/built/admin/assets/posts-Bx1RWrub.js +0 -1
- package/core/built/admin/assets/web-DSME3e8c.js +0 -1
|
@@ -2,18 +2,16 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.renderText = renderText;
|
|
4
4
|
function renderText(data) {
|
|
5
|
-
return `Your gift is ready
|
|
5
|
+
return `Your gift is ready
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Thanks for supporting ${data.siteTitle}. Share the link below to give someone access to ${data.gift.tierName} membership for ${data.gift.cadenceLabel}.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
${data.gift.link}
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
The link can be redeemed once and expires on ${data.gift.expiresAt}.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Happy gifting.
|
|
14
14
|
|
|
15
15
|
---
|
|
16
|
-
|
|
17
|
-
Sent to ${data.toEmail} from ${data.siteDomain}.
|
|
18
|
-
You received this email because you purchased a gift subscription on ${data.siteTitle}.`;
|
|
16
|
+
This message was sent from ${data.siteDomain} to ${data.toEmail}.`;
|
|
19
17
|
}
|
|
@@ -11,18 +11,16 @@ export interface GiftPurchaseConfirmationData {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export function renderText(data: GiftPurchaseConfirmationData): string {
|
|
14
|
-
return `Your gift is ready
|
|
14
|
+
return `Your gift is ready
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Thanks for supporting ${data.siteTitle}. Share the link below to give someone access to ${data.gift.tierName} membership for ${data.gift.cadenceLabel}.
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
${data.gift.link}
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
The link can be redeemed once and expires on ${data.gift.expiresAt}.
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
Happy gifting.
|
|
23
23
|
|
|
24
24
|
---
|
|
25
|
-
|
|
26
|
-
Sent to ${data.toEmail} from ${data.siteDomain}.
|
|
27
|
-
You received this email because you purchased a gift subscription on ${data.siteTitle}.`;
|
|
25
|
+
This message was sent from ${data.siteDomain} to ${data.toEmail}.`;
|
|
28
26
|
}
|
|
@@ -6,14 +6,13 @@
|
|
|
6
6
|
<title>Your gift subscription is ending soon</title>
|
|
7
7
|
<style>
|
|
8
8
|
@media only screen and (max-width: 620px) {
|
|
9
|
-
table.body h1 { font-size: 22px !important; padding-bottom: 16px !important; }
|
|
10
9
|
table.body p, table.body td, table.body a { font-size: 16px !important; }
|
|
11
10
|
table.body .wrapper { padding: 10px !important; }
|
|
12
11
|
table.body .content { padding: 0 !important; }
|
|
13
12
|
table.body .container { padding: 0 !important; width: 100% !important; }
|
|
14
13
|
table.body .main { border-radius: 0 !important; }
|
|
15
|
-
table.body
|
|
16
|
-
table.body p.small, table.body a.small { font-size:
|
|
14
|
+
table.body .btn a { width: 100% !important; }
|
|
15
|
+
table.body p.small, table.body a.small { font-size: 11px !important; }
|
|
17
16
|
}
|
|
18
17
|
</style>
|
|
19
18
|
</head>
|
|
@@ -39,37 +38,18 @@
|
|
|
39
38
|
{{/if}}
|
|
40
39
|
<tr>
|
|
41
40
|
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top;">
|
|
42
|
-
<
|
|
43
|
-
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; margin: 0; padding-bottom: 24px;">Your gift subscription
|
|
44
|
-
<
|
|
41
|
+
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #15212A; font-weight: normal; line-height: 24px; margin: 0; padding-bottom: 24px;">Hey there,</p>
|
|
42
|
+
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; line-height: 24px; margin: 0; padding-bottom: 24px;">Your gift subscription expires on <strong>{{gift.consumesAt}}</strong>.</p>
|
|
43
|
+
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; font-weight: normal; line-height: 24px; margin: 0; padding-bottom: 32px;">If you've been enjoying {{siteTitle}}, continue your membership for {{gift.priceAfter}} to keep full access to every post and newsletter.</p>
|
|
44
|
+
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box;">
|
|
45
45
|
<tbody>
|
|
46
46
|
<tr>
|
|
47
|
-
<td align="left" style="
|
|
48
|
-
<table border="0" cellpadding="0" cellspacing="0">
|
|
49
|
-
<tr>
|
|
50
|
-
<td style="padding-right: 8px; background-color: #F4F5F6; text-align: left; vertical-align: middle;" valign="middle">
|
|
51
|
-
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 700;">Gift subscription</p>
|
|
52
|
-
<p class="large" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 400;">{{gift.tierName}}</p>
|
|
53
|
-
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 700;">Ends on</p>
|
|
54
|
-
<p class="large" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 400;">{{gift.consumesAt}}</p>
|
|
55
|
-
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 700;">Price after gift ends</p>
|
|
56
|
-
<p class="large" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 0; color: #15171A; font-weight: 400;">{{gift.priceAfter}}</p>
|
|
57
|
-
</td>
|
|
58
|
-
</tr>
|
|
59
|
-
</table>
|
|
60
|
-
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box; padding-top: 24px;">
|
|
47
|
+
<td align="left" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; vertical-align: top;">
|
|
48
|
+
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
|
|
61
49
|
<tbody>
|
|
62
50
|
<tr>
|
|
63
|
-
<td
|
|
64
|
-
<
|
|
65
|
-
<tbody>
|
|
66
|
-
<tr>
|
|
67
|
-
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; vertical-align: top; background-color: {{accentColor}}; border-radius: 8px; text-align: center;">
|
|
68
|
-
<a href="{{gift.manageSubscriptionUrl}}" target="_blank" style="display: inline-block; color: #ffffff; background-color: {{accentColor}}; border: solid 1px {{accentColor}}; border-radius: 8px; padding: 10px 20px; text-decoration: none;">Continue subscription</a>
|
|
69
|
-
</td>
|
|
70
|
-
</tr>
|
|
71
|
-
</tbody>
|
|
72
|
-
</table>
|
|
51
|
+
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; vertical-align: top; background-color: {{accentColor}}; border-radius: 6px; text-align: center;">
|
|
52
|
+
<a href="{{gift.manageSubscriptionUrl}}" target="_blank" style="display: inline-block; color: #ffffff; background-color: {{accentColor}}; border: solid 1px {{accentColor}}; border-radius: 6px; box-sizing: border-box; cursor: pointer; text-decoration: none; font-size: 16px; font-weight: 600; margin: 0; padding: 12px 22px; border-color: {{accentColor}};">Continue membership</a>
|
|
73
53
|
</td>
|
|
74
54
|
</tr>
|
|
75
55
|
</tbody>
|
|
@@ -83,13 +63,8 @@
|
|
|
83
63
|
|
|
84
64
|
<!-- START FOOTER -->
|
|
85
65
|
<tr>
|
|
86
|
-
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size:
|
|
87
|
-
<p class="small" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height:
|
|
88
|
-
</td>
|
|
89
|
-
</tr>
|
|
90
|
-
<tr>
|
|
91
|
-
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 12px; vertical-align: top;">
|
|
92
|
-
<p class="small" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 18px; font-size: 12px; color: #7C8B9A; font-weight: normal; margin: 0;">You received this email because your gift subscription to <a class="small" href="{{siteUrl}}" style="text-decoration: underline; color: #7C8B9A; font-size: 12px;">{{siteTitle}}</a> is ending soon.</p>
|
|
66
|
+
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top; padding-top: 80px;">
|
|
67
|
+
<p class="small" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 16px; font-size: 11px; color: #738A94; font-weight: normal; margin: 0;">This message was sent from <a class="small" href="{{siteUrl}}" style="text-decoration: underline; color: #738A94; font-size: 11px;">{{siteDomain}}</a> to <a class="small" href="mailto:{{memberEmail}}" style="text-decoration: underline; color: #738A94; font-size: 11px;">{{memberEmail}}</a>.</p>
|
|
93
68
|
</td>
|
|
94
69
|
</tr>
|
|
95
70
|
|
|
@@ -2,19 +2,15 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.renderText = renderText;
|
|
4
4
|
function renderText(data) {
|
|
5
|
-
return `
|
|
5
|
+
return `Hey there,
|
|
6
6
|
|
|
7
|
-
Your gift subscription
|
|
7
|
+
Your gift subscription expires on ${data.gift.consumesAt}.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
Ends on: ${data.gift.consumesAt}
|
|
11
|
-
Price after gift ends: ${data.gift.priceAfter}
|
|
9
|
+
If you've been enjoying ${data.siteTitle}, continue your membership for ${data.gift.priceAfter} to keep full access to every post and newsletter.
|
|
12
10
|
|
|
13
|
-
Continue
|
|
11
|
+
Continue membership:
|
|
14
12
|
${data.gift.manageSubscriptionUrl}
|
|
15
13
|
|
|
16
14
|
---
|
|
17
|
-
|
|
18
|
-
Sent to ${data.memberEmail} from ${data.siteDomain}.
|
|
19
|
-
You received this email because your gift subscription to ${data.siteTitle} is ending soon.`;
|
|
15
|
+
This message was sent from ${data.siteDomain} to ${data.memberEmail}.`;
|
|
20
16
|
}
|
|
@@ -14,19 +14,15 @@ export interface GiftReminderData {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export function renderText(data: GiftReminderData): string {
|
|
17
|
-
return `
|
|
17
|
+
return `Hey there,
|
|
18
18
|
|
|
19
|
-
Your gift subscription
|
|
19
|
+
Your gift subscription expires on ${data.gift.consumesAt}.
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
Ends on: ${data.gift.consumesAt}
|
|
23
|
-
Price after gift ends: ${data.gift.priceAfter}
|
|
21
|
+
If you've been enjoying ${data.siteTitle}, continue your membership for ${data.gift.priceAfter} to keep full access to every post and newsletter.
|
|
24
22
|
|
|
25
|
-
Continue
|
|
23
|
+
Continue membership:
|
|
26
24
|
${data.gift.manageSubscriptionUrl}
|
|
27
25
|
|
|
28
26
|
---
|
|
29
|
-
|
|
30
|
-
Sent to ${data.memberEmail} from ${data.siteDomain}.
|
|
31
|
-
You received this email because your gift subscription to ${data.siteTitle} is ending soon.`;
|
|
27
|
+
This message was sent from ${data.siteDomain} to ${data.memberEmail}.`;
|
|
32
28
|
}
|
|
@@ -74,7 +74,7 @@ class GiftService {
|
|
|
74
74
|
throw new errors_1.default.NotFoundError({ message: `Tier not found: ${data.tierId}` });
|
|
75
75
|
}
|
|
76
76
|
try {
|
|
77
|
-
await this.deps.staffServiceEmails.
|
|
77
|
+
await this.deps.staffServiceEmails.notifyGiftPurchased({
|
|
78
78
|
name: member?.get('name') ?? null,
|
|
79
79
|
email: member?.get('email') ?? data.buyerEmail,
|
|
80
80
|
memberId: member?.id ?? null,
|
|
@@ -80,7 +80,7 @@ interface GiftEmailService {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
interface StaffServiceEmails {
|
|
83
|
-
|
|
83
|
+
notifyGiftPurchased(data: {
|
|
84
84
|
name: string | null;
|
|
85
85
|
email: string;
|
|
86
86
|
memberId: string | null;
|
|
@@ -208,7 +208,7 @@ export class GiftService {
|
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
try {
|
|
211
|
-
await this.deps.staffServiceEmails.
|
|
211
|
+
await this.deps.staffServiceEmails.notifyGiftPurchased({
|
|
212
212
|
name: member?.get('name') ?? null,
|
|
213
213
|
email: member?.get('email') ?? data.buyerEmail,
|
|
214
214
|
memberId: member?.id ?? null,
|
|
@@ -9,7 +9,6 @@ const emailAddressService = require('../email-address');
|
|
|
9
9
|
const settingsHelpers = require('../settings-helpers');
|
|
10
10
|
const EmailAddressParser = require('../email-address/email-address-parser');
|
|
11
11
|
const mail = require('../mail');
|
|
12
|
-
// @ts-expect-error type checker has trouble with the dynamic exporting in models
|
|
13
12
|
const {WelcomeEmailAutomation, WelcomeEmailAutomatedEmail, Newsletter} = require('../../models');
|
|
14
13
|
const MemberWelcomeEmailRenderer = require('./member-welcome-email-renderer');
|
|
15
14
|
const {MEMBER_WELCOME_EMAIL_LOG_KEY, MEMBER_WELCOME_EMAIL_TAG, MEMBER_WELCOME_EMAIL_SLUGS, MESSAGES} = require('./constants');
|
|
@@ -65,6 +65,45 @@ function extractGiftToken(input) {
|
|
|
65
65
|
return input.trim();
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Validate that a candidate return URL (e.g. Stripe Checkout success/cancel URL) points back
|
|
70
|
+
* to the configured Ghost site. Returns `undefined` when the candidate is missing, malformed,
|
|
71
|
+
* on a different origin, or outside of the site's subpath (for subpath installs) — leaving
|
|
72
|
+
* the downstream Stripe service to fall back to its configured default URL.
|
|
73
|
+
*
|
|
74
|
+
* This prevents the public checkout endpoints being abused as open-redirect surfaces.
|
|
75
|
+
*
|
|
76
|
+
* @param {string | undefined} candidate - URL provided in the request body
|
|
77
|
+
* @param {string} siteUrl - The site URL returned by urlUtils.getSiteUrl()
|
|
78
|
+
* @returns {string | undefined} The candidate URL if same-origin, otherwise undefined
|
|
79
|
+
*/
|
|
80
|
+
function sanitizeReturnUrl(candidate, siteUrl) {
|
|
81
|
+
if (typeof candidate !== 'string' || candidate.length === 0) {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let site;
|
|
86
|
+
let url;
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
site = new URL(siteUrl);
|
|
90
|
+
url = new URL(candidate);
|
|
91
|
+
} catch {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (url.origin !== site.origin) {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Normalize site/candidate paths to trailing-slash form so that /blog and /blog/
|
|
100
|
+
// are treated equivalently when the site is configured at a subpath.
|
|
101
|
+
const sitePath = site.pathname.endsWith('/') ? site.pathname : `${site.pathname}/`;
|
|
102
|
+
const urlPath = url.pathname.endsWith('/') ? url.pathname : `${url.pathname}/`;
|
|
103
|
+
|
|
104
|
+
return urlPath.startsWith(sitePath) ? url.href : undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
68
107
|
module.exports = class RouterController {
|
|
69
108
|
#inboxLinksDnsResolver = new dns.Resolver({maxTimeout: 1000});
|
|
70
109
|
|
|
@@ -202,9 +241,10 @@ module.exports = class RouterController {
|
|
|
202
241
|
customer = await this._stripeAPIService.getCustomer(subscription.get('customer_id'));
|
|
203
242
|
}
|
|
204
243
|
|
|
244
|
+
const siteUrl = this._urlUtils.getSiteUrl();
|
|
205
245
|
const session = await this._stripeAPIService.createCheckoutSetupSession(customer, {
|
|
206
|
-
successUrl: req.body.successUrl,
|
|
207
|
-
cancelUrl: req.body.cancelUrl,
|
|
246
|
+
successUrl: sanitizeReturnUrl(req.body.successUrl, siteUrl),
|
|
247
|
+
cancelUrl: sanitizeReturnUrl(req.body.cancelUrl, siteUrl),
|
|
208
248
|
subscription_id: req.body.subscription_id,
|
|
209
249
|
currency
|
|
210
250
|
});
|
|
@@ -267,8 +307,9 @@ module.exports = class RouterController {
|
|
|
267
307
|
|
|
268
308
|
const configurationId = this._settingsCache.get('stripe_billing_portal_configuration_id');
|
|
269
309
|
|
|
310
|
+
const siteUrl = this._urlUtils.getSiteUrl();
|
|
270
311
|
const session = await this._stripeAPIService.createBillingPortalSession(customer, {
|
|
271
|
-
returnUrl: req.body.returnUrl,
|
|
312
|
+
returnUrl: sanitizeReturnUrl(req.body.returnUrl, siteUrl),
|
|
272
313
|
...(configurationId && {configurationId})
|
|
273
314
|
});
|
|
274
315
|
const sessionInfo = {
|
|
@@ -694,10 +735,14 @@ module.exports = class RouterController {
|
|
|
694
735
|
metadata.newsletters = JSON.stringify(await this._validateNewsletters(JSON.parse(metadata.newsletters)));
|
|
695
736
|
}
|
|
696
737
|
|
|
738
|
+
const siteUrl = this._urlUtils.getSiteUrl();
|
|
739
|
+
const successUrl = sanitizeReturnUrl(req.body.successUrl, siteUrl);
|
|
740
|
+
const cancelUrl = sanitizeReturnUrl(req.body.cancelUrl, siteUrl);
|
|
741
|
+
|
|
697
742
|
// Build options
|
|
698
743
|
const options = {
|
|
699
|
-
successUrl
|
|
700
|
-
cancelUrl
|
|
744
|
+
successUrl,
|
|
745
|
+
cancelUrl,
|
|
701
746
|
email: req.body.customerEmail,
|
|
702
747
|
member,
|
|
703
748
|
metadata,
|
|
@@ -788,8 +833,8 @@ module.exports = class RouterController {
|
|
|
788
833
|
...options,
|
|
789
834
|
...data,
|
|
790
835
|
duration: 1, // gifts are currently 1 month or 1 year only
|
|
791
|
-
successUrl:
|
|
792
|
-
cancelUrl:
|
|
836
|
+
successUrl: siteUrl,
|
|
837
|
+
cancelUrl: options.cancelUrl || siteUrl
|
|
793
838
|
});
|
|
794
839
|
}
|
|
795
840
|
|
|
@@ -349,10 +349,6 @@ module.exports = class MemberBREADService {
|
|
|
349
349
|
withRelated.add('productEvents');
|
|
350
350
|
}
|
|
351
351
|
|
|
352
|
-
if (withRelated.has('email_recipients')) {
|
|
353
|
-
withRelated.add('email_recipients.email');
|
|
354
|
-
}
|
|
355
|
-
|
|
356
352
|
const model = await this.memberRepository.get(data, {
|
|
357
353
|
...options,
|
|
358
354
|
withRelated: Array.from(withRelated)
|
|
@@ -610,10 +606,6 @@ module.exports = class MemberBREADService {
|
|
|
610
606
|
withRelated.add('productEvents');
|
|
611
607
|
}
|
|
612
608
|
|
|
613
|
-
if (withRelated.has('email_recipients')) {
|
|
614
|
-
withRelated.add('email_recipients.email');
|
|
615
|
-
}
|
|
616
|
-
|
|
617
609
|
//option param to skip distinct from count query, distinct adds a lot of latency and in this case the result set will always be unique.
|
|
618
610
|
options.useBasicCount = true;
|
|
619
611
|
|
|
@@ -95,10 +95,20 @@ class RouteSettings {
|
|
|
95
95
|
await this.createBackupFile(this.settingsPath, this.backupPath);
|
|
96
96
|
await this.saveFile(filePath, this.settingsPath);
|
|
97
97
|
|
|
98
|
-
urlService.
|
|
98
|
+
const isLazy = urlService.facade.isLazy();
|
|
99
|
+
const resetUrlState = () => {
|
|
100
|
+
if (isLazy) {
|
|
101
|
+
// Drop registered router configs; reloadFrontend re-registers them.
|
|
102
|
+
urlService.facade.reset();
|
|
103
|
+
} else {
|
|
104
|
+
urlService.resetGenerators({releaseResourcesOnly: true});
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
resetUrlState();
|
|
99
109
|
|
|
100
110
|
const bringBackValidRoutes = async () => {
|
|
101
|
-
|
|
111
|
+
resetUrlState();
|
|
102
112
|
|
|
103
113
|
await this.restoreBackupFile(this.settingsPath, this.backupPath);
|
|
104
114
|
|
|
@@ -114,6 +124,12 @@ class RouteSettings {
|
|
|
114
124
|
});
|
|
115
125
|
}
|
|
116
126
|
|
|
127
|
+
// The lazy URL service has nothing to precompute, so the readiness
|
|
128
|
+
// poll below is unnecessary; hasFinished() returns true immediately.
|
|
129
|
+
if (isLazy) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
117
133
|
// @TODO: how can we get rid of this from here?
|
|
118
134
|
let tries = 0;
|
|
119
135
|
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
{{/if}}
|
|
29
29
|
<tr>
|
|
30
30
|
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top;">
|
|
31
|
-
<h1 style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 26px; color: #15212A; font-weight: bold; line-height: 28px; margin: 0; padding-bottom: 24px;">
|
|
31
|
+
<h1 style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 26px; color: #15212A; font-weight: bold; line-height: 28px; margin: 0; padding-bottom: 24px;">A gift subscription was purchased</h1>
|
|
32
32
|
<table width="100" border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; table-layout: fixed; width: 100%; min-width: 100%; box-sizing: border-box; background: #F4F5F6; border-radius: 8px;">
|
|
33
33
|
<tbody>
|
|
34
34
|
<tr>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module.exports = function giftText(data) {
|
|
2
2
|
// Be careful when you indent the email, because whitespaces are visible in emails!
|
|
3
3
|
return `
|
|
4
|
-
|
|
4
|
+
A gift subscription was purchased
|
|
5
5
|
|
|
6
6
|
From: ${data.gift.name}
|
|
7
7
|
Tier: ${data.gift.tierName} • ${data.gift.cadenceLabel}
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
{{/if}}
|
|
34
34
|
<tr>
|
|
35
35
|
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top;">
|
|
36
|
-
<h1 style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 26px; color: #15212A; font-weight: bold; line-height: 28px; margin: 0; padding-bottom: 24px;">
|
|
36
|
+
<h1 style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 26px; color: #15212A; font-weight: bold; line-height: 28px; margin: 0; padding-bottom: 24px;">A gift subscription was redeemed</h1>
|
|
37
37
|
<table width="100" border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; table-layout: fixed; width: 100%; min-width: 100%; box-sizing: border-box; background: #F4F5F6; border-radius: 8px;">
|
|
38
38
|
<tbody>
|
|
39
39
|
<tr>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module.exports = function (data) {
|
|
2
2
|
// Be careful when you indent the email, because whitespaces are visible in emails!
|
|
3
3
|
return `
|
|
4
|
-
|
|
4
|
+
A gift subscription was redeemed
|
|
5
5
|
|
|
6
6
|
Member: ${data.memberData.name}
|
|
7
7
|
Tier: ${data.tierData.name}${data.tierData.details ? ` • ${data.tierData.details}` : ''}
|
|
@@ -317,7 +317,7 @@ class StaffServiceEmails {
|
|
|
317
317
|
*
|
|
318
318
|
* @returns {Promise<void>}
|
|
319
319
|
*/
|
|
320
|
-
async
|
|
320
|
+
async notifyGiftPurchased({name, email, memberId, amount, currency, tierName, cadence, duration}) {
|
|
321
321
|
const users = await this.models.User.getEmailAlertUsers('gift-subscriptions');
|
|
322
322
|
const formattedAmount = this.getFormattedAmount({currency, amount: amount / 100});
|
|
323
323
|
|
|
@@ -2,6 +2,7 @@ const config = require('../../../shared/config');
|
|
|
2
2
|
const LocalFileCache = require('./local-file-cache');
|
|
3
3
|
const UrlService = require('./url-service');
|
|
4
4
|
const UrlServiceFacade = require('./url-service-facade');
|
|
5
|
+
const LazyUrlService = require('./lazy-url-service');
|
|
5
6
|
|
|
6
7
|
// NOTE: instead of a path we could give UrlService a "data-resolver" of some sort
|
|
7
8
|
// so it doesn't have to contain the logic to read data at all. This would be
|
|
@@ -22,7 +23,21 @@ if (process.env.NODE_ENV.startsWith('test')){
|
|
|
22
23
|
|
|
23
24
|
const cache = new LocalFileCache({storagePath, writeDisabled});
|
|
24
25
|
const urlService = new UrlService({cache});
|
|
25
|
-
|
|
26
|
+
|
|
27
|
+
// LazyUrlService is only attached to the facade when the lazyRouting flag is
|
|
28
|
+
// on. The eager UrlService stays in the require graph either way so test code
|
|
29
|
+
// and partially-migrated callers continue to work. The model layer is only
|
|
30
|
+
// pulled in when the flag is on so flag-off boot keeps its existing
|
|
31
|
+
// require-graph shape.
|
|
32
|
+
let lazyUrlService = null;
|
|
33
|
+
if (config.get('lazyRouting')) {
|
|
34
|
+
const models = require('../../models');
|
|
35
|
+
const {createFindResource} = require('./lazy-find-resource');
|
|
36
|
+
|
|
37
|
+
lazyUrlService = new LazyUrlService({findResource: createFindResource(models)});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const urlServiceFacade = new UrlServiceFacade({urlService, lazyUrlService});
|
|
26
41
|
|
|
27
42
|
// Singleton: default export remains the eager UrlService for backwards
|
|
28
43
|
// compatibility with existing imports. The new facade is exposed alongside
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createFindResource = createFindResource;
|
|
4
|
+
/**
|
|
5
|
+
* Builds the on-demand DB lookup hook used by LazyUrlService.resolveUrl.
|
|
6
|
+
*
|
|
7
|
+
* The eager UrlService only registers URLs for resources matching the
|
|
8
|
+
* resourceConfig filter (status:published, public visibility). Mirror that
|
|
9
|
+
* here so reverse lookups can't return drafts / private resources even though
|
|
10
|
+
* we now hit the DB on demand. We also disambiguate posts vs pages — both
|
|
11
|
+
* share `models.Post` and a permalink template like `/:slug/` could otherwise
|
|
12
|
+
* return either record.
|
|
13
|
+
*
|
|
14
|
+
* For tags and authors we use the public-facing scoped models (TagPublic,
|
|
15
|
+
* Author) which carry the shouldHavePosts gate, so reverse lookups can't
|
|
16
|
+
* return tags/users with no published posts (and in particular can't expose
|
|
17
|
+
* staff User accounts via a guessed slug).
|
|
18
|
+
*/
|
|
19
|
+
function createFindResource(models) {
|
|
20
|
+
const loadOne = async (Model, query, options = {}) => {
|
|
21
|
+
const result = await Model.findOne(query, { require: false, ...options });
|
|
22
|
+
return result ? result.toJSON() : null;
|
|
23
|
+
};
|
|
24
|
+
// Posts and pages need their tags/authors relations loaded so NQL
|
|
25
|
+
// filters like `tag:news` / `author:jane` can be evaluated against the
|
|
26
|
+
// returned record (LazyUrlService re-checks the route's filter after
|
|
27
|
+
// the DB lookup). Without these, every tag- or author-filtered route
|
|
28
|
+
// would silently 404.
|
|
29
|
+
const POSTLIKE_RELATIONS = ['tags', 'authors'];
|
|
30
|
+
const loadPostlike = (Model, query, dbType) => {
|
|
31
|
+
return loadOne(Model, { ...query, type: dbType, status: 'published' }, { withRelated: POSTLIKE_RELATIONS });
|
|
32
|
+
};
|
|
33
|
+
return (type, query) => {
|
|
34
|
+
if (type === 'posts') {
|
|
35
|
+
return loadPostlike(models.Post, query, 'post');
|
|
36
|
+
}
|
|
37
|
+
if (type === 'pages') {
|
|
38
|
+
return loadPostlike(models.Post, query, 'page');
|
|
39
|
+
}
|
|
40
|
+
if (type === 'tags') {
|
|
41
|
+
return loadOne(models.TagPublic, { ...query, visibility: 'public' });
|
|
42
|
+
}
|
|
43
|
+
if (type === 'authors') {
|
|
44
|
+
return loadOne(models.Author, { ...query, visibility: 'public' });
|
|
45
|
+
}
|
|
46
|
+
return Promise.resolve(null);
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
module.exports = { createFindResource };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type {FindResource} from './lazy-url-service';
|
|
2
|
+
|
|
3
|
+
interface BookshelfModel {
|
|
4
|
+
findOne(query: Record<string, unknown>, options?: Record<string, unknown>): Promise<{toJSON(): Record<string, unknown>} | null>;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface Models {
|
|
8
|
+
Post: BookshelfModel;
|
|
9
|
+
TagPublic: BookshelfModel;
|
|
10
|
+
Author: BookshelfModel;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Builds the on-demand DB lookup hook used by LazyUrlService.resolveUrl.
|
|
15
|
+
*
|
|
16
|
+
* The eager UrlService only registers URLs for resources matching the
|
|
17
|
+
* resourceConfig filter (status:published, public visibility). Mirror that
|
|
18
|
+
* here so reverse lookups can't return drafts / private resources even though
|
|
19
|
+
* we now hit the DB on demand. We also disambiguate posts vs pages — both
|
|
20
|
+
* share `models.Post` and a permalink template like `/:slug/` could otherwise
|
|
21
|
+
* return either record.
|
|
22
|
+
*
|
|
23
|
+
* For tags and authors we use the public-facing scoped models (TagPublic,
|
|
24
|
+
* Author) which carry the shouldHavePosts gate, so reverse lookups can't
|
|
25
|
+
* return tags/users with no published posts (and in particular can't expose
|
|
26
|
+
* staff User accounts via a guessed slug).
|
|
27
|
+
*/
|
|
28
|
+
export function createFindResource(models: Models): FindResource {
|
|
29
|
+
const loadOne = async (Model: BookshelfModel, query: Record<string, unknown>, options: Record<string, unknown> = {}) => {
|
|
30
|
+
const result = await Model.findOne(query, {require: false, ...options});
|
|
31
|
+
return result ? result.toJSON() : null;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Posts and pages need their tags/authors relations loaded so NQL
|
|
35
|
+
// filters like `tag:news` / `author:jane` can be evaluated against the
|
|
36
|
+
// returned record (LazyUrlService re-checks the route's filter after
|
|
37
|
+
// the DB lookup). Without these, every tag- or author-filtered route
|
|
38
|
+
// would silently 404.
|
|
39
|
+
const POSTLIKE_RELATIONS = ['tags', 'authors'];
|
|
40
|
+
|
|
41
|
+
const loadPostlike = (Model: BookshelfModel, query: Record<string, unknown>, dbType: string) => {
|
|
42
|
+
return loadOne(Model, {...query, type: dbType, status: 'published'}, {withRelated: POSTLIKE_RELATIONS});
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return (type: string, query: Record<string, string>) => {
|
|
46
|
+
if (type === 'posts') {
|
|
47
|
+
return loadPostlike(models.Post, query, 'post');
|
|
48
|
+
}
|
|
49
|
+
if (type === 'pages') {
|
|
50
|
+
return loadPostlike(models.Post, query, 'page');
|
|
51
|
+
}
|
|
52
|
+
if (type === 'tags') {
|
|
53
|
+
return loadOne(models.TagPublic, {...query, visibility: 'public'});
|
|
54
|
+
}
|
|
55
|
+
if (type === 'authors') {
|
|
56
|
+
return loadOne(models.Author, {...query, visibility: 'public'});
|
|
57
|
+
}
|
|
58
|
+
return Promise.resolve(null);
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = {createFindResource};
|