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
|
@@ -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-a9c788f6f0b1ea6cf0f14ca4b13f4496.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.55%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%22e35f1aa34a%22%2C%22activitypubFilename%22%3A%22activitypub.js%22%2C%22activitypubHash%22%3A%22d7e993cdd1%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,13 +20,13 @@
|
|
|
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-BGG7NckO.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
|
-
<link rel="modulepreload" crossorigin href="./assets/hooks-
|
|
29
|
+
<link rel="modulepreload" crossorigin href="./assets/hooks-CixO01lL.js">
|
|
30
30
|
<link rel="modulepreload" crossorigin href="./assets/dist-DGzPJ_bO.js">
|
|
31
31
|
<link rel="modulepreload" crossorigin href="./assets/dist-CQJY6SVY.js">
|
|
32
32
|
<link rel="modulepreload" crossorigin href="./assets/dist-P_ohUlTi.js">
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
<link rel="modulepreload" crossorigin href="./assets/dist-2Tnz2Yfq.js">
|
|
41
41
|
<link rel="modulepreload" crossorigin href="./assets/input-surface-BpAjZXY5.js">
|
|
42
42
|
<link rel="modulepreload" crossorigin href="./assets/update-queries-C4CnvJaW.js">
|
|
43
|
-
<link rel="modulepreload" crossorigin href="./assets/users-
|
|
44
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
43
|
+
<link rel="modulepreload" crossorigin href="./assets/users-Dkv8NQZe.js">
|
|
44
|
+
<link rel="stylesheet" crossorigin href="./assets/index-9Cw8z3rA.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.481.6c7395a49484bb4583aa.js"></script>
|
|
66
|
+
<script src="./assets/chunk.524.cffd399783a519e404a9.js"></script>
|
|
67
|
+
<script src="./assets/ghost-c4057474943effc568c5659ccc4909d7.js"></script>
|
|
68
68
|
</body>
|
|
69
69
|
</html>
|
|
@@ -16,9 +16,10 @@ const FRONTEND_SUBSCRIBABLE_EVENTS = ['site.changed', 'url.added', 'url.removed'
|
|
|
16
16
|
// Require from the handlebars framework
|
|
17
17
|
const {SafeString} = require('./handlebars');
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
// sanitize-html over DOMPurify here: DOMPurify needs a DOM, and jsdom costs
|
|
20
|
+
// ~116MB RSS to load. This module is on the boot path, so that was paid by
|
|
21
|
+
// every process — including ones that never render a caption.
|
|
22
|
+
const sanitizeHtml = require('sanitize-html');
|
|
22
23
|
|
|
23
24
|
module.exports = {
|
|
24
25
|
getFrontendKey: async () => {
|
|
@@ -48,11 +49,9 @@ module.exports = {
|
|
|
48
49
|
(Array.isArray(data) ? data : [data]).forEach((resource) => {
|
|
49
50
|
// feature_image_caption contains HTML, making it a SafeString spares theme devs from triple-curlies
|
|
50
51
|
if (resource.feature_image_caption) {
|
|
51
|
-
const sanitizedCaption =
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
ALLOW_DATA_ATTR: false,
|
|
55
|
-
ALLOW_ARIA_ATTR: false
|
|
52
|
+
const sanitizedCaption = sanitizeHtml(resource.feature_image_caption, {
|
|
53
|
+
allowedTags: ['a', 'b', 'i', 'span'],
|
|
54
|
+
allowedAttributes: {'*': ['href', 'style']}
|
|
56
55
|
});
|
|
57
56
|
resource.feature_image_caption = new SafeString(sanitizedCaption);
|
|
58
57
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('frontend:routing');
|
|
2
|
-
const _ = require('lodash');
|
|
3
2
|
const StaticRoutesRouter = require('./static-routes-router');
|
|
4
3
|
const StaticPagesRouter = require('./static-pages-router');
|
|
5
4
|
const CollectionRouter = require('./collection-router');
|
|
@@ -111,30 +110,30 @@ class RouterManager {
|
|
|
111
110
|
this.siteRouter.mountRouter(previewRouter.router());
|
|
112
111
|
this.registry.setRouter('previewRouter', previewRouter);
|
|
113
112
|
|
|
114
|
-
|
|
115
|
-
const staticRoutesRouter = new StaticRoutesRouter(
|
|
113
|
+
for (const route of routerSettings.routes) {
|
|
114
|
+
const staticRoutesRouter = new StaticRoutesRouter(route.path, route, this.routerCreated.bind(this));
|
|
116
115
|
this.siteRouter.mountRouter(staticRoutesRouter.router());
|
|
117
116
|
|
|
118
117
|
this.registry.setRouter(staticRoutesRouter.identifier, staticRoutesRouter);
|
|
119
|
-
}
|
|
118
|
+
}
|
|
120
119
|
|
|
121
|
-
|
|
122
|
-
const collectionRouter = new CollectionRouter(
|
|
120
|
+
for (const collection of routerSettings.collections) {
|
|
121
|
+
const collectionRouter = new CollectionRouter(collection.path, collection, RESOURCE_CONFIG, this.routerCreated.bind(this));
|
|
123
122
|
this.siteRouter.mountRouter(collectionRouter.router());
|
|
124
123
|
this.registry.setRouter(collectionRouter.identifier, collectionRouter);
|
|
125
|
-
}
|
|
124
|
+
}
|
|
126
125
|
|
|
127
126
|
const staticPagesRouter = new StaticPagesRouter(RESOURCE_CONFIG, this.routerCreated.bind(this));
|
|
128
127
|
this.siteRouter.mountRouter(staticPagesRouter.router());
|
|
129
128
|
|
|
130
129
|
this.registry.setRouter('staticPagesRouter', staticPagesRouter);
|
|
131
130
|
|
|
132
|
-
|
|
133
|
-
const taxonomyRouter = new TaxonomyRouter(key,
|
|
131
|
+
for (const taxonomy of routerSettings.taxonomies) {
|
|
132
|
+
const taxonomyRouter = new TaxonomyRouter(taxonomy.key, taxonomy.permalink, RESOURCE_CONFIG, this.routerCreated.bind(this));
|
|
134
133
|
this.siteRouter.mountRouter(taxonomyRouter.router());
|
|
135
134
|
|
|
136
135
|
this.registry.setRouter(taxonomyRouter.identifier, taxonomyRouter);
|
|
137
|
-
}
|
|
136
|
+
}
|
|
138
137
|
|
|
139
138
|
const appRouter = new ParentRouter('AppsRouter');
|
|
140
139
|
this.siteRouter.mountRouter(appRouter.router());
|
|
@@ -186,10 +185,11 @@ module.exports = RouterManager;
|
|
|
186
185
|
*/
|
|
187
186
|
|
|
188
187
|
/**
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
188
|
+
* The shape RouterManager consumes — produced by the activation bridge. This is
|
|
189
|
+
* a temporary reference into server code; when the bridge is removed (HKG-1898)
|
|
190
|
+
* it becomes the domain `RouteSettings` from @tryghost/adapter-base-route-settings.
|
|
191
|
+
*
|
|
192
|
+
* @typedef {import('../../../server/services/route-settings/activation-bridge').RouterSettings} RouteSettings
|
|
193
193
|
*/
|
|
194
194
|
|
|
195
195
|
/**
|
|
@@ -30,12 +30,12 @@ class StaticRoutesRouter extends ParentRouter {
|
|
|
30
30
|
this.limit = object.limit;
|
|
31
31
|
this.order = object.order;
|
|
32
32
|
|
|
33
|
-
this.
|
|
33
|
+
this.type = object.type;
|
|
34
34
|
|
|
35
35
|
debug(this.route.value, this.templates, this.filter, this.data);
|
|
36
36
|
this._registerChannelRoutes();
|
|
37
37
|
} else {
|
|
38
|
-
this.contentType = object.
|
|
38
|
+
this.contentType = object.contentType;
|
|
39
39
|
debug(this.route.value, this.templates);
|
|
40
40
|
this._registerStaticRoute();
|
|
41
41
|
}
|
|
@@ -56,11 +56,11 @@ class StaticRoutesRouter extends ParentRouter {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
// REGISTER: channel route
|
|
59
|
-
this.mountRoute(this.route.value, controllers[this.
|
|
59
|
+
this.mountRoute(this.route.value, controllers[this.type]);
|
|
60
60
|
|
|
61
61
|
// REGISTER: pagination
|
|
62
62
|
this.router().param('page', middleware.pageParam);
|
|
63
|
-
this.mountRoute(urlUtils.urlJoin(this.route.value, 'page', ':page(\\d+)'), controllers[this.
|
|
63
|
+
this.mountRoute(urlUtils.urlJoin(this.route.value, 'page', ':page(\\d+)'), controllers[this.type]);
|
|
64
64
|
|
|
65
65
|
this.routerCreated(this);
|
|
66
66
|
}
|
|
@@ -74,7 +74,7 @@ class StaticRoutesRouter extends ParentRouter {
|
|
|
74
74
|
*/
|
|
75
75
|
_prepareChannelContext(req, res, next) {
|
|
76
76
|
res.routerOptions = {
|
|
77
|
-
type: this.
|
|
77
|
+
type: this.type,
|
|
78
78
|
name: this.routerName,
|
|
79
79
|
context: [this.routerName],
|
|
80
80
|
filter: this.filter,
|
|
@@ -131,11 +131,9 @@ class StaticRoutesRouter extends ParentRouter {
|
|
|
131
131
|
* @returns {boolean}
|
|
132
132
|
*/
|
|
133
133
|
isChannel(object) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
return this.controller === 'channel';
|
|
134
|
+
// buildRouterSettings always sets `type` ('channel' | 'template') on
|
|
135
|
+
// every route, so a channel is simply `type === 'channel'`.
|
|
136
|
+
return object?.type === 'channel';
|
|
139
137
|
}
|
|
140
138
|
}
|
|
141
139
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const automationsApi = __importStar(require("../../services/automations/automations-api"));
|
|
37
|
+
const controller = {
|
|
38
|
+
docName: 'automation_action_links',
|
|
39
|
+
browse: {
|
|
40
|
+
headers: {
|
|
41
|
+
cacheInvalidate: false
|
|
42
|
+
},
|
|
43
|
+
options: [
|
|
44
|
+
'automation_id',
|
|
45
|
+
'action_id'
|
|
46
|
+
],
|
|
47
|
+
validation: {
|
|
48
|
+
options: {
|
|
49
|
+
automation_id: {
|
|
50
|
+
required: true
|
|
51
|
+
},
|
|
52
|
+
action_id: {
|
|
53
|
+
required: true
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
permissions: {
|
|
58
|
+
docName: 'automations',
|
|
59
|
+
method: 'read'
|
|
60
|
+
},
|
|
61
|
+
async query(frame) {
|
|
62
|
+
return {
|
|
63
|
+
data: await automationsApi.browseActionLinks(frame.options.automation_id, frame.options.action_id)
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
module.exports = controller;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as automationsApi from '../../services/automations/automations-api';
|
|
2
|
+
|
|
3
|
+
type BrowseFrame = {
|
|
4
|
+
options: {
|
|
5
|
+
automation_id: string;
|
|
6
|
+
action_id: string;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const controller = {
|
|
11
|
+
docName: 'automation_action_links',
|
|
12
|
+
|
|
13
|
+
browse: {
|
|
14
|
+
headers: {
|
|
15
|
+
cacheInvalidate: false
|
|
16
|
+
},
|
|
17
|
+
options: [
|
|
18
|
+
'automation_id',
|
|
19
|
+
'action_id'
|
|
20
|
+
],
|
|
21
|
+
validation: {
|
|
22
|
+
options: {
|
|
23
|
+
automation_id: {
|
|
24
|
+
required: true
|
|
25
|
+
},
|
|
26
|
+
action_id: {
|
|
27
|
+
required: true
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
permissions: {
|
|
32
|
+
docName: 'automations',
|
|
33
|
+
method: 'read'
|
|
34
|
+
},
|
|
35
|
+
async query(frame: BrowseFrame) {
|
|
36
|
+
return {
|
|
37
|
+
data: await automationsApi.browseActionLinks(
|
|
38
|
+
frame.options.automation_id,
|
|
39
|
+
frame.options.action_id
|
|
40
|
+
)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
module.exports = controller;
|
|
@@ -11,6 +11,10 @@ module.exports = {
|
|
|
11
11
|
return apiFramework.pipeline(require('./automations'), localUtils);
|
|
12
12
|
},
|
|
13
13
|
|
|
14
|
+
get automationActionLinks() {
|
|
15
|
+
return apiFramework.pipeline(require('./automation-action-links'), localUtils);
|
|
16
|
+
},
|
|
17
|
+
|
|
14
18
|
get automationEmailPreviews() {
|
|
15
19
|
return apiFramework.pipeline(require('./automation-email-previews'), localUtils);
|
|
16
20
|
},
|
|
@@ -174,8 +174,6 @@ const controller = {
|
|
|
174
174
|
async query(frame) {
|
|
175
175
|
const content = await fs.readFile(frame.file.path, 'utf8');
|
|
176
176
|
await routeSettings.api.upload(content);
|
|
177
|
-
const getRoutesHash = () => routeSettings.api.getCurrentHash();
|
|
178
|
-
await settingsService.syncRoutesHash(getRoutesHash);
|
|
179
177
|
}
|
|
180
178
|
},
|
|
181
179
|
|
|
@@ -24,7 +24,17 @@ const forPost = (id, attrs, frame, type = 'posts') => {
|
|
|
24
24
|
return attrs;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
// Diagnostic only: hand the compare-mode URL service the producing api
|
|
28
|
+
// endpoint. The parity/thin-resource logs capture a caller stack, but it
|
|
29
|
+
// truncates at the async api-framework boundary and never names the
|
|
30
|
+
// endpoint; the api-framework Frame carries it, so pass it through. Omitted
|
|
31
|
+
// for non-api callers (mentions, email) whose frame has no endpoint fields.
|
|
32
|
+
const options = {absolute: true};
|
|
33
|
+
if (frame && (frame.docName || frame.method)) {
|
|
34
|
+
options.serializerContext = {apiType: frame.apiType, docName: frame.docName, method: frame.method};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
attrs.url = urlService.facade.getUrlForResource({...attrs, id, type}, options);
|
|
28
38
|
|
|
29
39
|
/**
|
|
30
40
|
* CASE: admin api should serve preview urls
|
package/core/server/data/migrations/versions/6.55/2026-07-27-11-25-44-add-to-hash-to-redirects.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const logging = require('@tryghost/logging');
|
|
2
|
+
const {combineNonTransactionalMigrations, createAddColumnMigration, createNonTransactionalMigration} = require('../../utils');
|
|
3
|
+
const {addIndex, addUnique, dropIndex, dropUnique, getIndexes} = require('../../../schema/commands');
|
|
4
|
+
|
|
5
|
+
const uniqueColumns = ['automation_action_revision_id', 'to_hash'];
|
|
6
|
+
const leftoverIndexName = 'redirects_automation_action_revision_id_index';
|
|
7
|
+
|
|
8
|
+
module.exports = combineNonTransactionalMigrations(
|
|
9
|
+
createAddColumnMigration('redirects', 'to_hash', {
|
|
10
|
+
type: 'binary',
|
|
11
|
+
maxlength: 32,
|
|
12
|
+
nullable: true
|
|
13
|
+
}, {algorithm: 'auto'}),
|
|
14
|
+
createNonTransactionalMigration(
|
|
15
|
+
async function up(knex) {
|
|
16
|
+
await addUnique('redirects', uniqueColumns, knex);
|
|
17
|
+
|
|
18
|
+
// A previous down() leaves a standalone index behind (see below).
|
|
19
|
+
// InnoDB drops the index it created for the foreign key itself once
|
|
20
|
+
// the composite unique can serve the constraint, but not one we
|
|
21
|
+
// created, so drop it here to keep a rolled-back-and-remigrated
|
|
22
|
+
// database in the same shape as every other one.
|
|
23
|
+
const indexes = await getIndexes('redirects', knex);
|
|
24
|
+
|
|
25
|
+
if (indexes.includes(leftoverIndexName)) {
|
|
26
|
+
await dropIndex('redirects', ['automation_action_revision_id'], knex);
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
async function down(knex) {
|
|
30
|
+
try {
|
|
31
|
+
await dropUnique('redirects', uniqueColumns, knex);
|
|
32
|
+
} catch (err) {
|
|
33
|
+
// The composite unique ends up as the only index covering the
|
|
34
|
+
// automation_action_revision_id FK: fresh installs create it
|
|
35
|
+
// that way, and on upgrades InnoDB drops the FK's own index
|
|
36
|
+
// once the composite can serve the constraint. MySQL won't
|
|
37
|
+
// drop the last index a foreign key depends on, so re-add a
|
|
38
|
+
// single-column one when it objects.
|
|
39
|
+
if (err.code === 'ER_DROP_INDEX_FK') {
|
|
40
|
+
logging.warn('Cannot drop unique constraint over redirects(automation_action_revision_id, to_hash) while it backs the foreign key, re-adding index for automation_action_revision_id');
|
|
41
|
+
|
|
42
|
+
await addIndex('redirects', ['automation_action_revision_id'], knex);
|
|
43
|
+
await dropUnique('redirects', uniqueColumns, knex);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
throw err;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
)
|
|
51
|
+
);
|
|
@@ -25,6 +25,11 @@ function addTableColumn(tableName, tableBuilder, columnName, columnSpec = schema
|
|
|
25
25
|
// creation distinguishes between text with fieldtype, string with maxlength and all others
|
|
26
26
|
if (columnSpec.type === 'text' && Object.prototype.hasOwnProperty.call(columnSpec, 'fieldtype')) {
|
|
27
27
|
column = tableBuilder[columnSpec.type](columnName, columnSpec.fieldtype);
|
|
28
|
+
} else if (columnSpec.type === 'binary' && Object.prototype.hasOwnProperty.call(columnSpec, 'maxlength')) {
|
|
29
|
+
// knex emits an unbounded `blob` for a length-less binary column, which
|
|
30
|
+
// MySQL can only index with a prefix. Passing the length gives us
|
|
31
|
+
// `varbinary(N)`, which is indexable in full.
|
|
32
|
+
column = tableBuilder[columnSpec.type](columnName, columnSpec.maxlength);
|
|
28
33
|
} else if (columnSpec.type === 'string') {
|
|
29
34
|
if (Object.prototype.hasOwnProperty.call(columnSpec, 'maxlength')) {
|
|
30
35
|
column = tableBuilder[columnSpec.type](columnName, columnSpec.maxlength);
|
|
@@ -1263,8 +1263,12 @@ module.exports = {
|
|
|
1263
1263
|
to: {type: 'string', maxlength: 2000, nullable: false},
|
|
1264
1264
|
post_id: {type: 'string', maxlength: 24, nullable: true, unique: false, references: 'posts.id', setNullDelete: true},
|
|
1265
1265
|
automation_action_revision_id: {type: 'string', maxlength: 24, nullable: true, references: 'automation_action_revisions.id', setNullDelete: true},
|
|
1266
|
+
to_hash: {type: 'binary', maxlength: 32, nullable: true},
|
|
1266
1267
|
created_at: {type: 'dateTime', nullable: false},
|
|
1267
|
-
updated_at: {type: 'dateTime', nullable: true}
|
|
1268
|
+
updated_at: {type: 'dateTime', nullable: true},
|
|
1269
|
+
'@@UNIQUE_CONSTRAINTS@@': [
|
|
1270
|
+
['automation_action_revision_id', 'to_hash']
|
|
1271
|
+
]
|
|
1268
1272
|
},
|
|
1269
1273
|
members_click_events: {
|
|
1270
1274
|
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.browse = browse;
|
|
7
7
|
exports.read = read;
|
|
8
|
+
exports.browseActionLinks = browseActionLinks;
|
|
8
9
|
exports.edit = edit;
|
|
9
10
|
exports.requestPoll = requestPoll;
|
|
10
11
|
exports.trigger = trigger;
|
|
@@ -15,6 +16,7 @@ exports.retryStep = retryStep;
|
|
|
15
16
|
exports.recordEmailSent = recordEmailSent;
|
|
16
17
|
exports.getAutomatedEmailRecipientsByMailgunIds = getAutomatedEmailRecipientsByMailgunIds;
|
|
17
18
|
exports.trackEmailDeliveredAndOpened = trackEmailDeliveredAndOpened;
|
|
19
|
+
exports.trackEmailClicked = trackEmailClicked;
|
|
18
20
|
const errors_1 = __importDefault(require("@tryghost/errors"));
|
|
19
21
|
const tpl_1 = __importDefault(require("@tryghost/tpl"));
|
|
20
22
|
const bson_objectid_1 = __importDefault(require("bson-objectid"));
|
|
@@ -30,6 +32,7 @@ const StartAutomationsPollEvent = require('./events/start-automations-poll-event
|
|
|
30
32
|
const MAX_AUTOMATION_ACTIONS = 20;
|
|
31
33
|
const messages = {
|
|
32
34
|
automationNotFound: 'Automation not found.',
|
|
35
|
+
automationActionNotFound: 'Automation action not found.',
|
|
33
36
|
invalidAutomationPayload: 'Automation edit payload must include status, actions, and edges.',
|
|
34
37
|
invalidAutomationStatus: 'Automation status must be one of: active, inactive.',
|
|
35
38
|
duplicateAutomationActionIdentity: 'Automation action identifiers must be unique.',
|
|
@@ -86,6 +89,15 @@ async function read(automationId) {
|
|
|
86
89
|
}
|
|
87
90
|
return automation;
|
|
88
91
|
}
|
|
92
|
+
async function browseActionLinks(automationId, actionId) {
|
|
93
|
+
const links = await repository.getAutomationActionLinks(automationId, actionId);
|
|
94
|
+
if (!links) {
|
|
95
|
+
throw new errors_1.default.NotFoundError({
|
|
96
|
+
message: (0, tpl_1.default)(messages.automationActionNotFound)
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return links;
|
|
100
|
+
}
|
|
89
101
|
async function edit(automationId, data) {
|
|
90
102
|
const parsedData = await validateEditData(data);
|
|
91
103
|
const automation = await repository.edit(automationId, parsedData);
|
|
@@ -311,3 +323,6 @@ async function getAutomatedEmailRecipientsByMailgunIds(...args) {
|
|
|
311
323
|
async function trackEmailDeliveredAndOpened(...args) {
|
|
312
324
|
return await repository.trackEmailDeliveredAndOpened(...args);
|
|
313
325
|
}
|
|
326
|
+
async function trackEmailClicked(...args) {
|
|
327
|
+
await repository.trackEmailClicked(...args);
|
|
328
|
+
}
|
|
@@ -20,6 +20,7 @@ const MAX_AUTOMATION_ACTIONS = 20;
|
|
|
20
20
|
|
|
21
21
|
const messages = {
|
|
22
22
|
automationNotFound: 'Automation not found.',
|
|
23
|
+
automationActionNotFound: 'Automation action not found.',
|
|
23
24
|
invalidAutomationPayload: 'Automation edit payload must include status, actions, and edges.',
|
|
24
25
|
invalidAutomationStatus: 'Automation status must be one of: active, inactive.',
|
|
25
26
|
duplicateAutomationActionIdentity: 'Automation action identifiers must be unique.',
|
|
@@ -87,6 +88,18 @@ export async function read(automationId: string) {
|
|
|
87
88
|
return automation;
|
|
88
89
|
}
|
|
89
90
|
|
|
91
|
+
export async function browseActionLinks(automationId: string, actionId: string) {
|
|
92
|
+
const links = await repository.getAutomationActionLinks(automationId, actionId);
|
|
93
|
+
|
|
94
|
+
if (!links) {
|
|
95
|
+
throw new errors.NotFoundError({
|
|
96
|
+
message: tpl(messages.automationActionNotFound)
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return links;
|
|
101
|
+
}
|
|
102
|
+
|
|
90
103
|
export async function edit(automationId: string, data: unknown) {
|
|
91
104
|
const parsedData = await validateEditData(data);
|
|
92
105
|
|
|
@@ -375,3 +388,9 @@ export async function trackEmailDeliveredAndOpened(
|
|
|
375
388
|
) {
|
|
376
389
|
return await repository.trackEmailDeliveredAndOpened(...args);
|
|
377
390
|
}
|
|
391
|
+
|
|
392
|
+
export async function trackEmailClicked(
|
|
393
|
+
...args: Parameters<AutomationsRepository['trackEmailClicked']>
|
|
394
|
+
) {
|
|
395
|
+
await repository.trackEmailClicked(...args);
|
|
396
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type {ReadonlyDeep} from 'type-fest';
|
|
2
|
+
import type {Knex} from 'knex';
|
|
2
3
|
|
|
3
4
|
export interface Pagination {
|
|
4
5
|
page: number;
|
|
@@ -32,6 +33,11 @@ export interface AutomationEmailStats {
|
|
|
32
33
|
clicked_rate: number | null;
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
export interface AutomationActionLink {
|
|
37
|
+
url: string;
|
|
38
|
+
clicked_count: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
35
41
|
export interface SendEmailAction {
|
|
36
42
|
id: string;
|
|
37
43
|
type: 'send_email';
|
|
@@ -131,6 +137,7 @@ export type AutomationStepTerminalStatus =
|
|
|
131
137
|
export interface AutomationsRepository {
|
|
132
138
|
browse(): Promise<Page<AutomationSummary>>;
|
|
133
139
|
getById(id: string): Promise<Automation | null>;
|
|
140
|
+
getAutomationActionLinks(automationId: string, actionId: string): Promise<AutomationActionLink[] | null>;
|
|
134
141
|
edit(id: string, data: EditAutomationData): Promise<Automation | null>;
|
|
135
142
|
trigger(options: {
|
|
136
143
|
memberEmail: string;
|
|
@@ -195,4 +202,15 @@ export interface AutomationsRepository {
|
|
|
195
202
|
trackEmailDeliveredAndOpened(
|
|
196
203
|
eventsByAutomatedEmailRecipientId: ReadonlyDeep<Map<string, AutomatedEmailEvents>>
|
|
197
204
|
): Promise<void>;
|
|
205
|
+
/**
|
|
206
|
+
* Track the first click for an automated email recipient.
|
|
207
|
+
*
|
|
208
|
+
*/
|
|
209
|
+
trackEmailClicked(options: {
|
|
210
|
+
automationActionRevisionId: string;
|
|
211
|
+
memberId: string;
|
|
212
|
+
clickedAt: Readonly<Date>;
|
|
213
|
+
}, transactionOptions?: {
|
|
214
|
+
transacting?: Knex.Transaction;
|
|
215
|
+
}): Promise<void>;
|
|
198
216
|
}
|