ghost 6.47.0 → 6.48.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/MigratorConfig.js +0 -1
- package/bin/create-migration.js +1 -1
- package/bin/generate-golden-email.js +0 -1
- package/components/tryghost-i18n-0.0.0.tgz +0 -0
- package/core/boot.js +13 -7
- package/core/built/admin/assets/{PolarAngleAxis-CLluELUg.js → PolarAngleAxis-DufzOBEz.js} +1 -1
- package/core/built/admin/assets/{_baseAssignValue-Dz1iqdwD.js → _baseAssignValue-BjUm4QeA.js} +1 -1
- package/core/built/admin/assets/{a-large-small-BA74vHPp.js → a-large-small-D0RF_WS9.js} +1 -1
- package/core/built/admin/assets/{account-migration-B-NqdKuN.js → account-migration-B7n6ezrZ.js} +1 -1
- package/core/built/admin/assets/activitypub/{account-migration-ClmVrAXr.js → account-migration-BJeRnMCl.js} +3 -3
- package/core/built/admin/assets/activitypub/activitypub.js +1 -1
- package/core/built/admin/assets/activitypub/{at-sign-9F3qd4Nn.js → at-sign-BygVOKdj.js} +2 -2
- package/core/built/admin/assets/activitypub/{avatar-flipboard-jBHA13Ej.js → avatar-flipboard-CNcjI8lK.js} +2 -2
- package/core/built/admin/assets/activitypub/{bluesky-sharing-1irhvVtF.js → bluesky-sharing-mgBNeRfA.js} +4 -4
- package/core/built/admin/assets/activitypub/{copy-DXRvsGiK.js → copy-BMChXIQc.js} +2 -2
- package/core/built/admin/assets/activitypub/{deleted-feed-item-67VQJde4.js → deleted-feed-item-D-spb6V1.js} +2 -2
- package/core/built/admin/assets/activitypub/{edit-profile-BfpbEj98.js → edit-profile-CDQ3V8-c.js} +4 -4
- package/core/built/admin/assets/activitypub/{feed-DUrI7SPC.js → feed-Vqr3WU-V.js} +4 -4
- package/core/built/admin/assets/activitypub/{hash-CMtB10PN.js → hash-D7SSXVOg.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-2xq8SJ7A.js → inbox-BHk5Dw27.js} +2 -2
- package/core/built/admin/assets/activitypub/{index-BT97xLpd.js → index-BejkeH-m.js} +2 -2
- package/core/built/admin/assets/activitypub/{index-DssHQAjK.js → index-BqL23J-m.js} +32 -32
- package/core/built/admin/assets/activitypub/{index-Ba6FXt8y.js → index-CHhvjNuG.js} +6 -6
- package/core/built/admin/assets/activitypub/{index-ERtz4Bvy.js → index-CZsNO9hA.js} +5 -5
- package/core/built/admin/assets/activitypub/{index-sgY7lLrg.js → index-CaIhRYzz.js} +3 -3
- package/core/built/admin/assets/activitypub/{index-D1EWoXrK.js → index-DA2MkQVu.js} +2 -2
- package/core/built/admin/assets/activitypub/{index-sudOvKcY.js → index-DHhFcmfu.js} +21 -21
- package/core/built/admin/assets/activitypub/{index-Ccighz1t.js → index-sh2bDSls.js} +4 -4
- package/core/built/admin/assets/activitypub/{label-KQSsSjmN.js → label-D80yhOIm.js} +2 -2
- package/core/built/admin/assets/activitypub/{moderation-B78if-r0.js → moderation-LJNmKBCi.js} +3 -3
- package/core/built/admin/assets/activitypub/{note-Bfr7ovn7.js → note-CyjKLcQ-.js} +4 -4
- package/core/built/admin/assets/activitypub/{reply-CehesW8Z.js → reply-BiiCFx7G.js} +2 -2
- package/core/built/admin/assets/activitypub/{separator-B_Ly_Qq8.js → separator-CY3PMmtI.js} +2 -2
- package/core/built/admin/assets/activitypub/{settings-D6R97p7v.js → settings-BQy6Gr7R.js} +3 -3
- package/core/built/admin/assets/activitypub/{step-1-Cl1YxgZ1.js → step-1-DCqRwgf3.js} +3 -3
- package/core/built/admin/assets/activitypub/{step-2-DDyR--Qe.js → step-2-DQlhehTO.js} +5 -5
- package/core/built/admin/assets/activitypub/{step-3-Don8gv0n.js → step-3-DAUCAcxW.js} +7 -7
- package/core/built/admin/assets/activitypub/{tabs-DTpfm-wu.js → tabs-B_6UuI7r.js} +3 -3
- package/core/built/admin/assets/activitypub/{topic-filter-Oee10885.js → topic-filter--FT1A_dc.js} +2 -2
- 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-CGZuiDGx.js → code-editor-view-Bp_sCJR0.js} +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-CwEblQZC.js → index-BPXA3pTW.js} +5 -5
- package/core/built/admin/assets/admin-x-settings/{index-rr_UntGE.js → index-BS375mg1.js} +3 -3
- package/core/built/admin/assets/admin-x-settings/{index-BpM6W2br.js → index-B_PlQoEG.js} +3 -3
- package/core/built/admin/assets/admin-x-settings/{index-BGdS5xw4.js → index-BflhAbu2.js} +3 -3
- package/core/built/admin/assets/admin-x-settings/{index-DtWUeJhp.js → index-CftMe8fe.js} +3 -3
- package/core/built/admin/assets/admin-x-settings/{index-B65inCwj.js → index-DRB7FH6y.js} +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-DSX261S9.js → index-DijZ3oAM.js} +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-DF6YEs01.js → index-cV0Lof9H.js} +1526 -1500
- package/core/built/admin/assets/admin-x-settings/{index-CluZYkUC.js → index-qc4x3vZj.js} +3 -3
- package/core/built/admin/assets/admin-x-settings/{modals-Bf4KOU31.js → modals-CkjuEByw.js} +55 -67
- package/core/built/admin/assets/{arrow-right-BxP3aeA5.js → arrow-right-BF77Yx73.js} +1 -1
- package/core/built/admin/assets/{at-sign-Bf0y-jim.js → at-sign-DhJF3n_V.js} +1 -1
- package/core/built/admin/assets/{audience-DHR1WgFK.js → audience-DHPKKayV.js} +1 -1
- package/core/built/admin/assets/{automated-emails-DbJbDLib.js → automated-emails-DInKYTEC.js} +1 -1
- package/core/built/admin/assets/{automations-aLgTzSsZ.js → automations-DJpeZw9T.js} +1 -1
- package/core/built/admin/assets/automations-DpQKN9j0.js +1 -0
- package/core/built/admin/assets/{avatar-flipboard-B0F2LMW5.js → avatar-flipboard-yRdmFC6R.js} +1 -1
- package/core/built/admin/assets/{bluesky-sharing-DjhtIuyr.js → bluesky-sharing-qSiCERoR.js} +1 -1
- package/core/built/admin/assets/{chart-BGwxlD0z.js → chart-CP2N9ZC-.js} +1 -1
- package/core/built/admin/assets/{chevron-up-C2FeVzre.js → chevron-up-BQ6fOpFR.js} +1 -1
- package/core/built/admin/assets/{chunk.524.31b31f7958dfae110016.js → chunk.524.ad4560630d4d62fbceb5.js} +4 -4
- package/core/built/admin/assets/{chunk.582.a855020953886af6e83b.js → chunk.582.fd4878e4e0af31ef86c0.js} +6 -6
- package/core/built/admin/assets/{chunk.972.258e6b1dab79b2189f1c.js → chunk.98.69968e0f393d82c2949c.js} +4 -4
- package/core/built/admin/assets/{code-editor-view-DzPkm_nJ.js → code-editor-view-CVODiRVJ.js} +1 -1
- package/core/built/admin/assets/{comments-DgItXUWN.js → comments-CP-NxopD.js} +1 -1
- package/core/built/admin/assets/{content-helpers-BNefU_PE.js → content-helpers-Crzkusiq.js} +1 -1
- package/core/built/admin/assets/{copy-DoNNUZlb.js → copy-r1LFsU75.js} +1 -1
- package/core/built/admin/assets/{data-list-CO1cZQRU.js → data-list-CzSc7z-2.js} +1 -1
- package/core/built/admin/assets/{deleted-feed-item-DC2wSAhv.js → deleted-feed-item-PyEM8FiX.js} +1 -1
- package/core/built/admin/assets/{dropzone-BTODLafn.js → dropzone-DI8A9qO6.js} +1 -1
- package/core/built/admin/assets/{edit-profile-BMMpmWIZ.js → edit-profile-DlJ64lV7.js} +1 -1
- package/core/built/admin/assets/editor-CevNZicQ.js +8 -0
- package/core/built/admin/assets/{empty-indicator-Dza0FQlj.js → empty-indicator-Dxg05yWg.js} +1 -1
- package/core/built/admin/assets/{en-yf9qAqTu.js → en-D93GVp3o.js} +1 -1
- package/core/built/admin/assets/{eye-CAqnnfQF.js → eye-6POPPpTw.js} +1 -1
- package/core/built/admin/assets/{feed-BkNsbWiF.js → feed-P_RMYb5x.js} +1 -1
- package/core/built/admin/assets/{field-Ddb1ZOqM.js → field-DeNjwWrL.js} +1 -1
- package/core/built/admin/assets/{filter-query-core-5Mac92kh.js → filter-query-core-DFs5WSmA.js} +1 -1
- package/core/built/admin/assets/{filters-BCb4bgtg.js → filters-C_BSQgb7.js} +1 -1
- package/core/built/admin/assets/{gh-chart-C6zkIg8k.js → gh-chart-pNHAbeQn.js} +1 -1
- package/core/built/admin/assets/{ghost-5313258fd425ced5db6516acde01ba36.js → ghost-3f5b3569e0beec61266cb74077677fbc.js} +3 -3
- package/core/built/admin/assets/{growth-ezUlN8OE.js → growth-B83tVOYO.js} +1 -1
- package/core/built/admin/assets/{hash-DE2OmChI.js → hash-BCTATV5W.js} +1 -1
- package/core/built/admin/assets/{inbox-OthiHvlV.js → inbox-rE3c5suT.js} +1 -1
- package/core/built/admin/assets/{index-BLyzLfyx.js → index-7EiRc5Ay.js} +1 -1
- package/core/built/admin/assets/{index-CV5v0eFE.js → index-B5bpZGTI.js} +1 -1
- package/core/built/admin/assets/index-BNtzAd7H.css +1 -0
- package/core/built/admin/assets/{index-BC1yWeaJ.js → index-BT33w2Io.js} +1 -1
- package/core/built/admin/assets/{index-DjffQ3VJ.js → index-BbkXWdhb.js} +1 -1
- package/core/built/admin/assets/{index-DH6PA3W6.js → index-BfhSrnrJ.js} +1 -1
- package/core/built/admin/assets/{index-CRvK0Iac.js → index-BmR_OUpb.js} +1 -1
- package/core/built/admin/assets/{index-CNV7fxKZ.js → index-BuhMgn5c.js} +1 -1
- package/core/built/admin/assets/{index-pkixJbdq.js → index-C-075SzJ.js} +34 -34
- package/core/built/admin/assets/{index-DJoByVS8.js → index-CB3hJd9E.js} +1 -1
- package/core/built/admin/assets/{index-D3bb-MEJ.js → index-CCISLIQd.js} +1 -1
- package/core/built/admin/assets/{index-BIEhcKiV.js → index-Cnptpx11.js} +1 -1
- package/core/built/admin/assets/{index-DK1cu59e.js → index-CrBRU81r.js} +1 -1
- package/core/built/admin/assets/{index-B0_p1-R0.js → index-CscUHFJp.js} +1 -1
- package/core/built/admin/assets/{index-Bi5qzf86.js → index-D3ZzfuJt.js} +1 -1
- package/core/built/admin/assets/{index-CKPny_6u.js → index-D4wn-U0V.js} +1 -1
- package/core/built/admin/assets/{index-YzcsD17L.js → index-DNRlkUeu.js} +1 -1
- package/core/built/admin/assets/{index-BoMsDCxD.js → index-D_G6negZ.js} +1 -1
- package/core/built/admin/assets/{index-CwsmINtV.js → index-DfxHSK1s.js} +1 -1
- package/core/built/admin/assets/{index-X91I8PcS.js → index-Dpc3COTl.js} +1 -1
- package/core/built/admin/assets/{index-c1h-EQpd.js → index-UsbnU2jy.js} +1 -1
- package/core/built/admin/assets/{index-oKgNVmwC.js → index-VfrJzh5m.js} +1 -1
- package/core/built/admin/assets/{index-D5MrQBSe.js → index-fbxc8Dpt.js} +1 -1
- package/core/built/admin/assets/{input-group-BOLMrDfJ.js → input-group-BLygwiDK.js} +1 -1
- package/core/built/admin/assets/{koenig-lexical-DlzcWUPw.js → koenig-lexical-B9U3sBS5.js} +1 -1
- package/core/built/admin/assets/{kpi-card-BlF7JKRC.js → kpi-card-Bmnvvp4X.js} +1 -1
- package/core/built/admin/assets/{kpi-card-HDshm3lO.js → kpi-card-CGVtzdgJ.js} +1 -1
- package/core/built/admin/assets/{kpi-tabs-Dty6zMSP.js → kpi-tabs-DxbvMkIU.js} +1 -1
- package/core/built/admin/assets/{kpis-CFz4r5wW.js → kpis-CQBAGVjP.js} +1 -1
- package/core/built/admin/assets/{label-CVzNcbjb.js → label-DpTwHQi_.js} +1 -1
- package/core/built/admin/assets/{links-D9dHwDlP.js → links-Bdw25FJF.js} +1 -1
- package/core/built/admin/assets/{list-page-shoQED7N.js → list-page-BvTQwP5F.js} +1 -1
- package/core/built/admin/assets/{loader-circle-cad701gH.js → loader-circle-3HJkQKWf.js} +1 -1
- package/core/built/admin/assets/{mail-DG4PKpZy.js → mail-DvrqP73n.js} +1 -1
- package/core/built/admin/assets/{main-layout-BmFla3Se.js → main-layout-BAPpm0bz.js} +1 -1
- package/core/built/admin/assets/{members-d19Z6k1C.js → members-CF4CzJSf.js} +1 -1
- package/core/built/admin/assets/minus-DJIePlnU.js +1 -0
- package/core/built/admin/assets/modals-CVsVi0zu.js +35 -0
- package/core/built/admin/assets/{moderation-BWBLnh4h.js → moderation-BX1KH5QL.js} +1 -1
- package/core/built/admin/assets/{newsletter-DNY4MroI.js → newsletter-BlKeb58e.js} +1 -1
- package/core/built/admin/assets/{newsletters-B10iBPP-.js → newsletters-3In8H0Z5.js} +1 -1
- package/core/built/admin/assets/{note-S0b6wjRR.js → note-ybwJCAxm.js} +1 -1
- package/core/built/admin/assets/{offers-B-9mZqKd.js → offers-Cl0ZNEMr.js} +1 -1
- package/core/built/admin/assets/{onboarding-route-AMFn0X5G.js → onboarding-route-A3ECeMAi.js} +1 -1
- package/core/built/admin/assets/{overview-WKadyZRW.js → overview-DrMrfsk-.js} +1 -1
- package/core/built/admin/assets/{pagemenu-BofQcFaF.js → pagemenu-O7HbsehH.js} +1 -1
- package/core/built/admin/assets/{pencil-LfMcD0vS.js → pencil-CknTbJeE.js} +1 -1
- package/core/built/admin/assets/{pin-CayUjL8e.js → pin-BNs-JdQ3.js} +1 -1
- package/core/built/admin/assets/{post-analytics-DMHvt3Jl.js → post-analytics-Dk1VloZp.js} +1 -1
- package/core/built/admin/assets/{post-analytics-context-10cqioP1.js → post-analytics-context-kPL94Ivz.js} +1 -1
- package/core/built/admin/assets/{post-analytics-header-DlICE18K.js → post-analytics-header-CpMOozq-.js} +1 -1
- package/core/built/admin/assets/{post-share-modal-Cfwy2UwD.js → post-share-modal-F0dobdhF.js} +1 -1
- package/core/built/admin/assets/posts/{alert-dialog-cQK32v9G.js → alert-dialog-NoPLULhG.js} +5 -5
- package/core/built/admin/assets/posts/{app-utils-DQ2hixop.js → app-utils-BiM9QO4L.js} +3 -3
- package/core/built/admin/assets/posts/{automations-BqkU9-zY.js → automations-BgIAmq9f.js} +3 -3
- package/core/built/admin/assets/posts/{automations-CoilKpVH.js → automations-D_XwO405.js} +30 -30
- package/core/built/admin/assets/posts/{avatar-BejAO6Rj.js → avatar-BYsYOpmq.js} +5 -5
- package/core/built/admin/assets/posts/{badge-BYGRyd2f.js → badge-B54vLono.js} +2 -2
- package/core/built/admin/assets/posts/{button-CG2Dpjqy.js → button-DMLfwOQt.js} +3 -3
- package/core/built/admin/assets/posts/{check-fsC5BvGC.js → check-D6qDi62T.js} +2 -2
- package/core/built/admin/assets/posts/{comments-CsyF5C1S.js → comments-CyYCbvUG.js} +26 -26
- package/core/built/admin/assets/posts/{data-list-JslRF2F5.js → data-list-B0DrMm-x.js} +4 -4
- package/core/built/admin/assets/posts/{dialog-3KY3A8t5.js → dialog-DtEZpY1q.js} +3 -3
- package/core/built/admin/assets/posts/{editor-3HPW_xW4.js → editor-LJHLmUoW.js} +1943 -1943
- package/core/built/admin/assets/posts/{ellipsis-BhK5S70U.js → ellipsis-JeIF0Biz.js} +2 -2
- package/core/built/admin/assets/posts/{empty-indicator-C9DbE9wZ.js → empty-indicator-dktzN32y.js} +2 -2
- package/core/built/admin/assets/posts/{filters-BCBDCnQF.js → filters-D1L-LU_m.js} +10 -10
- package/core/built/admin/assets/posts/{get-site-timezone-83C41MGz.js → get-site-timezone-dsyl8nlB.js} +3 -3
- package/core/built/admin/assets/posts/{growth-COoL1kMe.js → growth-yX8UB_ce.js} +12 -12
- package/core/built/admin/assets/posts/{heading-D3VqNO3z.js → heading-CNte43wH.js} +2 -2
- package/core/built/admin/assets/posts/{hooks-5YpJYWOw.js → hooks-BIgv7QBp.js} +2 -2
- package/core/built/admin/assets/posts/{index-Dqn5WIFh.js → index-2FO6Vy0r.js} +11 -11
- package/core/built/admin/assets/posts/{input-C1qcZBGw.js → input-BycmlCp8.js} +3 -3
- package/core/built/admin/assets/posts/{input-surface-C4vUAdbV.js → input-surface-vY3ACaso.js} +2 -2
- package/core/built/admin/assets/posts/{koenig-lexical-NZjxFY0X.js → koenig-lexical-Cq8SUcZC.js} +2 -2
- package/core/built/admin/assets/posts/{kpis-C2FEXoUT.js → kpis-CAJcU1w0.js} +27 -27
- package/core/built/admin/assets/posts/{label-CmJzmJY7.js → label-BmLTflMF.js} +3 -3
- package/core/built/admin/assets/posts/{links-nzud7t_t.js → links-BVcmMZmF.js} +4 -4
- package/core/built/admin/assets/posts/{list-page-D_rv4TgN.js → list-page-DX2WRn5T.js} +4 -4
- package/core/built/admin/assets/posts/{loading-indicator-DAOD6O2b.js → loading-indicator-C6FD4nS6.js} +3 -3
- package/core/built/admin/assets/posts/{mail-CtH0gTpq.js → mail-BMyoNUVM.js} +2 -2
- package/core/built/admin/assets/posts/{main-layout-VqvCxOXQ.js → main-layout-ClnxdW1Y.js} +2 -2
- package/core/built/admin/assets/posts/{message-circle-D4bCjxKS.js → message-circle-DwfHX-p1.js} +2 -2
- package/core/built/admin/assets/posts/{newsletter-BfwFyv6d.js → newsletter-Bid5_Bim.js} +19 -19
- package/core/built/admin/assets/posts/{overview-DKGum6ro.js → overview-BXti6Wg_.js} +18 -18
- package/core/built/admin/assets/posts/{pencil-Cffx-vm3.js → pencil-ChTk8IpW.js} +2 -2
- package/core/built/admin/assets/posts/{plus-D2ekwsq0.js → plus-DIKls8C6.js} +2 -2
- package/core/built/admin/assets/posts/{popover-DdbdKflP.js → popover-DVZsAT5u.js} +4 -4
- package/core/built/admin/assets/posts/{post-analytics-BleJYoNZ.js → post-analytics-DBL_kS0g.js} +6 -6
- package/core/built/admin/assets/posts/{post-analytics-context-DT3Avn9Q.js → post-analytics-context-Dr5y5nxd.js} +6 -6
- package/core/built/admin/assets/posts/{post-analytics-header-BktlrM_v.js → post-analytics-header-LH2-Jzgp.js} +13 -13
- package/core/built/admin/assets/posts/{post-share-modal-G8v3ZuSg.js → post-share-modal-D4DrfNfJ.js} +6 -6
- package/core/built/admin/assets/posts/{posts-CdWV_nlm.js → posts-ByxhcCrn.js} +2 -2
- package/core/built/admin/assets/posts/posts.js +1 -1
- package/core/built/admin/assets/posts/{search-CXP00fG8.js → search-DZR23LP-.js} +2 -2
- package/core/built/admin/assets/posts/{settings-DEXxDYc2.js → settings-CWyOKKnK.js} +2 -2
- package/core/built/admin/assets/posts/{sheet-CXHmD_eT.js → sheet-CNe7II5c.js} +3 -3
- package/core/built/admin/assets/posts/{site-C6DajagV.js → site-H9RAiQVv.js} +2 -2
- package/core/built/admin/assets/posts/{skeleton-Bihnkn8q.js → skeleton-Cn6x1x_C.js} +3 -3
- package/core/built/admin/assets/posts/{source-icon-C3oQy3UI.js → source-icon-BZOYujd1.js} +4 -4
- package/core/built/admin/assets/posts/{stats-DFicPtCX.js → stats-ugHCg8Oe.js} +4 -4
- package/core/built/admin/assets/posts/{table-DSJmeU5t.js → table-dG3Q5X95.js} +2 -2
- package/core/built/admin/assets/posts/{tabs-oRdVpN8P.js → tabs--EW8uL5B.js} +4 -4
- package/core/built/admin/assets/posts/{tags-6v5Qt_05.js → tags-DVdOfiSf.js} +2 -2
- package/core/built/admin/assets/posts/{tags-Upnkm0CF.js → tags-i0HJqNYU.js} +16 -16
- package/core/built/admin/assets/posts/{tooltip-Ci7pesWi.js → tooltip-ZvsT1Fs9.js} +4 -4
- package/core/built/admin/assets/posts/{trash-2-NCYWjRlg.js → trash-2-BT9fEHUL.js} +2 -2
- package/core/built/admin/assets/posts/{value-CcwzOh2_.js → value-DoFcsWDk.js} +2 -2
- package/core/built/admin/assets/posts/{virtual-list-window-CNahQBXY.js → virtual-list-window-Bt47XODn.js} +3 -3
- package/core/built/admin/assets/posts/{web-C3KrPq0T.js → web-BMTRLT-h.js} +20 -20
- package/core/built/admin/assets/posts/{zap-k30mZEuO.js → zap-DcIfEzKX.js} +2 -2
- package/core/built/admin/assets/{posts-DAfLuxAA.js → posts-DODOCB1E.js} +1 -1
- package/core/built/admin/assets/{power-Drkzg1xX.js → power-BZ7BM82m.js} +1 -1
- package/core/built/admin/assets/{referrers-TYxTJ2On.js → referrers-xwNb6L-8.js} +1 -1
- package/core/built/admin/assets/{repeat-CTM1qAzl.js → repeat-DHMCQMCA.js} +1 -1
- package/core/built/admin/assets/{reply-DXN16g7W.js → reply-DoLURZi2.js} +1 -1
- package/core/built/admin/assets/{rocket-DLixlWs0.js → rocket-BDH89dZr.js} +1 -1
- package/core/built/admin/assets/{select-DbiLEqYb.js → select-oE39QRll.js} +1 -1
- package/core/built/admin/assets/{send-B71C_46D.js → send-QgRN_yRy.js} +1 -1
- package/core/built/admin/assets/{settings-BBMPA3MP.js → settings-CF4NLxuS.js} +4 -4
- package/core/built/admin/assets/{settings-lSFHFrsJ.js → settings-D7e6KLAi.js} +1 -1
- package/core/built/admin/assets/{share-modal-CXrYja6o.js → share-modal-BxIh7i7o.js} +1 -1
- package/core/built/admin/assets/{sort-button-WYo-9Kge.js → sort-button-CEe_40Pi.js} +1 -1
- package/core/built/admin/assets/{source-icon-D6BzjYPP.js → source-icon-CzTNLO6j.js} +1 -1
- package/core/built/admin/assets/{sprout-DVfG_qhy.js → sprout-B2fN41p-.js} +1 -1
- package/core/built/admin/assets/{square-BbUJwQJi.js → square-CJZsjE-3.js} +1 -1
- package/core/built/admin/assets/{stats-CnXALQ4j.js → stats-jTxCK1CB.js} +1 -1
- package/core/built/admin/assets/{stats-view-B-yeMUkl.js → stats-view-B4jz69N8.js} +1 -1
- package/core/built/admin/assets/{step-1-DUl_hlpV.js → step-1-BovjhDNc.js} +1 -1
- package/core/built/admin/assets/{step-2-DjX_A4Ke.js → step-2-ClG40BfX.js} +1 -1
- package/core/built/admin/assets/{step-3-CnDCCA8T.js → step-3-ZMSyEHZI.js} +1 -1
- package/core/built/admin/assets/{table-AagTvMa9.js → table-B8Pj96or.js} +1 -1
- package/core/built/admin/assets/{tabs-IDJWnQD-.js → tabs-BZosOk0o.js} +1 -1
- package/core/built/admin/assets/{tags-CXAcIDw-.js → tags-BpWoYBfB.js} +1 -1
- package/core/built/admin/assets/{tags-C3lykbgu.js → tags-DZukMyuU.js} +1 -1
- package/core/built/admin/assets/{textarea-Cc3nUXep.js → textarea-BtnzsOk7.js} +1 -1
- package/core/built/admin/assets/{thumbs-down-SsjC2lMD.js → thumbs-down-CZTzyz7D.js} +1 -1
- package/core/built/admin/assets/{tiers-J8AeZmRd.js → tiers-Do7zBqAk.js} +1 -1
- package/core/built/admin/assets/{toggle-group-BzD8yimX.js → toggle-group-abk9FYXi.js} +1 -1
- package/core/built/admin/assets/{topic-filter-CA_L6ehz.js → topic-filter-CdMvjkmc.js} +1 -1
- package/core/built/admin/assets/{trash-BKhRqxsX.js → trash-DNXRsBf0.js} +1 -1
- package/core/built/admin/assets/{underline-_HIgX6e1.js → underline-HXWuVCAi.js} +1 -1
- package/core/built/admin/assets/{undo-2-ClkkOS0O.js → undo-2-C2ntNJKC.js} +1 -1
- package/core/built/admin/assets/{upload-DEPHbyvo.js → upload-D52l7HIb.js} +1 -1
- package/core/built/admin/assets/{use-growth-stats-BM2qWt0C.js → use-growth-stats-Dgs0UxDT.js} +1 -1
- package/core/built/admin/assets/{use-simple-pagination-zyklfekn.js → use-simple-pagination-Cj2rVggA.js} +1 -1
- package/core/built/admin/assets/{user-round-check-DoaGU2hC.js → user-round-check-C0GdT99c.js} +1 -1
- package/core/built/admin/assets/{user-round-x-CwEKNuU6.js → user-round-x-CVCvxuUG.js} +1 -1
- package/core/built/admin/assets/{virtual-list-window-C0Cv0gUC.js → virtual-list-window-mdQS2bt_.js} +1 -1
- package/core/built/admin/assets/{wallet-cards-BjdG5glh.js → wallet-cards-CtLF01jQ.js} +1 -1
- package/core/built/admin/assets/{web-BZNnanCT.js → web-DTib8_n8.js} +1 -1
- package/core/built/admin/index.html +6 -6
- package/core/frontend/helpers/ghost_foot.js +12 -2
- package/core/frontend/helpers/prev_post.js +1 -1
- package/core/frontend/helpers/tpl/gift-toast.hbs +130 -0
- package/core/frontend/services/proxy.js +9 -0
- package/core/frontend/services/routing/controllers/entry/canonical-url.js +15 -0
- package/core/frontend/services/routing/controllers/entry/canonical-url.ts +14 -0
- package/core/frontend/services/routing/controllers/entry/gift-links.js +82 -0
- package/core/frontend/services/routing/controllers/entry/gift-links.ts +92 -0
- package/core/frontend/services/routing/controllers/entry/markdown.js +62 -0
- package/core/frontend/services/routing/controllers/entry/markdown.ts +66 -0
- package/core/frontend/services/routing/controllers/entry.js +96 -105
- package/core/frontend/services/routing/controllers/entry.ts +97 -0
- package/core/frontend/services/routing/controllers/index.js +1 -1
- package/core/frontend/web/middleware/frontend-caching.js +9 -0
- package/core/server/adapters/cache/Redis.js +0 -1
- package/core/server/adapters/redirects/RedirectsStoreBase.d.ts +0 -4
- package/core/server/adapters/scheduling/scheduling-default.js +8 -1
- package/core/server/api/endpoints/gift-links.js +23 -16
- package/core/server/api/endpoints/gift-links.ts +25 -17
- package/core/server/api/endpoints/index.js +0 -1
- package/core/server/api/endpoints/previews.js +3 -24
- package/core/server/api/endpoints/utils/serializers/output/gift-links.js +4 -4
- package/core/server/api/endpoints/utils/serializers/output/gift-links.ts +4 -4
- package/core/server/api/endpoints/utils/serializers/output/index.js +0 -1
- package/core/server/api/endpoints/utils/validators/input/automation_email_previews.js +8 -9
- package/core/server/api/endpoints/utils/validators/input/index.js +0 -1
- package/core/server/data/migrations/versions/6.48/2026-06-23-00-00-00-drop-gift-links-revoked-at.js +6 -0
- package/core/server/data/migrations/versions/6.48/2026-06-23-00-00-01-rename-gift-links-revoke-all-permission-to-remove-all.js +37 -0
- package/core/server/data/migrations/versions/6.48/2026-06-24-00-00-00-drop-gift-links-redemption-columns.js +10 -0
- package/core/server/data/schema/fixtures/fixtures.json +2 -2
- package/core/server/data/schema/schema.js +1 -4
- package/core/server/data/seeders/data-generator.js +4 -1
- package/core/server/data/seeders/importers/members-login-events-importer.js +0 -1
- package/core/server/data/seeders/importers/members-newsletters-importer.js +0 -1
- package/core/server/data/seeders/importers/members-paid-subscription-events-importer.js +0 -1
- package/core/server/data/seeders/importers/members-status-events-importer.js +0 -1
- package/core/server/data/seeders/importers/members-stripe-customers-importer.js +0 -1
- package/core/server/data/seeders/importers/members-stripe-customers-subscriptions-importer.js +0 -1
- package/core/server/data/seeders/importers/members-subscribe-events-importer.js +0 -1
- package/core/server/data/seeders/importers/members-subscription-created-events-importer.js +0 -1
- package/core/server/lib/lexical.js +62 -36
- package/core/server/lib/post-revisions.js +0 -2
- package/core/server/lib/post-revisions.ts +0 -3
- package/core/server/models/index.js +0 -3
- package/core/server/services/auth/reset-authentication.js +0 -2
- package/core/server/services/auth/reset-authentication.ts +0 -3
- package/core/server/services/auth/session/index.js +0 -1
- package/core/server/services/automations/automations-api.js +59 -17
- package/core/server/services/automations/automations-api.ts +69 -16
- package/core/server/services/automations/database-automations-repository.js +58 -3
- package/core/server/services/automations/database-automations-repository.ts +70 -3
- package/core/server/services/automations/stale-lock-cutoff.js +7 -0
- package/core/server/services/automations/stale-lock-cutoff.ts +5 -0
- package/core/server/services/email-address/email-address-parser.js.d.ts +0 -1
- package/core/server/services/email-service/batch-sending-service.js +99 -26
- package/core/server/services/email-service/email-service-wrapper.js +7 -2
- package/core/server/services/email-service/email-service.js +146 -4
- package/core/server/services/gift-links/database.js +0 -3
- package/core/server/services/gift-links/database.ts +1 -4
- package/core/server/services/gift-links/index.js +2 -2
- package/core/server/services/gift-links/index.ts +4 -2
- package/core/server/services/gift-links/models.js +0 -2
- package/core/server/services/gift-links/models.ts +0 -2
- package/core/server/services/gift-links/queries.js +2 -2
- package/core/server/services/gift-links/queries.ts +2 -2
- package/core/server/services/gift-links/serializers.js +0 -2
- package/core/server/services/gift-links/serializers.ts +0 -2
- package/core/server/services/gift-links/service.js +54 -35
- package/core/server/services/gift-links/service.ts +70 -35
- package/core/server/services/gifts/gift-reminder-scheduler.js +0 -2
- package/core/server/services/gifts/gift-reminder-scheduler.ts +0 -2
- package/core/server/services/indexnow.js +8 -1
- package/core/server/services/internal-keys/index.js +0 -1
- package/core/server/services/internal-keys/index.ts +0 -2
- package/core/server/services/lib/dynamic-redirect-manager.d.ts +0 -1
- package/core/server/services/lib/in-memory-repository.js +0 -1
- package/core/server/services/lib/in-memory-repository.ts +0 -2
- package/core/server/services/members/api.js +0 -3
- package/core/server/services/members/create-paid-member-shim.js +31 -0
- package/core/server/services/members/create-paid-member-shim.ts +31 -0
- package/core/server/services/members/emails/signin.js +0 -1
- package/core/server/services/members/members-api/members-api.js +0 -2
- package/core/server/services/members/members-api/repositories/member-repository.js +0 -3
- package/core/server/services/members/service.js +4 -0
- package/core/server/services/post-scheduling/index.js +0 -2
- package/core/server/services/post-scheduling/index.ts +0 -2
- package/core/server/services/post-scheduling/post-scheduling.js +0 -5
- package/core/server/services/post-scheduling/post-scheduling.ts +0 -5
- package/core/server/services/recommendations/service/bookshelf-repository.js +0 -1
- package/core/server/services/recommendations/service/bookshelf-repository.ts +0 -4
- package/core/server/services/recommendations/service/incoming-recommendation-service.js +0 -1
- package/core/server/services/recommendations/service/incoming-recommendation-service.ts +0 -1
- package/core/server/services/recommendations/service/recommendation-controller.js +0 -1
- package/core/server/services/recommendations/service/recommendation-controller.ts +0 -1
- package/core/server/services/recommendations/service/recommendation-metadata-service.js +0 -1
- package/core/server/services/recommendations/service/recommendation-metadata-service.ts +0 -1
- package/core/server/services/recommendations/service/recommendation.js +0 -1
- package/core/server/services/recommendations/service/recommendation.ts +0 -1
- package/core/server/services/remote-flags/index.js +0 -1
- package/core/server/services/remote-flags/index.ts +0 -1
- package/core/server/services/slack.js +5 -1
- package/core/server/services/update-check/index.js +0 -1
- package/core/server/services/url/lazy-find-resource.js +0 -1
- package/core/server/services/url/lazy-find-resource.ts +0 -2
- package/core/server/services/url/lazy-url-service.js +0 -1
- package/core/server/services/url/lazy-url-service.ts +0 -2
- package/core/server/services/url/permalink-matcher.js +0 -1
- package/core/server/services/url/permalink-matcher.ts +0 -2
- package/core/server/services/url/router-filter.js +0 -2
- package/core/server/services/url/router-filter.ts +0 -2
- package/core/server/services/url/url-service-facade.js +0 -1
- package/core/server/services/url/url-service-facade.ts +0 -2
- package/core/server/services/verification/verification-webhook-service.js +0 -1
- package/core/server/services/verification/verification-webhook-service.ts +0 -1
- package/core/server/web/api/endpoints/admin/routes.js +7 -7
- package/core/shared/events-ts/post-deleted-event.js +0 -2
- package/core/shared/events-ts/post-deleted-event.ts +0 -2
- package/core/shared/labs.js +0 -1
- package/package.json +8 -16
- package/pnpm-lock.yaml +162 -113
- package/core/built/admin/assets/automations-BthXBzzq.js +0 -1
- package/core/built/admin/assets/editor-91xrI2A3.js +0 -8
- package/core/built/admin/assets/index-BYb09f-7.css +0 -1
- package/core/built/admin/assets/minus-DmrVsJ-z.js +0 -1
- package/core/built/admin/assets/modals-dnqxCS3l.js +0 -35
- package/core/server/models/outbox.js +0 -24
- package/core/server/services/outbox/events/start-outbox-processing-event.js +0 -22
- package/core/server/services/outbox/handlers/member-created.js +0 -85
- package/core/server/services/outbox/index.js +0 -49
- package/core/server/services/outbox/jobs/index.js +0 -30
- package/core/server/services/outbox/jobs/lib/constants.js +0 -17
- package/core/server/services/outbox/jobs/lib/process-entries.js +0 -117
- package/core/server/services/outbox/jobs/lib/process-outbox.js +0 -96
- package/core/server/services/outbox/jobs/outbox-job.js +0 -36
- package/types/ghost-storage-base.d.ts +0 -22
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import {format, parse} from 'node:url';
|
|
2
|
+
import type {ParsedUrlQueryInput} from 'node:querystring';
|
|
3
|
+
import type {Request} from 'express';
|
|
4
|
+
import type {Entry, EntryResponse} from '../entry';
|
|
5
|
+
|
|
6
|
+
const dataService = require('../../../data');
|
|
7
|
+
const renderer = require('../../../rendering');
|
|
8
|
+
const proxy = require('../../../proxy');
|
|
9
|
+
const hbs = require('../../../theme-engine/engine');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Build this request's URL with `?gift` removed, preserving path, subdirectory
|
|
13
|
+
* and other query params. Rebuilt from req.query (qs-parsed) so bracket forms
|
|
14
|
+
* like `?gift[]=x` are dropped too — otherwise the stripped redirect would loop.
|
|
15
|
+
*/
|
|
16
|
+
function strippedGiftUrl(req: Request): string {
|
|
17
|
+
const query = {...req.query} as ParsedUrlQueryInput;
|
|
18
|
+
delete query.gift;
|
|
19
|
+
return format({pathname: parse(req.originalUrl).pathname, query});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Flag the render as a gift view so `ghost_foot` injects the toast. Stores the
|
|
24
|
+
* token (not just a boolean) for a later analytics pass-through. Internal flag,
|
|
25
|
+
* not a public theme API.
|
|
26
|
+
*/
|
|
27
|
+
function setGiftTemplateFlag(res: EntryResponse, token: string): void {
|
|
28
|
+
const localTemplateOptions = hbs.getLocalTemplateOptions(res.locals);
|
|
29
|
+
hbs.updateLocalTemplateOptions(res.locals, {
|
|
30
|
+
...localTemplateOptions,
|
|
31
|
+
data: {
|
|
32
|
+
...localTemplateOptions.data,
|
|
33
|
+
_gift: token
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Whether this request is an attempt to use a gift link: the feature is enabled
|
|
40
|
+
* and a `?gift` param is present (in any form).
|
|
41
|
+
*/
|
|
42
|
+
export function isGiftRequest(req: Request): boolean {
|
|
43
|
+
return proxy.labs.isSet('giftLinks') && req.query.gift !== undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The gift token from the request, or null. A non-string form (e.g. `?gift[]=x`)
|
|
48
|
+
* isn't a token.
|
|
49
|
+
*/
|
|
50
|
+
function giftToken(req: Request): string | null {
|
|
51
|
+
return typeof req.query.gift === 'string' ? req.query.gift : null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Re-read the entry as a paid-member shim (the grant `/p/` previews use) to
|
|
56
|
+
* reveal gated content, then render it. The shim is passed as the read context
|
|
57
|
+
* only, so it never leaks into res.locals/@member.
|
|
58
|
+
*/
|
|
59
|
+
async function renderUnlocked(req: Request, res: EntryResponse, token: string) {
|
|
60
|
+
const giftLookup = await dataService.entryLookup(req.path, res.routerOptions, {
|
|
61
|
+
...res.locals,
|
|
62
|
+
member: await proxy.createPaidMemberShim()
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Don't index the unlocked variant; keep the token out of the Referer on
|
|
66
|
+
// sub-resource requests.
|
|
67
|
+
res.set('X-Robots-Tag', 'noindex');
|
|
68
|
+
res.set('Referrer-Policy', 'no-referrer');
|
|
69
|
+
setGiftTemplateFlag(res, token);
|
|
70
|
+
|
|
71
|
+
return renderer.renderEntry(req, res)(giftLookup.entry);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* CASE: gift-link reader access. A gift link is the post's real URL plus
|
|
76
|
+
* `?gift=TOKEN`; the token is verified against the entry living at this URL, so
|
|
77
|
+
* a token for one post can never unlock another. A valid token unlocks the
|
|
78
|
+
* entry; anything else strips `?gift` and 301s to the clean URL.
|
|
79
|
+
*/
|
|
80
|
+
export async function serveGiftRequest(req: Request, res: EntryResponse, entry: Entry) {
|
|
81
|
+
const token = giftToken(req);
|
|
82
|
+
|
|
83
|
+
if (token && await proxy.giftLinks.service.isValidTokenForPost(token, entry.id)) {
|
|
84
|
+
return renderUnlocked(req, res, token);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Invalid token, or one for a different post: strip it and 301 to the clean
|
|
88
|
+
// URL (the page still renders, paywalled). res.redirect, not
|
|
89
|
+
// urlUtils.redirect301, so the no-store set for ?gift requests survives and
|
|
90
|
+
// the token-bearing redirect isn't cached.
|
|
91
|
+
return res.redirect(301, strippedGiftUrl(req));
|
|
92
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
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.isMdRequest = isMdRequest;
|
|
7
|
+
exports.serveMdRequest = serveMdRequest;
|
|
8
|
+
exports.isAcceptsRequest = isAcceptsRequest;
|
|
9
|
+
exports.serveAcceptsRequest = serveAcceptsRequest;
|
|
10
|
+
const canonical_url_1 = __importDefault(require("./canonical-url"));
|
|
11
|
+
const config = require('../../../../../shared/config');
|
|
12
|
+
const urlUtils = require('../../../../../shared/url-utils');
|
|
13
|
+
const { getAcceptedMarkdownContentType, getMarkdownPath, renderEntryMarkdown } = require('../../../llms/markdown');
|
|
14
|
+
const MEMBERS_ONLY_MARKDOWN = '# Members-only content\n\nThis post requires a subscription and is not available for public access.\n';
|
|
15
|
+
function llmsEnabled(req) {
|
|
16
|
+
const llmsService = req.app.get('llmsService') || null;
|
|
17
|
+
return Boolean(llmsService && llmsService.isEnabled());
|
|
18
|
+
}
|
|
19
|
+
function isPublic(entry) {
|
|
20
|
+
return entry.visibility === 'public';
|
|
21
|
+
}
|
|
22
|
+
function serveMarkdown(res, entry) {
|
|
23
|
+
const llmsIndexUrl = urlUtils.urlFor({ relativeUrl: '/llms.txt' }, true);
|
|
24
|
+
res.set('Cache-Control', `public, max-age=${config.get('caching:llms:maxAge')}`);
|
|
25
|
+
res.set('Content-Location', getMarkdownPath(new URL(entry.url).pathname));
|
|
26
|
+
res.type('text/markdown');
|
|
27
|
+
return res.send(renderEntryMarkdown(entry, { llmsIndexUrl }));
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Whether this is a `.md` URL request (the scoped suffix route sets the flag).
|
|
31
|
+
*/
|
|
32
|
+
function isMdRequest(res) {
|
|
33
|
+
return Boolean(res.routerOptions.isMarkdownRequest);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Serve a `.md` URL as markdown for LLM consumption. When the feature is
|
|
37
|
+
* disabled we redirect to the canonical (html) url; members-only content is
|
|
38
|
+
* refused.
|
|
39
|
+
*/
|
|
40
|
+
function serveMdRequest(req, res, entry) {
|
|
41
|
+
if (!llmsEnabled(req)) {
|
|
42
|
+
return res.redirect(302, (0, canonical_url_1.default)(req, entry));
|
|
43
|
+
}
|
|
44
|
+
if (!isPublic(entry)) {
|
|
45
|
+
return res.status(403).type('text/markdown').send(MEMBERS_ONLY_MARKDOWN);
|
|
46
|
+
}
|
|
47
|
+
return serveMarkdown(res, entry);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Whether the request negotiates markdown via the Accept header — only for a
|
|
51
|
+
* public entry with the llms feature enabled, otherwise it renders as html.
|
|
52
|
+
*/
|
|
53
|
+
function isAcceptsRequest(req, entry) {
|
|
54
|
+
return isPublic(entry) && Boolean(getAcceptedMarkdownContentType(req)) && llmsEnabled(req);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Serve markdown negotiated via the Accept header.
|
|
58
|
+
*/
|
|
59
|
+
function serveAcceptsRequest(res, entry) {
|
|
60
|
+
res.vary('Accept');
|
|
61
|
+
return serveMarkdown(res, entry);
|
|
62
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type {Request, Response} from 'express';
|
|
2
|
+
import type {Entry, EntryResponse} from '../entry';
|
|
3
|
+
import buildCanonicalUrl from './canonical-url';
|
|
4
|
+
|
|
5
|
+
const config = require('../../../../../shared/config');
|
|
6
|
+
const urlUtils = require('../../../../../shared/url-utils');
|
|
7
|
+
const {getAcceptedMarkdownContentType, getMarkdownPath, renderEntryMarkdown} = require('../../../llms/markdown');
|
|
8
|
+
|
|
9
|
+
const MEMBERS_ONLY_MARKDOWN = '# Members-only content\n\nThis post requires a subscription and is not available for public access.\n';
|
|
10
|
+
|
|
11
|
+
function llmsEnabled(req: Request): boolean {
|
|
12
|
+
const llmsService = req.app.get('llmsService') || null;
|
|
13
|
+
return Boolean(llmsService && llmsService.isEnabled());
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function isPublic(entry: Entry): boolean {
|
|
17
|
+
return entry.visibility === 'public';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function serveMarkdown(res: Response, entry: Entry) {
|
|
21
|
+
const llmsIndexUrl = urlUtils.urlFor({relativeUrl: '/llms.txt'}, true);
|
|
22
|
+
res.set('Cache-Control', `public, max-age=${config.get('caching:llms:maxAge')}`);
|
|
23
|
+
res.set('Content-Location', getMarkdownPath(new URL(entry.url).pathname));
|
|
24
|
+
res.type('text/markdown');
|
|
25
|
+
return res.send(renderEntryMarkdown(entry, {llmsIndexUrl}));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Whether this is a `.md` URL request (the scoped suffix route sets the flag).
|
|
30
|
+
*/
|
|
31
|
+
export function isMdRequest(res: EntryResponse): boolean {
|
|
32
|
+
return Boolean(res.routerOptions.isMarkdownRequest);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Serve a `.md` URL as markdown for LLM consumption. When the feature is
|
|
37
|
+
* disabled we redirect to the canonical (html) url; members-only content is
|
|
38
|
+
* refused.
|
|
39
|
+
*/
|
|
40
|
+
export function serveMdRequest(req: Request, res: Response, entry: Entry) {
|
|
41
|
+
if (!llmsEnabled(req)) {
|
|
42
|
+
return res.redirect(302, buildCanonicalUrl(req, entry));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!isPublic(entry)) {
|
|
46
|
+
return res.status(403).type('text/markdown').send(MEMBERS_ONLY_MARKDOWN);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return serveMarkdown(res, entry);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Whether the request negotiates markdown via the Accept header — only for a
|
|
54
|
+
* public entry with the llms feature enabled, otherwise it renders as html.
|
|
55
|
+
*/
|
|
56
|
+
export function isAcceptsRequest(req: Request, entry: Entry): boolean {
|
|
57
|
+
return isPublic(entry) && Boolean(getAcceptedMarkdownContentType(req)) && llmsEnabled(req);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Serve markdown negotiated via the Accept header.
|
|
62
|
+
*/
|
|
63
|
+
export function serveAcceptsRequest(res: Response, entry: Entry) {
|
|
64
|
+
res.vary('Accept');
|
|
65
|
+
return serveMarkdown(res, entry);
|
|
66
|
+
}
|
|
@@ -1,114 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.entryController = entryController;
|
|
40
|
+
const markdown = __importStar(require("./entry/markdown"));
|
|
41
|
+
const giftLinks = __importStar(require("./entry/gift-links"));
|
|
42
|
+
const canonical_url_1 = __importDefault(require("./entry/canonical-url"));
|
|
1
43
|
const debug = require('@tryghost/debug')('services:routing:controllers:entry');
|
|
2
|
-
const url = require('url');
|
|
3
44
|
const config = require('../../../../shared/config');
|
|
4
45
|
const urlUtils = require('../../../../shared/url-utils');
|
|
5
46
|
const dataService = require('../../data');
|
|
6
47
|
const renderer = require('../../rendering');
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
res.
|
|
18
|
-
return
|
|
48
|
+
/**
|
|
49
|
+
* The request's last url param is `/edit`: redirect to the admin editor, or fall
|
|
50
|
+
* through to a 404 when admin redirects are disabled.
|
|
51
|
+
*/
|
|
52
|
+
function editRedirect(res, next, entry) {
|
|
53
|
+
if (!config.get('admin:redirects')) {
|
|
54
|
+
debug('is edit url but admin redirects are disabled');
|
|
55
|
+
return next();
|
|
56
|
+
}
|
|
57
|
+
debug('redirect. is edit url');
|
|
58
|
+
const resourceType = res.routerOptions.context?.includes('page') ? 'page' : 'post';
|
|
59
|
+
return urlUtils.redirectToAdmin(302, res, `/#/editor/${resourceType}/${entry.id}`);
|
|
19
60
|
}
|
|
20
|
-
|
|
21
61
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* @param {Object} res
|
|
25
|
-
* @param {Function} next
|
|
26
|
-
* @returns {Promise}
|
|
62
|
+
* The requested path no longer matches the entry's canonical url — happens with
|
|
63
|
+
* date permalinks after a publish date change.
|
|
27
64
|
*/
|
|
28
|
-
|
|
65
|
+
function isPermalinkStale(req, entry) {
|
|
66
|
+
return urlUtils.absoluteToRelative(entry.url, { withoutSubdirectory: true }) !== req.path;
|
|
67
|
+
}
|
|
68
|
+
async function entryController(req, res, next) {
|
|
29
69
|
debug('entryController', res.routerOptions);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
search: url.parse(req.originalUrl).search
|
|
67
|
-
}));
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (entry.visibility !== 'public') {
|
|
71
|
-
return res.status(403).type('text/markdown').send(
|
|
72
|
-
'# Members-only content\n\nThis post requires a subscription and is not available for public access.\n'
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return serveMarkdown(res, entry);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* CASE: Permalink is not valid anymore, we redirect him permanently to the correct one
|
|
81
|
-
* This should only happen if you have date permalinks enabled and you change
|
|
82
|
-
* your publish date.
|
|
83
|
-
*
|
|
84
|
-
* @NOTE:
|
|
85
|
-
*
|
|
86
|
-
* Ensure we redirect to the correct post url including subdirectory.
|
|
87
|
-
*/
|
|
88
|
-
if (urlUtils.absoluteToRelative(entry.url, {withoutSubdirectory: true}) !== req.path) {
|
|
89
|
-
debug('redirect');
|
|
90
|
-
|
|
91
|
-
return urlUtils.redirect301(res, url.format({
|
|
92
|
-
pathname: url.parse(entry.url).pathname,
|
|
93
|
-
search: url.parse(req.originalUrl).search
|
|
94
|
-
}));
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// CASE: Accept: text/markdown content negotiation
|
|
98
|
-
if (entry.visibility === 'public') {
|
|
99
|
-
const markdownContentType = getAcceptedMarkdownContentType(req);
|
|
100
|
-
|
|
101
|
-
if (markdownContentType) {
|
|
102
|
-
const llmsService = getLlmsService(req);
|
|
103
|
-
|
|
104
|
-
if (llmsService && llmsService.isEnabled()) {
|
|
105
|
-
res.vary('Accept');
|
|
106
|
-
return serveMarkdown(res, entry);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return renderer.renderEntry(req, res)(entry);
|
|
112
|
-
})
|
|
113
|
-
.catch(renderer.handleError(next));
|
|
114
|
-
};
|
|
70
|
+
try {
|
|
71
|
+
const lookup = await dataService.entryLookup(req.path, res.routerOptions, res.locals);
|
|
72
|
+
const entry = lookup ? lookup.entry : false;
|
|
73
|
+
if (!entry || lookup.isUnknownOption) {
|
|
74
|
+
debug('no entry or unknown option');
|
|
75
|
+
return next();
|
|
76
|
+
}
|
|
77
|
+
if (lookup.isEditURL) {
|
|
78
|
+
return editRedirect(res, next, entry);
|
|
79
|
+
}
|
|
80
|
+
// MUST run before the permalink redirect below: a `.md` path can never
|
|
81
|
+
// equal the entry's canonical (html) path, so the redirect would always
|
|
82
|
+
// fire and 301 the request to html, losing the markdown intent.
|
|
83
|
+
if (markdown.isMdRequest(res)) {
|
|
84
|
+
return markdown.serveMdRequest(req, res, entry);
|
|
85
|
+
}
|
|
86
|
+
if (isPermalinkStale(req, entry)) {
|
|
87
|
+
debug('redirect');
|
|
88
|
+
return urlUtils.redirect301(res, (0, canonical_url_1.default)(req, entry));
|
|
89
|
+
}
|
|
90
|
+
// MUST run after the permalink redirect above: negotiation rides on the
|
|
91
|
+
// canonical URL, so a stale dated-permalink URL is 301'd to canonical
|
|
92
|
+
// first, then markdown is served.
|
|
93
|
+
if (markdown.isAcceptsRequest(req, entry)) {
|
|
94
|
+
return markdown.serveAcceptsRequest(res, entry);
|
|
95
|
+
}
|
|
96
|
+
if (giftLinks.isGiftRequest(req)) {
|
|
97
|
+
return await giftLinks.serveGiftRequest(req, res, entry);
|
|
98
|
+
}
|
|
99
|
+
return renderer.renderEntry(req, res)(entry);
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
return renderer.handleError(next)(err);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type {NextFunction, Request, Response} from 'express';
|
|
2
|
+
import * as markdown from './entry/markdown';
|
|
3
|
+
import * as giftLinks from './entry/gift-links';
|
|
4
|
+
import buildCanonicalUrl from './entry/canonical-url';
|
|
5
|
+
|
|
6
|
+
const debug = require('@tryghost/debug')('services:routing:controllers:entry');
|
|
7
|
+
const config = require('../../../../shared/config');
|
|
8
|
+
const urlUtils = require('../../../../shared/url-utils');
|
|
9
|
+
const dataService = require('../../data');
|
|
10
|
+
const renderer = require('../../rendering');
|
|
11
|
+
|
|
12
|
+
// Routing context the router attaches to the matched entry's response.
|
|
13
|
+
export interface RouterOptions {
|
|
14
|
+
isMarkdownRequest?: boolean;
|
|
15
|
+
context?: string[];
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface EntryResponse extends Response {
|
|
20
|
+
routerOptions: RouterOptions;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// The resolved post/page entry from the data layer; only the fields used here are typed.
|
|
24
|
+
export interface Entry {
|
|
25
|
+
id: string;
|
|
26
|
+
url: string;
|
|
27
|
+
visibility: string;
|
|
28
|
+
[key: string]: unknown;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The request's last url param is `/edit`: redirect to the admin editor, or fall
|
|
33
|
+
* through to a 404 when admin redirects are disabled.
|
|
34
|
+
*/
|
|
35
|
+
function editRedirect(res: EntryResponse, next: NextFunction, entry: Entry) {
|
|
36
|
+
if (!config.get('admin:redirects')) {
|
|
37
|
+
debug('is edit url but admin redirects are disabled');
|
|
38
|
+
return next();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
debug('redirect. is edit url');
|
|
42
|
+
const resourceType = res.routerOptions.context?.includes('page') ? 'page' : 'post';
|
|
43
|
+
return urlUtils.redirectToAdmin(302, res, `/#/editor/${resourceType}/${entry.id}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The requested path no longer matches the entry's canonical url — happens with
|
|
48
|
+
* date permalinks after a publish date change.
|
|
49
|
+
*/
|
|
50
|
+
function isPermalinkStale(req: Request, entry: Entry): boolean {
|
|
51
|
+
return urlUtils.absoluteToRelative(entry.url, {withoutSubdirectory: true}) !== req.path;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function entryController(req: Request, res: EntryResponse, next: NextFunction): Promise<void | Response> {
|
|
55
|
+
debug('entryController', res.routerOptions);
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const lookup = await dataService.entryLookup(req.path, res.routerOptions, res.locals);
|
|
59
|
+
const entry = lookup ? lookup.entry : false;
|
|
60
|
+
|
|
61
|
+
if (!entry || lookup.isUnknownOption) {
|
|
62
|
+
debug('no entry or unknown option');
|
|
63
|
+
return next();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (lookup.isEditURL) {
|
|
67
|
+
return editRedirect(res, next, entry);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// MUST run before the permalink redirect below: a `.md` path can never
|
|
71
|
+
// equal the entry's canonical (html) path, so the redirect would always
|
|
72
|
+
// fire and 301 the request to html, losing the markdown intent.
|
|
73
|
+
if (markdown.isMdRequest(res)) {
|
|
74
|
+
return markdown.serveMdRequest(req, res, entry);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (isPermalinkStale(req, entry)) {
|
|
78
|
+
debug('redirect');
|
|
79
|
+
return urlUtils.redirect301(res, buildCanonicalUrl(req, entry));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// MUST run after the permalink redirect above: negotiation rides on the
|
|
83
|
+
// canonical URL, so a stale dated-permalink URL is 301'd to canonical
|
|
84
|
+
// first, then markdown is served.
|
|
85
|
+
if (markdown.isAcceptsRequest(req, entry)) {
|
|
86
|
+
return markdown.serveAcceptsRequest(res, entry);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (giftLinks.isGiftRequest(req)) {
|
|
90
|
+
return await giftLinks.serveGiftRequest(req, res, entry);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return renderer.renderEntry(req, res)(entry);
|
|
94
|
+
} catch (err) {
|
|
95
|
+
return renderer.handleError(next)(err);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* @file Middleware to set the appropriate cache headers on the frontend
|
|
3
3
|
*/
|
|
4
4
|
const config = require('../../../shared/config');
|
|
5
|
+
const labs = require('../../../shared/labs');
|
|
5
6
|
const shared = require('../../../server/web/shared');
|
|
6
7
|
const {api} = require('../../services/proxy');
|
|
7
8
|
const preview = require('../../services/theme-engine/preview');
|
|
@@ -69,6 +70,14 @@ const getMiddleware = async (getFreeTier = async () => {
|
|
|
69
70
|
return shared.middleware.cacheControl('noCache')(req, res, next);
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
// CASE: never cache gift-link reads. A ?gift render holds unlocked gated
|
|
74
|
+
// content with no member cookie, so the edge would otherwise serve it to
|
|
75
|
+
// everyone. Keyed on the param's presence so an empty `?gift=` still
|
|
76
|
+
// bypasses.
|
|
77
|
+
if (req.query?.gift !== undefined && labs.isSet('giftLinks')) {
|
|
78
|
+
return shared.middleware.cacheControl('noCache')(req, res, next);
|
|
79
|
+
}
|
|
80
|
+
|
|
72
81
|
// CASE: Cache member's content if this feature is enabled
|
|
73
82
|
if (req.member && shouldCacheMembersContent) {
|
|
74
83
|
// Set the 'cache-control' header to 'public'
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/* eslint-disable ghost/filenames/match-regex --
|
|
2
|
-
* PascalCase mirrors the runtime `RedirectsStoreBase.js` so the TS
|
|
3
|
-
* adapter can default-import via the matching declaration file.
|
|
4
|
-
*/
|
|
5
1
|
declare class RedirectsStoreBase {
|
|
6
2
|
readonly requiredFns: ReadonlyArray<string>;
|
|
7
3
|
}
|
|
@@ -36,6 +36,13 @@ function SchedulingDefault(options) {
|
|
|
36
36
|
|
|
37
37
|
this.deletedJobs = {};
|
|
38
38
|
this.isRunning = false;
|
|
39
|
+
|
|
40
|
+
// The HTTP client is held as an instance property so unit tests can
|
|
41
|
+
// swap it for a deterministic stub. Doing the dependency injection in
|
|
42
|
+
// the constructor (rather than `require`ing inside _pingUrl) avoids
|
|
43
|
+
// pulling `@tryghost/request`'s cacheable-lookup singleton — racy
|
|
44
|
+
// under nock + shared workers — into the unit-test path.
|
|
45
|
+
this.request = request;
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
util.inherits(SchedulingDefault, SchedulingBase);
|
|
@@ -292,7 +299,7 @@ SchedulingDefault.prototype._pingUrl = function (object) {
|
|
|
292
299
|
}
|
|
293
300
|
}
|
|
294
301
|
|
|
295
|
-
return request(url, options).catch((err) => {
|
|
302
|
+
return this.request(url, options).catch((err) => {
|
|
296
303
|
const {statusCode} = err;
|
|
297
304
|
|
|
298
305
|
// CASE: post/page was deleted already
|