ghost 6.55.0 → 6.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
- package/components/tryghost-kg-default-cards-10.3.3.tgz +0 -0
- package/components/tryghost-kg-default-nodes-2.1.4.tgz +0 -0
- package/components/tryghost-kg-default-transforms-1.3.3.tgz +0 -0
- package/components/tryghost-kg-html-to-lexical-1.3.3.tgz +0 -0
- package/components/tryghost-kg-lexical-html-renderer-1.4.3.tgz +0 -0
- package/components/tryghost-nql-string-0.0.0.tgz +0 -0
- package/core/built/admin/assets/{Combination-CJG7Flp-.js → Combination-BCm9FL4o.js} +1 -1
- package/core/built/admin/assets/{PolarAngleAxis-BGjf7sBu.js → PolarAngleAxis-C4qtHy1B.js} +1 -1
- package/core/built/admin/assets/{RadialBarChart-zeFf9mBz.js → RadialBarChart-Bh09Stsj.js} +1 -1
- package/core/built/admin/assets/{account-migration-sjHLx5dy.js → account-migration-D3O_yVck.js} +1 -1
- package/core/built/admin/assets/activitypub/{account-migration-DoYXtnZ8.js → account-migration-D8FbP-Z-.js} +7 -7
- package/core/built/admin/assets/activitypub/activitypub.js +6 -6
- package/core/built/admin/assets/activitypub/{avatar-B3DEDB1l.js → avatar-11EA1VSs.js} +2 -2
- package/core/built/admin/assets/activitypub/{avatar-flipboard-Cqrg0XMx.js → avatar-flipboard-jfbLG-xe.js} +2 -2
- package/core/built/admin/assets/activitypub/{bluesky-sharing-Bxeh6MKW.js → bluesky-sharing-f1jTQe2P.js} +16 -16
- package/core/built/admin/assets/activitypub/{content-formatters-BpQARh28.js → content-formatters-nmTOPY9M.js} +3 -3
- package/core/built/admin/assets/activitypub/{copy-D9UWIAan.js → copy-BCwo9ep3.js} +2 -2
- package/core/built/admin/assets/activitypub/{dist-D8sFfdpC.js → dist-BEfX-0aW.js} +12 -12
- package/core/built/admin/assets/activitypub/{domain-CWueFW6q.js → domain-AaDlZlrA.js} +9 -9
- package/core/built/admin/assets/activitypub/{edit-profile-BGxPU7N5.js → edit-profile-DPi8Tl5M.js} +945 -945
- package/core/built/admin/assets/activitypub/{explore-DBK-u0Ch.js → explore-CxTzjILQ.js} +8 -8
- package/core/built/admin/assets/activitypub/{feed-Bcir5RQZ.js → feed-DPv5x2MX.js} +33 -33
- package/core/built/admin/assets/activitypub/{field-BV-CIT3d.js → field-DbgA8lat.js} +3 -3
- package/core/built/admin/assets/activitypub/{hash-BC9Q0N4c.js → hash-BWuZoYkA.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-DQ4ZnC1m.js → inbox-35VDQ2K2.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-yC0nfvzl.js → inbox-DSrF_i1O.js} +85 -85
- package/core/built/admin/assets/activitypub/{label-DTRS9TDk.js → label-CPjFBbo7.js} +3 -3
- package/core/built/admin/assets/activitypub/layout-CTgrezN7.js +1 -0
- package/core/built/admin/assets/activitypub/{moderation-Dwl9t0pb.js → moderation-BkDC1XVZ.js} +11 -11
- package/core/built/admin/assets/activitypub/{note-B-zKsZ7r.js → note-xEyH4xT-.js} +7 -7
- package/core/built/admin/assets/activitypub/{notifications-DjJZD54t.js → notifications-DClaL3ui.js} +9 -9
- package/core/built/admin/assets/activitypub/{preferences-CoU8vs6d.js → preferences-DpnOa-4Z.js} +20 -20
- package/core/built/admin/assets/activitypub/{profile-BAsaJekx.js → profile-Dvi9mz-6.js} +237 -237
- package/core/built/admin/assets/activitypub/{reply-CpdMeAO5.js → reply-Cs9ySd6Z.js} +2 -2
- package/core/built/admin/assets/activitypub/{routes-CLH3OVTP.js → routes-DFBeTnfn.js} +1139 -1150
- package/core/built/admin/assets/activitypub/{separator-BI1FLsYM.js → separator-C5u_VjzY.js} +3 -3
- package/core/built/admin/assets/activitypub/{settings-DA6pNJ-Q.js → settings-CV2fFu6D.js} +13 -13
- package/core/built/admin/assets/activitypub/{site-CQD42Pw5.js → site-_TgDd_EM.js} +81 -81
- package/core/built/admin/assets/activitypub/{step-1-DIDg4aHb.js → step-1-BfnNL2oF.js} +5 -5
- package/core/built/admin/assets/activitypub/{step-2-BfGe8BYt.js → step-2-DLndZkO-.js} +6 -6
- package/core/built/admin/assets/activitypub/{step-3-mE9spAqw.js → step-3-A-PZTtRE.js} +10 -10
- package/core/built/admin/assets/activitypub/{tabs-Cg0saow1.js → tabs-BUbz7tea.js} +317 -315
- package/core/built/admin/assets/activitypub/{topic-filter-DLHBZT93.js → topic-filter-BdX_xYYT.js} +3 -3
- package/core/built/admin/assets/activitypub/{use-activity-pub-queries-VzbN99u_.js → use-activity-pub-queries-EwuRemHh.js} +2 -2
- package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-CAwXTmqH.js → use-navigate-with-base-path-cy4ACBc4.js} +92 -90
- package/core/built/admin/assets/activitypub/{use-reply-chain-data-Br4aOWOh.js → use-reply-chain-data-CyFcGfan.js} +5 -5
- package/core/built/admin/assets/activitypub/{x-C8tAnXly.js → x-7zk8mV2p.js} +2 -2
- package/core/built/admin/assets/{audience-DjEWNhp3.js → audience-CkBb2LlO.js} +1 -1
- package/core/built/admin/assets/{automated-emails-DRy7qY-J.js → automated-emails-6t2bIRWf.js} +1 -1
- package/core/built/admin/assets/{automations-Bx3VcFIL.js → automations-cMllU545.js} +1 -1
- package/core/built/admin/assets/{bluesky-sharing-ClMd0-Ft.js → bluesky-sharing-DPGpyD-G.js} +1 -1
- package/core/built/admin/assets/{box-BWbhXh21.js → box-By_EgGtm.js} +1 -1
- package/core/built/admin/assets/{breadcrumb-BD0U2ygg.js → breadcrumb-DIfcF5ow.js} +1 -1
- package/core/built/admin/assets/build-CsiH26uS.js +1 -0
- package/core/built/admin/assets/{card-DGoDJzrn.js → card-aUHX0hyR.js} +1 -1
- package/core/built/admin/assets/{chart-BuRT2Y6p.js → chart-BnXyfH11.js} +1 -1
- package/core/built/admin/assets/{chart-helpers-fjQKRVyY.js → chart-helpers-DV0FkdcD.js} +1 -1
- package/core/built/admin/assets/{checkbox-CWa28lgO.js → checkbox-BIl077a0.js} +1 -1
- package/core/built/admin/assets/{chunk.481.6c7395a49484bb4583aa.js → chunk.332.05991a101d67f732dec3.js} +43 -42
- package/core/built/admin/assets/chunk.524.87b52f4fada529df2c4c.js +25 -0
- package/core/built/admin/assets/chunk.582.340bcca873e806cbd4bb.js +27 -0
- package/core/built/admin/assets/{code-editor-view-CCr0rBWS.js → code-editor-view-C0aWNwdl.js} +1 -1
- package/core/built/admin/assets/comments-qwhIjH3c.js +1 -0
- package/core/built/admin/assets/{container-BYz2Ed39.js → container-LiZf3XdY.js} +1 -1
- package/core/built/admin/assets/{data-list-16rkJYYi.js → data-list-CupF7bjM.js} +1 -1
- package/core/built/admin/assets/{date-range-select-BFp58EMk.js → date-range-select-dXc-GsVe.js} +1 -1
- package/core/built/admin/assets/{disabled-sources-indicator-CUxMfiPa.js → disabled-sources-indicator-DYAXW-6V.js} +1 -1
- package/core/built/admin/assets/dist-BUKmD51u.js +1 -0
- package/core/built/admin/assets/{dist-2Tnz2Yfq.js → dist-CNlMj7ML.js} +1 -1
- package/core/built/admin/assets/{dist-DGzPJ_bO.js → dist-Cb5RSHSg.js} +1 -1
- package/core/built/admin/assets/{dist-P_ohUlTi.js → dist-CqO2lWS0.js} +1 -1
- package/core/built/admin/assets/domain-B5ogXUBa.js +1 -0
- package/core/built/admin/assets/{dropzone-DlK-K2DQ.js → dropzone-BnFG7dDP.js} +1 -1
- package/core/built/admin/assets/{edit-profile-BKT-REJw.js → edit-profile-B07jCZy9.js} +1 -1
- package/core/built/admin/assets/editor-DDYSOXGN.js +8 -0
- package/core/built/admin/assets/{empty-indicator-oX1u1Pkn.js → empty-indicator-WB1WgT9h.js} +1 -1
- package/core/built/admin/assets/{explore-ClUpJZOp.js → explore-Ca1y8HW_.js} +1 -1
- package/core/built/admin/assets/{feed-DF92dUMP.js → feed-x4iFMBP4.js} +1 -1
- package/core/built/admin/assets/{field-Do7lieBA.js → field-ChCAabZg.js} +1 -1
- package/core/built/admin/assets/filter-sources-LdtH1e-T.js +1 -0
- package/core/built/admin/assets/filters-CLehPo_D.js +1 -0
- package/core/built/admin/assets/filters-CjVKRP8U.js +1 -0
- package/core/built/admin/assets/{ghost-c4057474943effc568c5659ccc4909d7.js → ghost-7f01131115186d1af84ead76e79646dd.js} +149 -129
- package/core/built/admin/assets/{ghost-a9c788f6f0b1ea6cf0f14ca4b13f4496.css → ghost-d38c30b866e59f2e06a357b5eb844fe5.css} +1 -1
- package/core/built/admin/assets/{ghost-dark-db36d795d21c61426837df40af270966.css → ghost-dark-1bffd5099ba7a749303133efacf8480b.css} +1 -1
- package/core/built/admin/assets/{gift-link-modal-f9VIMNDv.js → gift-link-modal-Buqh2vXh.js} +1 -1
- package/core/built/admin/assets/{growth-B3I6gNLm.js → growth-DajOBdN0.js} +1 -1
- package/core/built/admin/assets/{growth-CVw1My4S.js → growth-Dy2UOSay.js} +2 -2
- package/core/built/admin/assets/{inbox-CFBSu4Ed.js → inbox-BUOLDoji.js} +12 -12
- package/core/built/admin/assets/index-08n05-rc.js +67 -0
- package/core/built/admin/assets/index-DgUzgFvS.css +1 -0
- package/core/built/admin/assets/{inline-EMAm3okk.js → inline-Cw-gigDW.js} +1 -1
- package/core/built/admin/assets/input-group-uSK3XT7b.js +1 -0
- package/core/built/admin/assets/{input-surface-BpAjZXY5.js → input-surface-BdOe4KzO.js} +1 -1
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +9535 -9527
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +124 -124
- package/core/built/admin/assets/{koenig-lexical-f8vHRYvz.js → koenig-lexical-BFc4yFed.js} +86 -86
- package/core/built/admin/assets/{kpi-card-DSap9rAy.js → kpi-card-BN-E6WYX.js} +1 -1
- package/core/built/admin/assets/{kpi-card-DU0rn9Cj.js → kpi-card-CCt-qeWD.js} +1 -1
- package/core/built/admin/assets/{kpi-helpers-Dz33AV6h.js → kpi-helpers-DlxTbTs4.js} +1 -1
- package/core/built/admin/assets/{kpi-tabs-BtTTrKvJ.js → kpi-tabs-BlcUttpn.js} +1 -1
- package/core/built/admin/assets/{label-DS8Mi6G5.js → label-AVrxwmsz.js} +1 -1
- package/core/built/admin/assets/{label-picker-1DvSuLA1.js → label-picker-DVnpu_IF.js} +1 -1
- package/core/built/admin/assets/labels-DAIqKed7.js +1 -0
- package/core/built/admin/assets/layout-B0YLh7R9.js +1 -0
- package/core/built/admin/assets/link-helpers-D9LsDOkG.js +1 -0
- package/core/built/admin/assets/{list-page-B6ANtzkd.js → list-page-CdsnQR9N.js} +1 -1
- package/core/built/admin/assets/member-custom-fields-C8NpZYza.js +1 -0
- package/core/built/admin/assets/member-detail-CIVAdKXf.js +1 -0
- package/core/built/admin/assets/{members-CQpVFJu4.js → members-oLPiSkXD.js} +2 -2
- package/core/built/admin/assets/modals-CB7v-k3T.js +31 -0
- package/core/built/admin/assets/{moderation-DySbgyUS.js → moderation-CQ3urTFE.js} +1 -1
- package/core/built/admin/assets/newsletter-rbeDX9Wk.js +1 -0
- package/core/built/admin/assets/newsletters-vt3Ni1Ov.js +1 -0
- package/core/built/admin/assets/{note-C8kxrz6w.js → note-Dk877BTv.js} +1 -1
- package/core/built/admin/assets/{notifications-CgtSfX7t.js → notifications-CPlLjPxO.js} +1 -1
- package/core/built/admin/assets/{onboarding-route-4qtziA9d.js → onboarding-route-DdStOtjy.js} +1 -1
- package/core/built/admin/assets/{overview-Bf2sbznq.js → overview--4V-kx9c.js} +1 -1
- package/core/built/admin/assets/overview-DSLv30oz.js +1 -0
- package/core/built/admin/assets/{page-header-B3Wb9Jqv.js → page-header-pp4dWmOS.js} +1 -1
- package/core/built/admin/assets/{post-analytics-We-ko5Cb.js → post-analytics-BMJrI81R.js} +1 -1
- package/core/built/admin/assets/post-analytics-header-pU2m9ZuV.js +1 -0
- package/core/built/admin/assets/{post-analytics-provider-B6UynTGb.js → post-analytics-provider-Btml5vJI.js} +1 -1
- package/core/built/admin/assets/{post-share-modal-DFfO93_0.js → post-share-modal-AcUZkbfL.js} +1 -1
- package/core/built/admin/assets/preferences-Ru4CRPHC.js +5 -0
- package/core/built/admin/assets/profile-BNb1oj7D.js +1 -0
- package/core/built/admin/assets/{router-provider-C1R4AZ2o.js → router-provider-BmEZf6c6.js} +1 -1
- package/core/built/admin/assets/select-B0HteHrp.js +1 -0
- package/core/built/admin/assets/settings-B1C9Q3wo.js +4 -0
- package/core/built/admin/assets/{settings-WXNeWyU1.js → settings-BGgk7oCI.js} +1 -1
- package/core/built/admin/assets/{shade-app-B3heOAfz-CGXQwWeO.js → shade-app-B3heOAfz-BhQXxNkT.js} +1 -1
- package/core/built/admin/assets/{share-modal-DsgW3wbg.js → share-modal-BeuydaBf.js} +1 -1
- package/core/built/admin/assets/social-urls-DA2lXrhW.js +8 -0
- package/core/built/admin/assets/{sort-button-Bb_FCl7e.js → sort-button-Df4t2AL8.js} +1 -1
- package/core/built/admin/assets/{source-icon-BeaRxUn9.js → source-icon-ajoaKb41.js} +1 -1
- package/core/built/admin/assets/{stack-xiN0eQ6Z.js → stack-CIfOAAVf.js} +1 -1
- package/core/built/admin/assets/{stats-Bjj_kmJ1.js → stats-C_lfxZAw.js} +1 -1
- package/core/built/admin/assets/{stats-view-BxPbW6K1.js → stats-view-Cxot1iD8.js} +1 -1
- package/core/built/admin/assets/{step-1-B1e3-1F8.js → step-1-ogAA4lDr.js} +1 -1
- package/core/built/admin/assets/{step-2-C5WaeBJo.js → step-2-C5Xini-7.js} +1 -1
- package/core/built/admin/assets/{step-3-B46iVPwu.js → step-3-CZv1FV81.js} +2 -2
- package/core/built/admin/assets/{switch-z5Y6Uzt6.js → switch-CyQcwMAv.js} +1 -1
- package/core/built/admin/assets/{table-CB-4_HBJ.js → table-DFj1wiYv.js} +1 -1
- package/core/built/admin/assets/{tabs-CCEgKYzq.js → tabs-D4y1gqUd.js} +1 -1
- package/core/built/admin/assets/{tags-Cg2VJGmG.js → tags-Dmft-Y4O.js} +1 -1
- package/core/built/admin/assets/{text-DwtjwGkh.js → text-DU9Hv33_.js} +1 -1
- package/core/built/admin/assets/textarea-d9qEkMOP.js +1 -0
- package/core/built/admin/assets/{toggle-group-b3IlxvhV.js → toggle-group-R2QMFvXW.js} +1 -1
- package/core/built/admin/assets/{topic-filter-DAFT87tQ.js → topic-filter-DGDxDalo.js} +1 -1
- package/core/built/admin/assets/{use-analytics-data-S0FTd43s.js → use-analytics-data-C76v4LIp.js} +1 -1
- package/core/built/admin/assets/use-filter-params-WrdhAISv.js +1 -0
- package/core/built/admin/assets/{use-growth-stats-CIaiHOVb.js → use-growth-stats-aNXJOmQr.js} +1 -1
- package/core/built/admin/assets/{use-post-referrers-Cq2vBPrA.js → use-post-referrers-CG0SZioj.js} +1 -1
- package/core/built/admin/assets/{use-reply-chain-data-DBDRtGu-.js → use-reply-chain-data-RLdPKRmS.js} +1 -1
- package/core/built/admin/assets/{use-simple-pagination-CjYNxdwe.js → use-simple-pagination-DgcF69KI.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-query-falHAtuK.js → use-tinybird-query-C1R_Kbmy.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-token-D7-8ND1y.js → use-tinybird-token-C9fPjVM_.js} +1 -1
- package/core/built/admin/assets/{virtual-list-CvnC9zF2.js → virtual-list-BmTUzeoF.js} +1 -1
- package/core/built/admin/assets/{web-B_5GZRvH.js → web-BqW0b0_C.js} +1 -1
- package/core/built/admin/assets/web-Cy5GKMWM.js +2 -0
- package/core/built/admin/index.html +16 -16
- package/core/frontend/services/data/fetch-data.js +10 -5
- package/core/frontend/services/routing/api-adapter.js +115 -0
- package/core/frontend/services/routing/api-adapter.ts +157 -0
- package/core/frontend/services/routing/collection-router.js +5 -3
- package/core/frontend/services/routing/controllers/index.js +1 -1
- package/core/frontend/services/routing/controllers/rss.js +26 -31
- package/core/frontend/services/routing/controllers/rss.ts +66 -0
- package/core/frontend/services/routing/controllers/static.js +8 -7
- package/core/frontend/services/routing/parent-router.js +17 -4
- package/core/frontend/services/routing/permalink-adapter.js +26 -0
- package/core/frontend/services/routing/permalink-adapter.ts +23 -0
- package/core/frontend/services/routing/registry.js +59 -33
- package/core/frontend/services/routing/router-manager.js +9 -13
- package/core/frontend/services/routing/static-routes-router.js +4 -4
- package/core/frontend/services/routing/taxonomy-router.js +6 -2
- package/core/server/data/migrations/versions/6.56/2026-08-03-19-27-51-add-automation-run-step-id-to-automated-email-recipients.js +8 -0
- package/core/server/data/migrations/versions/6.56/2026-08-04-14-42-39-restrict-notification-add-permission-to-administrators.js +12 -0
- package/core/server/data/schema/fixtures/fixtures.json +2 -2
- package/core/server/data/schema/schema.js +1 -0
- package/core/server/models/post.js +10 -1
- package/core/server/services/automations/automations-repository.ts +3 -1
- package/core/server/services/automations/database-automations-repository.js +33 -14
- package/core/server/services/automations/database-automations-repository.ts +41 -15
- package/core/server/services/automations/get-scheduler-idempotency-key.js +13 -0
- package/core/server/services/automations/get-scheduler-idempotency-key.ts +8 -0
- package/core/server/services/automations/poll.js +3 -1
- package/core/server/services/automations/poll.ts +4 -1
- package/core/server/services/automations/service.js +5 -1
- package/core/server/services/automations/service.ts +5 -1
- package/core/server/services/gift-links/CONTEXT.md +9 -0
- package/core/server/services/gifts/CONTEXT.md +55 -0
- package/core/server/services/gifts/README.md +28 -0
- package/core/server/services/gifts/gift-bookshelf-repository.js +65 -45
- package/core/server/services/gifts/gift-bookshelf-repository.ts +203 -74
- package/core/server/services/gifts/gift-checkout-adapter.js +59 -0
- package/core/server/services/gifts/gift-checkout-offer.js +74 -0
- package/core/server/services/gifts/gift-checkout-offer.ts +127 -0
- package/core/server/services/gifts/gift-codec.js +63 -0
- package/core/server/services/gifts/gift-codec.ts +62 -0
- package/core/server/services/gifts/gift-controller.js +6 -31
- package/core/server/services/gifts/gift-controller.ts +6 -69
- package/core/server/services/gifts/gift-email-service.ts +3 -2
- package/core/server/services/gifts/gift-schema.js +34 -0
- package/core/server/services/gifts/gift-schema.ts +39 -0
- package/core/server/services/gifts/gift-service-wrapper.js +13 -11
- package/core/server/services/gifts/gift-service.js +265 -52
- package/core/server/services/gifts/gift-service.ts +431 -97
- package/core/server/services/gifts/gift.ts +16 -39
- package/core/server/services/link-tracking/link-click-repository.js +6 -1
- package/core/server/services/link-tracking/link-click-tracking-service.js +12 -3
- package/core/server/services/member-welcome-emails/member-welcome-email-renderer.js +4 -3
- package/core/server/services/member-welcome-emails/service.js +7 -3
- package/core/server/services/members/api.js +1 -2
- package/core/server/services/members/import-export/import/importer.ts +3 -2
- package/core/server/services/members/import-export/index.js +10 -2
- package/core/server/services/members/import-export/index.ts +14 -4
- package/core/server/services/members/members-api/controllers/router-controller.js +38 -36
- package/core/server/services/members/members-api/members-api.js +13 -7
- package/core/server/services/members/members-api/repositories/event-repository.js +4 -94
- package/core/server/services/members/members-api/services/member-bread-service.js +3 -3
- package/core/server/services/members/members-api/services/payments-service.js +6 -68
- package/core/server/services/members-custom-fields/schema.js +8 -1
- package/core/server/services/members-custom-fields/schema.ts +8 -0
- package/core/server/services/members-custom-fields/values-service.js +118 -2
- package/core/server/services/members-custom-fields/values-service.ts +134 -4
- package/core/server/services/oembed/oembed-service.js +44 -3
- package/core/server/services/posts/posts-service.js +11 -0
- package/core/server/services/route-settings/dynamic-routing-service.js +1 -3
- package/core/server/services/route-settings/route-settings-parser.js +25 -25
- package/core/server/services/route-settings/route-settings-parser.ts +27 -27
- package/core/server/services/route-settings/validation-errors.js +68 -4
- package/core/server/services/route-settings/validation-errors.ts +79 -4
- package/core/server/services/stats/posts-stats-service.js +27 -23
- package/core/server/services/stripe/services/webhook/charge-refunded-event-service.js +1 -1
- package/core/server/services/stripe/services/webhook/checkout-session-event-service.js +16 -4
- package/core/server/services/stripe/stripe-api.js +10 -0
- package/core/server/web/gift-preview/controller.js +10 -25
- package/core/shared/labs.js +2 -2
- package/package.json +10 -9
- package/pnpm-lock.yaml +28 -18
- package/pnpm-workspace.yaml +5 -4
- package/components/tryghost-kg-default-nodes-2.1.3.tgz +0 -0
- package/core/built/admin/assets/activitypub/layout-qx-bUL1k.js +0 -1
- package/core/built/admin/assets/chunk.524.cffd399783a519e404a9.js +0 -25
- package/core/built/admin/assets/chunk.582.eebf0d0b2d202bb746a5.js +0 -27
- package/core/built/admin/assets/comments-DyUSMUdj.js +0 -1
- package/core/built/admin/assets/dist-CjY-f5Z1.js +0 -1
- package/core/built/admin/assets/domain-C0bV2UV8.js +0 -1
- package/core/built/admin/assets/editor-Dp865O-4.js +0 -8
- package/core/built/admin/assets/filter-sources-BV2rnpfA.js +0 -1
- package/core/built/admin/assets/filters-CUXJ2Nm9.js +0 -1
- package/core/built/admin/assets/filters-DfbIWc_l.js +0 -1
- package/core/built/admin/assets/index-9Cw8z3rA.css +0 -1
- package/core/built/admin/assets/index-BGG7NckO.js +0 -67
- package/core/built/admin/assets/input-group-BSgG_lVw.js +0 -1
- package/core/built/admin/assets/labels-CDSUt4J_.js +0 -1
- package/core/built/admin/assets/layout-yQEi2qDM.js +0 -1
- package/core/built/admin/assets/link-helpers-7EYqvvHs.js +0 -1
- package/core/built/admin/assets/member-custom-fields-yOMHyAg9.js +0 -1
- package/core/built/admin/assets/member-detail-BAq7gDaa.js +0 -1
- package/core/built/admin/assets/modals-ReORvWnu.js +0 -31
- package/core/built/admin/assets/newsletter-CJQvtHqN.js +0 -1
- package/core/built/admin/assets/newsletters-B3QQXfGT.js +0 -1
- package/core/built/admin/assets/nql-BsgFMd93.js +0 -1
- package/core/built/admin/assets/overview-CBfePxcr.js +0 -1
- package/core/built/admin/assets/post-analytics-header-Bmc9su6q.js +0 -1
- package/core/built/admin/assets/preferences-DJPfHEGu.js +0 -5
- package/core/built/admin/assets/profile-DqBQp-gT.js +0 -1
- package/core/built/admin/assets/select-C5_xEt5b.js +0 -1
- package/core/built/admin/assets/settings-BwiVIXau.js +0 -4
- package/core/built/admin/assets/social-urls-BQPFEk1_.js +0 -8
- package/core/built/admin/assets/textarea-Bjccvh_E.js +0 -1
- package/core/built/admin/assets/use-filter-params-Gpoq2E1L.js +0 -1
- package/core/built/admin/assets/web-jFYWCKDn.js +0 -2
- package/core/server/services/gifts/gift-repository.js +0 -2
- package/core/server/services/gifts/gift-repository.ts +0 -29
- package/core/server/services/route-settings/activation-bridge.js +0 -174
- package/core/server/services/route-settings/activation-bridge.ts +0 -250
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<link rel="stylesheet" href="./assets/vendor-3bf602dfa6463c3400ffd81697767274.css">
|
|
5
|
-
<link rel="stylesheet" href="./assets/ghost-
|
|
6
|
-
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22editorUrl%22%3A%22%22%2C%22rootURL%22%3A%22%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%226.
|
|
5
|
+
<link rel="stylesheet" href="./assets/ghost-d38c30b866e59f2e06a357b5eb844fe5.css">
|
|
6
|
+
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22editorUrl%22%3A%22%22%2C%22rootURL%22%3A%22%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%226.56%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%2C%22editorFilename%22%3A%22koenig-lexical.umd.js%22%2C%22editorHash%22%3A%22571a1e1dc6%22%2C%22activitypubFilename%22%3A%22activitypub.js%22%2C%22activitypubHash%22%3A%2228a9fedb77%22%2C%22activitypubRemoteConfigUrl%22%3A%22%2F.ghost%2Factivitypub%2Fstable%2Fclient-config%22%7D">
|
|
7
7
|
|
|
8
8
|
<meta charset="UTF-8" />
|
|
9
9
|
|
|
@@ -20,28 +20,28 @@
|
|
|
20
20
|
|
|
21
21
|
<link rel="shortcut icon" href="./assets/favicon-C_q1KHzv.ico" />
|
|
22
22
|
<link rel="apple-touch-icon" href="./assets/apple-touch-icon-DhO18_4B.png" />
|
|
23
|
-
<script type="module" crossorigin src="./assets/index-
|
|
23
|
+
<script type="module" crossorigin src="./assets/index-08n05-rc.js"></script>
|
|
24
24
|
<link rel="modulepreload" crossorigin href="./assets/rolldown-runtime-DAXXjFlN.js">
|
|
25
25
|
<link rel="modulepreload" crossorigin href="./assets/react-Bb3IsNbi.js">
|
|
26
26
|
<link rel="modulepreload" crossorigin href="./assets/jsx-runtime-DX3WBWVO.js">
|
|
27
27
|
<link rel="modulepreload" crossorigin href="./assets/framework-provider-CmtnPXIT.js">
|
|
28
28
|
<link rel="modulepreload" crossorigin href="./assets/dist-Ndy-PuGN.js">
|
|
29
29
|
<link rel="modulepreload" crossorigin href="./assets/hooks-CixO01lL.js">
|
|
30
|
-
<link rel="modulepreload" crossorigin href="./assets/dist-
|
|
30
|
+
<link rel="modulepreload" crossorigin href="./assets/dist-Cb5RSHSg.js">
|
|
31
31
|
<link rel="modulepreload" crossorigin href="./assets/dist-CQJY6SVY.js">
|
|
32
|
-
<link rel="modulepreload" crossorigin href="./assets/dist-
|
|
33
|
-
<link rel="modulepreload" crossorigin href="./assets/Combination-
|
|
32
|
+
<link rel="modulepreload" crossorigin href="./assets/dist-CqO2lWS0.js">
|
|
33
|
+
<link rel="modulepreload" crossorigin href="./assets/Combination-BCm9FL4o.js">
|
|
34
34
|
<link rel="modulepreload" crossorigin href="./assets/preload-helper-DYl5dUZ5.js">
|
|
35
35
|
<link rel="modulepreload" crossorigin href="./assets/chunk-KS7C4IRE-DG9HTHQ3.js">
|
|
36
|
-
<link rel="modulepreload" crossorigin href="./assets/router-provider-
|
|
37
|
-
<link rel="modulepreload" crossorigin href="./assets/text-
|
|
38
|
-
<link rel="modulepreload" crossorigin href="./assets/shade-app-B3heOAfz-
|
|
39
|
-
<link rel="modulepreload" crossorigin href="./assets/dist-
|
|
40
|
-
<link rel="modulepreload" crossorigin href="./assets/dist-
|
|
41
|
-
<link rel="modulepreload" crossorigin href="./assets/input-surface-
|
|
36
|
+
<link rel="modulepreload" crossorigin href="./assets/router-provider-BmEZf6c6.js">
|
|
37
|
+
<link rel="modulepreload" crossorigin href="./assets/text-DU9Hv33_.js">
|
|
38
|
+
<link rel="modulepreload" crossorigin href="./assets/shade-app-B3heOAfz-BhQXxNkT.js">
|
|
39
|
+
<link rel="modulepreload" crossorigin href="./assets/dist-BUKmD51u.js">
|
|
40
|
+
<link rel="modulepreload" crossorigin href="./assets/dist-CNlMj7ML.js">
|
|
41
|
+
<link rel="modulepreload" crossorigin href="./assets/input-surface-BdOe4KzO.js">
|
|
42
42
|
<link rel="modulepreload" crossorigin href="./assets/update-queries-C4CnvJaW.js">
|
|
43
43
|
<link rel="modulepreload" crossorigin href="./assets/users-Dkv8NQZe.js">
|
|
44
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
44
|
+
<link rel="stylesheet" crossorigin href="./assets/index-DgUzgFvS.css">
|
|
45
45
|
</head>
|
|
46
46
|
<body class="react-admin">
|
|
47
47
|
<div id="ember-alerts-wormhole"></div>
|
|
@@ -62,8 +62,8 @@
|
|
|
62
62
|
<div id="ember-notifications-wormhole"></div>
|
|
63
63
|
<script src="./assets/vendor-25f0bb2b2de85265afdf33da79f14a44.js"></script>
|
|
64
64
|
<script src="./assets/chunk.211.6f7328d63c41536451b3.js"></script>
|
|
65
|
-
<script src="./assets/chunk.
|
|
66
|
-
<script src="./assets/chunk.524.
|
|
67
|
-
<script src="./assets/ghost-
|
|
65
|
+
<script src="./assets/chunk.332.05991a101d67f732dec3.js"></script>
|
|
66
|
+
<script src="./assets/chunk.524.87b52f4fada529df2c4c.js"></script>
|
|
67
|
+
<script src="./assets/ghost-7f01131115186d1af84ead76e79646dd.js"></script>
|
|
68
68
|
</body>
|
|
69
69
|
</html>
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Dynamically build and execute queries on the API
|
|
4
4
|
*/
|
|
5
5
|
const _ = require('lodash');
|
|
6
|
+
const {resolveRouteData} = require('../routing/api-adapter');
|
|
6
7
|
|
|
7
8
|
// The default settings for a default post query
|
|
8
9
|
const queryDefaults = {
|
|
@@ -93,9 +94,13 @@ async function fetchData(pathOptions, routerOptions, locals) {
|
|
|
93
94
|
// The filter can in theory contain a "%s" e.g. filter="primary_tag:%s"
|
|
94
95
|
promises.push(processQuery(postQuery, pathOptions.slug, locals));
|
|
95
96
|
|
|
97
|
+
const apiCalls = resolveRouteData(routerOptions.data);
|
|
98
|
+
|
|
96
99
|
// CASE: fetch more data defined by the router e.g. tags, authors - see TaxonomyRouter
|
|
97
|
-
_.each(
|
|
98
|
-
|
|
100
|
+
_.each(apiCalls, function (apiCall, name) {
|
|
101
|
+
// Merge into a fresh object: the resolved spec is read again below to
|
|
102
|
+
// shape the response, so it must stay as the adapter resolved it.
|
|
103
|
+
const dataQueryOptions = _.merge({}, apiCall, defaultDataQueryOptions[name]);
|
|
99
104
|
promises.push(processQuery(dataQueryOptions, pathOptions.slug, locals));
|
|
100
105
|
});
|
|
101
106
|
|
|
@@ -107,11 +112,11 @@ async function fetchData(pathOptions, routerOptions, locals) {
|
|
|
107
112
|
|
|
108
113
|
let resultIndex = 1;
|
|
109
114
|
|
|
110
|
-
_.each(
|
|
115
|
+
_.each(apiCalls, function (apiCall, name) {
|
|
111
116
|
if (results[resultIndex]) {
|
|
112
|
-
response.data[name] = results[resultIndex][
|
|
117
|
+
response.data[name] = results[resultIndex][apiCall.resource];
|
|
113
118
|
|
|
114
|
-
if (
|
|
119
|
+
if (apiCall.type === 'browse') {
|
|
115
120
|
response.data[name].meta = results[resultIndex].meta;
|
|
116
121
|
}
|
|
117
122
|
|
|
@@ -0,0 +1,115 @@
|
|
|
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.resolveApiCall = resolveApiCall;
|
|
7
|
+
exports.resolveRouteData = resolveRouteData;
|
|
8
|
+
exports.resolveResourceRead = resolveResourceRead;
|
|
9
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
10
|
+
const errors_1 = __importDefault(require("@tryghost/errors"));
|
|
11
|
+
const config_1 = require("./config");
|
|
12
|
+
/**
|
|
13
|
+
* Not every entry in QUERY is reachable from route data — `previews` and `email`
|
|
14
|
+
* are internal routers with no `type`, so they resolve to no API call.
|
|
15
|
+
*/
|
|
16
|
+
const RESOURCE_CONFIGS = config_1.QUERY;
|
|
17
|
+
/**
|
|
18
|
+
* The entry fields the Content API accepts as query options. Everything else on
|
|
19
|
+
* a data entry is routing metadata (`redirect`) or unsupported (`fields`), and
|
|
20
|
+
* is deliberately not forwarded.
|
|
21
|
+
*/
|
|
22
|
+
const ALLOWED_QUERY_OPTIONS = ['limit', 'order', 'filter', 'include', 'slug', 'visibility', 'status', 'page'];
|
|
23
|
+
function unknownResource(resource) {
|
|
24
|
+
return new errors_1.default.IncorrectUsageError({ message: `Unknown route data resource: ${resource}` });
|
|
25
|
+
}
|
|
26
|
+
// Shorthand names a resource the short way (`tag`), long form the way the API
|
|
27
|
+
// does (`tags`). Both land on the same config.
|
|
28
|
+
function splitShortForm(shortForm) {
|
|
29
|
+
const [key, slug] = shortForm.split('.');
|
|
30
|
+
return [key, slug];
|
|
31
|
+
}
|
|
32
|
+
function shortFormConfig(key) {
|
|
33
|
+
return RESOURCE_CONFIGS[key];
|
|
34
|
+
}
|
|
35
|
+
function longFormConfig(resource) {
|
|
36
|
+
return Object.values(RESOURCE_CONFIGS).find(config => config?.resource === resource);
|
|
37
|
+
}
|
|
38
|
+
function resolveShortForm(shortForm) {
|
|
39
|
+
const [key, slug] = splitShortForm(shortForm);
|
|
40
|
+
const config = shortFormConfig(key);
|
|
41
|
+
if (!config?.type) {
|
|
42
|
+
throw unknownResource(key);
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
controller: config.controller,
|
|
46
|
+
type: config.type,
|
|
47
|
+
resource: config.resource,
|
|
48
|
+
options: { ...config.options, slug }
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function resolveLongForm(entry) {
|
|
52
|
+
const config = longFormConfig(entry.resource);
|
|
53
|
+
if (!config) {
|
|
54
|
+
throw unknownResource(entry.resource);
|
|
55
|
+
}
|
|
56
|
+
const options = lodash_1.default.pick(entry, ALLOWED_QUERY_OPTIONS);
|
|
57
|
+
return {
|
|
58
|
+
controller: config.controller,
|
|
59
|
+
type: entry.type,
|
|
60
|
+
resource: config.resource,
|
|
61
|
+
// A `read` targets one known resource, so the resource defaults (e.g.
|
|
62
|
+
// `visibility: public` for tags) apply. A `browse` is author-driven and
|
|
63
|
+
// takes only what the entry asked for.
|
|
64
|
+
options: entry.type === 'read' ? lodash_1.default.defaults(options, config.options) : options
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Resolve a single route data entry into a Content API call.
|
|
69
|
+
*
|
|
70
|
+
* This is the one place that knows how a domain `resource` maps onto a Ghost API
|
|
71
|
+
* controller — routes.yaml talks about `tags`, the API is reached via
|
|
72
|
+
* `tagsPublic`. Callers work with the domain model and never name a controller.
|
|
73
|
+
*
|
|
74
|
+
* @example resolveApiCall('tag.food')
|
|
75
|
+
* // => {controller: 'tagsPublic', type: 'read', resource: 'tags', options: {slug: 'food', visibility: 'public'}}
|
|
76
|
+
*/
|
|
77
|
+
function resolveApiCall(entry) {
|
|
78
|
+
return typeof entry === 'string' ? resolveShortForm(entry) : resolveLongForm(entry);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Resolve a route's whole `data` block, keyed the way the theme sees it.
|
|
82
|
+
*
|
|
83
|
+
* Top-level shorthand (`data: tag.food`) has no author-given name, so it is
|
|
84
|
+
* keyed by its resource shorthand — `tag` — matching what themes already expect.
|
|
85
|
+
*/
|
|
86
|
+
function resolveRouteData(routeData) {
|
|
87
|
+
if (!routeData) {
|
|
88
|
+
return {};
|
|
89
|
+
}
|
|
90
|
+
if (typeof routeData === 'string') {
|
|
91
|
+
const [key] = splitShortForm(routeData);
|
|
92
|
+
return { [key]: resolveApiCall(routeData) };
|
|
93
|
+
}
|
|
94
|
+
return lodash_1.default.mapValues(routeData, entry => resolveApiCall(entry));
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The resource read a data entry claims, or `null` if it claims none.
|
|
98
|
+
*
|
|
99
|
+
* Only a `read` names a single resource; a `browse` pulls a set and claims
|
|
100
|
+
* nothing. Unlike {@link resolveApiCall} this answers a routing question, so an
|
|
101
|
+
* entry it cannot make sense of is simply not a claim rather than an error.
|
|
102
|
+
*
|
|
103
|
+
* @example resolveResourceRead('tag.food') // => {resource: 'tags', slug: 'food'}
|
|
104
|
+
*/
|
|
105
|
+
function resolveResourceRead(entry) {
|
|
106
|
+
if (typeof entry === 'string') {
|
|
107
|
+
const [key, slug] = splitShortForm(entry);
|
|
108
|
+
const config = shortFormConfig(key);
|
|
109
|
+
return config?.type === 'read' ? { resource: config.resource, slug } : null;
|
|
110
|
+
}
|
|
111
|
+
if (entry.type !== 'read') {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
return longFormConfig(entry.resource) ? { resource: entry.resource, slug: entry.slug } : null;
|
|
115
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import errors from '@tryghost/errors';
|
|
3
|
+
import {QUERY} from './config';
|
|
4
|
+
import type {RouteData, DataEntry, DataShortForm, DataLongFormEntry} from '@tryghost/adapter-base-route-settings';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A resolved Content API call: which controller to reach for, which method to
|
|
8
|
+
* call on it, and the options to call it with.
|
|
9
|
+
*/
|
|
10
|
+
export interface ApiCallSpec {
|
|
11
|
+
controller: string;
|
|
12
|
+
type: 'read' | 'browse';
|
|
13
|
+
resource: string;
|
|
14
|
+
options: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The resource read that a data entry claims — the router that declares it owns
|
|
19
|
+
* that slug, and other routers redirect to it.
|
|
20
|
+
*/
|
|
21
|
+
export interface ResourceRead {
|
|
22
|
+
resource: string;
|
|
23
|
+
slug: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface ResourceConfig {
|
|
27
|
+
controller: string;
|
|
28
|
+
type?: 'read' | 'browse';
|
|
29
|
+
resource: string;
|
|
30
|
+
options?: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Not every entry in QUERY is reachable from route data — `previews` and `email`
|
|
35
|
+
* are internal routers with no `type`, so they resolve to no API call.
|
|
36
|
+
*/
|
|
37
|
+
const RESOURCE_CONFIGS: Record<string, ResourceConfig | undefined> = QUERY;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The entry fields the Content API accepts as query options. Everything else on
|
|
41
|
+
* a data entry is routing metadata (`redirect`) or unsupported (`fields`), and
|
|
42
|
+
* is deliberately not forwarded.
|
|
43
|
+
*/
|
|
44
|
+
const ALLOWED_QUERY_OPTIONS = ['limit', 'order', 'filter', 'include', 'slug', 'visibility', 'status', 'page'];
|
|
45
|
+
|
|
46
|
+
function unknownResource(resource: string) {
|
|
47
|
+
return new errors.IncorrectUsageError({message: `Unknown route data resource: ${resource}`});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Shorthand names a resource the short way (`tag`), long form the way the API
|
|
51
|
+
// does (`tags`). Both land on the same config.
|
|
52
|
+
function splitShortForm(shortForm: DataShortForm): [string, string] {
|
|
53
|
+
const [key, slug] = shortForm.split('.');
|
|
54
|
+
return [key, slug];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function shortFormConfig(key: string): ResourceConfig | undefined {
|
|
58
|
+
return RESOURCE_CONFIGS[key];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function longFormConfig(resource: string): ResourceConfig | undefined {
|
|
62
|
+
return Object.values(RESOURCE_CONFIGS).find(config => config?.resource === resource);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function resolveShortForm(shortForm: DataShortForm): ApiCallSpec {
|
|
66
|
+
const [key, slug] = splitShortForm(shortForm);
|
|
67
|
+
const config = shortFormConfig(key);
|
|
68
|
+
|
|
69
|
+
if (!config?.type) {
|
|
70
|
+
throw unknownResource(key);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
controller: config.controller,
|
|
75
|
+
type: config.type,
|
|
76
|
+
resource: config.resource,
|
|
77
|
+
options: {...config.options, slug}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function resolveLongForm(entry: DataLongFormEntry): ApiCallSpec {
|
|
82
|
+
const config = longFormConfig(entry.resource);
|
|
83
|
+
|
|
84
|
+
if (!config) {
|
|
85
|
+
throw unknownResource(entry.resource);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const options: Record<string, unknown> = _.pick(entry, ALLOWED_QUERY_OPTIONS);
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
controller: config.controller,
|
|
92
|
+
type: entry.type,
|
|
93
|
+
resource: config.resource,
|
|
94
|
+
// A `read` targets one known resource, so the resource defaults (e.g.
|
|
95
|
+
// `visibility: public` for tags) apply. A `browse` is author-driven and
|
|
96
|
+
// takes only what the entry asked for.
|
|
97
|
+
options: entry.type === 'read' ? _.defaults(options, config.options) : options
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Resolve a single route data entry into a Content API call.
|
|
103
|
+
*
|
|
104
|
+
* This is the one place that knows how a domain `resource` maps onto a Ghost API
|
|
105
|
+
* controller — routes.yaml talks about `tags`, the API is reached via
|
|
106
|
+
* `tagsPublic`. Callers work with the domain model and never name a controller.
|
|
107
|
+
*
|
|
108
|
+
* @example resolveApiCall('tag.food')
|
|
109
|
+
* // => {controller: 'tagsPublic', type: 'read', resource: 'tags', options: {slug: 'food', visibility: 'public'}}
|
|
110
|
+
*/
|
|
111
|
+
export function resolveApiCall(entry: DataEntry): ApiCallSpec {
|
|
112
|
+
return typeof entry === 'string' ? resolveShortForm(entry) : resolveLongForm(entry);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Resolve a route's whole `data` block, keyed the way the theme sees it.
|
|
117
|
+
*
|
|
118
|
+
* Top-level shorthand (`data: tag.food`) has no author-given name, so it is
|
|
119
|
+
* keyed by its resource shorthand — `tag` — matching what themes already expect.
|
|
120
|
+
*/
|
|
121
|
+
export function resolveRouteData(routeData?: RouteData): Record<string, ApiCallSpec> {
|
|
122
|
+
if (!routeData) {
|
|
123
|
+
return {};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (typeof routeData === 'string') {
|
|
127
|
+
const [key] = splitShortForm(routeData);
|
|
128
|
+
|
|
129
|
+
return {[key]: resolveApiCall(routeData)};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return _.mapValues(routeData, entry => resolveApiCall(entry));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* The resource read a data entry claims, or `null` if it claims none.
|
|
137
|
+
*
|
|
138
|
+
* Only a `read` names a single resource; a `browse` pulls a set and claims
|
|
139
|
+
* nothing. Unlike {@link resolveApiCall} this answers a routing question, so an
|
|
140
|
+
* entry it cannot make sense of is simply not a claim rather than an error.
|
|
141
|
+
*
|
|
142
|
+
* @example resolveResourceRead('tag.food') // => {resource: 'tags', slug: 'food'}
|
|
143
|
+
*/
|
|
144
|
+
export function resolveResourceRead(entry: DataEntry): ResourceRead | null {
|
|
145
|
+
if (typeof entry === 'string') {
|
|
146
|
+
const [key, slug] = splitShortForm(entry);
|
|
147
|
+
const config = shortFormConfig(key);
|
|
148
|
+
|
|
149
|
+
return config?.type === 'read' ? {resource: config.resource, slug} : null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (entry.type !== 'read') {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return longFormConfig(entry.resource) ? {resource: entry.resource, slug: entry.slug} : null;
|
|
157
|
+
}
|
|
@@ -5,6 +5,7 @@ const ParentRouter = require('./parent-router');
|
|
|
5
5
|
const controllers = require('./controllers');
|
|
6
6
|
const middleware = require('./middleware');
|
|
7
7
|
const RSSRouter = require('./rss-router');
|
|
8
|
+
const {toExpressNotation} = require('./permalink-adapter');
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* @description Collection Router for post resource.
|
|
@@ -26,15 +27,16 @@ class CollectionRouter extends ParentRouter {
|
|
|
26
27
|
|
|
27
28
|
this.rss = object.rss !== false;
|
|
28
29
|
|
|
30
|
+
// convert domain `{slug}` -> Express/URL-service `:slug` at this boundary
|
|
29
31
|
this.permalinks = {
|
|
30
|
-
value: object.permalink
|
|
32
|
+
value: toExpressNotation(object.permalink)
|
|
31
33
|
};
|
|
32
34
|
|
|
33
35
|
// @NOTE: see renderer/templates - we use unshift to prepend the templates
|
|
34
36
|
this.templates = (object.templates || []).reverse();
|
|
35
37
|
|
|
36
38
|
this.filter = object.filter;
|
|
37
|
-
this.data = object.data || {
|
|
39
|
+
this.data = object.data || {};
|
|
38
40
|
this.order = object.order;
|
|
39
41
|
this.limit = object.limit;
|
|
40
42
|
|
|
@@ -116,7 +118,7 @@ class CollectionRouter extends ParentRouter {
|
|
|
116
118
|
templates: this.templates,
|
|
117
119
|
identifier: this.identifier,
|
|
118
120
|
name: this.routerName,
|
|
119
|
-
data: this.data
|
|
121
|
+
data: this.data
|
|
120
122
|
};
|
|
121
123
|
|
|
122
124
|
next();
|
|
@@ -1,52 +1,47 @@
|
|
|
1
|
-
|
|
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.rssController = rssController;
|
|
7
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
8
|
+
const node_url_1 = __importDefault(require("node:url"));
|
|
2
9
|
const debug = require('@tryghost/debug')('services:routing:controllers:rss');
|
|
3
|
-
const url = require('url');
|
|
4
10
|
const security = require('@tryghost/security');
|
|
5
11
|
const settingsCache = require('../../../../shared/settings-cache');
|
|
6
12
|
const rssService = require('../../rss');
|
|
7
13
|
const renderer = require('../../rendering');
|
|
8
14
|
const dataService = require('../../data');
|
|
9
|
-
|
|
10
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Generates the title for the RSS feed based on author, tag, or publication settings.
|
|
17
|
+
*/
|
|
11
18
|
function getTitle(relatedData) {
|
|
12
|
-
|
|
13
|
-
let titleStart =
|
|
14
|
-
|
|
19
|
+
const data = relatedData || {};
|
|
20
|
+
let titleStart = lodash_1.default.get(data, 'author[0].name') || lodash_1.default.get(data, 'tag[0].name') || '';
|
|
15
21
|
titleStart += titleStart ? ' - ' : '';
|
|
16
22
|
return titleStart + settingsCache.get('title');
|
|
17
23
|
}
|
|
18
|
-
|
|
19
24
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* @TODO: The collection controller does almost the same. Merge!
|
|
23
|
-
* @param {Object} req
|
|
24
|
-
* @param {Object} res
|
|
25
|
-
* @param {Function} next
|
|
25
|
+
* RSS routing controller.
|
|
26
|
+
* Fetches posts/meta data and delegates rendering to `rssService`.
|
|
26
27
|
*/
|
|
27
|
-
|
|
28
|
+
function rssController(req, res, next) {
|
|
28
29
|
debug('rssController');
|
|
29
|
-
|
|
30
30
|
const pathOptions = {
|
|
31
31
|
page: 1, // required for fetchData
|
|
32
32
|
slug: req.params.slug ? security.string.safe(req.params.slug) : undefined
|
|
33
33
|
};
|
|
34
|
-
|
|
35
34
|
// CASE: Ghost is using an rss cache - normalize the URL for use as a key
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
dataService.fetchData(pathOptions, res.routerOptions, res.locals)
|
|
35
|
+
const baseUrl = node_url_1.default.parse(req.originalUrl).pathname;
|
|
36
|
+
return dataService.fetchData(pathOptions, res.routerOptions, res.locals)
|
|
40
37
|
.then(function formatResult(result) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return response;
|
|
47
|
-
})
|
|
38
|
+
const response = lodash_1.default.pick(result, ['posts', 'meta']);
|
|
39
|
+
response.title = getTitle(result.data);
|
|
40
|
+
response.description = settingsCache.get('description');
|
|
41
|
+
return response;
|
|
42
|
+
})
|
|
48
43
|
.then(function (data) {
|
|
49
|
-
|
|
50
|
-
|
|
44
|
+
return rssService.render(res, baseUrl, data);
|
|
45
|
+
})
|
|
51
46
|
.catch(renderer.handleError(next));
|
|
52
|
-
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type {Request, Response, NextFunction} from 'express';
|
|
2
|
+
import _ from 'lodash';
|
|
3
|
+
import url from 'node:url';
|
|
4
|
+
|
|
5
|
+
const debug = require('@tryghost/debug')('services:routing:controllers:rss');
|
|
6
|
+
const security = require('@tryghost/security');
|
|
7
|
+
const settingsCache = require('../../../../shared/settings-cache');
|
|
8
|
+
const rssService = require('../../rss');
|
|
9
|
+
const renderer = require('../../rendering');
|
|
10
|
+
const dataService = require('../../data');
|
|
11
|
+
|
|
12
|
+
export interface RSSPathOptions {
|
|
13
|
+
page: number;
|
|
14
|
+
slug?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface RSSResponse extends Response {
|
|
18
|
+
routerOptions: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface RelatedData {
|
|
22
|
+
author?: Array<{name: string}>;
|
|
23
|
+
tag?: Array<{name: string}>;
|
|
24
|
+
[key: string]: unknown;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Generates the title for the RSS feed based on author, tag, or publication settings.
|
|
29
|
+
*/
|
|
30
|
+
function getTitle(relatedData?: RelatedData): string {
|
|
31
|
+
const data = relatedData || {};
|
|
32
|
+
let titleStart = _.get(data, 'author[0].name') || _.get(data, 'tag[0].name') || '';
|
|
33
|
+
|
|
34
|
+
titleStart += titleStart ? ' - ' : '';
|
|
35
|
+
return titleStart + settingsCache.get('title');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* RSS routing controller.
|
|
40
|
+
* Fetches posts/meta data and delegates rendering to `rssService`.
|
|
41
|
+
*/
|
|
42
|
+
export function rssController(req: Request, res: RSSResponse, next: NextFunction): Promise<void> {
|
|
43
|
+
debug('rssController');
|
|
44
|
+
|
|
45
|
+
const pathOptions: RSSPathOptions = {
|
|
46
|
+
page: 1, // required for fetchData
|
|
47
|
+
slug: req.params.slug ? security.string.safe(req.params.slug) : undefined
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// CASE: Ghost is using an rss cache - normalize the URL for use as a key
|
|
51
|
+
const baseUrl = url.parse(req.originalUrl).pathname;
|
|
52
|
+
|
|
53
|
+
return dataService.fetchData(pathOptions, res.routerOptions, res.locals)
|
|
54
|
+
.then(function formatResult(result: {posts?: unknown[]; meta?: unknown; data?: RelatedData}) {
|
|
55
|
+
const response = _.pick(result, ['posts', 'meta']) as Record<string, unknown>;
|
|
56
|
+
|
|
57
|
+
response.title = getTitle(result.data);
|
|
58
|
+
response.description = settingsCache.get('description');
|
|
59
|
+
|
|
60
|
+
return response;
|
|
61
|
+
})
|
|
62
|
+
.then(function (data: Record<string, unknown>) {
|
|
63
|
+
return rssService.render(res, baseUrl, data);
|
|
64
|
+
})
|
|
65
|
+
.catch(renderer.handleError(next));
|
|
66
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const debug = require('@tryghost/debug')('services:routing:controllers:static');
|
|
3
3
|
const renderer = require('../../rendering');
|
|
4
|
+
const {resolveRouteData} = require('../api-adapter');
|
|
4
5
|
|
|
5
6
|
function processQuery(query, locals) {
|
|
6
7
|
const api = require('../../proxy').api;
|
|
@@ -35,10 +36,10 @@ function processQuery(query, locals) {
|
|
|
35
36
|
module.exports = function staticController(req, res, next) {
|
|
36
37
|
debug('staticController', res.routerOptions);
|
|
37
38
|
|
|
38
|
-
|
|
39
|
+
const apiCalls = resolveRouteData(res.routerOptions.data);
|
|
39
40
|
|
|
40
|
-
_.
|
|
41
|
-
|
|
41
|
+
const promises = _.map(apiCalls, function (apiCall) {
|
|
42
|
+
return processQuery(apiCall, res.locals);
|
|
42
43
|
});
|
|
43
44
|
|
|
44
45
|
return Promise.all(promises)
|
|
@@ -49,13 +50,13 @@ module.exports = function staticController(req, res, next) {
|
|
|
49
50
|
response.data = {};
|
|
50
51
|
|
|
51
52
|
let resultIndex = 0;
|
|
52
|
-
_.each(
|
|
53
|
-
response.data[name] = result[resultIndex][
|
|
53
|
+
_.each(apiCalls, function (apiCall, name) {
|
|
54
|
+
response.data[name] = result[resultIndex][apiCall.resource];
|
|
54
55
|
|
|
55
|
-
if (
|
|
56
|
+
if (apiCall.type === 'browse') {
|
|
56
57
|
response.data[name].meta = result[resultIndex].meta;
|
|
57
58
|
// @TODO: remove in Ghost 3.0 (see https://github.com/TryGhost/Ghost/issues/10434)
|
|
58
|
-
response.data[name][
|
|
59
|
+
response.data[name][apiCall.resource] = result[resultIndex][apiCall.resource];
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
resultIndex = resultIndex + 1;
|
|
@@ -13,6 +13,7 @@ const express = require('../../../shared/express');
|
|
|
13
13
|
const _ = require('lodash');
|
|
14
14
|
const url = require('url');
|
|
15
15
|
const security = require('@tryghost/security');
|
|
16
|
+
const {resolveResourceRead} = require('./api-adapter');
|
|
16
17
|
const urlUtils = require('../../../shared/url-utils').default;
|
|
17
18
|
const registry = require('./registry');
|
|
18
19
|
|
|
@@ -197,6 +198,11 @@ class ParentRouter {
|
|
|
197
198
|
|
|
198
199
|
/**
|
|
199
200
|
* @description Figure out if the router has a redirect enabled.
|
|
201
|
+
*
|
|
202
|
+
* A route claims a resource by reading it: `data: tag.bacon` on this router
|
|
203
|
+
* means /tag/bacon/ should redirect here from wherever else it lives. Only
|
|
204
|
+
* `read` entries claim a slug, and an entry can opt out with `redirect: false`.
|
|
205
|
+
*
|
|
200
206
|
* @param {string} routerType
|
|
201
207
|
* @param {string} slug
|
|
202
208
|
* @returns {boolean}
|
|
@@ -204,14 +210,21 @@ class ParentRouter {
|
|
|
204
210
|
isRedirectEnabled(routerType, slug) {
|
|
205
211
|
debug('isRedirectEnabled', this.name, this.route && this.route.value, routerType, slug);
|
|
206
212
|
|
|
207
|
-
if (!this.data
|
|
213
|
+
if (!this.data) {
|
|
208
214
|
return false;
|
|
209
215
|
}
|
|
210
216
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
217
|
+
// Top-level shorthand (`data: tag.bacon`) is a single unnamed entry.
|
|
218
|
+
const entries = typeof this.data === 'string' ? [this.data] : Object.values(this.data);
|
|
219
|
+
|
|
220
|
+
return entries.some((entry) => {
|
|
221
|
+
if (typeof entry !== 'string' && entry.redirect === false) {
|
|
222
|
+
return false;
|
|
214
223
|
}
|
|
224
|
+
|
|
225
|
+
const read = resolveResourceRead(entry);
|
|
226
|
+
|
|
227
|
+
return !!read && read.resource === routerType && read.slug === slug;
|
|
215
228
|
});
|
|
216
229
|
}
|
|
217
230
|
|