emdash 0.8.0 → 0.10.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/dist/{adapters-BKSf3T9R.d.mts → adapters-BktHA7EO.d.mts} +1 -1
- package/dist/{adapters-BKSf3T9R.d.mts.map → adapters-BktHA7EO.d.mts.map} +1 -1
- package/dist/{apply-x0eMK1lX.mjs → apply-UsrFuO7l.mjs} +207 -355
- package/dist/apply-UsrFuO7l.mjs.map +1 -0
- package/dist/astro/index.d.mts +6 -6
- package/dist/astro/index.d.mts.map +1 -1
- package/dist/astro/index.mjs +118 -4
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +6 -7
- package/dist/astro/middleware/auth.d.mts.map +1 -1
- package/dist/astro/middleware/auth.mjs +14 -57
- package/dist/astro/middleware/auth.mjs.map +1 -1
- package/dist/astro/middleware/redirect.d.mts.map +1 -1
- package/dist/astro/middleware/redirect.mjs +15 -10
- package/dist/astro/middleware/redirect.mjs.map +1 -1
- package/dist/astro/middleware/request-context.d.mts.map +1 -1
- package/dist/astro/middleware/request-context.mjs +8 -5
- package/dist/astro/middleware/request-context.mjs.map +1 -1
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +70 -121
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/types.d.mts +25 -10
- package/dist/astro/types.d.mts.map +1 -1
- package/dist/{byline-Chbr2GoP.mjs → byline-C3vnhIpU.mjs} +4 -4
- package/dist/{byline-Chbr2GoP.mjs.map → byline-C3vnhIpU.mjs.map} +1 -1
- package/dist/bylines-esI7ioa9.mjs +113 -0
- package/dist/bylines-esI7ioa9.mjs.map +1 -0
- package/dist/cache-fTzxgMFJ.mjs +65 -0
- package/dist/cache-fTzxgMFJ.mjs.map +1 -0
- package/dist/{chunks-HGz06Soa.mjs → chunks-Da2-b-oA.mjs} +8 -2
- package/dist/{chunks-HGz06Soa.mjs.map → chunks-Da2-b-oA.mjs.map} +1 -1
- package/dist/cli/index.mjs +456 -90
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/cf-access.d.mts +1 -1
- package/dist/client/index.d.mts +1 -1
- package/dist/client/index.mjs +3 -3
- package/dist/client/index.mjs.map +1 -1
- package/dist/{config-BXwuX8Bx.mjs → config-CVssduLe.mjs} +1 -1
- package/dist/{config-BXwuX8Bx.mjs.map → config-CVssduLe.mjs.map} +1 -1
- package/dist/{content-BcQPYxdV.mjs → content-C7G4QXkK.mjs} +42 -14
- package/dist/content-C7G4QXkK.mjs.map +1 -0
- package/dist/db/index.d.mts +3 -3
- package/dist/db/index.mjs +2 -2
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/libsql.d.mts.map +1 -1
- package/dist/db/libsql.mjs +7 -2
- package/dist/db/libsql.mjs.map +1 -1
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/db/sqlite.d.mts.map +1 -1
- package/dist/db/sqlite.mjs +8 -3
- package/dist/db/sqlite.mjs.map +1 -1
- package/dist/{db-errors-l1Qh2RPR.mjs → db-errors-B7P2pSCn.mjs} +1 -1
- package/dist/{db-errors-l1Qh2RPR.mjs.map → db-errors-B7P2pSCn.mjs.map} +1 -1
- package/dist/{default-DCVqE5ib.mjs → default-pHuz9WF6.mjs} +1 -1
- package/dist/{default-DCVqE5ib.mjs.map → default-pHuz9WF6.mjs.map} +1 -1
- package/dist/{dialect-helpers-DhTzaUxP.mjs → dialect-helpers-BKCvISIQ.mjs} +19 -2
- package/dist/dialect-helpers-BKCvISIQ.mjs.map +1 -0
- package/dist/{error-zG5T1UGA.mjs → error-DqnRMM5z.mjs} +1 -1
- package/dist/{error-zG5T1UGA.mjs.map → error-DqnRMM5z.mjs.map} +1 -1
- package/dist/{index-DIb-CzNx.d.mts → index-DjPMOfO0.d.mts} +162 -87
- package/dist/index-DjPMOfO0.d.mts.map +1 -0
- package/dist/index.d.mts +11 -11
- package/dist/index.mjs +27 -24
- package/dist/{load-CyEoextb.mjs → load-sXRuM7Us.mjs} +2 -2
- package/dist/{load-CyEoextb.mjs.map → load-sXRuM7Us.mjs.map} +1 -1
- package/dist/{loader-CndGj8kM.mjs → loader-Bx2_9-5e.mjs} +53 -8
- package/dist/loader-Bx2_9-5e.mjs.map +1 -0
- package/dist/{manifest-schema-DH9xhc6t.mjs → manifest-schema-CXAbd1vH.mjs} +33 -3
- package/dist/manifest-schema-CXAbd1vH.mjs.map +1 -0
- package/dist/media/index.d.mts +1 -1
- package/dist/media/index.mjs +1 -1
- package/dist/media/local-runtime.d.mts +7 -7
- package/dist/{mode-BnAOqItE.mjs → mode-YhqNVef_.mjs} +1 -1
- package/dist/{mode-BnAOqItE.mjs.map → mode-YhqNVef_.mjs.map} +1 -1
- package/dist/options-nPxWnrya.mjs +117 -0
- package/dist/options-nPxWnrya.mjs.map +1 -0
- package/dist/page/index.d.mts +2 -2
- package/dist/{patterns-CrCYkMBb.mjs → patterns-DsUZ4uxI.mjs} +1 -1
- package/dist/{patterns-CrCYkMBb.mjs.map → patterns-DsUZ4uxI.mjs.map} +1 -1
- package/dist/{placeholder-D29tWZ7o.d.mts → placeholder-CDPtkelt.d.mts} +1 -1
- package/dist/{placeholder-D29tWZ7o.d.mts.map → placeholder-CDPtkelt.d.mts.map} +1 -1
- package/dist/{placeholder-C-fk5hYI.mjs → placeholder-Ci0RLeCk.mjs} +1 -1
- package/dist/{placeholder-C-fk5hYI.mjs.map → placeholder-Ci0RLeCk.mjs.map} +1 -1
- package/dist/plugins/adapt-sandbox-entry.d.mts +5 -5
- package/dist/plugins/adapt-sandbox-entry.d.mts.map +1 -1
- package/dist/plugins/adapt-sandbox-entry.mjs +6 -5
- package/dist/plugins/adapt-sandbox-entry.mjs.map +1 -1
- package/dist/public-url-B1AxbbbQ.mjs +51 -0
- package/dist/public-url-B1AxbbbQ.mjs.map +1 -0
- package/dist/{query-fqEdLFms.mjs → query-Bo-msrmu.mjs} +114 -16
- package/dist/query-Bo-msrmu.mjs.map +1 -0
- package/dist/{redirect-D_pshWdf.mjs → redirect-C5H7VGIX.mjs} +11 -6
- package/dist/redirect-C5H7VGIX.mjs.map +1 -0
- package/dist/{registry-C3Mr0ODu.mjs → registry-Beb7wxFc.mjs} +39 -5
- package/dist/registry-Beb7wxFc.mjs.map +1 -0
- package/dist/{request-cache-Ci7f5pBb.mjs → request-cache-C-tIpYIw.mjs} +1 -1
- package/dist/{request-cache-Ci7f5pBb.mjs.map → request-cache-C-tIpYIw.mjs.map} +1 -1
- package/dist/runner-Clwe4Mme.d.mts +44 -0
- package/dist/runner-Clwe4Mme.d.mts.map +1 -0
- package/dist/{runner-tQ7BJ4T7.mjs → runner-DMnlIkh4.mjs} +616 -191
- package/dist/runner-DMnlIkh4.mjs.map +1 -0
- package/dist/runtime.d.mts +6 -6
- package/dist/runtime.mjs +2 -2
- package/dist/{search-BoZYFuUk.mjs → search-DkN-BqsS.mjs} +270 -152
- package/dist/search-DkN-BqsS.mjs.map +1 -0
- package/dist/secrets-CZ8rxLX3.mjs +314 -0
- package/dist/secrets-CZ8rxLX3.mjs.map +1 -0
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +13 -11
- package/dist/seo/index.d.mts +1 -1
- package/dist/storage/local.d.mts +1 -1
- package/dist/storage/local.mjs +1 -1
- package/dist/storage/s3.d.mts +1 -1
- package/dist/storage/s3.mjs +1 -1
- package/dist/taxonomies-CTtewrSQ.mjs +407 -0
- package/dist/taxonomies-CTtewrSQ.mjs.map +1 -0
- package/dist/taxonomy-DSxx2K2L.mjs +218 -0
- package/dist/taxonomy-DSxx2K2L.mjs.map +1 -0
- package/dist/{tokens-D9vnZqYS.mjs → tokens-CyRDPVW2.mjs} +1 -1
- package/dist/{tokens-D9vnZqYS.mjs.map → tokens-CyRDPVW2.mjs.map} +1 -1
- package/dist/{transaction-Cn2rjY78.mjs → transaction-D44LBXvU.mjs} +1 -1
- package/dist/{transaction-Cn2rjY78.mjs.map → transaction-D44LBXvU.mjs.map} +1 -1
- package/dist/{transport-CUnEL3Vs.d.mts → transport-DX_5rpsq.d.mts} +1 -1
- package/dist/{transport-CUnEL3Vs.d.mts.map → transport-DX_5rpsq.d.mts.map} +1 -1
- package/dist/{transport-C9ugt2Nr.mjs → transport-xpzIjCIB.mjs} +6 -5
- package/dist/{transport-C9ugt2Nr.mjs.map → transport-xpzIjCIB.mjs.map} +1 -1
- package/dist/{types-BrA0xf5I.d.mts → types-B_CXXnzh.d.mts} +1 -1
- package/dist/{types-BrA0xf5I.d.mts.map → types-B_CXXnzh.d.mts.map} +1 -1
- package/dist/{types-DIMwPFub.d.mts → types-C-aFbqmA.d.mts} +1 -1
- package/dist/{types-DIMwPFub.d.mts.map → types-C-aFbqmA.d.mts.map} +1 -1
- package/dist/types-CoO6mpV3.mjs +68 -0
- package/dist/types-CoO6mpV3.mjs.map +1 -0
- package/dist/{types-i36XcA_X.d.mts → types-D19uBYWn.d.mts} +83 -7
- package/dist/types-D19uBYWn.d.mts.map +1 -0
- package/dist/{types-BmPPSUEx.d.mts → types-Dl1fgFjn.d.mts} +24 -2
- package/dist/{types-BmPPSUEx.d.mts.map → types-Dl1fgFjn.d.mts.map} +1 -1
- package/dist/{types-CS8FIX7L.d.mts → types-Dtx1mSMX.d.mts} +9 -1
- package/dist/types-Dtx1mSMX.d.mts.map +1 -0
- package/dist/{types-Bm1dn-q3.mjs → types-Eg829jj9.mjs} +1 -1
- package/dist/{types-Bm1dn-q3.mjs.map → types-Eg829jj9.mjs.map} +1 -1
- package/dist/{types-CgqmmMJB.mjs → types-K-EkEQCI.mjs} +1 -1
- package/dist/{types-CgqmmMJB.mjs.map → types-K-EkEQCI.mjs.map} +1 -1
- package/dist/{validate-CxVsLehf.mjs → validate-CBIbxM3L.mjs} +14 -10
- package/dist/validate-CBIbxM3L.mjs.map +1 -0
- package/dist/{validate-DHxmpFJt.d.mts → validate-DHGwADqO.d.mts} +18 -5
- package/dist/validate-DHGwADqO.d.mts.map +1 -0
- package/dist/{validation-C-ZpN2GI.mjs → validation-B1NYiEos.mjs} +6 -6
- package/dist/{validation-C-ZpN2GI.mjs.map → validation-B1NYiEos.mjs.map} +1 -1
- package/dist/version-CMD42IRC.mjs +7 -0
- package/dist/{version-Bbq8TCrz.mjs.map → version-CMD42IRC.mjs.map} +1 -1
- package/dist/{zod-generator-CpwccCIv.mjs → zod-generator-BNJDQBSZ.mjs} +11 -6
- package/dist/{zod-generator-CpwccCIv.mjs.map → zod-generator-BNJDQBSZ.mjs.map} +1 -1
- package/locals.d.ts +1 -6
- package/package.json +9 -8
- package/src/api/handlers/comments.ts +6 -4
- package/src/api/handlers/content.ts +40 -1
- package/src/api/handlers/dashboard.ts +29 -36
- package/src/api/handlers/device-flow.ts +5 -0
- package/src/api/handlers/marketplace.ts +11 -4
- package/src/api/handlers/menus.ts +256 -75
- package/src/api/handlers/oauth-authorization.ts +72 -33
- package/src/api/handlers/revision.ts +23 -14
- package/src/api/handlers/taxonomies.ts +273 -100
- package/src/api/public-url.ts +48 -2
- package/src/api/schemas/comments.ts +2 -2
- package/src/api/schemas/common.ts +7 -0
- package/src/api/schemas/content.ts +17 -0
- package/src/api/schemas/menus.ts +23 -0
- package/src/api/schemas/sections.ts +3 -3
- package/src/api/schemas/taxonomies.ts +39 -0
- package/src/api/schemas/users.ts +1 -1
- package/src/api/types.ts +5 -1
- package/src/astro/integration/index.ts +17 -0
- package/src/astro/integration/routes.ts +10 -0
- package/src/astro/integration/runtime.ts +30 -0
- package/src/astro/integration/virtual-modules.ts +32 -2
- package/src/astro/integration/vite-config.ts +6 -1
- package/src/astro/middleware/auth.ts +13 -6
- package/src/astro/middleware/redirect.ts +29 -16
- package/src/astro/middleware/request-context.ts +15 -5
- package/src/astro/middleware.ts +23 -9
- package/src/astro/routes/api/auth/invite/complete.ts +6 -1
- package/src/astro/routes/api/auth/passkey/register/verify.ts +6 -1
- package/src/astro/routes/api/auth/passkey/verify.ts +6 -1
- package/src/astro/routes/api/auth/signup/complete.ts +6 -1
- package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +2 -2
- package/src/astro/routes/api/content/[collection]/[id]/discard-draft.ts +4 -2
- package/src/astro/routes/api/content/[collection]/[id]/permanent.ts +1 -1
- package/src/astro/routes/api/content/[collection]/[id]/preview-url.ts +34 -12
- package/src/astro/routes/api/content/[collection]/[id]/publish.ts +32 -2
- package/src/astro/routes/api/content/[collection]/[id]/restore.ts +4 -2
- package/src/astro/routes/api/content/[collection]/[id]/revisions.ts +3 -2
- package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +8 -4
- package/src/astro/routes/api/content/[collection]/[id].ts +12 -0
- package/src/astro/routes/api/import/wordpress/execute.ts +3 -1
- package/src/astro/routes/api/import/wordpress/prepare.ts +7 -8
- package/src/astro/routes/api/import/wordpress/rewrite-url-helpers.ts +196 -0
- package/src/astro/routes/api/import/wordpress/rewrite-urls.ts +9 -177
- package/src/astro/routes/api/import/wordpress-plugin/execute.ts +3 -1
- package/src/astro/routes/api/manifest.ts +62 -45
- package/src/astro/routes/api/media/[id]/confirm.ts +10 -1
- package/src/astro/routes/api/media/providers/[providerId]/index.ts +12 -3
- package/src/astro/routes/api/menus/[name]/items.ts +16 -6
- package/src/astro/routes/api/menus/[name]/reorder.ts +8 -3
- package/src/astro/routes/api/menus/[name]/translations.ts +82 -0
- package/src/astro/routes/api/menus/[name].ts +19 -10
- package/src/astro/routes/api/menus/index.ts +9 -6
- package/src/astro/routes/api/openapi.json.ts +27 -10
- package/src/astro/routes/api/redirects/404s/index.ts +10 -4
- package/src/astro/routes/api/redirects/404s/summary.ts +4 -2
- package/src/astro/routes/api/redirects/[id].ts +10 -4
- package/src/astro/routes/api/redirects/index.ts +7 -3
- package/src/astro/routes/api/revisions/[revisionId]/index.ts +1 -1
- package/src/astro/routes/api/schema/collections/[slug]/fields/[fieldSlug].ts +0 -2
- package/src/astro/routes/api/schema/collections/[slug]/fields/index.ts +0 -1
- package/src/astro/routes/api/schema/collections/[slug]/fields/reorder.ts +0 -1
- package/src/astro/routes/api/schema/collections/[slug]/index.ts +2 -2
- package/src/astro/routes/api/schema/collections/index.ts +1 -1
- package/src/astro/routes/api/search/index.ts +10 -2
- package/src/astro/routes/api/sections/[slug].ts +10 -4
- package/src/astro/routes/api/sections/index.ts +7 -3
- package/src/astro/routes/api/setup/admin-verify.ts +6 -1
- package/src/astro/routes/api/snapshot.ts +44 -18
- package/src/astro/routes/api/taxonomies/[name]/terms/[slug]/translations.ts +89 -0
- package/src/astro/routes/api/taxonomies/[name]/terms/[slug].ts +22 -22
- package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +11 -14
- package/src/astro/routes/api/taxonomies/index.ts +9 -7
- package/src/astro/routes/api/themes/preview.ts +11 -5
- package/src/astro/types.ts +23 -3
- package/src/auth/allowed-origins.ts +168 -0
- package/src/auth/passkey-config.ts +35 -13
- package/src/bylines/index.ts +37 -88
- package/src/cli/commands/auth.ts +28 -6
- package/src/cli/commands/bundle-utils.ts +11 -2
- package/src/cli/commands/bundle.ts +28 -8
- package/src/cli/commands/content.ts +13 -0
- package/src/cli/commands/export-seed.ts +82 -21
- package/src/cli/commands/login.ts +8 -1
- package/src/cli/commands/plugin-init.ts +216 -90
- package/src/cli/commands/publish.ts +24 -0
- package/src/cli/commands/secrets.ts +183 -0
- package/src/cli/credentials.ts +1 -1
- package/src/cli/index.ts +5 -1
- package/src/client/index.ts +4 -4
- package/src/client/transport.ts +17 -7
- package/src/components/Break.astro +2 -2
- package/src/components/EmDashHead.astro +18 -13
- package/src/components/Embed.astro +1 -1
- package/src/components/Gallery.astro +1 -1
- package/src/components/Image.astro +1 -1
- package/src/components/InlinePortableTextEditor.tsx +104 -18
- package/src/config/secrets.ts +528 -0
- package/src/database/dialect-helpers.ts +50 -0
- package/src/database/migrations/034_published_at_index.ts +1 -1
- package/src/database/migrations/035_bounded_404_log.ts +56 -39
- package/src/database/migrations/036_i18n_menus_and_taxonomies.ts +477 -0
- package/src/database/migrations/runner.ts +158 -23
- package/src/database/repositories/content.ts +47 -12
- package/src/database/repositories/redirect.ts +14 -3
- package/src/database/repositories/taxonomy.ts +212 -82
- package/src/database/types.ts +10 -2
- package/src/db/libsql.ts +1 -3
- package/src/db/sqlite.ts +2 -5
- package/src/emdash-runtime.ts +84 -159
- package/src/i18n/resolve.ts +37 -0
- package/src/index.ts +9 -0
- package/src/loader.ts +73 -3
- package/src/mcp/server.ts +180 -54
- package/src/menus/index.ts +143 -124
- package/src/menus/types.ts +15 -1
- package/src/page/site-identity.ts +58 -0
- package/src/plugins/adapt-sandbox-entry.ts +22 -10
- package/src/plugins/context.ts +13 -10
- package/src/plugins/define-plugin.ts +40 -12
- package/src/plugins/hooks.ts +23 -19
- package/src/plugins/index.ts +9 -0
- package/src/plugins/manifest-schema.ts +37 -2
- package/src/plugins/types.ts +151 -11
- package/src/preview/urls.ts +23 -3
- package/src/query.ts +148 -5
- package/src/redirects/cache.ts +38 -18
- package/src/schema/registry.ts +56 -0
- package/src/schema/zod-generator.ts +39 -7
- package/src/seed/apply.ts +142 -54
- package/src/seed/types.ts +14 -1
- package/src/seed/validate.ts +27 -13
- package/src/settings/index.ts +80 -6
- package/src/settings/types.ts +23 -1
- package/src/taxonomies/index.ts +237 -210
- package/src/taxonomies/types.ts +10 -0
- package/dist/apply-x0eMK1lX.mjs.map +0 -1
- package/dist/bylines-CRNsVG88.mjs +0 -157
- package/dist/bylines-CRNsVG88.mjs.map +0 -1
- package/dist/cache-BkKBuIvS.mjs +0 -56
- package/dist/cache-BkKBuIvS.mjs.map +0 -1
- package/dist/chunk-ClPoSABd.mjs +0 -21
- package/dist/content-BcQPYxdV.mjs.map +0 -1
- package/dist/dialect-helpers-DhTzaUxP.mjs.map +0 -1
- package/dist/index-DIb-CzNx.d.mts.map +0 -1
- package/dist/loader-CndGj8kM.mjs.map +0 -1
- package/dist/manifest-schema-DH9xhc6t.mjs.map +0 -1
- package/dist/query-fqEdLFms.mjs.map +0 -1
- package/dist/redirect-D_pshWdf.mjs.map +0 -1
- package/dist/registry-C3Mr0ODu.mjs.map +0 -1
- package/dist/runner-OURCaApa.d.mts +0 -34
- package/dist/runner-OURCaApa.d.mts.map +0 -1
- package/dist/runner-tQ7BJ4T7.mjs.map +0 -1
- package/dist/search-BoZYFuUk.mjs.map +0 -1
- package/dist/taxonomies-B4IAshV8.mjs +0 -308
- package/dist/taxonomies-B4IAshV8.mjs.map +0 -1
- package/dist/types-CS8FIX7L.d.mts.map +0 -1
- package/dist/types-i36XcA_X.d.mts.map +0 -1
- package/dist/validate-CxVsLehf.mjs.map +0 -1
- package/dist/validate-DHxmpFJt.d.mts.map +0 -1
- package/dist/version-Bbq8TCrz.mjs +0 -7
package/src/astro/middleware.ts
CHANGED
|
@@ -46,6 +46,7 @@ import type { Database, Storage } from "../index.js";
|
|
|
46
46
|
import { createPublicMediaUrlResolver } from "../media/url.js";
|
|
47
47
|
import type { SandboxRunner } from "../plugins/sandbox/types.js";
|
|
48
48
|
import type { ResolvedPlugin } from "../plugins/types.js";
|
|
49
|
+
import { invalidateUrlPatternCache } from "../query.js";
|
|
49
50
|
import { getRequestContext, runWithContext } from "../request-context.js";
|
|
50
51
|
import type { EmDashConfig } from "./integration/runtime.js";
|
|
51
52
|
import type { EmDashHandlers } from "./types.js";
|
|
@@ -271,8 +272,15 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
271
272
|
// Read the Astro session user once up-front. Both the anonymous fast path
|
|
272
273
|
// and the full doInit path need this, and the session store is network-backed
|
|
273
274
|
// (KV / Durable Object) so we want to avoid re-fetching on the hot path.
|
|
274
|
-
// Skipped entirely for
|
|
275
|
-
|
|
275
|
+
// Skipped entirely for:
|
|
276
|
+
// - prerendered requests (no session at build time)
|
|
277
|
+
// - requests without an `astro-session` cookie (no session to look up)
|
|
278
|
+
// The cookie check matters on Cloudflare Workers, where Astro's session
|
|
279
|
+
// backend is KV: calling session.get() on every anonymous public request
|
|
280
|
+
// turns normal traffic into a flood of KV read misses. See #733.
|
|
281
|
+
const hasSessionCookie = cookies.get("astro-session") !== undefined;
|
|
282
|
+
const sessionUser =
|
|
283
|
+
context.isPrerendered || !hasSessionCookie ? null : await context.session?.get("user");
|
|
276
284
|
|
|
277
285
|
if (!isEmDashRoute && !isPublicRuntimeRoute && !hasEditCookie && !hasPreviewToken) {
|
|
278
286
|
if (!sessionUser && !playgroundDb) {
|
|
@@ -394,13 +402,13 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
394
402
|
// Runtime init runs migrations, so the DB is guaranteed set up
|
|
395
403
|
setupVerified = true;
|
|
396
404
|
|
|
397
|
-
//
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
405
|
+
// The manifest is no longer pre-loaded here. It's admin-only
|
|
406
|
+
// content that public/anonymous requests never read, and
|
|
407
|
+
// loading it on every request put logged-out hot paths on
|
|
408
|
+
// the same staleness budget as admin operations. Admin
|
|
409
|
+
// routes call `emdash.getManifest()` directly.
|
|
401
410
|
|
|
402
411
|
// Attach to locals for route handlers
|
|
403
|
-
locals.emdashManifest = manifest;
|
|
404
412
|
locals.emdash = {
|
|
405
413
|
// Content handlers
|
|
406
414
|
handleContentList: runtime.handleContentList.bind(runtime),
|
|
@@ -469,8 +477,14 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
469
477
|
// Configuration (for checking database type, auth mode, etc.)
|
|
470
478
|
config,
|
|
471
479
|
|
|
472
|
-
//
|
|
473
|
-
|
|
480
|
+
// Lazy manifest accessor — admin-only consumers call this on
|
|
481
|
+
// demand. `requestCached` inside `getManifest` dedupes within
|
|
482
|
+
// a single request.
|
|
483
|
+
getManifest: runtime.getManifest.bind(runtime),
|
|
484
|
+
|
|
485
|
+
// Clear the URL pattern cache after schema mutations that
|
|
486
|
+
// affect collection URL patterns.
|
|
487
|
+
invalidateUrlPatternCache,
|
|
474
488
|
|
|
475
489
|
// Sandbox runner (for marketplace plugin install/update)
|
|
476
490
|
getSandboxRunner: runtime.getSandboxRunner.bind(runtime),
|
|
@@ -17,6 +17,7 @@ import { apiError, apiSuccess, handleError } from "#api/error.js";
|
|
|
17
17
|
import { isParseError, parseBody } from "#api/parse.js";
|
|
18
18
|
import { getPublicOrigin } from "#api/public-url.js";
|
|
19
19
|
import { inviteCompleteBody } from "#api/schemas.js";
|
|
20
|
+
import { getConfiguredAllowedOrigins, validateAllowedOrigins } from "#auth/allowed-origins.js";
|
|
20
21
|
import { createChallengeStore } from "#auth/challenge-store.js";
|
|
21
22
|
import { getPasskeyConfig } from "#auth/passkey-config.js";
|
|
22
23
|
import { OptionsRepository } from "#db/repositories/options.js";
|
|
@@ -39,7 +40,11 @@ export const POST: APIRoute = async ({ request, locals, session }) => {
|
|
|
39
40
|
const options = new OptionsRepository(emdash.db);
|
|
40
41
|
const siteName = (await options.get<string>("emdash:site_title")) ?? undefined;
|
|
41
42
|
const siteUrl = getPublicOrigin(url, emdash?.config);
|
|
42
|
-
const
|
|
43
|
+
const allowedOrigins = validateAllowedOrigins(
|
|
44
|
+
siteUrl,
|
|
45
|
+
getConfiguredAllowedOrigins(emdash?.config),
|
|
46
|
+
);
|
|
47
|
+
const passkeyConfig = getPasskeyConfig(url, siteName, siteUrl, allowedOrigins);
|
|
43
48
|
|
|
44
49
|
// Verify the passkey registration response
|
|
45
50
|
const challengeStore = createChallengeStore(emdash.db);
|
|
@@ -15,6 +15,7 @@ import { apiError, apiSuccess } from "#api/error.js";
|
|
|
15
15
|
import { isParseError, parseBody } from "#api/parse.js";
|
|
16
16
|
import { getPublicOrigin } from "#api/public-url.js";
|
|
17
17
|
import { passkeyRegisterVerifyBody } from "#api/schemas.js";
|
|
18
|
+
import { getConfiguredAllowedOrigins, validateAllowedOrigins } from "#auth/allowed-origins.js";
|
|
18
19
|
import { createChallengeStore } from "#auth/challenge-store.js";
|
|
19
20
|
import { getPasskeyConfig } from "#auth/passkey-config.js";
|
|
20
21
|
import { OptionsRepository } from "#db/repositories/options.js";
|
|
@@ -60,7 +61,11 @@ export const POST: APIRoute = async ({ request, locals }) => {
|
|
|
60
61
|
const optionsRepo = new OptionsRepository(emdash.db);
|
|
61
62
|
const siteName = (await optionsRepo.get<string>("emdash:site_title")) ?? undefined;
|
|
62
63
|
const siteUrl = getPublicOrigin(url, emdash?.config);
|
|
63
|
-
const
|
|
64
|
+
const allowedOrigins = validateAllowedOrigins(
|
|
65
|
+
siteUrl,
|
|
66
|
+
getConfiguredAllowedOrigins(emdash?.config),
|
|
67
|
+
);
|
|
68
|
+
const passkeyConfig = getPasskeyConfig(url, siteName, siteUrl, allowedOrigins);
|
|
64
69
|
|
|
65
70
|
// Verify the registration response
|
|
66
71
|
const challengeStore = createChallengeStore(emdash.db);
|
|
@@ -15,6 +15,7 @@ import { apiError, apiSuccess, handleError } from "#api/error.js";
|
|
|
15
15
|
import { isParseError, parseBody } from "#api/parse.js";
|
|
16
16
|
import { getPublicOrigin } from "#api/public-url.js";
|
|
17
17
|
import { passkeyVerifyBody } from "#api/schemas.js";
|
|
18
|
+
import { getConfiguredAllowedOrigins, validateAllowedOrigins } from "#auth/allowed-origins.js";
|
|
18
19
|
import { createChallengeStore } from "#auth/challenge-store.js";
|
|
19
20
|
import { getPasskeyConfig } from "#auth/passkey-config.js";
|
|
20
21
|
import { OptionsRepository } from "#db/repositories/options.js";
|
|
@@ -35,7 +36,11 @@ export const POST: APIRoute = async ({ request, locals, session }) => {
|
|
|
35
36
|
const options = new OptionsRepository(emdash.db);
|
|
36
37
|
const siteName = (await options.get<string>("emdash:site_title")) ?? undefined;
|
|
37
38
|
const siteUrl = getPublicOrigin(url, emdash?.config);
|
|
38
|
-
const
|
|
39
|
+
const allowedOrigins = validateAllowedOrigins(
|
|
40
|
+
siteUrl,
|
|
41
|
+
getConfiguredAllowedOrigins(emdash?.config),
|
|
42
|
+
);
|
|
43
|
+
const passkeyConfig = getPasskeyConfig(url, siteName, siteUrl, allowedOrigins);
|
|
39
44
|
|
|
40
45
|
// Authenticate with passkey
|
|
41
46
|
const adapter = createKyselyAdapter(emdash.db);
|
|
@@ -17,6 +17,7 @@ import { apiError, apiSuccess, handleError } from "#api/error.js";
|
|
|
17
17
|
import { isParseError, parseBody } from "#api/parse.js";
|
|
18
18
|
import { getPublicOrigin } from "#api/public-url.js";
|
|
19
19
|
import { signupCompleteBody } from "#api/schemas.js";
|
|
20
|
+
import { getConfiguredAllowedOrigins, validateAllowedOrigins } from "#auth/allowed-origins.js";
|
|
20
21
|
import { createChallengeStore } from "#auth/challenge-store.js";
|
|
21
22
|
import { getPasskeyConfig } from "#auth/passkey-config.js";
|
|
22
23
|
import { OptionsRepository } from "#db/repositories/options.js";
|
|
@@ -39,7 +40,11 @@ export const POST: APIRoute = async ({ request, locals, session }) => {
|
|
|
39
40
|
const options = new OptionsRepository(emdash.db);
|
|
40
41
|
const siteName = (await options.get<string>("emdash:site_title")) ?? undefined;
|
|
41
42
|
const siteUrl = getPublicOrigin(url, emdash?.config);
|
|
42
|
-
const
|
|
43
|
+
const allowedOrigins = validateAllowedOrigins(
|
|
44
|
+
siteUrl,
|
|
45
|
+
getConfiguredAllowedOrigins(emdash?.config),
|
|
46
|
+
);
|
|
47
|
+
const passkeyConfig = getPasskeyConfig(url, siteName, siteUrl, allowedOrigins);
|
|
43
48
|
|
|
44
49
|
// Verify the passkey registration response
|
|
45
50
|
const challengeStore = createChallengeStore(emdash.db);
|
|
@@ -14,6 +14,7 @@ import { createCommentBody } from "#api/schemas.js";
|
|
|
14
14
|
import { getSiteBaseUrl } from "#api/site-url.js";
|
|
15
15
|
import { sendCommentNotification } from "#comments/notifications.js";
|
|
16
16
|
import { createComment, type CommentHookRunner } from "#comments/service.js";
|
|
17
|
+
import { resolveSecretsCached } from "#config/secrets.js";
|
|
17
18
|
import { CommentRepository } from "#db/repositories/comment.js";
|
|
18
19
|
import { validateIdentifier } from "#db/validate.js";
|
|
19
20
|
import { extractRequestMeta } from "#plugins/request-meta.js";
|
|
@@ -140,8 +141,7 @@ export const POST: APIRoute = async ({ params, request, locals }) => {
|
|
|
140
141
|
|
|
141
142
|
// Anti-spam: Rate limiting
|
|
142
143
|
const meta = extractRequestMeta(request, emdash.config);
|
|
143
|
-
const ipSalt =
|
|
144
|
-
import.meta.env.EMDASH_AUTH_SECRET || import.meta.env.AUTH_SECRET || "emdash-ip-salt";
|
|
144
|
+
const { ipSalt } = await resolveSecretsCached(emdash.db);
|
|
145
145
|
let ipHash: string;
|
|
146
146
|
if (meta.ip) {
|
|
147
147
|
ipHash = await hashIp(meta.ip, ipSalt);
|
|
@@ -44,11 +44,13 @@ export const POST: APIRoute = async ({ params, locals, cache }) => {
|
|
|
44
44
|
const denied = requireOwnerPerm(user, authorId, "content:edit_own", "content:edit_any");
|
|
45
45
|
if (denied) return denied;
|
|
46
46
|
|
|
47
|
-
const
|
|
47
|
+
const resolvedId = typeof existingItem?.id === "string" ? existingItem.id : id;
|
|
48
|
+
|
|
49
|
+
const result = await emdash.handleContentDiscardDraft(collection, resolvedId);
|
|
48
50
|
|
|
49
51
|
if (!result.success) return unwrapResult(result);
|
|
50
52
|
|
|
51
|
-
if (cache.enabled) await cache.invalidate({ tags: [collection,
|
|
53
|
+
if (cache.enabled) await cache.invalidate({ tags: [collection, resolvedId] });
|
|
52
54
|
|
|
53
55
|
return unwrapResult(result);
|
|
54
56
|
};
|
|
@@ -16,7 +16,7 @@ export const DELETE: APIRoute = async ({ params, locals, cache }) => {
|
|
|
16
16
|
const collection = params.collection!;
|
|
17
17
|
const id = params.id!;
|
|
18
18
|
|
|
19
|
-
const denied = requirePerm(user, "
|
|
19
|
+
const denied = requirePerm(user, "content:delete_permanent");
|
|
20
20
|
if (denied) return denied;
|
|
21
21
|
|
|
22
22
|
if (!emdash?.handleContentPermanentDelete) {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Request body:
|
|
7
7
|
* {
|
|
8
8
|
* expiresIn?: string | number; // Default: "1h"
|
|
9
|
-
* pathPattern?: string; // Default: "/{collection}/{id}"
|
|
9
|
+
* pathPattern?: string; // Default: "/{collection}/{id}" (or EMDASH_PREVIEW_PATH_PATTERN)
|
|
10
10
|
* }
|
|
11
11
|
*
|
|
12
12
|
* Response:
|
|
@@ -22,8 +22,11 @@ import { requirePerm } from "#api/authorize.js";
|
|
|
22
22
|
import { apiError, apiSuccess, handleError, unwrapResult } from "#api/error.js";
|
|
23
23
|
import { parseOptionalBody, isParseError } from "#api/parse.js";
|
|
24
24
|
import { contentPreviewUrlBody } from "#api/schemas.js";
|
|
25
|
+
import { resolveSecretsCached } from "#config/secrets.js";
|
|
25
26
|
import { getPreviewUrl } from "#preview/index.js";
|
|
26
27
|
|
|
28
|
+
import { getI18nConfig } from "../../../../../../i18n/config.js";
|
|
29
|
+
|
|
27
30
|
export const prerender = false;
|
|
28
31
|
|
|
29
32
|
const DURATION_PATTERN = /^(\d+)([smhdw])$/;
|
|
@@ -35,21 +38,23 @@ export const POST: APIRoute = async ({ params, request, locals }) => {
|
|
|
35
38
|
const collection = params.collection!;
|
|
36
39
|
const id = params.id!;
|
|
37
40
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (!previewSecret) {
|
|
42
|
-
return apiError(
|
|
43
|
-
"NOT_CONFIGURED",
|
|
44
|
-
"Preview not configured. Set EMDASH_PREVIEW_SECRET environment variable.",
|
|
45
|
-
500,
|
|
46
|
-
);
|
|
41
|
+
if (!emdash?.db) {
|
|
42
|
+
return apiError("NOT_CONFIGURED", "EmDash is not initialized", 500);
|
|
47
43
|
}
|
|
48
44
|
|
|
49
|
-
//
|
|
45
|
+
// Resolve the preview secret. Env override wins; otherwise a stable
|
|
46
|
+
// site-specific value is read from (or generated into) the options table.
|
|
47
|
+
// The resolver always returns a usable secret, so this path can no
|
|
48
|
+
// longer be silently disabled by a missing env var.
|
|
49
|
+
const { previewSecret } = await resolveSecretsCached(emdash.db);
|
|
50
|
+
|
|
51
|
+
// Verify the content exists. The fetched item also yields the entry's
|
|
52
|
+
// locale, used below to resolve the `{locale}` placeholder.
|
|
53
|
+
let entryLocale: string | null = null;
|
|
50
54
|
if (emdash?.handleContentGet) {
|
|
51
55
|
const result = await emdash.handleContentGet(collection, id);
|
|
52
56
|
if (!result.success) return unwrapResult(result);
|
|
57
|
+
entryLocale = result.data?.item?.locale ?? null;
|
|
53
58
|
}
|
|
54
59
|
|
|
55
60
|
// Parse request body
|
|
@@ -57,7 +62,23 @@ export const POST: APIRoute = async ({ params, request, locals }) => {
|
|
|
57
62
|
if (isParseError(body)) return body;
|
|
58
63
|
|
|
59
64
|
const expiresIn = body.expiresIn || "1h";
|
|
60
|
-
|
|
65
|
+
// Allow a project-wide default `pathPattern` so the admin's "View on site"
|
|
66
|
+
// link can match the site's actual route shape without each call having
|
|
67
|
+
// to override the default `/{collection}/{id}`.
|
|
68
|
+
const defaultPathPattern = import.meta.env.EMDASH_PREVIEW_PATH_PATTERN || "/{collection}/{id}";
|
|
69
|
+
const pathPattern = body.pathPattern || defaultPathPattern;
|
|
70
|
+
|
|
71
|
+
// Resolve the locale segment substituted for `{locale}`: empty when the
|
|
72
|
+
// entry is in the default locale and `prefixDefaultLocale` is `false`,
|
|
73
|
+
// the entry's own locale otherwise.
|
|
74
|
+
const i18n = getI18nConfig();
|
|
75
|
+
let localeSegment = "";
|
|
76
|
+
if (entryLocale && i18n) {
|
|
77
|
+
const isDefault = entryLocale === i18n.defaultLocale;
|
|
78
|
+
localeSegment = isDefault && !i18n.prefixDefaultLocale ? "" : entryLocale;
|
|
79
|
+
} else if (entryLocale) {
|
|
80
|
+
localeSegment = entryLocale;
|
|
81
|
+
}
|
|
61
82
|
|
|
62
83
|
// Calculate expiry timestamp
|
|
63
84
|
const expiresInSeconds = typeof expiresIn === "number" ? expiresIn : parseExpiresIn(expiresIn);
|
|
@@ -70,6 +91,7 @@ export const POST: APIRoute = async ({ params, request, locals }) => {
|
|
|
70
91
|
secret: previewSecret,
|
|
71
92
|
expiresIn,
|
|
72
93
|
pathPattern,
|
|
94
|
+
locale: localeSegment,
|
|
73
95
|
});
|
|
74
96
|
|
|
75
97
|
return apiSuccess({ url, expiresAt });
|
|
@@ -2,16 +2,25 @@
|
|
|
2
2
|
* Publish content - promotes draft to live
|
|
3
3
|
*
|
|
4
4
|
* POST /_emdash/api/content/{collection}/{id}/publish
|
|
5
|
+
*
|
|
6
|
+
* Optional JSON body: { publishedAt?: string }
|
|
7
|
+
* publishedAt — ISO 8601 datetime to backdate the publish (e.g. when
|
|
8
|
+
* migrating content). Writing publishedAt requires content:publish_any.
|
|
9
|
+
* Without it, the existing published_at is preserved on re-publish and
|
|
10
|
+
* falls back to the current time on first publish.
|
|
5
11
|
*/
|
|
6
12
|
|
|
13
|
+
import { hasPermission } from "@emdash-cms/auth";
|
|
7
14
|
import type { APIRoute } from "astro";
|
|
8
15
|
|
|
9
16
|
import { requireOwnerPerm } from "#api/authorize.js";
|
|
10
17
|
import { apiError, mapErrorStatus, unwrapResult } from "#api/error.js";
|
|
18
|
+
import { isParseError, parseOptionalBody } from "#api/parse.js";
|
|
19
|
+
import { contentPublishBody } from "#api/schemas.js";
|
|
11
20
|
|
|
12
21
|
export const prerender = false;
|
|
13
22
|
|
|
14
|
-
export const POST: APIRoute = async ({ params, locals, cache }) => {
|
|
23
|
+
export const POST: APIRoute = async ({ params, request, locals, cache }) => {
|
|
15
24
|
const { emdash, user } = locals;
|
|
16
25
|
const collection = params.collection!;
|
|
17
26
|
const id = params.id!;
|
|
@@ -20,6 +29,11 @@ export const POST: APIRoute = async ({ params, locals, cache }) => {
|
|
|
20
29
|
return apiError("NOT_CONFIGURED", "EmDash is not initialized", 500);
|
|
21
30
|
}
|
|
22
31
|
|
|
32
|
+
// Body is optional — empty body means use the legacy behavior (preserve
|
|
33
|
+
// or default published_at). Pass `publishedAt` to backdate.
|
|
34
|
+
const body = await parseOptionalBody(request, contentPublishBody, {});
|
|
35
|
+
if (isParseError(body)) return body;
|
|
36
|
+
|
|
23
37
|
// Fetch item to check ownership
|
|
24
38
|
const existing = await emdash.handleContentGet(collection, id);
|
|
25
39
|
if (!existing.success) {
|
|
@@ -44,9 +58,25 @@ export const POST: APIRoute = async ({ params, locals, cache }) => {
|
|
|
44
58
|
const denied = requireOwnerPerm(user, authorId, "content:publish_own", "content:publish_any");
|
|
45
59
|
if (denied) return denied;
|
|
46
60
|
|
|
61
|
+
// Schema narrows `publishedAt` to `string | undefined`; null is rejected
|
|
62
|
+
// at the schema layer (publish has no semantic meaning for "clear").
|
|
63
|
+
const publishedAt = body?.publishedAt;
|
|
64
|
+
|
|
65
|
+
// Backdating overwrites historical record — gate behind publish_any
|
|
66
|
+
// regardless of ownership.
|
|
67
|
+
if (publishedAt !== undefined && !hasPermission(user, "content:publish_any")) {
|
|
68
|
+
return apiError(
|
|
69
|
+
"FORBIDDEN",
|
|
70
|
+
"Setting publishedAt requires content:publish_any permission",
|
|
71
|
+
403,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
47
75
|
const resolvedId = typeof existingItem?.id === "string" ? existingItem.id : id;
|
|
48
76
|
|
|
49
|
-
const result = await emdash.handleContentPublish(collection, resolvedId
|
|
77
|
+
const result = await emdash.handleContentPublish(collection, resolvedId, {
|
|
78
|
+
publishedAt,
|
|
79
|
+
});
|
|
50
80
|
|
|
51
81
|
if (!result.success) return unwrapResult(result);
|
|
52
82
|
|
|
@@ -44,11 +44,13 @@ export const POST: APIRoute = async ({ params, locals, cache }) => {
|
|
|
44
44
|
const denied = requireOwnerPerm(user, authorId, "content:edit_own", "content:edit_any");
|
|
45
45
|
if (denied) return denied;
|
|
46
46
|
|
|
47
|
-
const
|
|
47
|
+
const resolvedId = typeof existingItem?.id === "string" ? existingItem.id : id;
|
|
48
|
+
|
|
49
|
+
const result = await emdash.handleContentRestore(collection, resolvedId);
|
|
48
50
|
|
|
49
51
|
if (!result.success) return unwrapResult(result);
|
|
50
52
|
|
|
51
|
-
if (cache.enabled) await cache.invalidate({ tags: [collection,
|
|
53
|
+
if (cache.enabled) await cache.invalidate({ tags: [collection, resolvedId] });
|
|
52
54
|
|
|
53
55
|
return unwrapResult(result);
|
|
54
56
|
};
|
|
@@ -22,9 +22,10 @@ export const GET: APIRoute = async ({ params, url, locals }) => {
|
|
|
22
22
|
return apiError("NOT_CONFIGURED", "EmDash is not initialized", 500);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
const
|
|
25
|
+
const limitParam = url.searchParams.get("limit");
|
|
26
|
+
const parsedLimit = limitParam ? parseInt(limitParam, 10) : undefined;
|
|
26
27
|
const result = await emdash.handleRevisionList(collection, id, {
|
|
27
|
-
limit:
|
|
28
|
+
limit: parsedLimit ? Math.max(1, Math.min(parsedLimit, 100)) : undefined,
|
|
28
29
|
});
|
|
29
30
|
|
|
30
31
|
return unwrapResult(result);
|
|
@@ -98,6 +98,10 @@ export const POST: APIRoute = async ({ params, request, locals }) => {
|
|
|
98
98
|
const editDenied = requireOwnerPerm(user, authorId, "content:edit_own", "content:edit_any");
|
|
99
99
|
if (editDenied) return editDenied;
|
|
100
100
|
|
|
101
|
+
// Resolve the canonical content ID from the handler result.
|
|
102
|
+
// The URL `id` param may be a slug; we must use the real ID for term storage.
|
|
103
|
+
const canonicalId = typeof existingItem?.id === "string" ? existingItem.id : id;
|
|
104
|
+
|
|
101
105
|
try {
|
|
102
106
|
const body = await parseBody(request, contentTermsBody);
|
|
103
107
|
if (isParseError(body)) return body;
|
|
@@ -120,15 +124,15 @@ export const POST: APIRoute = async ({ params, request, locals }) => {
|
|
|
120
124
|
}
|
|
121
125
|
}
|
|
122
126
|
|
|
123
|
-
// Set the terms (replaces existing)
|
|
124
|
-
await repo.setTermsForEntry(collection,
|
|
127
|
+
// Set the terms (replaces existing) using the canonical ID
|
|
128
|
+
await repo.setTermsForEntry(collection, canonicalId, taxonomy, termIds);
|
|
125
129
|
|
|
126
130
|
// Term assignments changed — invalidate the hasAnyTermAssignments cache
|
|
127
131
|
// so hydration on subsequent reads issues a fresh query.
|
|
128
132
|
invalidateTermCache();
|
|
129
133
|
|
|
130
|
-
// Get the updated terms
|
|
131
|
-
const terms = await repo.getTermsForEntry(collection,
|
|
134
|
+
// Get the updated terms using the canonical ID
|
|
135
|
+
const terms = await repo.getTermsForEntry(collection, canonicalId, taxonomy);
|
|
132
136
|
|
|
133
137
|
return apiSuccess({
|
|
134
138
|
terms: terms.map((t) => ({
|
|
@@ -47,6 +47,18 @@ export const GET: APIRoute = async ({ params, url, locals }) => {
|
|
|
47
47
|
if (status !== "published") {
|
|
48
48
|
return apiError("NOT_FOUND", `Content item not found: ${id}`, 404);
|
|
49
49
|
}
|
|
50
|
+
|
|
51
|
+
// Strip draft hydration data from response for users without read_drafts.
|
|
52
|
+
// handleContentGet overlays draft revision data onto item.data and exposes
|
|
53
|
+
// the published values in item.liveData. Without this, subscribers see
|
|
54
|
+
// unpublished edits in the data field.
|
|
55
|
+
if (item) {
|
|
56
|
+
if (item.liveData && typeof item.liveData === "object") {
|
|
57
|
+
item.data = item.liveData;
|
|
58
|
+
}
|
|
59
|
+
delete item.liveData;
|
|
60
|
+
delete item.draftRevisionId;
|
|
61
|
+
}
|
|
50
62
|
}
|
|
51
63
|
|
|
52
64
|
return unwrapResult(result);
|
|
@@ -60,7 +60,7 @@ export interface ImportResult {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
export const POST: APIRoute = async ({ request, locals }) => {
|
|
63
|
-
const { emdash,
|
|
63
|
+
const { emdash, user } = locals;
|
|
64
64
|
|
|
65
65
|
const denied = requirePerm(user, "import:execute");
|
|
66
66
|
if (denied) return denied;
|
|
@@ -70,6 +70,8 @@ export const POST: APIRoute = async ({ request, locals }) => {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
try {
|
|
73
|
+
const emdashManifest = await emdash.getManifest();
|
|
74
|
+
|
|
73
75
|
const formData = await request.formData();
|
|
74
76
|
const fileEntry = formData.get("file");
|
|
75
77
|
const file = fileEntry instanceof File ? fileEntry : null;
|
|
@@ -58,14 +58,13 @@ export const POST: APIRoute = async ({ request, locals }) => {
|
|
|
58
58
|
// eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- Zod schema output narrowed to PrepareRequest
|
|
59
59
|
const result = await prepareImport(emdash.db, body as PrepareRequest);
|
|
60
60
|
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
// the
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
emdash.invalidateManifest();
|
|
61
|
+
// Invalidate the URL pattern cache when prepare adds new collections so
|
|
62
|
+
// public routing picks up their patterns immediately. The manifest
|
|
63
|
+
// itself is built fresh per admin request, so cross-request
|
|
64
|
+
// staleness (the original failure mode in #747) is no longer
|
|
65
|
+
// possible — the execute step always reads live schema.
|
|
66
|
+
if (result.collectionsCreated.length > 0) {
|
|
67
|
+
emdash.invalidateUrlPatternCache();
|
|
69
68
|
}
|
|
70
69
|
|
|
71
70
|
return apiSuccess(result, result.success ? 200 : 400);
|