ghost 6.54.1 → 6.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/tryghost-adapter-base-scheduling-0.2.0.tgz +0 -0
- 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.3.tgz +0 -0
- package/core/boot.js +3 -6
- package/core/built/admin/assets/{account-migration-BklF2I_I.js → account-migration-sjHLx5dy.js} +1 -1
- package/core/built/admin/assets/activitypub/{account-migration-CNovr6W-.js → account-migration-DoYXtnZ8.js} +4 -4
- package/core/built/admin/assets/activitypub/activitypub.js +1 -1
- package/core/built/admin/assets/activitypub/{bluesky-sharing-Ceslb1_M.js → bluesky-sharing-Bxeh6MKW.js} +4 -4
- package/core/built/admin/assets/activitypub/{dist-DHAjBcV3.js → dist-D8sFfdpC.js} +2 -2
- package/core/built/admin/assets/activitypub/{domain-C1qQvJ8C.js → domain-CWueFW6q.js} +5 -5
- package/core/built/admin/assets/activitypub/{edit-profile-BcH-jEfM.js → edit-profile-BGxPU7N5.js} +3 -3
- package/core/built/admin/assets/activitypub/{explore-AxwBh9dI.js → explore-DBK-u0Ch.js} +3 -3
- package/core/built/admin/assets/activitypub/{feed-BaOlbh7a.js → feed-Bcir5RQZ.js} +3 -3
- package/core/built/admin/assets/activitypub/{field-B7mYYQpN.js → field-BV-CIT3d.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-vXez-5uJ.js → inbox-yC0nfvzl.js} +4 -4
- package/core/built/admin/assets/activitypub/{label-Chv5T8bw.js → label-DTRS9TDk.js} +2 -2
- package/core/built/admin/assets/activitypub/layout-qx-bUL1k.js +1 -0
- package/core/built/admin/assets/activitypub/{moderation-DtSM2i1I.js → moderation-Dwl9t0pb.js} +4 -4
- package/core/built/admin/assets/activitypub/{note-Dckp4S-2.js → note-B-zKsZ7r.js} +3 -3
- package/core/built/admin/assets/activitypub/{notifications-CG5T22vg.js → notifications-DjJZD54t.js} +3 -3
- package/core/built/admin/assets/activitypub/{preferences-D_799jdl.js → preferences-CoU8vs6d.js} +6 -6
- package/core/built/admin/assets/activitypub/{profile-D2JfXBQl.js → profile-BAsaJekx.js} +6 -6
- package/core/built/admin/assets/activitypub/{routes-CCHe2dQp.js → routes-CLH3OVTP.js} +18 -18
- package/core/built/admin/assets/activitypub/{settings-kx-9Enkz.js → settings-DA6pNJ-Q.js} +3 -3
- package/core/built/admin/assets/activitypub/{site-x4_IjVJ0.js → site-CQD42Pw5.js} +2 -2
- package/core/built/admin/assets/activitypub/{tabs-CyPRQQ1N.js → tabs-Cg0saow1.js} +7 -7
- package/core/built/admin/assets/activitypub/{use-reply-chain-data-BAB6fNGt.js → use-reply-chain-data-Br4aOWOh.js} +2 -2
- package/core/built/admin/assets/audience-DjEWNhp3.js +1 -0
- package/core/built/admin/assets/{automated-emails-of1MvVsv.js → automated-emails-DRy7qY-J.js} +2 -2
- package/core/built/admin/assets/{automations-DRFjW5fw.js → automations-Bx3VcFIL.js} +1 -1
- package/core/built/admin/assets/{automations-B7-y3dXt.js → automations-DhKeS_U5.js} +1 -1
- package/core/built/admin/assets/{bluesky-sharing-BbbjWdzs.js → bluesky-sharing-ClMd0-Ft.js} +1 -1
- package/core/built/admin/assets/{breadcrumb-B-M9Ip_2.js → breadcrumb-BD0U2ygg.js} +1 -1
- package/core/built/admin/assets/{chart-helpers-xiSsW8r0.js → chart-helpers-fjQKRVyY.js} +1 -1
- package/core/built/admin/assets/{checkbox-EhJKHFdZ.js → checkbox-CWa28lgO.js} +1 -1
- package/core/built/admin/assets/{chunk.361.3cdb584b973959e52ccc.js → chunk.481.6c7395a49484bb4583aa.js} +41 -38
- package/core/built/admin/assets/{chunk.524.8365cd9280405c0f6216.js → chunk.524.cffd399783a519e404a9.js} +4 -4
- package/core/built/admin/assets/{chunk.582.61d078937e6102769f2c.js → chunk.582.eebf0d0b2d202bb746a5.js} +6 -6
- package/core/built/admin/assets/{code-editor-view-CZtJFGhR.js → code-editor-view-CCr0rBWS.js} +1 -1
- package/core/built/admin/assets/comments-DyUSMUdj.js +1 -0
- package/core/built/admin/assets/{date-range-select-CTlyo8zW.js → date-range-select-BFp58EMk.js} +1 -1
- package/core/built/admin/assets/{disabled-sources-indicator-qs2GfLvl.js → disabled-sources-indicator-CUxMfiPa.js} +1 -1
- package/core/built/admin/assets/domain-C0bV2UV8.js +1 -0
- package/core/built/admin/assets/{dropzone-BmCXnw6J.js → dropzone-DlK-K2DQ.js} +1 -1
- package/core/built/admin/assets/{edit-profile-SGrJve5M.js → edit-profile-BKT-REJw.js} +1 -1
- package/core/built/admin/assets/editor-Dp865O-4.js +8 -0
- package/core/built/admin/assets/{explore-CUYLv9G9.js → explore-ClUpJZOp.js} +1 -1
- package/core/built/admin/assets/{feed-CPdJMttL.js → feed-DF92dUMP.js} +1 -1
- package/core/built/admin/assets/field-Do7lieBA.js +1 -0
- package/core/built/admin/assets/{filter-sources-Bieps75g.js → filter-sources-BV2rnpfA.js} +1 -1
- package/core/built/admin/assets/filters-CUXJ2Nm9.js +1 -0
- package/core/built/admin/assets/{filters-CuB-A7ma.js → filters-DfbIWc_l.js} +1 -1
- package/core/built/admin/assets/{ghost-3063eae29e6c9cc6d6e7131928a27da0.css → ghost-a9c788f6f0b1ea6cf0f14ca4b13f4496.css} +1 -1
- package/core/built/admin/assets/{ghost-05f9a7182ed56903dba05878a5b8422b.js → ghost-c4057474943effc568c5659ccc4909d7.js} +190 -190
- package/core/built/admin/assets/{ghost-dark-c8d0ab7f515b2010c173e23e9b32ea8a.css → ghost-dark-db36d795d21c61426837df40af270966.css} +1 -1
- package/core/built/admin/assets/gift-link-modal-f9VIMNDv.js +1 -0
- package/core/built/admin/assets/{gift-links-nBxJjZiQ.js → gift-links-CsUhefWG.js} +1 -1
- package/core/built/admin/assets/growth-B3I6gNLm.js +1 -0
- package/core/built/admin/assets/growth-CVw1My4S.js +3 -0
- package/core/built/admin/assets/{hooks-BApwwBE0.js → hooks-CixO01lL.js} +1 -1
- package/core/built/admin/assets/{inbox-Xx1CdnfO.js → inbox-CFBSu4Ed.js} +13 -13
- package/core/built/admin/assets/index-9Cw8z3rA.css +1 -0
- package/core/built/admin/assets/index-BGG7NckO.js +67 -0
- package/core/built/admin/assets/{input-group-CGOXcy4l.js → input-group-BSgG_lVw.js} +1 -1
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +974 -937
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +28 -28
- package/core/built/admin/assets/koenig-lexical/style.css +1 -1
- package/core/built/admin/assets/{koenig-lexical-B61IALf5.js → koenig-lexical-f8vHRYvz.js} +28 -28
- package/core/built/admin/assets/{kpi-card-DCdlyaSS.js → kpi-card-DSap9rAy.js} +1 -1
- package/core/built/admin/assets/{kpi-card-BlZaG1Lo.js → kpi-card-DU0rn9Cj.js} +1 -1
- package/core/built/admin/assets/{kpi-helpers-DpAuS1Nb.js → kpi-helpers-Dz33AV6h.js} +1 -1
- package/core/built/admin/assets/{kpi-tabs-Dme44bQS.js → kpi-tabs-BtTTrKvJ.js} +1 -1
- package/core/built/admin/assets/{label-dSs5aepE.js → label-DS8Mi6G5.js} +1 -1
- package/core/built/admin/assets/label-picker-1DvSuLA1.js +1 -0
- package/core/built/admin/assets/labels-CDSUt4J_.js +1 -0
- package/core/built/admin/assets/layout-yQEi2qDM.js +1 -0
- package/core/built/admin/assets/{link-helpers-tOlxmuH5.js → link-helpers-7EYqvvHs.js} +1 -1
- package/core/built/admin/assets/member-custom-fields-yOMHyAg9.js +1 -0
- package/core/built/admin/assets/member-detail-BAq7gDaa.js +1 -0
- package/core/built/admin/assets/members-CQpVFJu4.js +10 -0
- package/core/built/admin/assets/modals-ReORvWnu.js +31 -0
- package/core/built/admin/assets/moderation-DySbgyUS.js +1 -0
- package/core/built/admin/assets/newsletter-CJQvtHqN.js +1 -0
- package/core/built/admin/assets/newsletters-B3QQXfGT.js +1 -0
- package/core/built/admin/assets/{newsletters-D1jbtV-h.js → newsletters-BXJbJgA-.js} +1 -1
- package/core/built/admin/assets/note-C8kxrz6w.js +1 -0
- package/core/built/admin/assets/notifications-CgtSfX7t.js +1 -0
- package/core/built/admin/assets/{offers-CrY6nSRR.js → offers-DEniVRD5.js} +1 -1
- package/core/built/admin/assets/onboarding-route-4qtziA9d.js +1 -0
- package/core/built/admin/assets/overview-Bf2sbznq.js +1 -0
- package/core/built/admin/assets/overview-CBfePxcr.js +1 -0
- package/core/built/admin/assets/{page-header-op0eN36I.js → page-header-B3Wb9Jqv.js} +1 -1
- package/core/built/admin/assets/{pages-BbNWFcGj.js → pages-B4goRPUn.js} +1 -1
- package/core/built/admin/assets/{post-analytics-WwRDEnrV.js → post-analytics-We-ko5Cb.js} +1 -1
- package/core/built/admin/assets/post-analytics-header-Bmc9su6q.js +1 -0
- package/core/built/admin/assets/post-analytics-provider-B6UynTGb.js +1 -0
- package/core/built/admin/assets/{post-share-modal-Dx-MEjhI.js → post-share-modal-DFfO93_0.js} +1 -1
- package/core/built/admin/assets/{posts-EWpZIjkr.js → posts-DhXE7b6r.js} +1 -1
- package/core/built/admin/assets/preferences-DJPfHEGu.js +5 -0
- package/core/built/admin/assets/profile-DqBQp-gT.js +1 -0
- package/core/built/admin/assets/{referrers-BCfFYSHc.js → referrers-pmJT4k3-.js} +1 -1
- package/core/built/admin/assets/{select-C1qDZnW4.js → select-C5_xEt5b.js} +1 -1
- package/core/built/admin/assets/settings-BwiVIXau.js +4 -0
- package/core/built/admin/assets/{settings-H6HxFgfI.js → settings-WXNeWyU1.js} +1 -1
- package/core/built/admin/assets/{share-modal-BJCadJyp.js → share-modal-DsgW3wbg.js} +1 -1
- package/core/built/admin/assets/social-urls-BQPFEk1_.js +8 -0
- package/core/built/admin/assets/{sort-button-DyYlfYp7.js → sort-button-Bb_FCl7e.js} +1 -1
- package/core/built/admin/assets/{source-icon-_ubRDbg_.js → source-icon-BeaRxUn9.js} +1 -1
- package/core/built/admin/assets/{stats-BQZMGMvn.js → stats-Bjj_kmJ1.js} +1 -1
- package/core/built/admin/assets/{stats-view-Doei-qCX.js → stats-view-BxPbW6K1.js} +1 -1
- package/core/built/admin/assets/{step-1-IKiLBPoC.js → step-1-B1e3-1F8.js} +1 -1
- package/core/built/admin/assets/{step-2-jJ41PsDN.js → step-2-C5WaeBJo.js} +1 -1
- package/core/built/admin/assets/{step-3-CaqFAEqf.js → step-3-B46iVPwu.js} +2 -2
- package/core/built/admin/assets/{switch-t-WyENU2.js → switch-z5Y6Uzt6.js} +1 -1
- package/core/built/admin/assets/{table-DxUL78jU.js → table-CB-4_HBJ.js} +1 -1
- package/core/built/admin/assets/tabs-CCEgKYzq.js +1 -0
- package/core/built/admin/assets/{tags-CNujGwkT.js → tags-Cg2VJGmG.js} +1 -1
- package/core/built/admin/assets/{tiers-BgYLWRJv.js → tiers-DYsa4DCo.js} +1 -1
- package/core/built/admin/assets/{toggle-group-BeEpQ69p.js → toggle-group-b3IlxvhV.js} +1 -1
- package/core/built/admin/assets/{topic-filter-Bb5MOD-1.js → topic-filter-DAFT87tQ.js} +1 -1
- package/core/built/admin/assets/{use-analytics-data-Cds6XDjg.js → use-analytics-data-S0FTd43s.js} +1 -1
- package/core/built/admin/assets/use-filter-params-Gpoq2E1L.js +1 -0
- package/core/built/admin/assets/{use-growth-stats-D8uf98-G.js → use-growth-stats-CIaiHOVb.js} +1 -1
- package/core/built/admin/assets/{use-post-referrers-CRtgyuXB.js → use-post-referrers-Cq2vBPrA.js} +1 -1
- package/core/built/admin/assets/{use-reply-chain-data-DbKYX50v.js → use-reply-chain-data-DBDRtGu-.js} +1 -1
- package/core/built/admin/assets/{use-simple-pagination-DC01kpo0.js → use-simple-pagination-CjYNxdwe.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-query-C7bQRJWw.js → use-tinybird-query-falHAtuK.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-token-Az4tam7_.js → use-tinybird-token-D7-8ND1y.js} +1 -1
- package/core/built/admin/assets/{users-CcSCQFcQ.js → users-Dkv8NQZe.js} +1 -1
- package/core/built/admin/assets/{virtual-list-Dhuh9fGx.js → virtual-list-CvnC9zF2.js} +1 -1
- package/core/built/admin/assets/web-B_5GZRvH.js +1 -0
- package/core/built/admin/assets/web-jFYWCKDn.js +2 -0
- package/core/built/admin/index.html +9 -9
- package/core/frontend/services/proxy.js +7 -8
- package/core/frontend/services/routing/router-manager.js +14 -14
- package/core/frontend/services/routing/static-routes-router.js +8 -10
- package/core/server/api/endpoints/automation-action-links.js +68 -0
- package/core/server/api/endpoints/automation-action-links.ts +46 -0
- package/core/server/api/endpoints/index.js +4 -0
- package/core/server/api/endpoints/settings.js +0 -2
- package/core/server/api/endpoints/utils/serializers/output/utils/url.js +11 -1
- package/core/server/data/migrations/versions/6.55/2026-07-27-11-25-44-add-to-hash-to-redirects.js +51 -0
- package/core/server/data/migrations/versions/6.55/2026-07-28-12-08-00-remove-routes-hash-from-settings.js +5 -0
- package/core/server/data/schema/commands.js +5 -0
- package/core/server/data/schema/default-settings/default-settings.json +0 -4
- package/core/server/data/schema/schema.js +5 -1
- package/core/server/services/automations/automations-api.js +15 -0
- package/core/server/services/automations/automations-api.ts +19 -0
- package/core/server/services/automations/automations-repository.ts +18 -0
- package/core/server/services/automations/database-automations-repository.js +109 -26
- package/core/server/services/automations/database-automations-repository.ts +110 -7
- package/core/server/services/automations/database-date.js +10 -0
- package/core/server/services/automations/database-date.ts +5 -0
- package/core/server/services/automations/poll.js +5 -2
- package/core/server/services/automations/poll.ts +7 -2
- package/core/server/services/automations/scheduler-poll-time.js +40 -0
- package/core/server/services/automations/scheduler-poll-time.ts +38 -0
- package/core/server/services/automations/service.js +15 -3
- package/core/server/services/automations/service.ts +16 -3
- package/core/server/services/comments/comments-service-emails.js +10 -7
- package/core/server/services/comments/comments-service.js +9 -4
- package/core/server/services/email-rendering/partials/email-wrapper.hbs +3 -1
- package/core/server/services/email-service/email-service-wrapper.js +6 -1
- package/core/server/services/email-service/email-templates/partials/styles.hbs +8 -0
- package/core/server/services/email-service/newsletter-email-event-storage.js +41 -10
- package/core/server/services/lib/dynamic-redirect-manager.js +12 -3
- package/core/server/services/link-redirection/link-redirect-repository.js +41 -4
- package/core/server/services/link-redirection/link-redirect.js +2 -0
- package/core/server/services/link-redirection/link-redirect.ts +3 -1
- package/core/server/services/link-redirection/link-redirects-service.js +38 -2
- package/core/server/services/link-tracking/click-event.js +3 -0
- package/core/server/services/link-tracking/index.js +38 -6
- package/core/server/services/link-tracking/link-click-repository.js +30 -9
- package/core/server/services/link-tracking/link-click-tracking-service.js +44 -3
- package/core/server/services/member-attribution/outbound-link-tagger.js +3 -1
- package/core/server/services/member-welcome-emails/member-welcome-email-renderer.js +19 -4
- package/core/server/services/member-welcome-emails/service.js +11 -3
- package/core/server/services/members/import-export/csv/formula.js +16 -0
- package/core/server/services/members/import-export/csv/formula.ts +14 -0
- package/core/server/services/members/import-export/csv/index.js +3 -1
- package/core/server/services/members/import-export/csv/index.ts +1 -0
- package/core/server/services/members/import-export/import/completion-email.js +22 -3
- package/core/server/services/members/import-export/import/completion-email.ts +29 -7
- package/core/server/services/members/import-export/import/importer.js +25 -4
- package/core/server/services/members/import-export/import/importer.ts +42 -4
- package/core/server/services/members/import-export/index.js +9 -0
- package/core/server/services/members/import-export/index.ts +20 -1
- package/core/server/services/members/service.js +8 -1
- package/core/server/services/members-custom-fields/values-service.js +19 -7
- package/core/server/services/members-custom-fields/values-service.ts +19 -7
- package/core/server/services/mentions/service.js +5 -1
- package/core/server/services/route-settings/activation-bridge.js +59 -49
- package/core/server/services/route-settings/activation-bridge.ts +91 -51
- package/core/server/services/route-settings/dynamic-routing-service.js +22 -51
- package/core/server/services/route-settings/index.js +1 -16
- package/core/server/services/settings/settings-service.js +0 -18
- package/core/server/services/url/url-service-facade.js +1 -1
- package/core/server/services/url/url-service-facade.ts +8 -1
- package/core/server/web/api/endpoints/admin/routes.js +1 -0
- package/package.json +7 -7
- package/pnpm-lock.yaml +8 -8
- package/pnpm-workspace.yaml +5 -4
- package/core/built/admin/assets/Growth-BTP1LthE.js +0 -3
- package/core/built/admin/assets/Newsletters-CSEX0myc.js +0 -1
- package/core/built/admin/assets/Overview-BjACEsJF.js +0 -1
- package/core/built/admin/assets/Web-CxxxjCcv.js +0 -2
- package/core/built/admin/assets/activitypub/layout-CziktJXo.js +0 -1
- package/core/built/admin/assets/audience-X_XsZivI.js +0 -1
- package/core/built/admin/assets/comments-CvyXxOyN.js +0 -1
- package/core/built/admin/assets/constants-c-JNF9c2.js +0 -1
- package/core/built/admin/assets/domain-CPNXKVOV.js +0 -1
- package/core/built/admin/assets/editor-BOG2YPUm.js +0 -8
- package/core/built/admin/assets/field-D0XISCK2.js +0 -1
- package/core/built/admin/assets/filters-bXkkWro8.js +0 -1
- package/core/built/admin/assets/gift-link-modal-su8MsRdg.js +0 -1
- package/core/built/admin/assets/growth-Ba6S5FQX.js +0 -1
- package/core/built/admin/assets/index-D8PbzuT9.css +0 -1
- package/core/built/admin/assets/index-ukW_0Uie.js +0 -67
- package/core/built/admin/assets/label-picker-Uq91atex.js +0 -1
- package/core/built/admin/assets/labels-Cyayfd-e.js +0 -1
- package/core/built/admin/assets/layout-WUcz6tu1.js +0 -1
- package/core/built/admin/assets/member-custom-fields-NCzrMuba.js +0 -1
- package/core/built/admin/assets/member-detail-DPWDtYnN.js +0 -1
- package/core/built/admin/assets/members-B9rO7qr4.js +0 -10
- package/core/built/admin/assets/modals-CxRaan1J.js +0 -31
- package/core/built/admin/assets/moderation-D8zon60E.js +0 -1
- package/core/built/admin/assets/multi-select-combobox-1wYm3nle.js +0 -1
- package/core/built/admin/assets/newsletter-DbYMRxRS.js +0 -1
- package/core/built/admin/assets/note-PiWPnnrh.js +0 -1
- package/core/built/admin/assets/notifications-DEgqqB9G.js +0 -1
- package/core/built/admin/assets/onboarding-route-BzF6HeVn.js +0 -1
- package/core/built/admin/assets/overview-DixLeIqY.js +0 -1
- package/core/built/admin/assets/post-analytics-header-DsMoLx0R.js +0 -1
- package/core/built/admin/assets/post-analytics-provider-BJCPLMIb.js +0 -1
- package/core/built/admin/assets/preferences-D7-j6A4C.js +0 -5
- package/core/built/admin/assets/profile-Dl69QPp0.js +0 -1
- package/core/built/admin/assets/settings-BbmC_Iqq.js +0 -4
- package/core/built/admin/assets/social-urls-BPL1nADp.js +0 -8
- package/core/built/admin/assets/tabs-BQcJQ1ke.js +0 -1
- package/core/built/admin/assets/use-filter-params-DjXkhO54.js +0 -1
- package/core/built/admin/assets/web-LtHNiNBV.js +0 -1
- package/core/server/services/route-settings/default-settings-manager.js +0 -63
- package/core/server/services/route-settings/route-settings.js +0 -170
- package/core/server/services/route-settings/settings-loader.js +0 -60
- package/core/server/services/route-settings/settings-path-manager.js +0 -47
- package/core/server/services/route-settings/validate.js +0 -440
|
@@ -47,6 +47,9 @@ let membersApi;
|
|
|
47
47
|
let verificationTrigger;
|
|
48
48
|
|
|
49
49
|
const buildImporterDeps = ({stripeAPIService}) => {
|
|
50
|
+
// Required here, not statically: boot builds the custom fields services before this
|
|
51
|
+
// one (the exporter below relies on the same).
|
|
52
|
+
const customFields = require('../members-custom-fields');
|
|
50
53
|
return {
|
|
51
54
|
getTimezone: () => settingsCache.get('timezone'),
|
|
52
55
|
// A getter rather than a value because the threshold is an operator
|
|
@@ -81,7 +84,11 @@ const buildImporterDeps = ({stripeAPIService}) => {
|
|
|
81
84
|
knex: db.knex,
|
|
82
85
|
urlFor: urlUtils.urlFor.bind(urlUtils),
|
|
83
86
|
stripeAPIService,
|
|
84
|
-
productRepository: membersApi.productRepository
|
|
87
|
+
productRepository: membersApi.productRepository,
|
|
88
|
+
customFields: {
|
|
89
|
+
definitions: customFields.definitions,
|
|
90
|
+
values: customFields.values
|
|
91
|
+
}
|
|
85
92
|
};
|
|
86
93
|
};
|
|
87
94
|
|
|
@@ -137,8 +137,9 @@ class CustomFieldValuesService {
|
|
|
137
137
|
/**
|
|
138
138
|
* Resolve input into the writes it implies, rejecting anything invalid, and
|
|
139
139
|
* writing nothing. Returned so a caller can validate before it commits to a
|
|
140
|
-
* change it would have to unwind (the member edit
|
|
141
|
-
* apply the same plan without
|
|
140
|
+
* change it would have to unwind (the member edit and the importer both validate
|
|
141
|
+
* up front, before opening a transaction), then apply the same plan without
|
|
142
|
+
* re-resolving or re-validating.
|
|
142
143
|
*/
|
|
143
144
|
async planWrite(input) {
|
|
144
145
|
const values = this.parseValues(input);
|
|
@@ -191,14 +192,17 @@ class CustomFieldValuesService {
|
|
|
191
192
|
* Apply a plan from `planWrite`.
|
|
192
193
|
*
|
|
193
194
|
* Merge, not replace: only the fields in the plan are touched, so a caller
|
|
194
|
-
* that doesn't know about a field can't erase it.
|
|
195
|
-
*
|
|
195
|
+
* that doesn't know about a field can't erase it.
|
|
196
|
+
*
|
|
197
|
+
* Always transactional, so a mid-batch failure rolls the batch back. Passed an
|
|
198
|
+
* executor it joins that transaction -- the importer passes its per-member one, so a
|
|
199
|
+
* failed value write takes the member with it; passed nothing it opens its own.
|
|
196
200
|
*/
|
|
197
|
-
async applyWrite(memberId, writes) {
|
|
201
|
+
async applyWrite(memberId, writes, executor = this.knex) {
|
|
198
202
|
if (writes.length === 0) {
|
|
199
203
|
return;
|
|
200
204
|
}
|
|
201
|
-
|
|
205
|
+
const apply = async (trx) => {
|
|
202
206
|
for (const { field, value } of writes) {
|
|
203
207
|
const target = { member_id: memberId, custom_field_id: field.id };
|
|
204
208
|
if (value === undefined) {
|
|
@@ -213,7 +217,15 @@ class CustomFieldValuesService {
|
|
|
213
217
|
.onConflict(['member_id', 'custom_field_id'])
|
|
214
218
|
.merge({ ...valueColumns, updated_at: new Date() });
|
|
215
219
|
}
|
|
216
|
-
}
|
|
220
|
+
};
|
|
221
|
+
// isTransaction is knex's marker for a transactor: join an existing transaction
|
|
222
|
+
// rather than nesting a savepoint under it, otherwise open one.
|
|
223
|
+
if (executor.isTransaction) {
|
|
224
|
+
await apply(executor);
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
await executor.transaction(apply);
|
|
228
|
+
}
|
|
217
229
|
}
|
|
218
230
|
}
|
|
219
231
|
exports.CustomFieldValuesService = CustomFieldValuesService;
|
|
@@ -164,8 +164,9 @@ export class CustomFieldValuesService {
|
|
|
164
164
|
/**
|
|
165
165
|
* Resolve input into the writes it implies, rejecting anything invalid, and
|
|
166
166
|
* writing nothing. Returned so a caller can validate before it commits to a
|
|
167
|
-
* change it would have to unwind (the member edit
|
|
168
|
-
* apply the same plan without
|
|
167
|
+
* change it would have to unwind (the member edit and the importer both validate
|
|
168
|
+
* up front, before opening a transaction), then apply the same plan without
|
|
169
|
+
* re-resolving or re-validating.
|
|
169
170
|
*/
|
|
170
171
|
async planWrite(input: unknown): Promise<PlannedWrite[]> {
|
|
171
172
|
const values = this.parseValues(input);
|
|
@@ -225,15 +226,18 @@ export class CustomFieldValuesService {
|
|
|
225
226
|
* Apply a plan from `planWrite`.
|
|
226
227
|
*
|
|
227
228
|
* Merge, not replace: only the fields in the plan are touched, so a caller
|
|
228
|
-
* that doesn't know about a field can't erase it.
|
|
229
|
-
*
|
|
229
|
+
* that doesn't know about a field can't erase it.
|
|
230
|
+
*
|
|
231
|
+
* Always transactional, so a mid-batch failure rolls the batch back. Passed an
|
|
232
|
+
* executor it joins that transaction -- the importer passes its per-member one, so a
|
|
233
|
+
* failed value write takes the member with it; passed nothing it opens its own.
|
|
230
234
|
*/
|
|
231
|
-
async applyWrite(memberId: string, writes: PlannedWrite[]): Promise<void> {
|
|
235
|
+
async applyWrite(memberId: string, writes: PlannedWrite[], executor: Knex = this.knex): Promise<void> {
|
|
232
236
|
if (writes.length === 0) {
|
|
233
237
|
return;
|
|
234
238
|
}
|
|
235
239
|
|
|
236
|
-
|
|
240
|
+
const apply = async (trx: Knex) => {
|
|
237
241
|
for (const {field, value} of writes) {
|
|
238
242
|
const target = {member_id: memberId, custom_field_id: field.id};
|
|
239
243
|
|
|
@@ -250,6 +254,14 @@ export class CustomFieldValuesService {
|
|
|
250
254
|
.onConflict(['member_id', 'custom_field_id'])
|
|
251
255
|
.merge({...valueColumns, updated_at: new Date()});
|
|
252
256
|
}
|
|
253
|
-
}
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// isTransaction is knex's marker for a transactor: join an existing transaction
|
|
260
|
+
// rather than nesting a savepoint under it, otherwise open one.
|
|
261
|
+
if (executor.isTransaction) {
|
|
262
|
+
await apply(executor);
|
|
263
|
+
} else {
|
|
264
|
+
await executor.transaction(apply);
|
|
265
|
+
}
|
|
254
266
|
}
|
|
255
267
|
}
|
|
@@ -29,7 +29,11 @@ async function getPostData(post) {
|
|
|
29
29
|
|
|
30
30
|
function getPostUrl(id, postData) {
|
|
31
31
|
const jsonModel = {...postData};
|
|
32
|
-
|
|
32
|
+
// The URL service routes by resource type. Pages and posts share the Post
|
|
33
|
+
// model, so the page's own type must reach forPost — otherwise it defaults
|
|
34
|
+
// to 'posts', matches no post collection, and 404s under the lazy service.
|
|
35
|
+
const type = postData.type === 'page' ? 'pages' : 'posts';
|
|
36
|
+
outputSerializerUrlUtil.forPost(id, jsonModel, {options: {}}, type);
|
|
33
37
|
return jsonModel.url;
|
|
34
38
|
}
|
|
35
39
|
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
6
|
+
exports.buildRouterSettings = buildRouterSettings;
|
|
7
7
|
const lodash_1 = __importDefault(require("lodash"));
|
|
8
8
|
const errors_1 = __importDefault(require("@tryghost/errors"));
|
|
9
9
|
const config_1 = require("../../../frontend/services/routing/config");
|
|
@@ -87,78 +87,88 @@ function expandRouteData(routeData) {
|
|
|
87
87
|
function convertSlugsToColons(value) {
|
|
88
88
|
return value.replace(/{(\w+)}/g, ':$1');
|
|
89
89
|
}
|
|
90
|
-
function
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
expanded.data = expandRouteData(route.data);
|
|
95
|
-
}
|
|
90
|
+
function buildRouterRoute(route) {
|
|
91
|
+
const data = route.data !== undefined ? expandRouteData(route.data) : undefined;
|
|
92
|
+
// Build per branch: RouterRoute is a discriminated union, so each member is
|
|
93
|
+
// constructed as its concrete type. `route` is narrowed by the check.
|
|
96
94
|
if (route.type === 'channel') {
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
95
|
+
const result = {
|
|
96
|
+
path: route.path,
|
|
97
|
+
type: 'channel',
|
|
98
|
+
templates: route.templates || []
|
|
99
|
+
};
|
|
100
|
+
if (data !== undefined) {
|
|
101
|
+
result.data = data;
|
|
101
102
|
}
|
|
102
|
-
if (
|
|
103
|
-
|
|
103
|
+
if (route.filter !== undefined) {
|
|
104
|
+
result.filter = route.filter;
|
|
104
105
|
}
|
|
105
|
-
if (
|
|
106
|
-
|
|
106
|
+
if (route.order !== undefined) {
|
|
107
|
+
result.order = route.order;
|
|
107
108
|
}
|
|
108
|
-
if (
|
|
109
|
-
|
|
109
|
+
if (route.limit !== undefined) {
|
|
110
|
+
result.limit = route.limit;
|
|
110
111
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const template = route;
|
|
114
|
-
if (template.contentType !== undefined) {
|
|
115
|
-
expanded.content_type = template.contentType;
|
|
112
|
+
if (route.rss !== undefined) {
|
|
113
|
+
result.rss = route.rss;
|
|
116
114
|
}
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
const result = {
|
|
118
|
+
path: route.path,
|
|
119
|
+
type: 'template',
|
|
120
|
+
templates: route.templates || []
|
|
121
|
+
};
|
|
122
|
+
if (data !== undefined) {
|
|
123
|
+
result.data = data;
|
|
117
124
|
}
|
|
118
|
-
|
|
125
|
+
if (route.contentType !== undefined) {
|
|
126
|
+
result.contentType = route.contentType;
|
|
127
|
+
}
|
|
128
|
+
return result;
|
|
119
129
|
}
|
|
120
|
-
function
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
130
|
+
function buildRouterCollection(collection) {
|
|
131
|
+
const result = {
|
|
132
|
+
path: collection.path,
|
|
133
|
+
permalink: convertSlugsToColons(collection.permalink),
|
|
134
|
+
templates: collection.templates || []
|
|
135
|
+
};
|
|
124
136
|
if (collection.data !== undefined) {
|
|
125
|
-
|
|
137
|
+
result.data = expandRouteData(collection.data);
|
|
126
138
|
}
|
|
127
139
|
if (collection.filter !== undefined) {
|
|
128
|
-
|
|
140
|
+
result.filter = collection.filter;
|
|
129
141
|
}
|
|
130
142
|
if (collection.order !== undefined) {
|
|
131
|
-
|
|
143
|
+
result.order = collection.order;
|
|
132
144
|
}
|
|
133
145
|
if (collection.limit !== undefined) {
|
|
134
|
-
|
|
146
|
+
result.limit = collection.limit;
|
|
135
147
|
}
|
|
136
148
|
if (collection.rss !== undefined) {
|
|
137
|
-
|
|
149
|
+
result.rss = collection.rss;
|
|
138
150
|
}
|
|
139
|
-
return
|
|
151
|
+
return result;
|
|
140
152
|
}
|
|
141
153
|
/**
|
|
142
|
-
* Converts a RouteSettings domain model into the
|
|
143
|
-
*
|
|
154
|
+
* Converts a RouteSettings domain model into the array shape that
|
|
155
|
+
* RouterManager.start() iterates. Each route/collection carries its own `path`,
|
|
156
|
+
* and taxonomies become `{key, permalink}` entries, so the routing layer no
|
|
157
|
+
* longer reads paths from map keys.
|
|
144
158
|
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
159
|
+
* Temporary adapter: removed in HKG-1898 once the routers consume the domain
|
|
160
|
+
* model directly.
|
|
147
161
|
*/
|
|
148
|
-
function
|
|
149
|
-
const
|
|
150
|
-
for (const route of settings.routes) {
|
|
151
|
-
routes[route.path] = expandRoute(route);
|
|
152
|
-
}
|
|
153
|
-
const collections = {};
|
|
154
|
-
for (const collection of settings.collections) {
|
|
155
|
-
collections[collection.path] = expandCollection(collection);
|
|
156
|
-
}
|
|
157
|
-
const taxonomies = {};
|
|
162
|
+
function buildRouterSettings(settings) {
|
|
163
|
+
const taxonomies = [];
|
|
158
164
|
for (const [key, value] of Object.entries(settings.taxonomies)) {
|
|
159
165
|
if (value) {
|
|
160
|
-
taxonomies
|
|
166
|
+
taxonomies.push({ key, permalink: convertSlugsToColons(value) });
|
|
161
167
|
}
|
|
162
168
|
}
|
|
163
|
-
return {
|
|
169
|
+
return {
|
|
170
|
+
routes: settings.routes.map(buildRouterRoute),
|
|
171
|
+
collections: settings.collections.map(buildRouterCollection),
|
|
172
|
+
taxonomies
|
|
173
|
+
};
|
|
164
174
|
}
|
|
@@ -121,90 +121,130 @@ function convertSlugsToColons(value: string): string {
|
|
|
121
121
|
return value.replace(/{(\w+)}/g, ':$1');
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
/**
|
|
125
|
+
* Router-facing shapes. RouterManager consumes the domain model after the two
|
|
126
|
+
* conversions the bridge still applies: `data` expanded to `{query, router}`,
|
|
127
|
+
* and collection/taxonomy permalinks rewritten to `:slug`.
|
|
128
|
+
*
|
|
129
|
+
* Routes and collections are written as their domain counterpart with just
|
|
130
|
+
* `data` overridden to the expanded shape — `Omit<…, 'data'> & {data?}` rather
|
|
131
|
+
* than `extends`, because the override changes `data`'s type (interface
|
|
132
|
+
* extension can only add fields, not retype them). That keeps the delta from
|
|
133
|
+
* the domain model explicit: `data` is the only structural difference. When the
|
|
134
|
+
* bridge is removed (HKG-1898) the override falls away and these collapse back
|
|
135
|
+
* to `Route` / `CollectionConfig`.
|
|
136
|
+
*/
|
|
137
|
+
type RouterChannelRoute = Omit<ChannelRoute, 'data'> & {data?: ExpandedData};
|
|
138
|
+
type RouterTemplateRoute = Omit<TemplateRoute, 'data'> & {data?: ExpandedData};
|
|
139
|
+
type RouterRoute = RouterChannelRoute | RouterTemplateRoute;
|
|
140
|
+
|
|
141
|
+
type RouterCollection = Omit<CollectionConfig, 'data'> & {data?: ExpandedData};
|
|
142
|
+
|
|
143
|
+
// Taxonomies and RouteSettings have no direct domain counterpart to derive from:
|
|
144
|
+
// the domain stores taxonomies as a `{tag, author}` map, which the bridge
|
|
145
|
+
// flattens into these `{key, permalink}` entries, and RouterSettings drops
|
|
146
|
+
// `yamlSource` and swaps all three array element types (routes, collections,
|
|
147
|
+
// taxonomies) — so they stay standalone.
|
|
148
|
+
interface RouterTaxonomy {
|
|
149
|
+
key: string;
|
|
150
|
+
permalink: string;
|
|
151
|
+
}
|
|
126
152
|
|
|
127
|
-
|
|
153
|
+
export interface RouterSettings {
|
|
154
|
+
routes: RouterRoute[];
|
|
155
|
+
collections: RouterCollection[];
|
|
156
|
+
taxonomies: RouterTaxonomy[];
|
|
157
|
+
}
|
|
128
158
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
159
|
+
function buildRouterRoute(route: Route): RouterRoute {
|
|
160
|
+
const data = route.data !== undefined ? expandRouteData(route.data) : undefined;
|
|
132
161
|
|
|
162
|
+
// Build per branch: RouterRoute is a discriminated union, so each member is
|
|
163
|
+
// constructed as its concrete type. `route` is narrowed by the check.
|
|
133
164
|
if (route.type === 'channel') {
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
165
|
+
const result: RouterChannelRoute = {
|
|
166
|
+
path: route.path,
|
|
167
|
+
type: 'channel',
|
|
168
|
+
templates: route.templates || []
|
|
169
|
+
};
|
|
170
|
+
if (data !== undefined) {
|
|
171
|
+
result.data = data;
|
|
138
172
|
}
|
|
139
|
-
if (
|
|
140
|
-
|
|
173
|
+
if (route.filter !== undefined) {
|
|
174
|
+
result.filter = route.filter;
|
|
141
175
|
}
|
|
142
|
-
if (
|
|
143
|
-
|
|
176
|
+
if (route.order !== undefined) {
|
|
177
|
+
result.order = route.order;
|
|
144
178
|
}
|
|
145
|
-
if (
|
|
146
|
-
|
|
179
|
+
if (route.limit !== undefined) {
|
|
180
|
+
result.limit = route.limit;
|
|
147
181
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if (template.contentType !== undefined) {
|
|
151
|
-
expanded.content_type = template.contentType;
|
|
182
|
+
if (route.rss !== undefined) {
|
|
183
|
+
result.rss = route.rss;
|
|
152
184
|
}
|
|
185
|
+
return result;
|
|
153
186
|
}
|
|
154
187
|
|
|
155
|
-
|
|
188
|
+
const result: RouterTemplateRoute = {
|
|
189
|
+
path: route.path,
|
|
190
|
+
type: 'template',
|
|
191
|
+
templates: route.templates || []
|
|
192
|
+
};
|
|
193
|
+
if (data !== undefined) {
|
|
194
|
+
result.data = data;
|
|
195
|
+
}
|
|
196
|
+
if (route.contentType !== undefined) {
|
|
197
|
+
result.contentType = route.contentType;
|
|
198
|
+
}
|
|
199
|
+
return result;
|
|
156
200
|
}
|
|
157
201
|
|
|
158
|
-
function
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
202
|
+
function buildRouterCollection(collection: CollectionConfig): RouterCollection {
|
|
203
|
+
const result: RouterCollection = {
|
|
204
|
+
path: collection.path,
|
|
205
|
+
permalink: convertSlugsToColons(collection.permalink),
|
|
206
|
+
templates: collection.templates || []
|
|
207
|
+
};
|
|
163
208
|
|
|
164
209
|
if (collection.data !== undefined) {
|
|
165
|
-
|
|
210
|
+
result.data = expandRouteData(collection.data);
|
|
166
211
|
}
|
|
167
|
-
|
|
168
212
|
if (collection.filter !== undefined) {
|
|
169
|
-
|
|
213
|
+
result.filter = collection.filter;
|
|
170
214
|
}
|
|
171
215
|
if (collection.order !== undefined) {
|
|
172
|
-
|
|
216
|
+
result.order = collection.order;
|
|
173
217
|
}
|
|
174
218
|
if (collection.limit !== undefined) {
|
|
175
|
-
|
|
219
|
+
result.limit = collection.limit;
|
|
176
220
|
}
|
|
177
221
|
if (collection.rss !== undefined) {
|
|
178
|
-
|
|
222
|
+
result.rss = collection.rss;
|
|
179
223
|
}
|
|
180
224
|
|
|
181
|
-
return
|
|
225
|
+
return result;
|
|
182
226
|
}
|
|
183
227
|
|
|
184
228
|
/**
|
|
185
|
-
* Converts a RouteSettings domain model into the
|
|
186
|
-
*
|
|
229
|
+
* Converts a RouteSettings domain model into the array shape that
|
|
230
|
+
* RouterManager.start() iterates. Each route/collection carries its own `path`,
|
|
231
|
+
* and taxonomies become `{key, permalink}` entries, so the routing layer no
|
|
232
|
+
* longer reads paths from map keys.
|
|
187
233
|
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
234
|
+
* Temporary adapter: removed in HKG-1898 once the routers consume the domain
|
|
235
|
+
* model directly.
|
|
190
236
|
*/
|
|
191
|
-
export function
|
|
192
|
-
const
|
|
193
|
-
for (const route of settings.routes) {
|
|
194
|
-
routes[route.path] = expandRoute(route);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const collections: Record<string, any> = {};
|
|
198
|
-
for (const collection of settings.collections) {
|
|
199
|
-
collections[collection.path] = expandCollection(collection);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const taxonomies: Record<string, string> = {};
|
|
237
|
+
export function buildRouterSettings(settings: RouteSettings): RouterSettings {
|
|
238
|
+
const taxonomies: RouterTaxonomy[] = [];
|
|
203
239
|
for (const [key, value] of Object.entries(settings.taxonomies)) {
|
|
204
240
|
if (value) {
|
|
205
|
-
taxonomies
|
|
241
|
+
taxonomies.push({key, permalink: convertSlugsToColons(value)});
|
|
206
242
|
}
|
|
207
243
|
}
|
|
208
244
|
|
|
209
|
-
return {
|
|
245
|
+
return {
|
|
246
|
+
routes: settings.routes.map(buildRouterRoute),
|
|
247
|
+
collections: settings.collections.map(buildRouterCollection),
|
|
248
|
+
taxonomies
|
|
249
|
+
};
|
|
210
250
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
const crypto = require('crypto');
|
|
2
|
-
const fs = require('fs-extra');
|
|
3
1
|
const debug = require('@tryghost/debug')('services:route-settings:service');
|
|
4
2
|
const logging = require('@tryghost/logging');
|
|
5
3
|
const errors = require('@tryghost/errors');
|
|
@@ -9,12 +7,6 @@ const messages = {
|
|
|
9
7
|
loadError: 'Could not load routes.yaml file.'
|
|
10
8
|
};
|
|
11
9
|
|
|
12
|
-
/**
|
|
13
|
-
* md5 of the expanded default route settings — the routes_hash value for a
|
|
14
|
-
* site that has never customised its routes.
|
|
15
|
-
*/
|
|
16
|
-
const DEFAULT_ROUTES_SETTING_HASH = '3d180d52c663d173a6be791ef411ed01';
|
|
17
|
-
|
|
18
10
|
function isStoredContentError(err) {
|
|
19
11
|
return err.errorType === 'ValidationError' || err.errorType === 'IncorrectUsageError';
|
|
20
12
|
}
|
|
@@ -27,26 +19,21 @@ class DynamicRoutingService {
|
|
|
27
19
|
constructor() {
|
|
28
20
|
/** @type {RouteSettingsStore} */
|
|
29
21
|
this.store = null;
|
|
30
|
-
this.settingsLoader = null;
|
|
31
22
|
this.routerManager = null;
|
|
32
23
|
this.urlService = null;
|
|
33
24
|
}
|
|
34
25
|
|
|
35
26
|
/**
|
|
36
|
-
* Wire the storage-layer
|
|
37
|
-
*
|
|
38
|
-
*
|
|
27
|
+
* Wire the storage-layer dependency so the API surface (upload, download)
|
|
28
|
+
* works immediately after boot — even when the frontend is disabled and
|
|
29
|
+
* `start()` is never called.
|
|
39
30
|
*
|
|
40
31
|
* @param {object} deps
|
|
41
32
|
* @param {RouteSettingsStore} deps.store - adapter-manager provided store
|
|
42
|
-
* @param {object} [deps.settingsLoader] - legacy SettingsLoader used as a
|
|
43
|
-
* read-path fallback when the stricter store parser rejects a file
|
|
44
|
-
* (see loadRouteSettings)
|
|
45
33
|
*/
|
|
46
|
-
configure({store
|
|
34
|
+
configure({store}) {
|
|
47
35
|
debug('configure');
|
|
48
36
|
this.store = store;
|
|
49
|
-
this.settingsLoader = settingsLoader;
|
|
50
37
|
}
|
|
51
38
|
|
|
52
39
|
/**
|
|
@@ -67,49 +54,33 @@ class DynamicRoutingService {
|
|
|
67
54
|
}
|
|
68
55
|
|
|
69
56
|
async loadRouteSettings() {
|
|
70
|
-
const {
|
|
57
|
+
const {buildRouterSettings} = require('./activation-bridge');
|
|
71
58
|
|
|
72
59
|
try {
|
|
73
|
-
return
|
|
60
|
+
return buildRouterSettings(await this.store.get());
|
|
74
61
|
} catch (err) {
|
|
75
|
-
|
|
76
|
-
|
|
62
|
+
// A stored-content error means the site's routes.yaml is invalid —
|
|
63
|
+
// either it fails validation or it isn't parseable YAML. Log a
|
|
64
|
+
// targeted error so the failure is easy to spot in the logs, then
|
|
65
|
+
// rethrow so the caller surfaces the genuine error rather than
|
|
66
|
+
// silently degrading.
|
|
67
|
+
if (isStoredContentError(err)) {
|
|
68
|
+
logging.error(new errors.InternalServerError({
|
|
69
|
+
message: 'Route settings could not be loaded because the routes.yaml file is invalid. Please fix the file.',
|
|
70
|
+
code: 'ROUTE_SETTINGS_VALIDATION_ERROR',
|
|
71
|
+
err,
|
|
72
|
+
errorDetails: {reason: err.message}
|
|
73
|
+
}));
|
|
77
74
|
}
|
|
78
|
-
logging.error(new errors.InternalServerError({
|
|
79
|
-
message: 'Route settings failed the stricter validation and were loaded with the legacy validator instead. Please review the routes.yaml file.',
|
|
80
|
-
code: 'ROUTE_SETTINGS_VALIDATION_FALLBACK',
|
|
81
|
-
err,
|
|
82
|
-
errorDetails: {reason: err.message}
|
|
83
|
-
}));
|
|
84
|
-
|
|
85
|
-
return this.settingsLoader.loadSettings();
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
getDefaultHash() {
|
|
90
|
-
return DEFAULT_ROUTES_SETTING_HASH;
|
|
91
|
-
}
|
|
92
75
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return crypto.createHash('md5')
|
|
97
|
-
.update(JSON.stringify(expanded), 'binary')
|
|
98
|
-
.digest('hex');
|
|
76
|
+
throw err;
|
|
77
|
+
}
|
|
99
78
|
}
|
|
100
79
|
|
|
101
80
|
async download() {
|
|
102
|
-
|
|
103
|
-
const settings = await this.store.get();
|
|
104
|
-
|
|
105
|
-
return settings.yamlSource;
|
|
106
|
-
} catch (err) {
|
|
107
|
-
if (!this.settingsLoader || !isStoredContentError(err)) {
|
|
108
|
-
throw err;
|
|
109
|
-
}
|
|
81
|
+
const settings = await this.store.get();
|
|
110
82
|
|
|
111
|
-
|
|
112
|
-
}
|
|
83
|
+
return settings.yamlSource;
|
|
113
84
|
}
|
|
114
85
|
|
|
115
86
|
async upload(yamlContent) {
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
const config = require('../../../shared/config');
|
|
2
|
-
const parseYaml = require('./yaml-parser');
|
|
3
1
|
const DynamicRoutingService = require('./dynamic-routing-service');
|
|
4
2
|
|
|
5
3
|
const service = new DynamicRoutingService();
|
|
@@ -7,15 +5,9 @@ const service = new DynamicRoutingService();
|
|
|
7
5
|
module.exports = {
|
|
8
6
|
init: async () => {
|
|
9
7
|
const adapterManager = require('../adapter-manager').default;
|
|
10
|
-
const SettingsLoader = require('./settings-loader');
|
|
11
|
-
const SettingsPathManager = require('./settings-path-manager');
|
|
12
|
-
|
|
13
|
-
const settingsPathManager = new SettingsPathManager({type: 'routes', paths: [config.getContentPath('settings')]});
|
|
14
|
-
const settingsLoader = new SettingsLoader({parseYaml, settingFilePath: settingsPathManager.getDefaultFilePath()});
|
|
15
8
|
|
|
16
9
|
service.configure({
|
|
17
|
-
store: adapterManager.getAdapter('route-settings')
|
|
18
|
-
settingsLoader
|
|
10
|
+
store: adapterManager.getAdapter('route-settings')
|
|
19
11
|
});
|
|
20
12
|
},
|
|
21
13
|
|
|
@@ -27,10 +19,6 @@ module.exports = {
|
|
|
27
19
|
return service.loadRouteSettings.bind(service);
|
|
28
20
|
},
|
|
29
21
|
|
|
30
|
-
get getDefaultHash() {
|
|
31
|
-
return service.getDefaultHash.bind(service);
|
|
32
|
-
},
|
|
33
|
-
|
|
34
22
|
/**
|
|
35
23
|
* Methods backing the Admin API settings endpoint — delegate to the
|
|
36
24
|
* service instance so the endpoint stays decoupled from service wiring.
|
|
@@ -41,9 +29,6 @@ module.exports = {
|
|
|
41
29
|
},
|
|
42
30
|
get download() {
|
|
43
31
|
return service.download.bind(service);
|
|
44
|
-
},
|
|
45
|
-
get getCurrentHash() {
|
|
46
|
-
return service.getCurrentHash.bind(service);
|
|
47
32
|
}
|
|
48
33
|
}
|
|
49
34
|
};
|
|
@@ -183,24 +183,6 @@ module.exports = {
|
|
|
183
183
|
return fields;
|
|
184
184
|
},
|
|
185
185
|
|
|
186
|
-
/**
|
|
187
|
-
* Handles synchronization of routes.yaml hash loaded in the frontend with
|
|
188
|
-
* the value stored in the settings table.
|
|
189
|
-
* getRoutesHash is a function to allow keeping "frontend" decoupled from settings
|
|
190
|
-
*
|
|
191
|
-
* @param {function} getRoutesHash function fetching currently loaded routes file hash
|
|
192
|
-
*/
|
|
193
|
-
async syncRoutesHash(getRoutesHash) {
|
|
194
|
-
const currentRoutesHash = await getRoutesHash();
|
|
195
|
-
|
|
196
|
-
if (SettingsCache.get('routes_hash') !== currentRoutesHash) {
|
|
197
|
-
return await models.Settings.edit([{
|
|
198
|
-
key: 'routes_hash',
|
|
199
|
-
value: currentRoutesHash
|
|
200
|
-
}], {context: {internal: true}});
|
|
201
|
-
}
|
|
202
|
-
},
|
|
203
|
-
|
|
204
186
|
/**
|
|
205
187
|
* Handles email setting synchronization when email has been verified per instance
|
|
206
188
|
*
|
|
@@ -49,7 +49,7 @@ class UrlServiceFacade {
|
|
|
49
49
|
}
|
|
50
50
|
const url = this.urlService.getUrlByResourceId(resource.id, options);
|
|
51
51
|
if (this.isComparing() && !options?.skipComparison) {
|
|
52
|
-
const context = this._compareContext(resource);
|
|
52
|
+
const context = this._compareContext(resource, options?.serializerContext ? { serializer: options.serializerContext } : {});
|
|
53
53
|
// Snapshot: callers mutate the resource's nested objects in place
|
|
54
54
|
// after this returns, but the comparison runs later via setImmediate.
|
|
55
55
|
const snapshot = _.cloneDeep(resource);
|