ghost 6.53.0 → 6.54.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/ghost-storage-base-3.0.0.tgz +0 -0
- package/components/tryghost-adapter-base-cache-0.2.0.tgz +0 -0
- package/components/tryghost-adapter-base-scheduling-0.1.1.tgz +0 -0
- package/components/tryghost-adapter-base-sso-0.1.1.tgz +0 -0
- package/components/tryghost-admin-api-schema-0.0.0.tgz +0 -0
- package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
- package/components/tryghost-i18n-0.0.0.tgz +0 -0
- package/components/tryghost-kg-converters-1.2.3.tgz +0 -0
- package/components/tryghost-kg-default-nodes-2.1.3.tgz +0 -0
- package/components/tryghost-kg-lexical-html-renderer-1.4.3.tgz +0 -0
- package/core/boot.js +9 -6
- package/core/built/admin/assets/Combination-CJG7Flp-.js +41 -0
- package/core/built/admin/assets/Growth-CSaHoSVD.js +3 -0
- package/core/built/admin/assets/Newsletters-C5cs6FGo.js +1 -0
- package/core/built/admin/assets/Overview-j4NaeUNm.js +1 -0
- package/core/built/admin/assets/{PolarAngleAxis-pciQ0MOy.js → PolarAngleAxis-BGjf7sBu.js} +1 -1
- package/core/built/admin/assets/{RadialBarChart-DWmw2PcX.js → RadialBarChart-zeFf9mBz.js} +1 -1
- package/core/built/admin/assets/Web-_fD935OX.js +2 -0
- package/core/built/admin/assets/account-migration-DLvVA5yq.js +1 -0
- package/core/built/admin/assets/activitypub/{account-migration-BPZG37DJ.js → account-migration-DHbZcxGM.js} +13 -13
- package/core/built/admin/assets/activitypub/activitypub.js +7 -7
- package/core/built/admin/assets/activitypub/{avatar-ZYco0ITb.js → avatar-B3DEDB1l.js} +10 -10
- package/core/built/admin/assets/activitypub/{avatar-flipboard-D541lc4F.js → avatar-flipboard-Cqrg0XMx.js} +3 -3
- package/core/built/admin/assets/activitypub/{bluesky-sharing-NOHUtHjl.js → bluesky-sharing-zfxUey_L.js} +38 -37
- package/core/built/admin/assets/activitypub/{chunk-KS7C4IRE-B-GOFDPZ.js → chunk-KS7C4IRE-D4HEcEwi.js} +716 -710
- package/core/built/admin/assets/activitypub/{content-formatters-bwrp4iEO.js → content-formatters-BpQARh28.js} +229 -181
- package/core/built/admin/assets/activitypub/{copy-C7m7ovHw.js → copy-D9UWIAan.js} +2 -2
- package/core/built/admin/assets/activitypub/{dist-B5ira32t.js → dist-Ba_QxHHl.js} +5 -5
- package/core/built/admin/assets/activitypub/{domain-iqE4YXwD.js → domain-CmwSZLHK.js} +33 -33
- package/core/built/admin/assets/activitypub/{edit-profile-B8MFYh28.js → edit-profile-CXdwNmVG.js} +46 -45
- package/core/built/admin/assets/activitypub/{explore-BbdKodp3.js → explore-DfbjzGM1.js} +20 -20
- package/core/built/admin/assets/activitypub/{feed-Cl3A5owG.js → feed-DSEBJOqf.js} +15 -15
- package/core/built/admin/assets/activitypub/{field-DL6WyR-c.js → field-CY7XVcx6.js} +13 -13
- package/core/built/admin/assets/activitypub/{hash-6G3DzwL9.js → hash-BC9Q0N4c.js} +2 -2
- package/core/built/admin/assets/activitypub/{inbox-YmhuBice.js → inbox-DQ4ZnC1m.js} +2 -2
- package/core/built/admin/assets/activitypub/inbox-DtQ0kytT.js +1221 -0
- package/core/built/admin/assets/activitypub/{label-DGbLjDvE.js → label-Dw96GQ7u.js} +6 -6
- package/core/built/admin/assets/activitypub/layout-IdVq3lRy.js +1 -0
- package/core/built/admin/assets/activitypub/{moderation-qnBtk1zS.js → moderation-Dg_-I-8C.js} +32 -31
- package/core/built/admin/assets/activitypub/{note-C7ZRoSJv.js → note-Cy1-uKMY.js} +21 -21
- package/core/built/admin/assets/activitypub/{notifications-DiEA4b1o.js → notifications-B_fvoLIM.js} +17 -17
- package/core/built/admin/assets/activitypub/{onboarding-_JVgEwgV.js → onboarding-BFDkzeR2.js} +2 -2
- package/core/built/admin/assets/activitypub/{onboarding-DG4zu3qM.js → onboarding-D6rcBdPe.js} +5 -5
- package/core/built/admin/assets/activitypub/{preferences-B3FtocnP.js → preferences-ByJseZp6.js} +1607 -1606
- package/core/built/admin/assets/activitypub/{profile-D8ZJfYXB.js → profile-BFtbnIjz.js} +264 -263
- package/core/built/admin/assets/activitypub/{reply-C1vggenw.js → reply-CpdMeAO5.js} +2 -2
- package/core/built/admin/assets/activitypub/{routes-BOii-V9n.js → routes-Dh3xXfce.js} +1311 -1295
- package/core/built/admin/assets/activitypub/{separator-1gNokN5S.js → separator-BI1FLsYM.js} +4 -4
- package/core/built/admin/assets/activitypub/{settings--9Sb0nbl.js → settings-DTgqJPxM.js} +14 -14
- package/core/built/admin/assets/activitypub/{site-fkMmqv0V.js → site-CdL9ZLqm.js} +10 -10
- package/core/built/admin/assets/activitypub/{step-1-QhivSMw2.js → step-1-DIDg4aHb.js} +15 -15
- package/core/built/admin/assets/activitypub/{step-2-pMk9YwqD.js → step-2-BfGe8BYt.js} +7 -7
- package/core/built/admin/assets/activitypub/{step-3-DvmVC1fo.js → step-3-mE9spAqw.js} +12 -12
- package/core/built/admin/assets/activitypub/{tabs-Dc0FEEYD.js → tabs-ogqwoBlb.js} +241 -237
- package/core/built/admin/assets/activitypub/{topic-filter-CuDyM5bD.js → topic-filter-DLHBZT93.js} +4 -4
- package/core/built/admin/assets/activitypub/use-activity-pub-queries-VzbN99u_.js +1957 -0
- package/core/built/admin/assets/activitypub/{use-navigate-with-base-path-Drx1kdAK.js → use-navigate-with-base-path-CAwXTmqH.js} +165 -175
- package/core/built/admin/assets/activitypub/{use-reply-chain-data-BHkCHcW5.js → use-reply-chain-data-CxonteFD.js} +8 -8
- package/core/built/admin/assets/activitypub/users-BLeiOTHc.js +1464 -0
- package/core/built/admin/assets/activitypub/{x-BHCEe-c2.js → x-C8tAnXly.js} +2 -2
- package/core/built/admin/assets/{audience-adiFehTD.js → audience-BKRdsFmy.js} +1 -1
- package/core/built/admin/assets/{automated-emails-UWM02CMr.js → automated-emails-Cn87L5Md.js} +1 -1
- package/core/built/admin/assets/automations-B7-y3dXt.js +1 -0
- package/core/built/admin/assets/automations-BP_aHamJ.js +1 -0
- package/core/built/admin/assets/{avatar-flipboard-Lz3F9fNT.js → avatar-flipboard-C6Kj3Awu.js} +1 -1
- package/core/built/admin/assets/bluesky-sharing-Bh36XPh4.js +1 -0
- package/core/built/admin/assets/box-BWbhXh21.js +1 -0
- package/core/built/admin/assets/breadcrumb-D34YWnaw.js +1 -0
- package/core/built/admin/assets/card-DGoDJzrn.js +1 -0
- package/core/built/admin/assets/{chart-ChpW-NzQ.js → chart-BuRT2Y6p.js} +14 -14
- package/core/built/admin/assets/chart-helpers-DbklaJw9.js +1 -0
- package/core/built/admin/assets/checkbox-DA4-1UiI.js +1 -0
- package/core/built/admin/assets/chunk.524.45e0875f41b71e46e849.js +25 -0
- package/core/built/admin/assets/chunk.582.51defd4b009f0de8d4d5.js +27 -0
- package/core/built/admin/assets/{chunk.831.0703a12e49f51cec3f3a.js → chunk.589.096f4d0bb775e5229aa9.js} +78 -85
- package/core/built/admin/assets/{chunk.432.c2caa0b20f03e71d1a35.js → chunk.962.b73442b07be930bb5d85.js} +2216 -3188
- package/core/built/admin/assets/{chunk.432.c2caa0b20f03e71d1a35.js.LICENSE.txt → chunk.962.b73442b07be930bb5d85.js.LICENSE.txt} +0 -63
- package/core/built/admin/assets/code-editor-view-BKBrkffZ.js +1 -0
- package/core/built/admin/assets/comments-CzVNIT8b.js +1 -0
- package/core/built/admin/assets/container-BYz2Ed39.js +1 -0
- package/core/built/admin/assets/data-list-16rkJYYi.js +1 -0
- package/core/built/admin/assets/date-range-select-B5vSsrpx.js +1 -0
- package/core/built/admin/assets/disabled-sources-indicator-NkOIoMue.js +1 -0
- package/core/built/admin/assets/{dist-CsekE4FL.js → dist-CQJY6SVY.js} +1 -1
- package/core/built/admin/assets/dist-DGzPJ_bO.js +4 -0
- package/core/built/admin/assets/{dist-BbGrW05X.js → dist-DyViJwIy.js} +1 -1
- package/core/built/admin/assets/dist-Ndy-PuGN.js +1 -0
- package/core/built/admin/assets/dist-P_ohUlTi.js +1 -0
- package/core/built/admin/assets/domain-B7_n5nfx.js +1 -0
- package/core/built/admin/assets/dropzone-DcuJEEUf.js +5 -0
- package/core/built/admin/assets/edit-profile-rvDGrS8J.js +1 -0
- package/core/built/admin/assets/editor-BCiIyFjm.js +8 -0
- package/core/built/admin/assets/empty-indicator-oX1u1Pkn.js +1 -0
- package/core/built/admin/assets/esm-CFX93H5M.js +2 -0
- package/core/built/admin/assets/explore-BZAZOg5X.js +1 -0
- package/core/built/admin/assets/feed-CFV8xDw9.js +1 -0
- package/core/built/admin/assets/field-BW-M2Zwp.js +1 -0
- package/core/built/admin/assets/{filter-sources-v39ihhhV.js → filter-sources-Fx8wiYYw.js} +1 -1
- package/core/built/admin/assets/filters-CGX_pf2N.js +1 -0
- package/core/built/admin/assets/filters-DtcXMZDU.js +1 -0
- package/core/built/admin/assets/framework-provider-CmtnPXIT.js +4 -0
- package/core/built/admin/assets/{ghost-b57f077997b739a8ae9a70772dc016cb.js → ghost-b1dce91ad3232cc259ee5fbbf9526243.js} +294 -290
- package/core/built/admin/assets/ghost-dark-c8d0ab7f515b2010c173e23e9b32ea8a.css +1 -0
- package/core/built/admin/assets/gift-link-modal-Bk8qpnMw.js +1 -0
- package/core/built/admin/assets/{gift-links-CY3GwiVi.js → gift-links-nBxJjZiQ.js} +1 -1
- package/core/built/admin/assets/growth-Ck2s3JjR.js +1 -0
- package/core/built/admin/assets/hooks-BApwwBE0.js +2 -0
- package/core/built/admin/assets/{inbox-TEISAmth.js → inbox-BRp1_aaC.js} +35 -35
- package/core/built/admin/assets/index-BBf502Wg.js +69 -0
- package/core/built/admin/assets/index-BCCZKxP2.css +1 -0
- package/core/built/admin/assets/inline-EMAm3okk.js +1 -0
- package/core/built/admin/assets/input-group-DjuYnNCd.js +1 -0
- package/core/built/admin/assets/{react-dom-C6aoWSfl.js → jsx-runtime-DX3WBWVO.js} +1 -1
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +5 -2
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +2 -2
- package/core/built/admin/assets/{koenig-lexical-Di7b48ED.js → koenig-lexical-CM7UFNdL.js} +2 -2
- package/core/built/admin/assets/kpi-card-BrGH6FsT.js +1 -0
- package/core/built/admin/assets/kpi-card-CIGyijjx.js +1 -0
- package/core/built/admin/assets/kpi-helpers-_HRRsklE.js +1 -0
- package/core/built/admin/assets/kpi-tabs-BSvh9eoq.js +1 -0
- package/core/built/admin/assets/label-BOR9XdKl.js +1 -0
- package/core/built/admin/assets/label-picker-BdNAWuUX.js +1 -0
- package/core/built/admin/assets/{labels-GbxlXWtf.js → labels-Cyayfd-e.js} +1 -1
- package/core/built/admin/assets/layout-Bw4GnxXp.js +1 -0
- package/core/built/admin/assets/limit-service-9fwD33lU.js +20 -0
- package/core/built/admin/assets/link-helpers-tOlxmuH5.js +1 -0
- package/core/built/admin/assets/list-page-B6ANtzkd.js +1 -0
- package/core/built/admin/assets/mailchimp-Ds_NQ04w.svg +9 -0
- package/core/built/admin/assets/member-custom-fields-DvFyamLu.js +1 -0
- package/core/built/admin/assets/member-detail-Cg2vTqvJ.js +1 -0
- package/core/built/admin/assets/members-DsPxYAVm.js +10 -0
- package/core/built/admin/assets/modals-Bf_fyvHZ.js +31 -0
- package/core/built/admin/assets/moderation-Dlp7M5sa.js +1 -0
- package/core/built/admin/assets/multi-select-combobox-CH1o8XXQ.js +1 -0
- package/core/built/admin/assets/newsletter-CQ5CAYUI.js +1 -0
- package/core/built/admin/assets/{newsletters-DZm3yLj8.js → newsletters-D1jbtV-h.js} +1 -1
- package/core/built/admin/assets/note-BrAZ7Z0o.js +1 -0
- package/core/built/admin/assets/notifications-BnbHLOPC.js +1 -0
- package/core/built/admin/assets/nql-lang-B7faUf8D.js +19 -0
- package/core/built/admin/assets/{offers-D11QtPXX.js → offers-CrY6nSRR.js} +1 -1
- package/core/built/admin/assets/onboarding-route-eU146kuA.js +1 -0
- package/core/built/admin/assets/overview-BpbaK-xu.js +1 -0
- package/core/built/admin/assets/page-header-C0jVxtS8.js +1 -0
- package/core/built/admin/assets/{pages-BnObVRrf.js → pages-BbNWFcGj.js} +1 -1
- package/core/built/admin/assets/pintura-CXOmq-5H.svg +15 -0
- package/core/built/admin/assets/post-analytics-Iw7dk1mT.js +1 -0
- package/core/built/admin/assets/post-analytics-header-D9pJ4iNm.js +1 -0
- package/core/built/admin/assets/{post-analytics-provider-DSfBCtnj.js → post-analytics-provider-BDqcfBor.js} +1 -1
- package/core/built/admin/assets/post-share-modal-DBfKl2GZ.js +1 -0
- package/core/built/admin/assets/{posts-CwH8VJvB.js → posts-EWpZIjkr.js} +1 -1
- package/core/built/admin/assets/preferences-D4EANGoc.js +5 -0
- package/core/built/admin/assets/profile-dJ44Bn_W.js +1 -0
- package/core/built/admin/assets/{referrers-nxR52jPI.js → referrers-BCfFYSHc.js} +1 -1
- package/core/built/admin/assets/router-provider-C1R4AZ2o.js +1 -0
- package/core/built/admin/assets/select--2uVN9_-.js +1 -0
- package/core/built/admin/assets/settings-Bz3RZRmE.js +1 -0
- package/core/built/admin/assets/settings-SD9bxIpt.js +4 -0
- package/core/built/admin/assets/share-modal-CkdTTMJd.js +1 -0
- package/core/built/admin/assets/social-urls-BvFZwYcE.js +8 -0
- package/core/built/admin/assets/sort-button-DZp9Y2r1.js +1 -0
- package/core/built/admin/assets/source-icon-CHr9qJ2Y.js +1 -0
- package/core/built/admin/assets/stack-xiN0eQ6Z.js +1 -0
- package/core/built/admin/assets/stats-DGoXZxWE.js +1 -0
- package/core/built/admin/assets/stats-view-D2-1L0ms.js +1 -0
- package/core/built/admin/assets/step-1-RdahbwyG.js +1 -0
- package/core/built/admin/assets/step-2-D83PeRwd.js +1 -0
- package/core/built/admin/assets/step-3-p_Op4wv4.js +2 -0
- package/core/built/admin/assets/switch-qp518gFQ.js +1 -0
- package/core/built/admin/assets/table-B8LgTQJZ.js +1 -0
- package/core/built/admin/assets/tabs-D3ZOYkM7.js +1 -0
- package/core/built/admin/assets/tags-B4l891z2.js +1 -0
- package/core/built/admin/assets/text-DwtjwGkh.js +1 -0
- package/core/built/admin/assets/textarea-CTlb3Uu6.js +1 -0
- package/core/built/admin/assets/{tiers-EghDzCKc.js → tiers-BgYLWRJv.js} +1 -1
- package/core/built/admin/assets/toggle-group-DNXDkAwf.js +1 -0
- package/core/built/admin/assets/topic-filter-DZGjEcz3.js +1 -0
- package/core/built/admin/assets/{use-analytics-data-0l_OHtnR.js → use-analytics-data-CaT8RirX.js} +1 -1
- package/core/built/admin/assets/use-filter-params-DeQ8sX4N.js +1 -0
- package/core/built/admin/assets/use-growth-stats-9Db2CuJe.js +1 -0
- package/core/built/admin/assets/{use-post-referrers-DwI31Ril.js → use-post-referrers-BV54YcQS.js} +1 -1
- package/core/built/admin/assets/use-reply-chain-data-DuYZGDan.js +1 -0
- package/core/built/admin/assets/use-simple-pagination-8fTua9Nt.js +1 -0
- package/core/built/admin/assets/{use-tinybird-query-DvZBBMG7.js → use-tinybird-query-Dz156HvZ.js} +1 -1
- package/core/built/admin/assets/{use-tinybird-token-CQtebkvU.js → use-tinybird-token-C9RUdL_n.js} +1 -1
- package/core/built/admin/assets/{users-Pc8IRjk9.js → users-CcSCQFcQ.js} +1 -1
- package/core/built/admin/assets/virtual-list-BCvP0YF-.js +1 -0
- package/core/built/admin/assets/web-DTZnnUGr.js +1 -0
- package/core/built/admin/index.html +18 -22
- package/core/frontend/apps/private-blogging/index.js +1 -1
- package/core/frontend/apps/private-blogging/lib/middleware.js +1 -1
- package/core/frontend/helpers/split.js +2 -1
- package/core/frontend/meta/asset-url.js +1 -1
- package/core/frontend/meta/author-image.js +1 -1
- package/core/frontend/meta/blog-logo.js +1 -1
- package/core/frontend/meta/canonical-url.js +1 -1
- package/core/frontend/meta/cover-image.js +1 -1
- package/core/frontend/meta/get-meta.js +1 -1
- package/core/frontend/meta/og-image.js +1 -1
- package/core/frontend/meta/paginated-url.js +1 -1
- package/core/frontend/meta/twitter-image.js +1 -1
- package/core/frontend/meta/url.js +1 -1
- package/core/frontend/services/assets-minification/admin-auth-assets.js +1 -1
- package/core/frontend/services/llms/handler.js +1 -1
- package/core/frontend/services/proxy.js +31 -2
- package/core/frontend/services/routing/collection-router.js +1 -1
- package/core/frontend/services/routing/controllers/email-post.js +1 -1
- package/core/frontend/services/routing/controllers/entry/markdown.js +1 -1
- package/core/frontend/services/routing/controllers/entry/markdown.ts +1 -1
- package/core/frontend/services/routing/controllers/entry.js +1 -1
- package/core/frontend/services/routing/controllers/entry.ts +1 -1
- package/core/frontend/services/routing/controllers/previews.js +6 -7
- package/core/frontend/services/routing/controllers/unsubscribe.js +7 -3
- package/core/frontend/services/routing/email-router.js +1 -1
- package/core/frontend/services/routing/events.js +12 -0
- package/core/frontend/services/routing/middleware/page-param.js +1 -1
- package/core/frontend/services/routing/parent-router.js +1 -1
- package/core/frontend/services/routing/preview-router.js +1 -1
- package/core/frontend/services/routing/router-manager.js +4 -8
- package/core/frontend/services/routing/rss-router.js +1 -1
- package/core/frontend/services/routing/static-pages-router.js +1 -1
- package/core/frontend/services/routing/static-routes-router.js +1 -1
- package/core/frontend/services/routing/taxonomy-router.js +1 -1
- package/core/frontend/services/rss/generate-feed.js +1 -1
- package/core/frontend/services/sitemap/base-site-map-generator.js +23 -4
- package/core/frontend/services/sitemap/site-map-index-generator.js +1 -1
- package/core/frontend/services/sitemap/site-map-manager.js +14 -8
- package/core/frontend/services/sitemap/utils.js +1 -1
- package/core/frontend/services/theme-engine/middleware/update-global-template-options.js +1 -1
- package/core/frontend/services/theme-engine/middleware/update-local-template-options.js +1 -1
- package/core/frontend/utils/images.js +1 -1
- package/core/frontend/web/middleware/admin-toolbar.js +1 -1
- package/core/frontend/web/middleware/handle-image-sizes.js +2 -2
- package/core/frontend/web/middleware/redirect-ghost-to-admin.js +1 -1
- package/core/frontend/web/routers/serve-favicon.js +1 -1
- package/core/frontend/web/routers/serve-public-file.js +1 -1
- package/core/frontend/web/site.js +5 -5
- package/core/server/adapters/cache/AdapterCacheMemoryTTL.js +2 -2
- package/core/server/adapters/cache/MemoryCache.js +2 -2
- package/core/server/adapters/cache/Redis.js +1 -1
- package/core/server/adapters/cache/index.js +1 -1
- package/core/server/adapters/lib/redis/AdapterCacheRedis.js +2 -2
- package/core/server/adapters/route-settings/FileStore.js +2 -8
- package/core/server/adapters/route-settings/FileStore.ts +1 -6
- package/core/server/adapters/route-settings/S3RouteSettingsStore.js +206 -0
- package/core/server/adapters/route-settings/S3RouteSettingsStore.ts +199 -0
- package/core/server/adapters/route-settings/utils.js +13 -0
- package/core/server/adapters/route-settings/utils.ts +7 -0
- package/core/server/adapters/scheduling/deps.d.ts +0 -2
- package/core/server/adapters/scheduling/error-capture.js +76 -0
- package/core/server/adapters/scheduling/error-capture.ts +91 -0
- package/core/server/adapters/scheduling/utils.js +18 -34
- package/core/server/adapters/scheduling/utils.ts +36 -0
- package/core/server/adapters/storage/LocalFilesStorage.js +1 -1
- package/core/server/adapters/storage/LocalImagesStorage.js +1 -1
- package/core/server/adapters/storage/LocalMediaStorage.js +1 -1
- package/core/server/adapters/storage/LocalStorageBase.js +3 -3
- package/core/server/adapters/storage/S3Storage.js +2 -2
- package/core/server/adapters/storage/S3Storage.ts +1 -1
- package/core/server/adapters/storage/utils.js +1 -1
- package/core/server/api/endpoints/authentication.js +1 -1
- package/core/server/api/endpoints/featurebase.js +1 -1
- package/core/server/api/endpoints/files.js +2 -2
- package/core/server/api/endpoints/images.js +2 -2
- package/core/server/api/endpoints/libraries.d.ts +0 -1
- package/core/server/api/endpoints/media.js +8 -5
- package/core/server/api/endpoints/member-custom-fields.js +4 -1
- package/core/server/api/endpoints/member-custom-fields.ts +4 -1
- package/core/server/api/endpoints/posts.js +17 -1
- package/core/server/api/endpoints/search-index-public.js +4 -3
- package/core/server/api/endpoints/search-index.js +5 -4
- package/core/server/api/endpoints/settings-public.js +1 -1
- package/core/server/api/endpoints/settings.js +3 -1
- package/core/server/api/endpoints/utils/serializers/input/utils/url.js +9 -1
- package/core/server/api/endpoints/utils/serializers/output/default.js +7 -0
- package/core/server/api/endpoints/utils/serializers/output/files.js +1 -1
- package/core/server/api/endpoints/utils/serializers/output/media.js +1 -1
- package/core/server/api/endpoints/utils/serializers/output/member-custom-fields.js +7 -4
- package/core/server/api/endpoints/utils/serializers/output/member-custom-fields.ts +8 -4
- package/core/server/api/endpoints/utils/serializers/output/members.js +16 -26
- package/core/server/api/endpoints/utils/serializers/output/pages.js +7 -0
- package/core/server/api/endpoints/utils/serializers/output/posts.js +13 -0
- package/core/server/api/endpoints/utils/serializers/output/utils/url.js +1 -1
- package/core/server/api/endpoints/utils/validators/input/members.js +2 -52
- package/core/server/data/db/backup.js +1 -1
- package/core/server/data/importer/handlers/image.js +3 -3
- package/core/server/data/importer/import-manager.js +6 -4
- package/core/server/data/importer/importers/content-file-importer.js +2 -2
- package/core/server/data/migrations/utils/schema.js +4 -2
- package/core/server/data/migrations/versions/6.54/2026-07-19-16-59-00-add-content-import-permission.js +10 -0
- package/core/server/data/migrations/versions/6.54/2026-07-20-13-59-21-drop-email-tracked-sent-count-from-automation-action-revisions.js +7 -0
- package/core/server/data/migrations/versions/6.54/2026-07-21-01-58-20-add-automation-run-steps-poller-index.js +3 -0
- package/core/server/data/migrations/versions/6.54/2026-07-21-21-08-15-add-automated-email-recipients-mailgun-message-id-index.js +3 -0
- package/core/server/data/migrations/versions/6.54/2026-07-22-10-41-08-add-click-tracking-to-automated-email-recipients.js +13 -0
- package/core/server/data/migrations/versions/6.54/2026-07-22-15-10-30-add-email-clicked-count-to-automation-action-revisions.js +7 -0
- package/core/server/data/migrations/versions/6.54/2026-07-23-10-24-18-add-automation-action-revision-to-redirects.js +9 -0
- package/core/server/data/schema/commands.js +37 -3
- package/core/server/data/schema/fixtures/fixtures.json +7 -0
- package/core/server/data/schema/lib/default-index-name.js +20 -0
- package/core/server/data/schema/schema.js +40 -17
- package/core/server/data/seeders/data-generator.js +4 -2
- package/core/server/lib/image/image-size.js +5 -7
- package/core/server/lib/image/image-utils.js +2 -2
- package/core/server/lib/image/index.js +4 -4
- package/core/server/lib/lexical.js +3 -3
- package/core/server/lib/mobiledoc.js +2 -2
- package/core/server/lib/validate-password.js +1 -1
- package/core/server/models/action.js +9 -0
- package/core/server/models/base/plugins/generate-slug.js +1 -1
- package/core/server/models/collection.js +1 -1
- package/core/server/models/custom-theme-setting.js +1 -1
- package/core/server/models/member.js +73 -0
- package/core/server/models/newsletter.js +1 -1
- package/core/server/models/post.js +19 -5
- package/core/server/models/posts-meta.js +1 -1
- package/core/server/models/redirect.js +1 -1
- package/core/server/models/settings.js +1 -1
- package/core/server/models/snippet.js +1 -1
- package/core/server/models/tag.js +1 -1
- package/core/server/models/user.js +1 -1
- package/core/server/models/welcome-email-automated-email.js +1 -1
- package/core/server/services/activitypub/activity-pub-service-wrapper.js +1 -1
- package/core/server/services/adapter-manager/adapter-manager.js +5 -1
- package/core/server/services/adapter-manager/adapter-manager.ts +7 -2
- package/core/server/services/adapter-manager/index.js +18 -15
- package/core/server/services/adapter-manager/index.ts +23 -17
- package/core/server/services/adapter-manager/utils.js +124 -14
- package/core/server/services/adapter-manager/utils.ts +130 -14
- package/core/server/services/api-version-compatibility/mw-version-rewrites.js +1 -1
- package/core/server/services/audience-feedback/index.js +1 -1
- package/core/server/services/auth/passwordreset.js +1 -1
- package/core/server/services/auth/session/express-session.js +1 -1
- package/core/server/services/auth/session/index.js +2 -2
- package/core/server/services/auth/session/session-service.js +1 -1
- package/core/server/services/automations/automations-api.js +2 -19
- package/core/server/services/automations/automations-api.ts +2 -31
- package/core/server/services/automations/automations-repository.ts +15 -0
- package/core/server/services/automations/database-automations-repository.js +30 -2
- package/core/server/services/automations/database-automations-repository.ts +33 -2
- package/core/server/services/automations/poll.js +16 -12
- package/core/server/services/automations/poll.ts +17 -15
- package/core/server/services/automations/service.js +1 -1
- package/core/server/services/automations/service.ts +3 -13
- package/core/server/services/automations/welcome-email-automation-poll.js +2 -1
- package/core/server/services/comments/index.js +1 -1
- package/core/server/services/custom-redirects/index.js +2 -2
- package/core/server/services/email-analytics/email-analytics-service-wrapper.js +14 -13
- package/core/server/services/email-analytics/email-analytics-service.js +1 -1
- package/core/server/services/email-analytics/events/start-email-analytics-job-event.js +11 -15
- package/core/server/services/email-analytics/events/start-email-analytics-job-event.ts +15 -0
- package/core/server/services/email-analytics/index.js +2 -3
- package/core/server/services/email-analytics/index.ts +1 -2
- package/core/server/services/email-analytics/jobs/email-analytics-job-scheduler.js +1 -6
- package/core/server/services/email-analytics/jobs/email-analytics-job-scheduler.ts +0 -9
- package/core/server/services/email-analytics/jobs/fetch-latest/index.js +1 -1
- package/core/server/services/email-analytics/jobs/index.js +0 -2
- package/core/server/services/email-service/email-service-wrapper.js +1 -1
- package/core/server/services/email-service/email-service.js +20 -4
- package/core/server/services/explore-ping/explore-ping-service.js +24 -39
- package/core/server/services/explore-ping/explore-ping-service.ts +198 -0
- package/core/server/services/explore-ping/index.js +11 -11
- package/core/server/services/explore-ping/index.ts +42 -0
- package/core/server/services/gifts/gift-reminder-scheduler.js +1 -1
- package/core/server/services/gifts/gift-reminder-scheduler.ts +1 -1
- package/core/server/services/gifts/gift-service-wrapper.js +1 -1
- package/core/server/services/identity-tokens/identity-token-service-wrapper.js +1 -1
- package/core/server/services/indexnow-ping/constants.js +30 -0
- package/core/server/services/indexnow-ping/constants.ts +32 -0
- package/core/server/services/indexnow-ping/index.js +29 -253
- package/core/server/services/indexnow-ping/index.ts +37 -0
- package/core/server/services/indexnow-ping/indexnow-ping-service.js +229 -0
- package/core/server/services/indexnow-ping/indexnow-ping-service.ts +319 -0
- package/core/server/services/invites/index.js +1 -1
- package/core/server/services/lib/mailgun-client.js +3 -3
- package/core/server/services/link-redirection/index.js +2 -2
- package/core/server/services/link-redirection/link-redirect-repository.js +1 -1
- package/core/server/services/link-redirection/link-redirect.js +12 -13
- package/core/server/services/link-redirection/link-redirect.ts +22 -0
- package/core/server/services/link-redirection/link-redirects-service.js +1 -1
- package/core/server/services/link-tracking/full-post-link.js +2 -2
- package/core/server/services/link-tracking/index.js +1 -1
- package/core/server/services/mail/ghost-mailer.js +1 -1
- package/core/server/services/mail/index.js +1 -1
- package/core/server/services/media-inliner/external-media-inliner.js +2 -2
- package/core/server/services/media-inliner/service.js +4 -3
- package/core/server/services/member-attribution/index.js +1 -1
- package/core/server/services/member-welcome-emails/service.js +1 -1
- package/core/server/services/members/api.js +8 -2
- package/core/server/services/members/csv/index.js +10 -0
- package/core/server/services/members/csv/index.ts +3 -0
- package/core/server/services/members/csv/parse.js +94 -0
- package/core/server/services/members/csv/parse.ts +111 -0
- package/core/server/services/members/csv/types.js +2 -0
- package/core/server/services/members/csv/types.ts +39 -0
- package/core/server/services/members/csv/unparse.js +69 -0
- package/core/server/services/members/csv/unparse.ts +79 -0
- package/core/server/services/members/exporter/query.js +35 -8
- package/core/server/services/members/importer/config.js +35 -0
- package/core/server/services/members/importer/config.ts +39 -0
- package/core/server/services/members/importer/members-csv-importer.js +31 -13
- package/core/server/services/members/members-api/members-api.js +4 -2
- package/core/server/services/members/members-api/repositories/event-repository.js +13 -2
- package/core/server/services/members/members-api/services/member-bread-service.js +42 -19
- package/core/server/services/members/middleware.js +1 -1
- package/core/server/services/members/service.js +15 -2
- package/core/server/services/members/stripe-connect.js +1 -1
- package/core/server/services/members-custom-fields/actions.js +3 -0
- package/core/server/services/members-custom-fields/actions.ts +6 -2
- package/core/server/services/members-custom-fields/config.js +34 -0
- package/core/server/services/members-custom-fields/config.ts +38 -0
- package/core/server/services/members-custom-fields/definitions-service.js +156 -21
- package/core/server/services/members-custom-fields/definitions-service.ts +170 -25
- package/core/server/services/members-custom-fields/index.js +16 -3
- package/core/server/services/members-custom-fields/index.ts +17 -3
- package/core/server/services/members-custom-fields/values-service.js +56 -10
- package/core/server/services/members-custom-fields/values-service.ts +62 -10
- package/core/server/services/mentions/mention-sending-service.js +22 -2
- package/core/server/services/mentions/service.js +21 -5
- package/core/server/services/newsletters/index.js +1 -1
- package/core/server/services/oembed/oembed-service.js +122 -33
- package/core/server/services/oembed/service.js +4 -2
- package/core/server/services/offers/service.js +1 -1
- package/core/server/services/post-scheduling/index.js +4 -3
- package/core/server/services/post-scheduling/index.ts +4 -3
- package/core/server/services/post-scheduling/post-scheduling.js +1 -1
- package/core/server/services/post-scheduling/post-scheduling.ts +1 -1
- package/core/server/services/posts/post-email-handler.js +13 -6
- package/core/server/services/posts/post-scheduling.js +1 -1
- package/core/server/services/posts/posts-service-instance.js +1 -1
- package/core/server/services/posts/posts-service.js +2 -2
- package/core/server/services/posts-public/service.js +1 -1
- package/core/server/services/public-config/site.js +1 -1
- package/core/server/services/recommendations/recommendation-service-wrapper.js +1 -1
- package/core/server/services/recommendations/service/libraries.d.ts +0 -2
- package/core/server/services/route-settings/dynamic-routing-service.js +2 -3
- package/core/server/services/route-settings/index.js +1 -1
- package/core/server/services/route-settings/route-settings-parser.js +134 -124
- package/core/server/services/route-settings/route-settings-parser.ts +160 -125
- package/core/server/services/route-settings/validation-errors.js +178 -0
- package/core/server/services/route-settings/validation-errors.ts +196 -0
- package/core/server/services/settings/settings-service.js +2 -2
- package/core/server/services/settings-helpers/index.js +1 -1
- package/core/server/services/slack-notifications/service.js +1 -1
- package/core/server/services/slack-ping/constants.js +16 -0
- package/core/server/services/slack-ping/constants.ts +15 -0
- package/core/server/services/slack-ping/index.js +26 -204
- package/core/server/services/slack-ping/index.ts +35 -0
- package/core/server/services/slack-ping/slack-ping-service.js +214 -0
- package/core/server/services/slack-ping/slack-ping-service.ts +296 -0
- package/core/server/services/staff/index.js +1 -1
- package/core/server/services/stats/posts-stats-service.js +1 -1
- package/core/server/services/stats/service.js +1 -1
- package/core/server/services/stripe/service.js +1 -1
- package/core/server/services/tags-public/service.js +1 -1
- package/core/server/services/themes/validate.js +1 -1
- package/core/server/services/update-check/index.js +1 -1
- package/core/server/services/url/lazy-url-service.js +66 -6
- package/core/server/services/url/lazy-url-service.ts +73 -6
- package/core/server/services/url/url-generator.js +1 -1
- package/core/server/services/url/url-service-facade.js +68 -3
- package/core/server/services/url/url-service-facade.ts +79 -4
- package/core/server/services/url/url-service.js +1 -1
- package/core/server/services/url/urls.js +1 -1
- package/core/server/services/webhooks/index.js +19 -2
- package/core/server/services/webhooks/listen.js +6 -11
- package/core/server/services/webhooks/payload.js +1 -3
- package/core/server/services/webhooks/serialize.js +16 -1
- package/core/server/web/admin/app.js +1 -1
- package/core/server/web/admin/middleware/redirect-admin-urls.js +1 -1
- package/core/server/web/api/endpoints/admin/routes.js +1 -0
- package/core/server/web/api/middleware/cors.js +1 -1
- package/core/server/web/api/middleware/upload.js +4 -0
- package/core/server/web/gift-preview/controller.js +1 -1
- package/core/server/web/shared/middleware/redirect-amp-urls.js +1 -1
- package/core/server/web/shared/middleware/uncapitalise.js +2 -2
- package/core/server/web/shared/middleware/url-redirects.js +1 -1
- package/core/shared/config/defaults.json +8 -1
- package/core/shared/config/overrides.json +4 -0
- package/core/shared/url-utils.js +19 -15
- package/core/shared/url-utils.ts +20 -0
- package/ghost.js +0 -1
- package/package.json +26 -31
- package/pnpm-lock.yaml +681 -2789
- package/pnpm-workspace.yaml +31 -17
- package/components/tryghost-adapter-base-scheduling-0.0.0.tgz +0 -0
- package/components/tryghost-adapter-base-sso-0.0.0.tgz +0 -0
- package/core/built/admin/assets/Combination--5EZGaDs.js +0 -43
- package/core/built/admin/assets/Growth-C0NoKcHQ.js +0 -3
- package/core/built/admin/assets/Newsletters-Ed8kDjdy.js +0 -1
- package/core/built/admin/assets/Overview-CArPgRZt.js +0 -1
- package/core/built/admin/assets/Web-CjjYoVM3.js +0 -2
- package/core/built/admin/assets/account-migration-mfa5mNI9.js +0 -1
- package/core/built/admin/assets/activitypub/inbox-B3rjKMTv.js +0 -1221
- package/core/built/admin/assets/activitypub/layout-jcurg3rU.js +0 -1
- package/core/built/admin/assets/activitypub/use-activity-pub-queries-yqGRcnLn.js +0 -2569
- package/core/built/admin/assets/activitypub/users-CfyHIsTG.js +0 -1316
- package/core/built/admin/assets/automations-DkUnxNdv.js +0 -1
- package/core/built/admin/assets/automations-vjXofmV_.js +0 -1
- package/core/built/admin/assets/bluesky-sharing-T-pO3RcP.js +0 -1
- package/core/built/admin/assets/breadcrumb-CekPYTCn.js +0 -1
- package/core/built/admin/assets/button--i2uMDDn.js +0 -4
- package/core/built/admin/assets/card-CSWz_BXq.js +0 -1
- package/core/built/admin/assets/chart-helpers-C_4Ka_vT.js +0 -1
- package/core/built/admin/assets/chunk.524.8ce2c741473e2f0929dc.js +0 -25
- package/core/built/admin/assets/chunk.582.453459d6228d2955408b.js +0 -27
- package/core/built/admin/assets/code-editor-view-BfkwQto2.js +0 -1
- package/core/built/admin/assets/command-K75Jzs0G.js +0 -1
- package/core/built/admin/assets/comments-BEIi1DH-.js +0 -1
- package/core/built/admin/assets/container-BNXvlcjA.js +0 -1
- package/core/built/admin/assets/data-list-BmK5QMab.js +0 -1
- package/core/built/admin/assets/date-range-select-5sKzwbt7.js +0 -1
- package/core/built/admin/assets/disabled-sources-indicator-L_ssJTNr.js +0 -1
- package/core/built/admin/assets/dist-BBIJuyPg.js +0 -1
- package/core/built/admin/assets/dist-BHE0dp6o.js +0 -1
- package/core/built/admin/assets/dist-DJ6c4_De.js +0 -1
- package/core/built/admin/assets/dist-DW0QXQO3.js +0 -1
- package/core/built/admin/assets/dist-DxFLceO5.js +0 -1
- package/core/built/admin/assets/dist-QcACAOAD.js +0 -1
- package/core/built/admin/assets/dist-duxRDcQu.js +0 -177
- package/core/built/admin/assets/domain-CLaPZ9Rc.js +0 -1
- package/core/built/admin/assets/dropzone-E0bSX4_a.js +0 -5
- package/core/built/admin/assets/edit-profile-DqNPKaA4.js +0 -1
- package/core/built/admin/assets/editor-CkLXQDRp.js +0 -8
- package/core/built/admin/assets/empty-indicator-BTtOlgIo.js +0 -1
- package/core/built/admin/assets/esm-TSfGW7nz.js +0 -2
- package/core/built/admin/assets/explore-BEstLxKr.js +0 -1
- package/core/built/admin/assets/feed-C58p2voG.js +0 -1
- package/core/built/admin/assets/field-BSbDUHPv.js +0 -1
- package/core/built/admin/assets/filters-BgYv8Gt7.js +0 -1
- package/core/built/admin/assets/filters-DvenFkmK.js +0 -1
- package/core/built/admin/assets/framework-provider-jQktxm9P.js +0 -4
- package/core/built/admin/assets/ghost-dark-8590dea6cd695ff90f27d262c4464538.css +0 -1
- package/core/built/admin/assets/gift-link-modal-BPjXkEFt.js +0 -1
- package/core/built/admin/assets/growth-D4JNiVTe.js +0 -1
- package/core/built/admin/assets/hooks-Cjxexi_I.js +0 -2
- package/core/built/admin/assets/index-BacwlcOy.js +0 -67
- package/core/built/admin/assets/index-DxRNHH0o.css +0 -1
- package/core/built/admin/assets/inline-C22rnPro.js +0 -1
- package/core/built/admin/assets/input-group-CKfrILLP.js +0 -1
- package/core/built/admin/assets/jsx-runtime-CtMZ-PAS.js +0 -1
- package/core/built/admin/assets/kpi-card-DZxQKAIb.js +0 -1
- package/core/built/admin/assets/kpi-card-FidQ5zg3.js +0 -1
- package/core/built/admin/assets/kpi-helpers-C5T8Vaf1.js +0 -1
- package/core/built/admin/assets/kpi-tabs-_VRK6hGV.js +0 -1
- package/core/built/admin/assets/label-Cza7hPgn.js +0 -1
- package/core/built/admin/assets/label-picker-DgC_zLaw.js +0 -1
- package/core/built/admin/assets/layout-BxsXnKFX.js +0 -1
- package/core/built/admin/assets/limit-service-CTurluJQ.js +0 -20
- package/core/built/admin/assets/link-helpers-C3Gqy2cz.js +0 -1
- package/core/built/admin/assets/list-page-BtqCnow_.js +0 -1
- package/core/built/admin/assets/member-custom-fields-9uXFBqSt.js +0 -1
- package/core/built/admin/assets/member-detail-Dq6ZDIxo.js +0 -1
- package/core/built/admin/assets/members-CUWD9jal.js +0 -10
- package/core/built/admin/assets/modals-CIdwHVga.js +0 -31
- package/core/built/admin/assets/moderation-DTt04UDA.js +0 -1
- package/core/built/admin/assets/newsletter-cC8vcc9h.js +0 -1
- package/core/built/admin/assets/note-DZU0IY9e.js +0 -1
- package/core/built/admin/assets/notifications-NoHpBHJ4.js +0 -1
- package/core/built/admin/assets/nql-lang-CvCSOQG3.js +0 -19
- package/core/built/admin/assets/onboarding-route-Deg2hNm8.js +0 -1
- package/core/built/admin/assets/overview-BQPt_9w3.js +0 -1
- package/core/built/admin/assets/page-header-D6bEqPo2.js +0 -1
- package/core/built/admin/assets/post-analytics-header-BPw_B2_D.js +0 -1
- package/core/built/admin/assets/post-analytics-kp2_--XO.js +0 -1
- package/core/built/admin/assets/post-share-modal-Cave3Z4W.js +0 -1
- package/core/built/admin/assets/preferences-C7h3j_uy.js +0 -5
- package/core/built/admin/assets/profile-B2x4q2oB.js +0 -1
- package/core/built/admin/assets/router-provider-5fsX1CYn.js +0 -1
- package/core/built/admin/assets/select-PhPjIBBT.js +0 -1
- package/core/built/admin/assets/settings-D5P9Gwgo.js +0 -6
- package/core/built/admin/assets/settings-zUOzQV5I.js +0 -1
- package/core/built/admin/assets/share-modal-EfpfbuWF.js +0 -1
- package/core/built/admin/assets/social-urls-DgAF08Xc.js +0 -18
- package/core/built/admin/assets/sort-button-WOQYJm2H.js +0 -1
- package/core/built/admin/assets/source-icon-hm8Cj-Gx.js +0 -1
- package/core/built/admin/assets/stats-CnQDb62m.js +0 -1
- package/core/built/admin/assets/stats-view-CCfTJgSe.js +0 -1
- package/core/built/admin/assets/step-1-BGlhY4DS.js +0 -1
- package/core/built/admin/assets/step-2-D3sDEyFH.js +0 -1
- package/core/built/admin/assets/step-3-CvZoUdsc.js +0 -2
- package/core/built/admin/assets/switch-CNjkOmwz.js +0 -1
- package/core/built/admin/assets/table-C5nZYPnW.js +0 -1
- package/core/built/admin/assets/tabs-dtOcExye.js +0 -1
- package/core/built/admin/assets/tags-Dhm0FB_g.js +0 -1
- package/core/built/admin/assets/textarea-CeKw3wmV.js +0 -1
- package/core/built/admin/assets/toggle-group-DfhZIMTW.js +0 -1
- package/core/built/admin/assets/topic-filter-Cxe1Il41.js +0 -1
- package/core/built/admin/assets/typeof-Ca-duI-z.js +0 -1
- package/core/built/admin/assets/use-filter-params-Bub6BqNy.js +0 -1
- package/core/built/admin/assets/use-growth-stats-BJORomv0.js +0 -1
- package/core/built/admin/assets/use-reply-chain-data-B89YjVoo.js +0 -1
- package/core/built/admin/assets/use-simple-pagination-BL2SYXKi.js +0 -1
- package/core/built/admin/assets/virtual-list-DNJOSK9I.js +0 -1
- package/core/built/admin/assets/web-BHUE3YQc.js +0 -1
- package/core/server/adapters/scheduling/scheduling-base.js +0 -5
- package/core/server/adapters/sso/SSOBase.js +0 -5
- package/core/server/adapters/storage/index.js +0 -17
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const {createAddColumnMigration} = require('../../utils');
|
|
2
|
+
|
|
3
|
+
module.exports = createAddColumnMigration('redirects', 'automation_action_revision_id', {
|
|
4
|
+
type: 'string',
|
|
5
|
+
maxlength: 24,
|
|
6
|
+
nullable: true,
|
|
7
|
+
references: 'automation_action_revisions.id',
|
|
8
|
+
setNullDelete: true
|
|
9
|
+
}, {algorithm: 'auto'});
|
|
@@ -5,6 +5,7 @@ const tpl = require('@tryghost/tpl');
|
|
|
5
5
|
const db = require('../db');
|
|
6
6
|
const DatabaseInfo = require('@tryghost/database-info');
|
|
7
7
|
const schema = require('./schema');
|
|
8
|
+
const {defaultIndexName} = require('./lib/default-index-name');
|
|
8
9
|
|
|
9
10
|
const messages = {
|
|
10
11
|
hasPrimaryKeySQLiteError: 'Must use hasPrimaryKeySQLite on an SQLite3 database',
|
|
@@ -191,19 +192,38 @@ async function renameColumn(tableName, from, to, transaction = db.knex) {
|
|
|
191
192
|
});
|
|
192
193
|
}
|
|
193
194
|
|
|
195
|
+
/**
|
|
196
|
+
* Builds the column arguments for a MySQL index prefix, such as `col(123)`.
|
|
197
|
+
*
|
|
198
|
+
* @param {import('knex').Knex} knex
|
|
199
|
+
* @param {string|string[]} columns
|
|
200
|
+
* @param {number} length
|
|
201
|
+
* @returns {import('knex').Knex.Raw[]}
|
|
202
|
+
*/
|
|
203
|
+
function prefixIndexColumns(knex, columns, length) {
|
|
204
|
+
return (Array.isArray(columns) ? columns : [columns])
|
|
205
|
+
.map(col => knex.raw('?? (?)', [col, length]));
|
|
206
|
+
}
|
|
207
|
+
|
|
194
208
|
/**
|
|
195
209
|
* Adds an non-unique index to a table over the given columns.
|
|
196
210
|
*
|
|
197
211
|
* @param {string} tableName - name of the table to add indexes to
|
|
198
212
|
* @param {string|string[]} columns - column(s) to add indexes for
|
|
199
213
|
* @param {import('knex').Knex} [transaction] - connection object containing knex reference
|
|
214
|
+
* @param {object} [options]
|
|
215
|
+
* @param {number} [options.length] - MySQL only: create a prefix index of this many characters
|
|
200
216
|
*/
|
|
201
|
-
async function addIndex(tableName, columns, transaction = db.knex) {
|
|
217
|
+
async function addIndex(tableName, columns, transaction = db.knex, options = {}) {
|
|
202
218
|
try {
|
|
203
219
|
logging.info(`Adding index for '${columns}' in table '${tableName}'`);
|
|
204
220
|
|
|
205
221
|
return await transaction.schema.table(tableName, function (table) {
|
|
206
|
-
|
|
222
|
+
if (options.length && DatabaseInfo.isMySQL(transaction)) {
|
|
223
|
+
table.index(prefixIndexColumns(transaction, columns, options.length), defaultIndexName(tableName, columns));
|
|
224
|
+
} else {
|
|
225
|
+
table.index(columns);
|
|
226
|
+
}
|
|
207
227
|
});
|
|
208
228
|
} catch (err) {
|
|
209
229
|
if (err.code === 'SQLITE_ERROR') {
|
|
@@ -500,7 +520,21 @@ function createTable(table, transaction = db.knex, tableSpec = schema[table]) {
|
|
|
500
520
|
.forEach(column => addTableColumn(table, t, column, tableSpec[column]));
|
|
501
521
|
|
|
502
522
|
if (tableSpec['@@INDEXES@@']) {
|
|
503
|
-
tableSpec['@@INDEXES@@'].forEach(index =>
|
|
523
|
+
tableSpec['@@INDEXES@@'].forEach((index) => {
|
|
524
|
+
if (index && typeof index === 'object' && !Array.isArray(index)) {
|
|
525
|
+
if (index.length && DatabaseInfo.isMySQL(transaction)) {
|
|
526
|
+
t.index(
|
|
527
|
+
prefixIndexColumns(transaction, index.columns, index.length),
|
|
528
|
+
defaultIndexName(table, index.columns)
|
|
529
|
+
);
|
|
530
|
+
} else {
|
|
531
|
+
// SQLite doesn't support prefix indexes, so we index the whole thing.
|
|
532
|
+
t.index(index.columns);
|
|
533
|
+
}
|
|
534
|
+
} else {
|
|
535
|
+
t.index(index);
|
|
536
|
+
}
|
|
537
|
+
});
|
|
504
538
|
}
|
|
505
539
|
if (tableSpec['@@UNIQUE_CONSTRAINTS@@']) {
|
|
506
540
|
tableSpec['@@UNIQUE_CONSTRAINTS@@'].forEach(unique => t.unique(unique));
|
|
@@ -144,6 +144,11 @@
|
|
|
144
144
|
"action_type": "deleteAllContent",
|
|
145
145
|
"object_type": "db"
|
|
146
146
|
},
|
|
147
|
+
{
|
|
148
|
+
"name": "Import content",
|
|
149
|
+
"action_type": "importContent",
|
|
150
|
+
"object_type": "content_import"
|
|
151
|
+
},
|
|
147
152
|
{
|
|
148
153
|
"name": "Send mail",
|
|
149
154
|
"action_type": "send",
|
|
@@ -966,6 +971,7 @@
|
|
|
966
971
|
"entries": {
|
|
967
972
|
"Administrator": {
|
|
968
973
|
"db": "all",
|
|
974
|
+
"content_import": "all",
|
|
969
975
|
"mail": "all",
|
|
970
976
|
"notification": "all",
|
|
971
977
|
"post": "all",
|
|
@@ -1020,6 +1026,7 @@
|
|
|
1020
1026
|
},
|
|
1021
1027
|
"Self-Serve Migration Integration": {
|
|
1022
1028
|
"db": "importContent",
|
|
1029
|
+
"content_import": "importContent",
|
|
1023
1030
|
"member": "add",
|
|
1024
1031
|
"tag": "read"
|
|
1025
1032
|
},
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normally, we use Knex for index names. But in some cases, we need to derive
|
|
3
|
+
* them because Knex won't get it right. We try to match [Knex's code][0] as
|
|
4
|
+
* closely as possible.
|
|
5
|
+
*
|
|
6
|
+
* [0]: https://github.com/knex/knex/blob/e25d54bcb707714a17f5a5744eba5c4246bb4d1d/lib/schema/tablecompiler.js#L401-L415
|
|
7
|
+
*
|
|
8
|
+
* @param {string} tableName
|
|
9
|
+
* @param {string|string[]} columns
|
|
10
|
+
* @returns {string}
|
|
11
|
+
*/
|
|
12
|
+
function defaultIndexName(tableName, columns) {
|
|
13
|
+
if (!Array.isArray(columns)) {
|
|
14
|
+
columns = columns ? [columns] : [];
|
|
15
|
+
}
|
|
16
|
+
const table = tableName.replace(/\.|-/g, '_');
|
|
17
|
+
return (table + '_' + columns.join('_') + '_index').toLowerCase();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
exports.defaultIndexName = defaultIndexName;
|
|
@@ -1076,20 +1076,6 @@ module.exports = {
|
|
|
1076
1076
|
metadata: {type: 'string', maxlength: 2000, nullable: true},
|
|
1077
1077
|
queue_entry: {type: 'integer', nullable: true, unsigned: true}
|
|
1078
1078
|
},
|
|
1079
|
-
redirects: {
|
|
1080
|
-
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
1081
|
-
from: {type: 'string', maxlength: 191, nullable: false, index: true},
|
|
1082
|
-
to: {type: 'string', maxlength: 2000, nullable: false},
|
|
1083
|
-
post_id: {type: 'string', maxlength: 24, nullable: true, unique: false, references: 'posts.id', setNullDelete: true},
|
|
1084
|
-
created_at: {type: 'dateTime', nullable: false},
|
|
1085
|
-
updated_at: {type: 'dateTime', nullable: true}
|
|
1086
|
-
},
|
|
1087
|
-
members_click_events: {
|
|
1088
|
-
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
1089
|
-
member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id', cascadeDelete: true},
|
|
1090
|
-
redirect_id: {type: 'string', maxlength: 24, nullable: false, references: 'redirects.id', cascadeDelete: true},
|
|
1091
|
-
created_at: {type: 'dateTime', nullable: false}
|
|
1092
|
-
},
|
|
1093
1079
|
members_feedback: {
|
|
1094
1080
|
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
1095
1081
|
score: {type: 'integer', nullable: false, unsigned: true, defaultTo: 0},
|
|
@@ -1265,12 +1251,27 @@ module.exports = {
|
|
|
1265
1251
|
email_lexical: {type: 'text', maxlength: 1000000000, fieldtype: 'long', nullable: true},
|
|
1266
1252
|
email_design_setting_id: {type: 'string', maxlength: 24, nullable: true, references: 'email_design_settings.id', setNullDelete: true},
|
|
1267
1253
|
email_sent_count: {type: 'integer', nullable: true, unsigned: true},
|
|
1268
|
-
email_tracked_sent_count: {type: 'integer', nullable: true, unsigned: true},
|
|
1269
1254
|
email_opened_count: {type: 'integer', nullable: true, unsigned: true},
|
|
1255
|
+
email_clicked_count: {type: 'integer', nullable: true, unsigned: true},
|
|
1270
1256
|
'@@UNIQUE_CONSTRAINTS@@': [
|
|
1271
1257
|
['created_at', 'action_id']
|
|
1272
1258
|
]
|
|
1273
1259
|
},
|
|
1260
|
+
redirects: {
|
|
1261
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
1262
|
+
from: {type: 'string', maxlength: 191, nullable: false, index: true},
|
|
1263
|
+
to: {type: 'string', maxlength: 2000, nullable: false},
|
|
1264
|
+
post_id: {type: 'string', maxlength: 24, nullable: true, unique: false, references: 'posts.id', setNullDelete: true},
|
|
1265
|
+
automation_action_revision_id: {type: 'string', maxlength: 24, nullable: true, references: 'automation_action_revisions.id', setNullDelete: true},
|
|
1266
|
+
created_at: {type: 'dateTime', nullable: false},
|
|
1267
|
+
updated_at: {type: 'dateTime', nullable: true}
|
|
1268
|
+
},
|
|
1269
|
+
members_click_events: {
|
|
1270
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
1271
|
+
member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id', cascadeDelete: true},
|
|
1272
|
+
redirect_id: {type: 'string', maxlength: 24, nullable: false, references: 'redirects.id', cascadeDelete: true},
|
|
1273
|
+
created_at: {type: 'dateTime', nullable: false}
|
|
1274
|
+
},
|
|
1274
1275
|
automation_action_edges: {
|
|
1275
1276
|
source_action_id: {type: 'string', maxlength: 24, nullable: false, references: 'automation_actions.id', restrictDelete: true},
|
|
1276
1277
|
target_action_id: {type: 'string', maxlength: 24, nullable: false, references: 'automation_actions.id', restrictDelete: true},
|
|
@@ -1296,7 +1297,10 @@ module.exports = {
|
|
|
1296
1297
|
finished_at: {type: 'dateTime', nullable: true},
|
|
1297
1298
|
status: {type: 'string', maxlength: 50, nullable: false, defaultTo: 'pending', validations: {isIn: [['pending', 'automation disabled', 'failed', 'finished', 'member changed status', 'member unsubscribed']]}},
|
|
1298
1299
|
locked_by: {type: 'string', maxlength: 191, nullable: true},
|
|
1299
|
-
locked_at: {type: 'dateTime', nullable: true}
|
|
1300
|
+
locked_at: {type: 'dateTime', nullable: true},
|
|
1301
|
+
'@@INDEXES@@': [
|
|
1302
|
+
['status', 'ready_at', 'created_at', 'id']
|
|
1303
|
+
]
|
|
1300
1304
|
},
|
|
1301
1305
|
welcome_email_automated_emails: {
|
|
1302
1306
|
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
@@ -1338,9 +1342,28 @@ module.exports = {
|
|
|
1338
1342
|
mailgun_message_id: {type: 'string', maxlength: 1000, nullable: true},
|
|
1339
1343
|
delivered_at: {type: 'dateTime', nullable: true},
|
|
1340
1344
|
opened_at: {type: 'dateTime', nullable: true},
|
|
1345
|
+
clicked_at: {type: 'dateTime', nullable: true},
|
|
1341
1346
|
track_opens: {type: 'boolean', nullable: false, defaultTo: false},
|
|
1347
|
+
track_clicks: {type: 'boolean', nullable: false, defaultTo: false},
|
|
1342
1348
|
created_at: {type: 'dateTime', nullable: false},
|
|
1343
|
-
updated_at: {type: 'dateTime', nullable: true}
|
|
1349
|
+
updated_at: {type: 'dateTime', nullable: true},
|
|
1350
|
+
'@@INDEXES@@': [
|
|
1351
|
+
// `mailgun_message_id` is too long for a MySQL index, so we use a
|
|
1352
|
+
// prefix.
|
|
1353
|
+
//
|
|
1354
|
+
// We choose 31 because Mailgun message IDs look like this:
|
|
1355
|
+
//
|
|
1356
|
+
// 20200420080647.ab01cd02ef03ba04@mailgun.domain.example
|
|
1357
|
+
// YYYYMMDDHHMMSS.RANDOM-HEX-BYTES@DOMAIN
|
|
1358
|
+
//
|
|
1359
|
+
// That first part is unlikely to have conflicts, so let's use
|
|
1360
|
+
// that. This index is for performance, not uniqueness, so it's
|
|
1361
|
+
// okay if there's a conflict.
|
|
1362
|
+
//
|
|
1363
|
+
// Note that this prefix index only happens for MySQL. SQLite
|
|
1364
|
+
// indexes the full value.
|
|
1365
|
+
{columns: ['mailgun_message_id'], length: 31}
|
|
1366
|
+
]
|
|
1344
1367
|
},
|
|
1345
1368
|
gifts: {
|
|
1346
1369
|
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
@@ -59,8 +59,10 @@ class DataGenerator {
|
|
|
59
59
|
table.dependencies = Object.entries(this.schemaTables[table.name]).reduce((acc, [_col, data]) => {
|
|
60
60
|
if (data.references) {
|
|
61
61
|
const referencedTable = data.references.split('.')[0];
|
|
62
|
-
//
|
|
63
|
-
|
|
62
|
+
// Some nullable relationships point to tables the generator does not populate.
|
|
63
|
+
// Generated redirects belong to posts, so automation revisions are not a seed dependency.
|
|
64
|
+
const isUnsupportedOptionalDependency = referencedTable === 'subscriptions' || (referencedTable === 'automation_action_revisions' && data.nullable);
|
|
65
|
+
if (!acc.includes(referencedTable) && !isUnsupportedOptionalDependency) {
|
|
64
66
|
acc.push(referencedTable);
|
|
65
67
|
}
|
|
66
68
|
}
|
|
@@ -17,9 +17,9 @@ const FETCH_ONLY_FORMATS = [
|
|
|
17
17
|
];
|
|
18
18
|
|
|
19
19
|
class ImageSize {
|
|
20
|
-
constructor({config,
|
|
20
|
+
constructor({config, imageStore, storageUtils, validator, urlUtils, request, probe}) {
|
|
21
21
|
this.config = config;
|
|
22
|
-
this.
|
|
22
|
+
this.imageStore = imageStore;
|
|
23
23
|
this.storageUtils = storageUtils;
|
|
24
24
|
this.validator = validator;
|
|
25
25
|
this.urlUtils = urlUtils;
|
|
@@ -248,8 +248,7 @@ class ImageSize {
|
|
|
248
248
|
// get the storage readable filePath
|
|
249
249
|
filePath = this.storageUtils.getLocalImagesStoragePath(imagePath);
|
|
250
250
|
|
|
251
|
-
return this.
|
|
252
|
-
.read({path: filePath})
|
|
251
|
+
return this.imageStore.read({path: filePath})
|
|
253
252
|
.then((buf) => {
|
|
254
253
|
debug('Image fetched (storage):', filePath);
|
|
255
254
|
return this._imageSizeFromBuffer(buf);
|
|
@@ -298,10 +297,9 @@ class ImageSize {
|
|
|
298
297
|
*/
|
|
299
298
|
async getOriginalImagePath(imagePath) {
|
|
300
299
|
const {dir, name, ext} = path.parse(imagePath);
|
|
301
|
-
const storageInstance = this.storage.getStorage('images');
|
|
302
300
|
|
|
303
301
|
const preferredUnoptimizedImagePath = path.join(dir, `${name}_o${ext}`);
|
|
304
|
-
const preferredUnoptimizedImagePathExists = await
|
|
302
|
+
const preferredUnoptimizedImagePathExists = await this.imageStore.exists(preferredUnoptimizedImagePath);
|
|
305
303
|
if (preferredUnoptimizedImagePathExists) {
|
|
306
304
|
return preferredUnoptimizedImagePath;
|
|
307
305
|
}
|
|
@@ -315,7 +313,7 @@ class ImageSize {
|
|
|
315
313
|
}
|
|
316
314
|
|
|
317
315
|
const legacyOriginalImagePath = path.join(dir, `${imageName}_o${imageNumber || ''}${ext}`);
|
|
318
|
-
const legacyOriginalImageExists = await
|
|
316
|
+
const legacyOriginalImageExists = await this.imageStore.exists(legacyOriginalImagePath);
|
|
319
317
|
|
|
320
318
|
return legacyOriginalImageExists ? legacyOriginalImagePath : imagePath;
|
|
321
319
|
}
|
|
@@ -25,9 +25,9 @@ function probe(url, options = {}, {probeImageSize: probeImageSizeFn = probeImage
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
class ImageUtils {
|
|
28
|
-
constructor({config, urlUtils, settingsCache, storageUtils,
|
|
28
|
+
constructor({config, urlUtils, settingsCache, storageUtils, imageStore, validator, request, cacheStore}) {
|
|
29
29
|
this.blogIcon = new BlogIcon({config, urlUtils, settingsCache, storageUtils});
|
|
30
|
-
this.imageSize = new ImageSize({config,
|
|
30
|
+
this.imageSize = new ImageSize({config, imageStore, storageUtils, validator, urlUtils, request, probe});
|
|
31
31
|
this.cachedImageSizeFromUrl = new CachedImageSizeFromUrl({
|
|
32
32
|
getImageSizeFromUrl: this.imageSize.getImageSizeFromUrl.bind(this.imageSize),
|
|
33
33
|
cache: cacheStore
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
const request = require('@tryghost/request');
|
|
2
|
-
const urlUtils = require('../../../shared/url-utils');
|
|
3
|
-
const storage = require('../../adapters/storage');
|
|
2
|
+
const urlUtils = require('../../../shared/url-utils').default;
|
|
4
3
|
const storageUtils = require('../../adapters/storage/utils');
|
|
5
4
|
const validator = require('@tryghost/validator');
|
|
6
5
|
const config = require('../../../shared/config');
|
|
7
6
|
const settingsCache = require('../../../shared/settings-cache');
|
|
8
7
|
const ImageUtils = require('./image-utils');
|
|
9
8
|
|
|
10
|
-
const adapterManager = require('../../services/adapter-manager');
|
|
9
|
+
const adapterManager = require('../../services/adapter-manager').default;
|
|
11
10
|
|
|
12
11
|
const cacheStore = adapterManager.getAdapter('cache:imageSizes');
|
|
12
|
+
const imageStore = adapterManager.getAdapter('storage:images');
|
|
13
13
|
|
|
14
14
|
module.exports = new ImageUtils({
|
|
15
15
|
config,
|
|
16
16
|
urlUtils,
|
|
17
17
|
settingsCache,
|
|
18
18
|
storageUtils,
|
|
19
|
-
|
|
19
|
+
imageStore,
|
|
20
20
|
validator,
|
|
21
21
|
request,
|
|
22
22
|
cacheStore
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const errors = require('@tryghost/errors');
|
|
3
|
-
const urlUtils = require('../../shared/url-utils');
|
|
3
|
+
const urlUtils = require('../../shared/url-utils').default;
|
|
4
4
|
const config = require('../../shared/config');
|
|
5
5
|
const labs = require('../../shared/labs');
|
|
6
6
|
const settingsCache = require('../../shared/settings-cache');
|
|
7
|
-
const
|
|
7
|
+
const adapterManager = require('../services/adapter-manager').default;
|
|
8
8
|
|
|
9
9
|
let nodes;
|
|
10
10
|
let lexicalHtmlRenderer;
|
|
@@ -50,7 +50,7 @@ function buildRenderOptions(userOptions) {
|
|
|
50
50
|
// NOTE: the "saveRaw" check is smelly
|
|
51
51
|
return imageTransform.canTransformFiles()
|
|
52
52
|
&& imageTransform.shouldResizeFileExtension(ext)
|
|
53
|
-
&& typeof
|
|
53
|
+
&& typeof adapterManager.getAdapter('storage:images').saveRaw === 'function';
|
|
54
54
|
},
|
|
55
55
|
canTransformImageToFormat(format) {
|
|
56
56
|
const imageTransform = require('@tryghost/image-transform');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const config = require('../../shared/config');
|
|
3
|
-
const
|
|
3
|
+
const adapterManager = require('../services/adapter-manager').default;
|
|
4
4
|
|
|
5
5
|
let cardFactory;
|
|
6
6
|
let cards;
|
|
@@ -37,7 +37,7 @@ module.exports = {
|
|
|
37
37
|
// NOTE: the "saveRaw" check is smelly
|
|
38
38
|
return imageTransform.canTransformFiles()
|
|
39
39
|
&& imageTransform.shouldResizeFileExtension(ext)
|
|
40
|
-
&& typeof
|
|
40
|
+
&& typeof adapterManager.getAdapter('storage:images').saveRaw === 'function';
|
|
41
41
|
}
|
|
42
42
|
});
|
|
43
43
|
|
|
@@ -4,7 +4,7 @@ const validator = require('@tryghost/validator');
|
|
|
4
4
|
|
|
5
5
|
const tpl = require('@tryghost/tpl');
|
|
6
6
|
const settingsCache = require('../../shared/settings-cache');
|
|
7
|
-
const urlUtils = require('../../shared/url-utils');
|
|
7
|
+
const urlUtils = require('../../shared/url-utils').default;
|
|
8
8
|
|
|
9
9
|
const messages = {
|
|
10
10
|
passwordDoesNotComplyLength: 'Your password must be at least {minLength} characters long.',
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
const ghostBookshelf = require('./base');
|
|
2
2
|
|
|
3
|
+
// Member custom fields are owned by a raw-knex service rather than the Bookshelf
|
|
4
|
+
// registry. Actions still need a read model for `include=resource` to recognise
|
|
5
|
+
// their polymorphic resource type and load the current field definition.
|
|
6
|
+
const MemberCustomFieldResource = ghostBookshelf.Model.extend({
|
|
7
|
+
tableName: 'members_custom_fields'
|
|
8
|
+
});
|
|
9
|
+
|
|
3
10
|
const Action = ghostBookshelf.Model.extend({
|
|
4
11
|
tableName: 'actions',
|
|
5
12
|
|
|
@@ -18,6 +25,8 @@ const Action = ghostBookshelf.Model.extend({
|
|
|
18
25
|
candidates.push([User, 'security_action']);
|
|
19
26
|
}
|
|
20
27
|
|
|
28
|
+
candidates.push([MemberCustomFieldResource, 'member_custom_field']);
|
|
29
|
+
|
|
21
30
|
return candidates;
|
|
22
31
|
},
|
|
23
32
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const ghostBookshelf = require('./base');
|
|
3
|
-
const urlUtils = require('../../shared/url-utils');
|
|
3
|
+
const urlUtils = require('../../shared/url-utils').default;
|
|
4
4
|
|
|
5
5
|
const CustomThemeSetting = ghostBookshelf.Model.extend({
|
|
6
6
|
tableName: 'custom_theme_settings',
|
|
@@ -4,6 +4,70 @@ const _ = require('lodash');
|
|
|
4
4
|
const config = require('../../shared/config');
|
|
5
5
|
const {MemberCommentingCodec} = require('../services/members/commenting');
|
|
6
6
|
|
|
7
|
+
const DEEP_OFFSET_THRESHOLD = 1000;
|
|
8
|
+
|
|
9
|
+
// The rewrite only applies to the plain single-table browse query. Joins,
|
|
10
|
+
// group by, unions etc. at the top level of the statement mean some other
|
|
11
|
+
// query shape — but the same keywords inside parens (NQL filters and search
|
|
12
|
+
// compile to `where members.id in (select ... join ...)`) are fine, so only
|
|
13
|
+
// depth-0 SQL is inspected. Bindings are `?` placeholders, so no user data
|
|
14
|
+
// appears in the inspected string.
|
|
15
|
+
function sqlOutsideParens(sql) {
|
|
16
|
+
let depth = 0;
|
|
17
|
+
let result = '';
|
|
18
|
+
|
|
19
|
+
for (const char of sql) {
|
|
20
|
+
if (char === '(') {
|
|
21
|
+
depth += 1;
|
|
22
|
+
} else if (char === ')') {
|
|
23
|
+
depth -= 1;
|
|
24
|
+
} else if (depth === 0) {
|
|
25
|
+
result += char;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Deferred join for deep OFFSET pagination: plain LIMIT/OFFSET does a full
|
|
33
|
+
// row lookup for every discarded OFFSET row, so deep browse pages took ~10s
|
|
34
|
+
// on large sites. Instead, run LIMIT/OFFSET over a subquery selecting only
|
|
35
|
+
// the primary key (covered by the (created_at, id) index), then join back to
|
|
36
|
+
// fetch full rows for just the final page.
|
|
37
|
+
//
|
|
38
|
+
// The query is inspected via its compiled SQL (`qb.toSQL()`, public knex API)
|
|
39
|
+
// rather than knex's internal builder state. The prefix/suffix anchors below
|
|
40
|
+
// mean anything unexpected — custom selects, a lock clause, missing
|
|
41
|
+
// limit/offset — safely skips the rewrite.
|
|
42
|
+
function applyDeferredJoinForDeepOffset(qb) {
|
|
43
|
+
const {sql, bindings} = qb.toSQL();
|
|
44
|
+
|
|
45
|
+
// The plain browse query compiles to
|
|
46
|
+
// `select * from `members` [where ...] order by ... limit ? offset ?`;
|
|
47
|
+
// knex passes limit and offset as the last two bindings
|
|
48
|
+
if (!sql.startsWith('select * from `members`') || !sql.endsWith('limit ? offset ?')) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const offset = bindings[bindings.length - 1];
|
|
53
|
+
|
|
54
|
+
if (!Number.isInteger(offset) || offset < DEEP_OFFSET_THRESHOLD) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const topLevelSql = sqlOutsideParens(sql);
|
|
59
|
+
if ([' join ', ' group by ', ' having ', ' union '].some(keyword => topLevelSql.includes(keyword))) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Aliased so the default raw browse order (`created_at DESC, id DESC`)
|
|
64
|
+
// stays unambiguous in the outer query
|
|
65
|
+
const idSubquery = qb.clone().select('members.id as deep_page_id');
|
|
66
|
+
|
|
67
|
+
qb.clear('where').clear('limit').clear('offset')
|
|
68
|
+
.innerJoin(idSubquery.as('deep_page'), 'members.id', 'deep_page.deep_page_id');
|
|
69
|
+
}
|
|
70
|
+
|
|
7
71
|
const Member = ghostBookshelf.Model.extend({
|
|
8
72
|
tableName: 'members',
|
|
9
73
|
|
|
@@ -357,6 +421,15 @@ const Member = ghostBookshelf.Model.extend({
|
|
|
357
421
|
return result;
|
|
358
422
|
},
|
|
359
423
|
|
|
424
|
+
// Fires with the fully-built knex query (filters/order/limit/offset
|
|
425
|
+
// applied, select columns not yet); the count query runs before this
|
|
426
|
+
// event, so pagination meta is unaffected by the rewrite
|
|
427
|
+
onFetchingCollection: function onFetchingCollection(collection, columns, options) {
|
|
428
|
+
ghostBookshelf.Model.prototype.onFetchingCollection.apply(this, arguments);
|
|
429
|
+
|
|
430
|
+
applyDeferredJoinForDeepOffset(options.query);
|
|
431
|
+
},
|
|
432
|
+
|
|
360
433
|
onDestroyed: function onDestroyed(model, options) {
|
|
361
434
|
const result = ghostBookshelf.Model.prototype.onDestroyed.apply(this, arguments);
|
|
362
435
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const ghostBookshelf = require('./base');
|
|
2
2
|
const ObjectID = require('bson-objectid').default;
|
|
3
3
|
const crypto = require('crypto');
|
|
4
|
-
const urlUtils = require('../../shared/url-utils');
|
|
4
|
+
const urlUtils = require('../../shared/url-utils').default;
|
|
5
5
|
|
|
6
6
|
const Newsletter = ghostBookshelf.Model.extend({
|
|
7
7
|
tableName: 'newsletters',
|
|
@@ -14,7 +14,7 @@ const limitService = require('../services/limits');
|
|
|
14
14
|
const mobiledocLib = require('../lib/mobiledoc');
|
|
15
15
|
const lexicalLib = require('../lib/lexical');
|
|
16
16
|
const relations = require('./relations');
|
|
17
|
-
const urlUtils = require('../../shared/url-utils');
|
|
17
|
+
const urlUtils = require('../../shared/url-utils').default;
|
|
18
18
|
const {Tag} = require('./tag');
|
|
19
19
|
const {Newsletter} = require('./newsletter');
|
|
20
20
|
const {BadRequestError} = require('@tryghost/errors');
|
|
@@ -950,15 +950,29 @@ Post = ghostBookshelf.Model.extend({
|
|
|
950
950
|
},
|
|
951
951
|
|
|
952
952
|
authors: function authors() {
|
|
953
|
+
// posts_authors.id breaks sort_order ties deterministically (by attach
|
|
954
|
+
// order); without it MySQL can order tied rows differently per query,
|
|
955
|
+
// which changes the computed primary_author. See tags() below.
|
|
956
|
+
// `id` is pivoted in so it is in the SELECT list — the query uses
|
|
957
|
+
// DISTINCT, which requires ORDER BY columns to be selected.
|
|
953
958
|
return this.belongsToMany('User', 'posts_authors', 'post_id', 'author_id')
|
|
954
|
-
.withPivot('sort_order')
|
|
955
|
-
.query('orderBy', 'sort_order', 'ASC')
|
|
959
|
+
.withPivot(['sort_order', 'id'])
|
|
960
|
+
.query('orderBy', 'sort_order', 'ASC')
|
|
961
|
+
.query('orderBy', 'posts_authors.id', 'ASC');
|
|
956
962
|
},
|
|
957
963
|
|
|
958
964
|
tags: function tags() {
|
|
965
|
+
// posts_tags.id breaks sort_order ties deterministically (by attach
|
|
966
|
+
// order). primary_tag is tags[0]-if-public, so a non-deterministic tie
|
|
967
|
+
// order (which MySQL produces for tags sharing a sort_order) makes the
|
|
968
|
+
// primary tag — and the post's URL under a {primary_tag} permalink —
|
|
969
|
+
// depend on the query shape.
|
|
970
|
+
// `id` is pivoted in so it is in the SELECT list — the query uses
|
|
971
|
+
// DISTINCT, which requires ORDER BY columns to be selected.
|
|
959
972
|
return this.belongsToMany('Tag', 'posts_tags', 'post_id', 'tag_id')
|
|
960
|
-
.withPivot('sort_order')
|
|
961
|
-
.query('orderBy', 'sort_order', 'ASC')
|
|
973
|
+
.withPivot(['sort_order', 'id'])
|
|
974
|
+
.query('orderBy', 'sort_order', 'ASC')
|
|
975
|
+
.query('orderBy', 'posts_tags.id', 'ASC');
|
|
962
976
|
},
|
|
963
977
|
|
|
964
978
|
mobiledoc_revisions() {
|
|
@@ -6,7 +6,7 @@ const ghostBookshelf = require('./base');
|
|
|
6
6
|
const tpl = require('@tryghost/tpl');
|
|
7
7
|
const errors = require('@tryghost/errors');
|
|
8
8
|
const validator = require('@tryghost/validator');
|
|
9
|
-
const urlUtils = require('../../shared/url-utils');
|
|
9
|
+
const urlUtils = require('../../shared/url-utils').default;
|
|
10
10
|
const {WRITABLE_KEYS_ALLOWLIST} = require('../../shared/labs');
|
|
11
11
|
const {getOrGenerateSiteUuid} = require('../services/settings/settings-utils');
|
|
12
12
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const ghostBookshelf = require('./base');
|
|
2
|
-
const urlUtils = require('../../shared/url-utils');
|
|
2
|
+
const urlUtils = require('../../shared/url-utils').default;
|
|
3
3
|
const mobiledocLib = require('../lib/mobiledoc');
|
|
4
4
|
const lexicalLib = require('../lib/lexical');
|
|
5
5
|
const _ = require('lodash');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const ghostBookshelf = require('./base');
|
|
2
2
|
const tpl = require('@tryghost/tpl');
|
|
3
3
|
const errors = require('@tryghost/errors');
|
|
4
|
-
const urlUtils = require('../../shared/url-utils');
|
|
4
|
+
const urlUtils = require('../../shared/url-utils').default;
|
|
5
5
|
|
|
6
6
|
const messages = {
|
|
7
7
|
tagNotFound: 'Tag not found.'
|
|
@@ -9,7 +9,7 @@ const security = require('@tryghost/security');
|
|
|
9
9
|
const {pipeline} = require('@tryghost/promise');
|
|
10
10
|
const validatePassword = require('../lib/validate-password');
|
|
11
11
|
const permissions = require('../services/permissions');
|
|
12
|
-
const urlUtils = require('../../shared/url-utils');
|
|
12
|
+
const urlUtils = require('../../shared/url-utils').default;
|
|
13
13
|
const {setIsRoles} = require('./role-utils');
|
|
14
14
|
const activeStates = ['active', 'warn-1', 'warn-2', 'warn-3', 'warn-4'];
|
|
15
15
|
const ASSIGNABLE_ROLES = ['Administrator', 'Super Editor', 'Editor', 'Author', 'Contributor'];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const ghostBookshelf = require('./base');
|
|
2
2
|
const errors = require('@tryghost/errors');
|
|
3
|
-
const urlUtils = require('../../shared/url-utils');
|
|
3
|
+
const urlUtils = require('../../shared/url-utils').default;
|
|
4
4
|
const lexicalLib = require('../lib/lexical');
|
|
5
5
|
const {DEFAULT_EMAIL_DESIGN_SETTING_SLUG} = require('../services/member-welcome-emails/constants');
|
|
6
6
|
|
|
@@ -14,7 +14,7 @@ module.exports = class ActivityPubServiceWrapper {
|
|
|
14
14
|
const logging = require('@tryghost/logging');
|
|
15
15
|
const events = require('../../lib/common/events');
|
|
16
16
|
const {knex} = require('../../data/db');
|
|
17
|
-
const urlUtils = require('../../../shared/url-utils');
|
|
17
|
+
const urlUtils = require('../../../shared/url-utils').default;
|
|
18
18
|
const IdentityTokenServiceWrapper = require('../identity-tokens');
|
|
19
19
|
const settingsCache = require('../../../shared/settings-cache');
|
|
20
20
|
|