emdash 0.31.1 → 0.33.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-C0ONzkUP.d.mts → adapters-BMyNSSZi.d.mts} +9 -1
- package/dist/adapters-BMyNSSZi.d.mts.map +1 -0
- package/dist/after-CBU2vkoO.mjs +96 -0
- package/dist/after-CBU2vkoO.mjs.map +1 -0
- package/dist/{allowed-origins-CCEi9bPI.mjs → allowed-origins-CG1pACtS.mjs} +2 -2
- package/dist/{allowed-origins-CCEi9bPI.mjs.map → allowed-origins-CG1pACtS.mjs.map} +1 -1
- package/dist/api/route-utils.d.mts +3 -4
- package/dist/api/route-utils.d.mts.map +1 -1
- package/dist/api/route-utils.mjs +23 -21
- package/dist/api/route-utils.mjs.map +1 -1
- package/dist/api/schemas/index.d.mts +2 -2
- package/dist/api/schemas/index.mjs +5 -5
- package/dist/{api-BkqFzIu4.mjs → api-D2hunekQ.mjs} +254 -48
- package/dist/api-D2hunekQ.mjs.map +1 -0
- package/dist/{api-tokens-uPt8UDpx.mjs → api-tokens-CSND0i1z.mjs} +2 -2
- package/dist/{api-tokens-uPt8UDpx.mjs.map → api-tokens-CSND0i1z.mjs.map} +1 -1
- package/dist/{apply-DuXsqll7.mjs → apply-kwNEJW2l.mjs} +36 -23
- package/dist/apply-kwNEJW2l.mjs.map +1 -0
- package/dist/astro/index.d.mts +12 -12
- package/dist/astro/index.mjs +17 -3
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +10 -10
- package/dist/astro/middleware/auth.mjs +8 -8
- package/dist/astro/middleware/redirect.d.mts.map +1 -1
- package/dist/astro/middleware/redirect.mjs +8 -8
- package/dist/astro/middleware/redirect.mjs.map +1 -1
- package/dist/astro/middleware/request-context.mjs +4 -4
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.d.mts +10 -10
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +370 -171
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +7 -7
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs +7 -7
- package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +4 -4
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +5 -5
- package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +5 -5
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +10 -10
- package/dist/astro/routes/api/admin/byline-fields/index.mjs +10 -10
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +10 -10
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +19 -18
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +19 -18
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/admin/bylines/index.mjs +19 -19
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +16 -16
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs.map +1 -1
- package/dist/astro/routes/api/admin/comments/_id_.mjs +7 -7
- package/dist/astro/routes/api/admin/comments/bulk.mjs +12 -12
- package/dist/astro/routes/api/admin/comments/counts.mjs +7 -7
- package/dist/astro/routes/api/admin/comments/index.mjs +12 -12
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +5 -5
- package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +4 -4
- package/dist/astro/routes/api/admin/media-usage/repair.mjs +13 -11
- package/dist/astro/routes/api/admin/media-usage/repair.mjs.map +1 -1
- package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +4 -4
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs +4 -4
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +47 -44
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +47 -44
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +46 -43
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/mcp.mjs +5 -5
- package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs +46 -43
- package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +46 -43
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +46 -43
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/index.mjs +46 -43
- package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +3 -3
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +46 -43
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +46 -43
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +46 -43
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +46 -43
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +47 -44
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +46 -43
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +47 -44
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/updates.mjs +46 -43
- package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +46 -43
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +3 -3
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +46 -43
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/users/_id_/disable.mjs +3 -3
- package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +8 -8
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +5 -5
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs.map +1 -1
- package/dist/astro/routes/api/admin/users/index.mjs +7 -7
- package/dist/astro/routes/api/auth/dev-bypass.mjs +7 -6
- package/dist/astro/routes/api/auth/dev-bypass.mjs.map +1 -1
- package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
- package/dist/astro/routes/api/auth/invite/complete.mjs +12 -12
- package/dist/astro/routes/api/auth/invite/index.mjs +10 -10
- package/dist/astro/routes/api/auth/invite/index.mjs.map +1 -1
- package/dist/astro/routes/api/auth/invite/register-options.mjs +11 -11
- package/dist/astro/routes/api/auth/logout.mjs +3 -3
- package/dist/astro/routes/api/auth/magic-link/send.mjs +12 -12
- package/dist/astro/routes/api/auth/magic-link/send.mjs.map +1 -1
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +3 -3
- package/dist/astro/routes/api/auth/me.mjs +8 -8
- package/dist/astro/routes/api/auth/mode.mjs +3 -3
- package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +4 -4
- package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +7 -7
- package/dist/astro/routes/api/auth/passkey/index.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/options.mjs +13 -13
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +11 -11
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +12 -12
- package/dist/astro/routes/api/auth/passkey/verify.mjs +12 -12
- package/dist/astro/routes/api/auth/signup/complete.mjs +12 -12
- package/dist/astro/routes/api/auth/signup/request.mjs +12 -12
- package/dist/astro/routes/api/auth/signup/request.mjs.map +1 -1
- package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +18 -18
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs.map +1 -1
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +15 -15
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +14 -14
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +9 -9
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs +19 -17
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs +19 -17
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +9 -9
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +17 -16
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_.d.mts.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +10 -10
- package/dist/astro/routes/api/content/_collection_/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/authors.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/index.mjs +9 -9
- package/dist/astro/routes/api/content/_collection_/trash.mjs +8 -8
- package/dist/astro/routes/api/dashboard.mjs +9 -9
- package/dist/astro/routes/api/dev/emails.mjs +3 -3
- package/dist/astro/routes/api/import/probe.d.mts +3 -3
- package/dist/astro/routes/api/import/probe.mjs +19 -19
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +4 -4
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +11 -11
- package/dist/astro/routes/api/import/wordpress/execute.mjs +16 -15
- package/dist/astro/routes/api/import/wordpress/execute.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress/media.d.mts +10 -10
- package/dist/astro/routes/api/import/wordpress/media.mjs +10 -10
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +11 -11
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +11 -11
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +13 -11
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +19 -19
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +11 -11
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +28 -27
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs.map +1 -1
- package/dist/astro/routes/api/manifest.mjs +5 -5
- package/dist/astro/routes/api/mcp.mjs +42 -42
- package/dist/astro/routes/api/media/_id_/confirm.d.mts.map +1 -1
- package/dist/astro/routes/api/media/_id_/confirm.mjs +106 -37
- package/dist/astro/routes/api/media/_id_/confirm.mjs.map +1 -1
- package/dist/astro/routes/api/media/_id_/upload.d.mts +8 -0
- package/dist/astro/routes/api/media/_id_/upload.d.mts.map +1 -0
- package/dist/astro/routes/api/media/_id_/upload.mjs +168 -0
- package/dist/astro/routes/api/media/_id_/upload.mjs.map +1 -0
- package/dist/astro/routes/api/media/_id_/usage.mjs +15 -13
- package/dist/astro/routes/api/media/_id_/usage.mjs.map +1 -1
- package/dist/astro/routes/api/media/_id_.d.mts.map +1 -1
- package/dist/astro/routes/api/media/_id_.mjs +22 -15
- package/dist/astro/routes/api/media/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/media/file/_...key_.mjs +2 -2
- package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +3 -3
- package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +3 -3
- package/dist/astro/routes/api/media/providers/index.mjs +3 -3
- package/dist/astro/routes/api/media/upload-url.d.mts.map +1 -1
- package/dist/astro/routes/api/media/upload-url.mjs +35 -24
- package/dist/astro/routes/api/media/upload-url.mjs.map +1 -1
- package/dist/astro/routes/api/media.mjs +14 -13
- package/dist/astro/routes/api/media.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +12 -11
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_/items.mjs +12 -11
- package/dist/astro/routes/api/menus/_name_/items.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +12 -11
- package/dist/astro/routes/api/menus/_name_/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_/translations.mjs +12 -11
- package/dist/astro/routes/api/menus/_name_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_.mjs +12 -11
- package/dist/astro/routes/api/menus/_name_.mjs.map +1 -1
- package/dist/astro/routes/api/menus/index.mjs +12 -11
- package/dist/astro/routes/api/menus/index.mjs.map +1 -1
- package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
- package/dist/astro/routes/api/oauth/device/authorize.mjs +6 -6
- package/dist/astro/routes/api/oauth/device/code.mjs +9 -9
- package/dist/astro/routes/api/oauth/device/token.mjs +8 -8
- package/dist/astro/routes/api/oauth/register.mjs +3 -3
- package/dist/astro/routes/api/oauth/token/refresh.mjs +6 -6
- package/dist/astro/routes/api/oauth/token/revoke.mjs +6 -6
- package/dist/astro/routes/api/oauth/token.mjs +6 -6
- package/dist/astro/routes/api/openapi.json.mjs +62 -9
- package/dist/astro/routes/api/openapi.json.mjs.map +1 -1
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +5 -5
- package/dist/astro/routes/api/redirects/404s/index.mjs +12 -12
- package/dist/astro/routes/api/redirects/404s/summary.mjs +12 -12
- package/dist/astro/routes/api/redirects/_id_.mjs +14 -13
- package/dist/astro/routes/api/redirects/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/redirects/index.mjs +14 -13
- package/dist/astro/routes/api/redirects/index.mjs.map +1 -1
- package/dist/astro/routes/api/relations/_id_/index.mjs +19 -17
- package/dist/astro/routes/api/relations/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/relations/_id_/translations.mjs +14 -12
- package/dist/astro/routes/api/relations/_id_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/relations/index.mjs +19 -17
- package/dist/astro/routes/api/relations/index.mjs.map +1 -1
- package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +3 -3
- package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +4 -4
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +46 -43
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +46 -43
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +46 -43
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +46 -43
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/index.mjs +46 -43
- package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/reorder.d.mts +8 -0
- package/dist/astro/routes/api/schema/collections/reorder.d.mts.map +1 -0
- package/dist/astro/routes/api/schema/collections/reorder.mjs +66 -0
- package/dist/astro/routes/api/schema/collections/reorder.mjs.map +1 -0
- package/dist/astro/routes/api/schema/index.mjs +12 -8
- package/dist/astro/routes/api/schema/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +46 -43
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/index.mjs +46 -43
- package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
- package/dist/astro/routes/api/search/enable.d.mts +1 -0
- package/dist/astro/routes/api/search/enable.d.mts.map +1 -1
- package/dist/astro/routes/api/search/enable.mjs +18 -13
- package/dist/astro/routes/api/search/enable.mjs.map +1 -1
- package/dist/astro/routes/api/search/index.mjs +12 -11
- package/dist/astro/routes/api/search/index.mjs.map +1 -1
- package/dist/astro/routes/api/search/rebuild.mjs +14 -13
- package/dist/astro/routes/api/search/rebuild.mjs.map +1 -1
- package/dist/astro/routes/api/search/stats.mjs +8 -7
- package/dist/astro/routes/api/search/stats.mjs.map +1 -1
- package/dist/astro/routes/api/search/suggest.mjs +12 -11
- package/dist/astro/routes/api/search/suggest.mjs.map +1 -1
- package/dist/astro/routes/api/sections/_slug_.mjs +11 -11
- package/dist/astro/routes/api/sections/index.mjs +11 -11
- package/dist/astro/routes/api/settings/backups/archives/_name_.mjs +7 -7
- package/dist/astro/routes/api/settings/backups/archives/index.mjs +6 -6
- package/dist/astro/routes/api/settings/backups/export.mjs +7 -7
- package/dist/astro/routes/api/settings/backups/index.mjs +8 -8
- package/dist/astro/routes/api/settings/email.mjs +5 -5
- package/dist/astro/routes/api/settings.mjs +16 -16
- package/dist/astro/routes/api/setup/admin-verify.mjs +13 -13
- package/dist/astro/routes/api/setup/admin.mjs +12 -12
- package/dist/astro/routes/api/setup/dev-bypass.mjs +31 -29
- package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -1
- package/dist/astro/routes/api/setup/dev-reset.mjs +3 -3
- package/dist/astro/routes/api/setup/index.mjs +33 -31
- package/dist/astro/routes/api/setup/index.mjs.map +1 -1
- package/dist/astro/routes/api/setup/status.mjs +4 -4
- package/dist/astro/routes/api/snapshot.mjs +9 -9
- package/dist/astro/routes/api/taxonomies/_name_/reorder.d.mts +8 -0
- package/dist/astro/routes/api/taxonomies/_name_/reorder.d.mts.map +1 -0
- package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs +43 -0
- package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs.map +1 -0
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +17 -16
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +17 -16
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.d.mts.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +22 -18
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/index.mjs +17 -16
- package/dist/astro/routes/api/taxonomies/index.mjs.map +1 -1
- package/dist/astro/routes/api/themes/preview.mjs +7 -7
- package/dist/astro/routes/api/typegen.mjs +5 -5
- package/dist/astro/routes/api/well-known/auth.mjs +2 -2
- package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
- package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +8 -8
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +11 -11
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +11 -11
- package/dist/astro/routes/api/widget-areas/_name_.mjs +6 -6
- package/dist/astro/routes/api/widget-areas/index.mjs +11 -11
- package/dist/astro/routes/api/widget-components.mjs +3 -3
- package/dist/astro/routes/robots.txt.mjs +9 -9
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +13 -13
- package/dist/astro/routes/sitemap.xml.mjs +10 -10
- package/dist/astro/types.d.mts +20 -13
- package/dist/astro/types.d.mts.map +1 -1
- package/dist/auth/providers/github.d.mts +1 -1
- package/dist/auth/providers/google.d.mts +1 -1
- package/dist/{authorize-6WPZa1xZ.mjs → authorize-qRhWQnel.mjs} +2 -2
- package/dist/{authorize-6WPZa1xZ.mjs.map → authorize-qRhWQnel.mjs.map} +1 -1
- package/dist/{backup-DoW-H0r4.mjs → backup-CtA6LDNw.mjs} +5 -5
- package/dist/{backup-DoW-H0r4.mjs.map → backup-CtA6LDNw.mjs.map} +1 -1
- package/dist/{byline-BTOxUlFF.mjs → byline-Cp6csL84.mjs} +9 -9
- package/dist/{byline-BTOxUlFF.mjs.map → byline-Cp6csL84.mjs.map} +1 -1
- package/dist/{byline-fields-CdU_LTF1.mjs → byline-fields-CNc0Yq5i.mjs} +2 -2
- package/dist/{byline-fields-CdU_LTF1.mjs.map → byline-fields-CNc0Yq5i.mjs.map} +1 -1
- package/dist/{byline-registry-BCuOp4UF.mjs → byline-registry-ZkPKqEwC.mjs} +4 -4
- package/dist/{byline-registry-BCuOp4UF.mjs.map → byline-registry-ZkPKqEwC.mjs.map} +1 -1
- package/dist/{bylines-Bc-3xtpz.mjs → bylines-CEj5CdT4.mjs} +7 -7
- package/dist/{bylines-Bc-3xtpz.mjs.map → bylines-CEj5CdT4.mjs.map} +1 -1
- package/dist/{bylines-oVN0iPwX.mjs → bylines-cf-C9lmF.mjs} +5 -5
- package/dist/{bylines-oVN0iPwX.mjs.map → bylines-cf-C9lmF.mjs.map} +1 -1
- package/dist/cache-C9FptTFR.mjs +108 -0
- package/dist/cache-C9FptTFR.mjs.map +1 -0
- package/dist/{challenge-store-BFzgFRog.mjs → challenge-store-D9VNKELo.mjs} +1 -1
- package/dist/{challenge-store-BFzgFRog.mjs.map → challenge-store-D9VNKELo.mjs.map} +1 -1
- package/dist/{chunks-Bq-5DPrD.mjs → chunks-DbYzib_k.mjs} +2 -2
- package/dist/{chunks-Bq-5DPrD.mjs.map → chunks-DbYzib_k.mjs.map} +1 -1
- package/dist/cli/index.mjs +41 -32
- 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 +2 -2
- package/dist/{comment-B9cUud8H.mjs → comment-BIkJ6Fil.mjs} +3 -3
- package/dist/{comment-B9cUud8H.mjs.map → comment-BIkJ6Fil.mjs.map} +1 -1
- package/dist/{comment-reaction-VUXO981F.mjs → comment-reaction-D-laEceb.mjs} +2 -2
- package/dist/{comment-reaction-VUXO981F.mjs.map → comment-reaction-D-laEceb.mjs.map} +1 -1
- package/dist/{comments-PXj1E9Si.mjs → comments-0_w6KChA.mjs} +3 -3
- package/dist/{comments-PXj1E9Si.mjs.map → comments-0_w6KChA.mjs.map} +1 -1
- package/dist/{components-DZkL4Fx0.mjs → components-BPxrRjpE.mjs} +1 -1
- package/dist/{components-DZkL4Fx0.mjs.map → components-BPxrRjpE.mjs.map} +1 -1
- package/dist/{config-B6b_BobR.mjs → config-DyF3OKg_.mjs} +15 -10
- package/dist/config-DyF3OKg_.mjs.map +1 -0
- package/dist/{content-BC43pICM.mjs → content-CJ94NEx5.mjs} +170 -117
- package/dist/content-CJ94NEx5.mjs.map +1 -0
- package/dist/{content-refresh-CPMSauD3.mjs → content-refresh-Xw7aKmou.mjs} +7 -7
- package/dist/{content-refresh-CPMSauD3.mjs.map → content-refresh-Xw7aKmou.mjs.map} +1 -1
- package/dist/{context-DOurRIrb.mjs → context-C_1nrqnW.mjs} +14 -14
- package/dist/{context-DOurRIrb.mjs.map → context-C_1nrqnW.mjs.map} +1 -1
- package/dist/{cron-BlKIMD_e.mjs → cron-DRV-UC4K.mjs} +1 -1
- package/dist/{cron-BlKIMD_e.mjs.map → cron-DRV-UC4K.mjs.map} +1 -1
- package/dist/{dashboard-CBm4zhc3.mjs → dashboard-DINnNVVU.mjs} +5 -5
- package/dist/{dashboard-CBm4zhc3.mjs.map → dashboard-DINnNVVU.mjs.map} +1 -1
- package/dist/db/index.d.mts +3 -3
- package/dist/db/index.mjs +3 -2
- package/dist/db/index.mjs.map +1 -1
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/{default-C4v9KP56.mjs → default-BD2FG0xV.mjs} +1 -1
- package/dist/{default-C4v9KP56.mjs.map → default-BD2FG0xV.mjs.map} +1 -1
- package/dist/deferred-tasks-C-WRCcKo.d.mts +9 -0
- package/dist/deferred-tasks-C-WRCcKo.d.mts.map +1 -0
- package/dist/{device-flow-BeA6lUS1.mjs → device-flow-CTqd8mud.mjs} +5 -5
- package/dist/{device-flow-BeA6lUS1.mjs.map → device-flow-CTqd8mud.mjs.map} +1 -1
- package/dist/{dialect-helpers-BtOwo2us.mjs → dialect-helpers-BSbfaO3F.mjs} +43 -16
- package/dist/dialect-helpers-BSbfaO3F.mjs.map +1 -0
- package/dist/{email-console-C-9Ng8DM.mjs → email-console-C6WdcWbL.mjs} +1 -1
- package/dist/{email-console-C-9Ng8DM.mjs.map → email-console-C6WdcWbL.mjs.map} +1 -1
- package/dist/{error-DJ98TRbQ.mjs → error-oh_9i6bR.mjs} +3 -3
- package/dist/{error-DJ98TRbQ.mjs.map → error-oh_9i6bR.mjs.map} +1 -1
- package/dist/{errors-DtEXIQQV.mjs → errors-BucQ8VYz.mjs} +2 -1
- package/dist/errors-BucQ8VYz.mjs.map +1 -0
- package/dist/{escape-CFedIB1C.mjs → escape-CXps5F_v.mjs} +1 -1
- package/dist/{escape-CFedIB1C.mjs.map → escape-CXps5F_v.mjs.map} +1 -1
- package/dist/{field-defs-cache-KfNJ_qLQ.mjs → field-defs-cache-WD82dMQq.mjs} +4 -4
- package/dist/{field-defs-cache-KfNJ_qLQ.mjs.map → field-defs-cache-WD82dMQq.mjs.map} +1 -1
- package/dist/{fts-manager-hyCU9OOp.mjs → fts-manager-BITakW_R.mjs} +37 -13
- package/dist/fts-manager-BITakW_R.mjs.map +1 -0
- package/dist/{hash-DFFrkivP.mjs → hash-Cg0TXJPV.mjs} +8 -4
- package/dist/hash-Cg0TXJPV.mjs.map +1 -0
- package/dist/{hreflang-CIxH_LKz.d.mts → hreflang-BaLCNcDx.d.mts} +2 -2
- package/dist/{hreflang-CIxH_LKz.d.mts.map → hreflang-BaLCNcDx.d.mts.map} +1 -1
- package/dist/{hreflang-CbYeCzl9.mjs → hreflang-BdZuVBhL.mjs} +7 -7
- package/dist/{hreflang-CbYeCzl9.mjs.map → hreflang-BdZuVBhL.mjs.map} +1 -1
- package/dist/{import-oDYCgNYQ.mjs → import-C-9uqyT3.mjs} +6 -6
- package/dist/{import-oDYCgNYQ.mjs.map → import-C-9uqyT3.mjs.map} +1 -1
- package/dist/{index-BZJ_5FOX.d.mts → index-M37iz29I.d.mts} +112 -37
- package/dist/index-M37iz29I.d.mts.map +1 -0
- package/dist/{index-BEui-cRd.d.mts → index-MGw47uWq.d.mts} +3 -3
- package/dist/{index-BEui-cRd.d.mts.map → index-MGw47uWq.d.mts.map} +1 -1
- package/dist/index.d.mts +19 -19
- package/dist/index.mjs +69 -67
- package/dist/{load-Dg5gtWma.mjs → load-DV1DZuUJ.mjs} +2 -2
- package/dist/{load-Dg5gtWma.mjs.map → load-DV1DZuUJ.mjs.map} +1 -1
- package/dist/{loader-D4rigvjR.mjs → loader-CGugY7Zl.mjs} +29 -15
- package/dist/loader-CGugY7Zl.mjs.map +1 -0
- package/dist/{manifest-schema-bCq54i7F.mjs → manifest-schema-Da7kj23y.mjs} +1 -1
- package/dist/{manifest-schema-bCq54i7F.mjs.map → manifest-schema-Da7kj23y.mjs.map} +1 -1
- package/dist/media/index.d.mts +1 -1
- package/dist/media/local-runtime.d.mts +12 -12
- package/dist/media/local-runtime.mjs +8 -8
- package/dist/{media-its1WLYv.mjs → media-BP7S-nHT.mjs} +60 -15
- package/dist/media-BP7S-nHT.mjs.map +1 -0
- package/dist/{media-BJ6SRY3g.mjs → media-OhYL-7Io.mjs} +8 -4
- package/dist/{media-BJ6SRY3g.mjs.map → media-OhYL-7Io.mjs.map} +1 -1
- package/dist/{media-allowlist-jUppPWFy.mjs → media-allowlist-D-peH1H5.mjs} +1 -1
- package/dist/{media-allowlist-jUppPWFy.mjs.map → media-allowlist-D-peH1H5.mjs.map} +1 -1
- package/dist/{media-upload-DNu3QySc.mjs → media-upload-DtrwU2B0.mjs} +7 -7
- package/dist/{media-upload-DNu3QySc.mjs.map → media-upload-DtrwU2B0.mjs.map} +1 -1
- package/dist/{media-url-BCm5vBn6.mjs → media-url-DYs_YLOx.mjs} +1 -1
- package/dist/{media-url-BCm5vBn6.mjs.map → media-url-DYs_YLOx.mjs.map} +1 -1
- package/dist/{media-usage-DPQ3K4xB.mjs → media-usage-BlxfTkN0.mjs} +8 -8
- package/dist/{media-usage-DPQ3K4xB.mjs.map → media-usage-BlxfTkN0.mjs.map} +1 -1
- package/dist/{menus-BMGzWyWH.mjs → menus-BcQWxeXu.mjs} +5 -5
- package/dist/{menus-BMGzWyWH.mjs.map → menus-BcQWxeXu.mjs.map} +1 -1
- package/dist/{menus-CPf_SW7u.mjs → menus-Y54RDPIN.mjs} +215 -38
- package/dist/menus-Y54RDPIN.mjs.map +1 -0
- package/dist/{mode-fiXRMfeA.mjs → mode-BWyTBe3I.mjs} +1 -1
- package/dist/{mode-fiXRMfeA.mjs.map → mode-BWyTBe3I.mjs.map} +1 -1
- package/dist/{oauth-authorization-oqfbBuMH.mjs → oauth-authorization-ClvktmrL.mjs} +5 -5
- package/dist/{oauth-authorization-oqfbBuMH.mjs.map → oauth-authorization-ClvktmrL.mjs.map} +1 -1
- package/dist/{oauth-clients-BlrUHAsf.mjs → oauth-clients-8ao-U66l.mjs} +1 -1
- package/dist/{oauth-clients-BlrUHAsf.mjs.map → oauth-clients-8ao-U66l.mjs.map} +1 -1
- package/dist/{oauth-state-store-DnvjRMy6.mjs → oauth-state-store-B1ofdTiQ.mjs} +1 -1
- package/dist/{oauth-state-store-DnvjRMy6.mjs.map → oauth-state-store-B1ofdTiQ.mjs.map} +1 -1
- package/dist/{oauth-user-lookup-oUllxvAv.mjs → oauth-user-lookup-CscEuIiR.mjs} +1 -1
- package/dist/{oauth-user-lookup-oUllxvAv.mjs.map → oauth-user-lookup-CscEuIiR.mjs.map} +1 -1
- package/dist/object-cache/memory.d.mts +1 -1
- package/dist/{object-cache-Bok5j2ae.mjs → object-cache-71tPfV4A.mjs} +106 -5
- package/dist/object-cache-71tPfV4A.mjs.map +1 -0
- package/dist/{options-BqMkWfYa.d.mts → options-CA4eVX6P.d.mts} +3 -3
- package/dist/{options-BqMkWfYa.d.mts.map → options-CA4eVX6P.d.mts.map} +1 -1
- package/dist/{options-BlmBHTvX.mjs → options-CBXIHUoT.mjs} +1 -1
- package/dist/{options-BlmBHTvX.mjs.map → options-CBXIHUoT.mjs.map} +1 -1
- package/dist/page/index.d.mts +2 -2
- package/dist/{parse-53zyxlpv.mjs → parse-CJKNuewM.mjs} +2 -2
- package/dist/{parse-53zyxlpv.mjs.map → parse-CJKNuewM.mjs.map} +1 -1
- package/dist/{passkey-config-C-SKpc0-.mjs → passkey-config-D-zrzMJI.mjs} +1 -1
- package/dist/{passkey-config-C-SKpc0-.mjs.map → passkey-config-D-zrzMJI.mjs.map} +1 -1
- package/dist/{placeholder-Dop47JMM.d.mts → placeholder-DaeNS-VG.d.mts} +1 -1
- package/dist/{placeholder-Dop47JMM.d.mts.map → placeholder-DaeNS-VG.d.mts.map} +1 -1
- package/dist/plugin-types.d.mts +1 -1
- package/dist/plugin-utils.d.mts +10 -10
- package/dist/plugins/adapt-sandbox-entry.d.mts +10 -10
- package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
- package/dist/{portable-text-sDj92Kxv.mjs → portable-text-B_kqBmXB.mjs} +1 -1
- package/dist/{portable-text-sDj92Kxv.mjs.map → portable-text-B_kqBmXB.mjs.map} +1 -1
- package/dist/{preview-D4Jnbfx7.mjs → preview-BU6PaLpM.mjs} +2 -2
- package/dist/{preview-D4Jnbfx7.mjs.map → preview-BU6PaLpM.mjs.map} +1 -1
- package/dist/{public-url-DSGTnJFw.mjs → public-url-Ox9oOlgp.mjs} +14 -3
- package/dist/public-url-Ox9oOlgp.mjs.map +1 -0
- package/dist/{query-Bz9bWZfm.mjs → query-C5w7PsRQ.mjs} +43 -44
- package/dist/query-C5w7PsRQ.mjs.map +1 -0
- package/dist/{query-DdFGHzQF.mjs → query-rC9HKv1N.mjs} +4 -4
- package/dist/{query-DdFGHzQF.mjs.map → query-rC9HKv1N.mjs.map} +1 -1
- package/dist/{rate-limit-D3IEiUi2.mjs → rate-limit-kug6q99V.mjs} +2 -2
- package/dist/{rate-limit-D3IEiUi2.mjs.map → rate-limit-kug6q99V.mjs.map} +1 -1
- package/dist/{redirect-BoUac4NT.mjs → redirect-DIJxXKOG.mjs} +3 -3
- package/dist/{redirect-BoUac4NT.mjs.map → redirect-DIJxXKOG.mjs.map} +1 -1
- package/dist/{redirect-Botom7X6.mjs → redirect-Egf2R9yP.mjs} +1 -1
- package/dist/{redirect-Botom7X6.mjs.map → redirect-Egf2R9yP.mjs.map} +1 -1
- package/dist/{redirects-ChbpqKiS.mjs → redirects-Bzm812bY.mjs} +5 -5
- package/dist/{redirects-ChbpqKiS.mjs.map → redirects-Bzm812bY.mjs.map} +1 -1
- package/dist/{redirects-D457qwt0.mjs → redirects-G8wAhWAf.mjs} +25 -7
- package/dist/redirects-G8wAhWAf.mjs.map +1 -0
- package/dist/{registry-y21kjhfG.mjs → registry-Dbodb4Uo.mjs} +59 -14
- package/dist/registry-Dbodb4Uo.mjs.map +1 -0
- package/dist/{relation-Du0r04E0.mjs → relation-Cbe3Sd3m.mjs} +3 -3
- package/dist/{relation-Du0r04E0.mjs.map → relation-Cbe3Sd3m.mjs.map} +1 -1
- package/dist/{relations-CZMpBqEd.mjs → relations-Bw_E1TXo.mjs} +3 -3
- package/dist/{relations-CZMpBqEd.mjs.map → relations-Bw_E1TXo.mjs.map} +1 -1
- package/dist/{relations-qKmSj9KM.d.mts → relations-G2ta7GIl.d.mts} +50 -12
- package/dist/relations-G2ta7GIl.d.mts.map +1 -0
- package/dist/{relations-DtX1UMBH.mjs → relations-qHwpUe66.mjs} +6 -6
- package/dist/{relations-DtX1UMBH.mjs.map → relations-qHwpUe66.mjs.map} +1 -1
- package/dist/request-context.d.mts +3 -0
- package/dist/request-context.d.mts.map +1 -1
- package/dist/request-context.mjs.map +1 -1
- package/dist/{request-meta-DzXYYI-n.mjs → request-meta-Df-jFbAd.mjs} +2 -2
- package/dist/{request-meta-DzXYYI-n.mjs.map → request-meta-Df-jFbAd.mjs.map} +1 -1
- package/dist/{resolve-DFr4kq72.mjs → resolve-DZzG4HqM.mjs} +2 -2
- package/dist/{resolve-DFr4kq72.mjs.map → resolve-DZzG4HqM.mjs.map} +1 -1
- package/dist/{runner-BdeLEgad.mjs → runner-BFTrkYhu.mjs} +497 -213
- package/dist/runner-BFTrkYhu.mjs.map +1 -0
- package/dist/{runner-Dd8m8k9F.d.mts → runner-BaADli8l.d.mts} +2 -2
- package/dist/{runner-Dd8m8k9F.d.mts.map → runner-BaADli8l.d.mts.map} +1 -1
- package/dist/runtime.d.mts +11 -11
- package/dist/runtime.mjs +3 -3
- package/dist/{schema-BH8P8vaC.mjs → schema-CiQXr6_F.mjs} +3 -3
- package/dist/{schema-BH8P8vaC.mjs.map → schema-CiQXr6_F.mjs.map} +1 -1
- package/dist/{scopes-DTIkIaGA.mjs → scopes-CNJVT5i5.mjs} +3 -3
- package/dist/{scopes-DTIkIaGA.mjs.map → scopes-CNJVT5i5.mjs.map} +1 -1
- package/dist/{search-D-Yhpmvs.mjs → search-FZhx5dDU.mjs} +7 -7
- package/dist/{search-D-Yhpmvs.mjs.map → search-FZhx5dDU.mjs.map} +1 -1
- package/dist/{secrets-CQ-69kF9.mjs → secrets-DLd8RXee.mjs} +4 -4
- package/dist/{secrets-CQ-69kF9.mjs.map → secrets-DLd8RXee.mjs.map} +1 -1
- package/dist/{sections-CzVBAKjw.mjs → sections-BrIrMUCn.mjs} +3 -3
- package/dist/{sections-CzVBAKjw.mjs.map → sections-BrIrMUCn.mjs.map} +1 -1
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +25 -23
- package/dist/seo/index.d.mts +3 -3
- package/dist/seo/index.mjs +18 -16
- package/dist/seo/index.mjs.map +1 -1
- package/dist/{seo-C3GDfT0V.mjs → seo-CobNfOvb.mjs} +2 -2
- package/dist/{seo-C3GDfT0V.mjs.map → seo-CobNfOvb.mjs.map} +1 -1
- package/dist/{seo-kJzVAmmu.mjs → seo-DTHRntNC.mjs} +3 -3
- package/dist/{seo-kJzVAmmu.mjs.map → seo-DTHRntNC.mjs.map} +1 -1
- package/dist/{service-CQnc276a.mjs → service-B-pYGPgR.mjs} +4 -4
- package/dist/{service-CQnc276a.mjs.map → service-B-pYGPgR.mjs.map} +1 -1
- package/dist/{session-user-DbHqKDKe.mjs → session-user-BvM9-zNl.mjs} +2 -2
- package/dist/{session-user-DbHqKDKe.mjs.map → session-user-BvM9-zNl.mjs.map} +1 -1
- package/dist/{settings-Dtc0DU31.mjs → settings-DPHau-WH.mjs} +3 -3
- package/dist/{settings-Dtc0DU31.mjs.map → settings-DPHau-WH.mjs.map} +1 -1
- package/dist/{settings-C2PaBS49.mjs → settings-kJhGiQ4z.mjs} +7 -7
- package/dist/{settings-C2PaBS49.mjs.map → settings-kJhGiQ4z.mjs.map} +1 -1
- package/dist/{setup-complete-CdIlqloh.mjs → setup-complete-u50Qm8rY.mjs} +2 -2
- package/dist/{setup-complete-CdIlqloh.mjs.map → setup-complete-u50Qm8rY.mjs.map} +1 -1
- package/dist/{setup-nonce-DqiIPr-J.mjs → setup-nonce-DCjiiOD2.mjs} +1 -1
- package/dist/{setup-nonce-DqiIPr-J.mjs.map → setup-nonce-DCjiiOD2.mjs.map} +1 -1
- package/dist/{single-flight-cache-C2exrGAi.mjs → single-flight-cache-DW3w11Pu.mjs} +1 -1
- package/dist/{single-flight-cache-C2exrGAi.mjs.map → single-flight-cache-DW3w11Pu.mjs.map} +1 -1
- package/dist/site-url-I-lJ5kma.mjs +16 -0
- package/dist/site-url-I-lJ5kma.mjs.map +1 -0
- package/dist/{snapshot-BdpUJKD-.mjs → snapshot-I_fjLDaD.mjs} +2 -2
- package/dist/{snapshot-BdpUJKD-.mjs.map → snapshot-I_fjLDaD.mjs.map} +1 -1
- package/dist/{ssrf-CviKqWmq.mjs → ssrf-CTa6v21i.mjs} +1 -1
- package/dist/{ssrf-CviKqWmq.mjs.map → ssrf-CTa6v21i.mjs.map} +1 -1
- package/dist/{state-xxv6ZTMv.mjs → state-CQvoefYP.mjs} +1 -1
- package/dist/{state-xxv6ZTMv.mjs.map → state-CQvoefYP.mjs.map} +1 -1
- package/dist/{status-COfycGIU.mjs → status-CIsmlC1E.mjs} +1 -1
- package/dist/{status-COfycGIU.mjs.map → status-CIsmlC1E.mjs.map} +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 +2 -2
- package/dist/storage/s3.mjs.map +1 -1
- package/dist/{taxonomies-BN6M9Nrv.mjs → taxonomies-CKuaZER5.mjs} +90 -9
- package/dist/taxonomies-CKuaZER5.mjs.map +1 -0
- package/dist/{taxonomies-r7pqLBCn.mjs → taxonomies-CLN1N92E.mjs} +57 -32
- package/dist/taxonomies-CLN1N92E.mjs.map +1 -0
- package/dist/{taxonomy-0mCI25HN.mjs → taxonomy-pOrB2kZe.mjs} +142 -15
- package/dist/taxonomy-pOrB2kZe.mjs.map +1 -0
- package/dist/{tokens-DVltpO2D.mjs → tokens-DagaJGK8.mjs} +1 -1
- package/dist/{tokens-DVltpO2D.mjs.map → tokens-DagaJGK8.mjs.map} +1 -1
- package/dist/{transaction-D0FOsb3X.mjs → transaction-NQj4VJ7Z.mjs} +1 -1
- package/dist/{transaction-D0FOsb3X.mjs.map → transaction-NQj4VJ7Z.mjs.map} +1 -1
- package/dist/{transport-Vd7VdqHv.d.mts → transport-CAwTaTp_.d.mts} +1 -1
- package/dist/{transport-Vd7VdqHv.d.mts.map → transport-CAwTaTp_.d.mts.map} +1 -1
- package/dist/{transport-CKrlqSpD.mjs → transport-DVsa5YYo.mjs} +1 -1
- package/dist/{transport-CKrlqSpD.mjs.map → transport-DVsa5YYo.mjs.map} +1 -1
- package/dist/{trusted-proxy-CwjQj0YG.mjs → trusted-proxy-D20_7fyX.mjs} +1 -1
- package/dist/{trusted-proxy-CwjQj0YG.mjs.map → trusted-proxy-D20_7fyX.mjs.map} +1 -1
- package/dist/{types-BfYUPJdP.d.mts → types-BGWPMiBO.d.mts} +2 -2
- package/dist/{types-BfYUPJdP.d.mts.map → types-BGWPMiBO.d.mts.map} +1 -1
- package/dist/{types-iVeZ5g6N.d.mts → types-BK6U01Ro.d.mts} +1 -1
- package/dist/{types-iVeZ5g6N.d.mts.map → types-BK6U01Ro.d.mts.map} +1 -1
- package/dist/{types-BD40g7zz.mjs → types-BjxTKxrZ.mjs} +1 -1
- package/dist/{types-BD40g7zz.mjs.map → types-BjxTKxrZ.mjs.map} +1 -1
- package/dist/{types-Kmaxdarz.d.mts → types-BnFP1M3J.d.mts} +22 -1
- package/dist/types-BnFP1M3J.d.mts.map +1 -0
- package/dist/{types-C7U0HlEr.d.mts → types-C8oz_ayJ.d.mts} +1 -1
- package/dist/{types-C7U0HlEr.d.mts.map → types-C8oz_ayJ.d.mts.map} +1 -1
- package/dist/{types-B4vm81VH.d.mts → types-CbUcuxxc.d.mts} +1 -1
- package/dist/{types-B4vm81VH.d.mts.map → types-CbUcuxxc.d.mts.map} +1 -1
- package/dist/{types-CrRUFdej.mjs → types-Cd8iuS1J.mjs} +10 -3
- package/dist/{types-CrRUFdej.mjs.map → types-Cd8iuS1J.mjs.map} +1 -1
- package/dist/types-CtzytVXn.mjs +18 -0
- package/dist/types-CtzytVXn.mjs.map +1 -0
- package/dist/{types-BvB7gDOD.d.mts → types-Cz8hNch2.d.mts} +5 -5
- package/dist/{types-BvB7gDOD.d.mts.map → types-Cz8hNch2.d.mts.map} +1 -1
- package/dist/{types-B97jg3aW.d.mts → types-DLG4IFEQ.d.mts} +1 -1
- package/dist/{types-B97jg3aW.d.mts.map → types-DLG4IFEQ.d.mts.map} +1 -1
- package/dist/{types-DPKhI5ib.d.mts → types-Dc4ntQjQ.d.mts} +18 -1
- package/dist/types-Dc4ntQjQ.d.mts.map +1 -0
- package/dist/{types-BNfPHfay.d.mts → types-LoagAGzH.d.mts} +1 -1
- package/dist/{types-BNfPHfay.d.mts.map → types-LoagAGzH.d.mts.map} +1 -1
- package/dist/{types-o7xo7VgH.mjs → types-NBI3rF-_.mjs} +3 -2
- package/dist/types-NBI3rF-_.mjs.map +1 -0
- package/dist/upload-attempts-D8QAFLUJ.mjs +27 -0
- package/dist/upload-attempts-D8QAFLUJ.mjs.map +1 -0
- package/dist/{user-aTXqmXBz.mjs → user-DBrq4UVq.mjs} +3 -3
- package/dist/{user-aTXqmXBz.mjs.map → user-DBrq4UVq.mjs.map} +1 -1
- package/dist/{utils-BbUgmeZG.mjs → utils-hLzi_2Fn.mjs} +2 -2
- package/dist/{utils-BbUgmeZG.mjs.map → utils-hLzi_2Fn.mjs.map} +1 -1
- package/dist/{validate-VPnKoIzW.mjs → validate-BpwoDOgt.mjs} +25 -2
- package/dist/validate-BpwoDOgt.mjs.map +1 -0
- package/dist/{validate-DULfgpJP.d.mts → validate-C3oSmA7F.d.mts} +15 -5
- package/dist/validate-C3oSmA7F.d.mts.map +1 -0
- package/dist/{validate-CDT1jl6_.mjs → validate-mKo3RxBM.mjs} +4 -4
- package/dist/{validate-CDT1jl6_.mjs.map → validate-mKo3RxBM.mjs.map} +1 -1
- package/dist/{validation-DlD0E35W.mjs → validation-BgpucsvF.mjs} +11 -9
- package/dist/{validation-DlD0E35W.mjs.map → validation-BgpucsvF.mjs.map} +1 -1
- package/dist/version-C4PnpmoV.mjs +7 -0
- package/dist/{version-BRMngh9I.mjs.map → version-C4PnpmoV.mjs.map} +1 -1
- package/dist/{widgets-5fipNkMT.mjs → widgets-CJJtkiKh.mjs} +3 -3
- package/dist/{widgets-5fipNkMT.mjs.map → widgets-CJJtkiKh.mjs.map} +1 -1
- package/dist/{wxr-taxonomies-DrihupaC.mjs → wxr-taxonomies-9PPSy8X6.mjs} +4 -4
- package/dist/{wxr-taxonomies-DrihupaC.mjs.map → wxr-taxonomies-9PPSy8X6.mjs.map} +1 -1
- package/dist/{wxr-taxonomies-B4tMuN5T.d.mts → wxr-taxonomies-Bkpp_2tK.d.mts} +1 -1
- package/dist/{wxr-taxonomies-B4tMuN5T.d.mts.map → wxr-taxonomies-Bkpp_2tK.d.mts.map} +1 -1
- package/dist/{zod-generator-CwkHWz4f.mjs → zod-generator-CG64Hnbh.mjs} +3 -3
- package/dist/{zod-generator-CwkHWz4f.mjs.map → zod-generator-CG64Hnbh.mjs.map} +1 -1
- package/package.json +12 -8
- package/src/after.ts +6 -1
- package/src/api/errors.ts +1 -0
- package/src/api/handlers/content.ts +45 -4
- package/src/api/handlers/marketplace.ts +14 -0
- package/src/api/handlers/media.ts +4 -4
- package/src/api/handlers/registry.ts +14 -0
- package/src/api/handlers/revision.ts +19 -5
- package/src/api/handlers/schema.ts +37 -0
- package/src/api/handlers/snapshot.ts +1 -1
- package/src/api/handlers/taxonomies.ts +111 -10
- package/src/api/openapi/document.ts +84 -3
- package/src/api/public-url.ts +14 -2
- package/src/api/schemas/media.ts +9 -2
- package/src/api/schemas/schema.ts +13 -0
- package/src/api/schemas/search.ts +2 -0
- package/src/api/schemas/taxonomies.ts +34 -1
- package/src/api/site-url.ts +16 -6
- package/src/astro/integration/routes.ts +19 -0
- package/src/astro/integration/runtime.ts +13 -0
- package/src/astro/integration/virtual-modules.ts +2 -0
- package/src/astro/middleware/redirect.ts +2 -10
- package/src/astro/middleware/scoped-db.ts +35 -4
- package/src/astro/middleware.ts +56 -22
- package/src/astro/routes/api/admin/comments/[id]/status.ts +1 -1
- package/src/astro/routes/api/admin/users/[id]/send-recovery.ts +2 -2
- package/src/astro/routes/api/auth/invite/index.ts +2 -2
- package/src/astro/routes/api/auth/magic-link/send.ts +2 -2
- package/src/astro/routes/api/auth/signup/request.ts +2 -2
- package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +1 -1
- package/src/astro/routes/api/content/[collection]/[id].ts +3 -1
- package/src/astro/routes/api/media/[id]/confirm.ts +148 -59
- package/src/astro/routes/api/media/[id]/upload.ts +262 -0
- package/src/astro/routes/api/media/[id].ts +19 -10
- package/src/astro/routes/api/media/upload-url.ts +28 -31
- package/src/astro/routes/api/schema/collections/reorder.ts +31 -0
- package/src/astro/routes/api/schema/index.ts +2 -0
- package/src/astro/routes/api/search/enable.ts +5 -1
- package/src/astro/routes/api/search/rebuild.ts +6 -1
- package/src/astro/routes/api/taxonomies/[name]/reorder.ts +41 -0
- package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +6 -3
- package/src/astro/types.ts +7 -0
- package/src/cleanup.ts +54 -26
- package/src/cli/commands/dev.ts +13 -3
- package/src/cli/commands/export-seed.ts +2 -0
- package/src/cli/index.ts +1 -1
- package/src/components/CommentForm.astro +1 -1
- package/src/components/Comments.astro +1 -1
- package/src/components/InlinePortableTextEditor.tsx +175 -31
- package/src/components/OrderedList.astro +14 -0
- package/src/components/PortableText.astro +26 -4
- package/src/components/code-mark.ts +11 -0
- package/src/components/index.ts +12 -1
- package/src/components/ordered-list.ts +549 -0
- package/src/content/converters/numbered-list.ts +148 -0
- package/src/content/converters/portable-text-to-prosemirror.ts +59 -9
- package/src/content/converters/prosemirror-to-portable-text.ts +23 -12
- package/src/content/converters/types.ts +2 -0
- package/src/content/portable-text-lists.ts +205 -0
- package/src/database/dialect-helpers.ts +41 -22
- package/src/database/migrations/054_media_upload_attempts.ts +41 -0
- package/src/database/migrations/055_content_translation_group_locale_index.ts +67 -0
- package/src/database/migrations/056_taxonomy_term_sort_order.ts +189 -0
- package/src/database/migrations/057_collection_hidden.ts +24 -0
- package/src/database/migrations/058_collection_sort_order.ts +20 -0
- package/src/database/migrations/059_revision_prune_queue.ts +36 -0
- package/src/database/migrations/runner.ts +12 -0
- package/src/database/repositories/content.ts +193 -183
- package/src/database/repositories/media.ts +158 -28
- package/src/database/repositories/revision.ts +82 -17
- package/src/database/repositories/taxonomy.ts +202 -7
- package/src/database/repositories/types.ts +7 -0
- package/src/database/types.ts +23 -1
- package/src/database/validate.ts +39 -0
- package/src/db/adapters.ts +8 -0
- package/src/deferred-tasks.ts +71 -0
- package/src/emdash-runtime.ts +277 -88
- package/src/i18n/config.ts +17 -10
- package/src/index.ts +3 -0
- package/src/loader.ts +46 -24
- package/src/media/upload-attempts.ts +33 -0
- package/src/media/usage/gc.ts +50 -0
- package/src/object-cache/index.ts +160 -3
- package/src/plugins/routes.ts +34 -0
- package/src/plugins/storage-indexes.ts +50 -11
- package/src/query.ts +50 -44
- package/src/redirects/cache.ts +79 -19
- package/src/request-context.ts +3 -0
- package/src/scheduled-publish.ts +6 -1
- package/src/schema/registry.ts +95 -3
- package/src/schema/types.ts +24 -0
- package/src/search/fts-manager.ts +50 -10
- package/src/search/index.ts +2 -0
- package/src/search/types.ts +6 -0
- package/src/seed/apply.ts +22 -2
- package/src/seed/types.ts +10 -0
- package/src/storage/s3.ts +1 -1
- package/src/taxonomies/index.ts +11 -7
- package/src/taxonomies/term-counts.ts +56 -28
- package/src/ui-comments.ts +19 -0
- package/src/ui.ts +11 -3
- package/src/utils/hash.ts +14 -3
- package/src/virtual-modules.d.ts +8 -0
- package/dist/adapters-C0ONzkUP.d.mts.map +0 -1
- package/dist/after-B1IIdH3Y.mjs +0 -29
- package/dist/after-B1IIdH3Y.mjs.map +0 -1
- package/dist/api-BkqFzIu4.mjs.map +0 -1
- package/dist/apply-DuXsqll7.mjs.map +0 -1
- package/dist/cache-DDwy3JDW.mjs +0 -65
- package/dist/cache-DDwy3JDW.mjs.map +0 -1
- package/dist/config-B6b_BobR.mjs.map +0 -1
- package/dist/content-BC43pICM.mjs.map +0 -1
- package/dist/dialect-helpers-BtOwo2us.mjs.map +0 -1
- package/dist/errors-DtEXIQQV.mjs.map +0 -1
- package/dist/fts-manager-hyCU9OOp.mjs.map +0 -1
- package/dist/hash-DFFrkivP.mjs.map +0 -1
- package/dist/index-BZJ_5FOX.d.mts.map +0 -1
- package/dist/loader-D4rigvjR.mjs.map +0 -1
- package/dist/media-its1WLYv.mjs.map +0 -1
- package/dist/menus-CPf_SW7u.mjs.map +0 -1
- package/dist/object-cache-Bok5j2ae.mjs.map +0 -1
- package/dist/public-url-DSGTnJFw.mjs.map +0 -1
- package/dist/query-Bz9bWZfm.mjs.map +0 -1
- package/dist/redirects-D457qwt0.mjs.map +0 -1
- package/dist/registry-y21kjhfG.mjs.map +0 -1
- package/dist/relations-qKmSj9KM.d.mts.map +0 -1
- package/dist/runner-BdeLEgad.mjs.map +0 -1
- package/dist/site-url-NGJT6NTU.mjs +0 -13
- package/dist/site-url-NGJT6NTU.mjs.map +0 -1
- package/dist/taxonomies-BN6M9Nrv.mjs.map +0 -1
- package/dist/taxonomies-r7pqLBCn.mjs.map +0 -1
- package/dist/taxonomy-0mCI25HN.mjs.map +0 -1
- package/dist/types-DPKhI5ib.d.mts.map +0 -1
- package/dist/types-Kmaxdarz.d.mts.map +0 -1
- package/dist/types-o7xo7VgH.mjs.map +0 -1
- package/dist/validate-DULfgpJP.d.mts.map +0 -1
- package/dist/validate-VPnKoIzW.mjs.map +0 -1
- package/dist/version-BRMngh9I.mjs +0 -7
- /package/dist/{api-tokens-Cvmixds7.mjs → api-tokens-Dai9dND0.mjs} +0 -0
- /package/dist/{ssrf-BB__xfNM.mjs → ssrf-Cj568Dgs.mjs} +0 -0
- /package/dist/{types-CK0LxPUQ.mjs → types-DKuWPYV5.mjs} +0 -0
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import "../../../../../base64-B-PsqheR.mjs";
|
|
2
|
-
import "../../../../../types-
|
|
2
|
+
import "../../../../../types-Cd8iuS1J.mjs";
|
|
3
|
+
import { t as MediaRepository } from "../../../../../media-BP7S-nHT.mjs";
|
|
3
4
|
import { t as enrichImageMetadata } from "../../../../../enrich-CFJJgxs_.mjs";
|
|
4
|
-
import { n as
|
|
5
|
-
import { r as
|
|
6
|
-
import {
|
|
7
|
-
import "../../../../../
|
|
8
|
-
import "../../../../../
|
|
9
|
-
import "../../../../../
|
|
5
|
+
import { n as computeContentHash, t as MAX_CONTENT_HASH_BYTES } from "../../../../../hash-Cg0TXJPV.mjs";
|
|
6
|
+
import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-oh_9i6bR.mjs";
|
|
7
|
+
import { r as parseOptionalBody, t as isParseError } from "../../../../../parse-CJKNuewM.mjs";
|
|
8
|
+
import { i as mediaConfirmBody } from "../../../../../media-OhYL-7Io.mjs";
|
|
9
|
+
import "../../../../../redirects-G8wAhWAf.mjs";
|
|
10
|
+
import "../../../../../relations-Bw_E1TXo.mjs";
|
|
11
|
+
import "../../../../../status-CIsmlC1E.mjs";
|
|
10
12
|
import "../../../../../api/schemas/index.mjs";
|
|
11
|
-
import { n as requireOwnerPerm, r as requirePerm } from "../../../../../authorize-
|
|
12
|
-
import { MediaRepository } from "emdash";
|
|
13
|
+
import { n as requireOwnerPerm, r as requirePerm } from "../../../../../authorize-qRhWQnel.mjs";
|
|
13
14
|
|
|
14
15
|
//#region src/astro/routes/api/media/[id]/confirm.ts
|
|
15
16
|
const prerender = false;
|
|
@@ -20,7 +21,7 @@ const prerender = false;
|
|
|
20
21
|
* on the very uploads that flow was designed for. LQIP is progressive
|
|
21
22
|
* enhancement: large images simply ship without a server-generated placeholder.
|
|
22
23
|
*/
|
|
23
|
-
const MAX_PLACEHOLDER_DOWNLOAD_BYTES =
|
|
24
|
+
const MAX_PLACEHOLDER_DOWNLOAD_BYTES = MAX_CONTENT_HASH_BYTES;
|
|
24
25
|
/**
|
|
25
26
|
* Add URL to media item (relative URL for portability)
|
|
26
27
|
*/
|
|
@@ -30,6 +31,55 @@ function addUrlToMedia(item) {
|
|
|
30
31
|
url: `/_emdash/api/media/file/${item.storageKey}`
|
|
31
32
|
};
|
|
32
33
|
}
|
|
34
|
+
async function cancelDownload(download) {
|
|
35
|
+
try {
|
|
36
|
+
await download.body.cancel();
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error("[media] confirm download cancellation failed:", error);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async function forgetUploadAttempt(repo, storageKey) {
|
|
42
|
+
try {
|
|
43
|
+
await repo.deleteUploadAttempt(storageKey);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error("[media] confirm upload attempt cleanup failed:", error);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async function confirmationConflict(repo, id) {
|
|
49
|
+
const current = await repo.findById(id);
|
|
50
|
+
if (!current) return apiError("NOT_FOUND", `Media item not found: ${id}`, 404);
|
|
51
|
+
if (current.status === "ready") {
|
|
52
|
+
await forgetUploadAttempt(repo, current.storageKey);
|
|
53
|
+
return apiSuccess({ item: addUrlToMedia(current) });
|
|
54
|
+
}
|
|
55
|
+
if (current.status === "pending") return apiError("INVALID_STATE", "Media item changed during confirmation", 409);
|
|
56
|
+
return apiError("INVALID_STATE", `Media item is not pending: ${current.status}`, 400);
|
|
57
|
+
}
|
|
58
|
+
async function consumeDownload(download) {
|
|
59
|
+
const reader = download.body.getReader();
|
|
60
|
+
try {
|
|
61
|
+
const bytes = new Uint8Array(download.size);
|
|
62
|
+
let receivedSize = 0;
|
|
63
|
+
while (true) {
|
|
64
|
+
const { done, value } = await reader.read();
|
|
65
|
+
if (done) break;
|
|
66
|
+
if (receivedSize + value.byteLength > bytes.byteLength) throw new Error("Stored file exceeds its reported size");
|
|
67
|
+
bytes.set(value, receivedSize);
|
|
68
|
+
receivedSize += value.byteLength;
|
|
69
|
+
}
|
|
70
|
+
if (receivedSize !== download.size) throw new Error("Stored file size does not match its reported size");
|
|
71
|
+
return bytes;
|
|
72
|
+
} catch (error) {
|
|
73
|
+
try {
|
|
74
|
+
await reader.cancel(error);
|
|
75
|
+
} catch (cancelError) {
|
|
76
|
+
console.error("[media] confirm download cancellation failed:", cancelError);
|
|
77
|
+
}
|
|
78
|
+
throw error;
|
|
79
|
+
} finally {
|
|
80
|
+
reader.releaseLock();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
33
83
|
/**
|
|
34
84
|
* Confirm upload completion
|
|
35
85
|
*/
|
|
@@ -46,48 +96,67 @@ const POST = async ({ params, request, locals }) => {
|
|
|
46
96
|
const repo = new MediaRepository(emdash.db);
|
|
47
97
|
const existing = await repo.findById(id);
|
|
48
98
|
if (!existing) return apiError("NOT_FOUND", `Media item not found: ${id}`, 404);
|
|
49
|
-
|
|
50
|
-
const ownerDenied = requireOwnerPerm(user, existing.authorId ?? "", "media:edit_own", "media:edit_any");
|
|
99
|
+
const ownerDenied = requireOwnerPerm(user, existing.authorId ?? "", "media:upload", "media:edit_any");
|
|
51
100
|
if (ownerDenied) return ownerDenied;
|
|
101
|
+
if (existing.status === "ready") {
|
|
102
|
+
await forgetUploadAttempt(repo, existing.storageKey);
|
|
103
|
+
return apiSuccess({ item: addUrlToMedia(existing) });
|
|
104
|
+
}
|
|
105
|
+
if (existing.status !== "pending") return apiError("INVALID_STATE", `Media item is not pending: ${existing.status}`, 400);
|
|
106
|
+
if (body.size !== void 0 && existing.size !== null && body.size !== existing.size) return apiError("UPLOAD_SIZE_MISMATCH", "Confirmed size does not match the pending media item", 400);
|
|
107
|
+
let confirmedSize = existing.size ?? body.size;
|
|
108
|
+
let contentHash = existing.contentHash;
|
|
109
|
+
let imageBytes;
|
|
52
110
|
if (emdash.storage) {
|
|
53
111
|
if (!await emdash.storage.exists(existing.storageKey)) {
|
|
54
|
-
await repo.markFailed(id);
|
|
112
|
+
if (!await repo.markFailed(id, existing.storageKey)) return await confirmationConflict(repo, id);
|
|
55
113
|
return apiError("FILE_NOT_FOUND", "File was not uploaded to storage", 400);
|
|
56
114
|
}
|
|
115
|
+
const storedFile = await emdash.storage.download(existing.storageKey);
|
|
116
|
+
if (confirmedSize !== void 0 && storedFile.size !== confirmedSize) {
|
|
117
|
+
await cancelDownload(storedFile);
|
|
118
|
+
return apiError("UPLOAD_SIZE_MISMATCH", "Stored file size does not match the pending media item", 400);
|
|
119
|
+
}
|
|
120
|
+
confirmedSize = storedFile.size;
|
|
121
|
+
const isImage = existing.mimeType.startsWith("image/");
|
|
122
|
+
const canBuffer = storedFile.size <= MAX_PLACEHOLDER_DOWNLOAD_BYTES;
|
|
123
|
+
const hasServerHash = contentHash !== null && await repo.hasUploadAttempt(existing.storageKey);
|
|
124
|
+
if (canBuffer && (isImage || !hasServerHash)) {
|
|
125
|
+
const bytes = await consumeDownload(storedFile);
|
|
126
|
+
contentHash = bytes.byteLength > 0 ? await computeContentHash(bytes) : null;
|
|
127
|
+
if (isImage && bytes.byteLength > 0) imageBytes = bytes;
|
|
128
|
+
} else {
|
|
129
|
+
if (!hasServerHash) contentHash = null;
|
|
130
|
+
await cancelDownload(storedFile);
|
|
131
|
+
}
|
|
132
|
+
if (isImage && !canBuffer) console.warn(`[media] confirm skipping placeholder: object ${existing.storageKey} reported size ${storedFile.size} bytes (> ${MAX_PLACEHOLDER_DOWNLOAD_BYTES})`);
|
|
57
133
|
}
|
|
58
134
|
let blurhash;
|
|
59
135
|
let dominantColor;
|
|
60
136
|
let width = body.width;
|
|
61
137
|
let height = body.height;
|
|
62
|
-
if (
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
blurhash = enriched.blurhash;
|
|
74
|
-
dominantColor = enriched.dominantColor;
|
|
75
|
-
width = width ?? enriched.width;
|
|
76
|
-
height = height ?? enriched.height;
|
|
77
|
-
}
|
|
78
|
-
} catch (error) {
|
|
79
|
-
console.error("[media] confirm placeholder generation failed:", error);
|
|
80
|
-
}
|
|
81
|
-
else console.warn(`[media] confirm skipping placeholder: object ${existing.storageKey} reported size ${knownSize} bytes (> ${MAX_PLACEHOLDER_DOWNLOAD_BYTES})`);
|
|
138
|
+
if (imageBytes) try {
|
|
139
|
+
const enriched = await enrichImageMetadata(imageBytes, existing.mimeType, { knownDimensions: body.width != null && body.height != null ? {
|
|
140
|
+
width: body.width,
|
|
141
|
+
height: body.height
|
|
142
|
+
} : void 0 });
|
|
143
|
+
blurhash = enriched.blurhash;
|
|
144
|
+
dominantColor = enriched.dominantColor;
|
|
145
|
+
width = width ?? enriched.width;
|
|
146
|
+
height = height ?? enriched.height;
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error("[media] confirm placeholder generation failed:", error);
|
|
82
149
|
}
|
|
83
150
|
const item = await repo.confirmUpload(id, {
|
|
84
|
-
size:
|
|
151
|
+
size: confirmedSize,
|
|
85
152
|
width,
|
|
86
153
|
height,
|
|
87
154
|
blurhash,
|
|
88
|
-
dominantColor
|
|
89
|
-
|
|
90
|
-
|
|
155
|
+
dominantColor,
|
|
156
|
+
contentHash
|
|
157
|
+
}, existing.storageKey);
|
|
158
|
+
if (!item) return await confirmationConflict(repo, id);
|
|
159
|
+
await forgetUploadAttempt(repo, item.storageKey);
|
|
91
160
|
return apiSuccess({ item: addUrlToMedia(item) });
|
|
92
161
|
} catch (error) {
|
|
93
162
|
return handleError(error, "Failed to confirm upload", "CONFIRM_ERROR");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"confirm.mjs","names":[],"sources":["../../../../../../src/astro/routes/api/media/[id]/confirm.ts"],"sourcesContent":["/**\n * Confirm media upload endpoint\n *\n * POST /_emdash/api/media/{id}/confirm\n *\n * Confirms that the client has successfully uploaded the file to storage.\n * Marks the media record as ready and optionally updates metadata.\n */\n\nimport type { APIRoute } from \"astro\";\nimport { MediaRepository } from \"emdash\";\n\nimport { requireOwnerPerm, requirePerm } from \"#api/authorize.js\";\nimport { apiError, apiSuccess, handleError } from \"#api/error.js\";\nimport { isParseError, parseOptionalBody } from \"#api/parse.js\";\nimport { mediaConfirmBody } from \"#api/schemas.js\";\nimport { enrichImageMetadata } from \"#media/enrich.js\";\nimport type { MediaItem } from \"#types\";\n\nexport const prerender = false;\n\n/**\n * Max raw bytes to buffer for server-side LQIP generation at confirm time. The\n * signed-URL upload flow exists so large files bypass server buffering — re-reading\n * the whole object into a Worker's 128 MB heap to compute a blurhash would OOM\n * on the very uploads that flow was designed for. LQIP is progressive\n * enhancement: large images simply ship without a server-generated placeholder.\n */\nconst MAX_PLACEHOLDER_DOWNLOAD_BYTES = 8 * 1024 * 1024;\n\n/**\n * Add URL to media item (relative URL for portability)\n */\nfunction addUrlToMedia(item: MediaItem): MediaItem & { url: string } {\n\treturn {\n\t\t...item,\n\t\turl: `/_emdash/api/media/file/${item.storageKey}`,\n\t};\n}\n\n/**\n * Confirm upload completion\n */\nexport const POST: APIRoute = async ({ params, request, locals }) => {\n\tconst { emdash, user } = locals;\n\tconst { id } = params;\n\n\tconst denied = requirePerm(user, \"media:upload\");\n\tif (denied) return denied;\n\n\tif (!id) {\n\t\treturn apiError(\"INVALID_REQUEST\", \"Media ID is required\", 400);\n\t}\n\n\tif (!emdash?.db) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\n\ttry {\n\t\tconst body = await parseOptionalBody(request, mediaConfirmBody, {});\n\t\tif (isParseError(body)) return body;\n\n\t\tconst repo = new MediaRepository(emdash.db);\n\n\t\t// Get the media item first to check status\n\t\tconst existing = await repo.findById(id);\n\t\tif (!existing) {\n\t\t\treturn apiError(\"NOT_FOUND\", `Media item not found: ${id}`, 404);\n\t\t}\n\n\t\tif (existing.status !== \"pending\") {\n\t\t\treturn apiError(\"INVALID_STATE\", `Media item is not pending: ${existing.status}`, 400);\n\t\t}\n\n\t\t// Only the uploader or a user with media:edit_any can confirm/fail a pending upload\n\t\tconst ownerDenied = requireOwnerPerm(\n\t\t\tuser,\n\t\t\texisting.authorId ?? \"\",\n\t\t\t\"media:edit_own\",\n\t\t\t\"media:edit_any\",\n\t\t);\n\t\tif (ownerDenied) return ownerDenied;\n\n\t\t// Optionally verify the file exists in storage\n\t\tif (emdash.storage) {\n\t\t\tconst exists = await emdash.storage.exists(existing.storageKey);\n\t\t\tif (!exists) {\n\t\t\t\t// Mark as failed\n\t\t\t\tawait repo.markFailed(id);\n\t\t\t\treturn apiError(\"FILE_NOT_FOUND\", \"File was not uploaded to storage\", 400);\n\t\t\t}\n\t\t}\n\n\t\t// For images, read the just-uploaded bytes back from storage once to\n\t\t// generate LQIP placeholders (and server-side dimensions as a fallback).\n\t\t// The signed-URL flow uploads directly to storage, so this confirm is the\n\t\t// only point at which the server sees the bytes. Best-effort: a decode\n\t\t// failure must not block the upload from being marked ready. We also cap\n\t\t// the download size — buffering a large original into a Worker heap to\n\t\t// compute a 32px blurhash would OOM on the uploads the signed-URL path\n\t\t// exists to support, so oversized files skip the server-side placeholder.\n\t\tlet blurhash: string | undefined;\n\t\tlet dominantColor: string | undefined;\n\t\tlet width = body.width;\n\t\tlet height = body.height;\n\t\tif (emdash.storage && existing.mimeType.startsWith(\"image/\")) {\n\t\t\tconst knownSize = body.size ?? existing.size ?? undefined;\n\t\t\tconst tooLarge = knownSize != null && knownSize > MAX_PLACEHOLDER_DOWNLOAD_BYTES;\n\t\t\tif (!tooLarge) {\n\t\t\t\ttry {\n\t\t\t\t\tconst { body: stream } = await emdash.storage.download(existing.storageKey);\n\t\t\t\t\tconst bytes = new Uint8Array(await new Response(stream).arrayBuffer());\n\t\t\t\t\t// Defense-in-depth for the unknown-size case: even though we\n\t\t\t\t\t// already buffered it, refuse the decode so we don't also pay\n\t\t\t\t\t// the (larger) RGBA allocation.\n\t\t\t\t\tif (bytes.byteLength > MAX_PLACEHOLDER_DOWNLOAD_BYTES) {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`[media] confirm skipping placeholder: object ${existing.storageKey} is ${bytes.byteLength} bytes (> ${MAX_PLACEHOLDER_DOWNLOAD_BYTES})`,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst enriched = await enrichImageMetadata(bytes, existing.mimeType, {\n\t\t\t\t\t\t\tknownDimensions:\n\t\t\t\t\t\t\t\tbody.width != null && body.height != null\n\t\t\t\t\t\t\t\t\t? { width: body.width, height: body.height }\n\t\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tblurhash = enriched.blurhash;\n\t\t\t\t\t\tdominantColor = enriched.dominantColor;\n\t\t\t\t\t\twidth = width ?? enriched.width;\n\t\t\t\t\t\theight = height ?? enriched.height;\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(\"[media] confirm placeholder generation failed:\", error);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[media] confirm skipping placeholder: object ${existing.storageKey} reported size ${knownSize} bytes (> ${MAX_PLACEHOLDER_DOWNLOAD_BYTES})`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Confirm the upload\n\t\tconst item = await repo.confirmUpload(id, {\n\t\t\tsize: body.size,\n\t\t\twidth,\n\t\t\theight,\n\t\t\tblurhash,\n\t\t\tdominantColor,\n\t\t});\n\n\t\tif (!item) {\n\t\t\treturn apiError(\"CONFIRM_FAILED\", \"Failed to confirm upload\", 500);\n\t\t}\n\n\t\t// Add URL to the response (relative URL for portability)\n\t\tconst itemWithUrl = addUrlToMedia(item);\n\n\t\treturn apiSuccess({ item: itemWithUrl });\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to confirm upload\", \"CONFIRM_ERROR\");\n\t}\n};\n"],"mappings":";;;;;;;;;;;;;;AAmBA,MAAa,YAAY;;;;;;;;AASzB,MAAM,iCAAiC,IAAI,OAAO;;;;AAKlD,SAAS,cAAc,MAA8C;AACpE,QAAO;EACN,GAAG;EACH,KAAK,2BAA2B,KAAK;EACrC;;;;;AAMF,MAAa,OAAiB,OAAO,EAAE,QAAQ,SAAS,aAAa;CACpE,MAAM,EAAE,QAAQ,SAAS;CACzB,MAAM,EAAE,OAAO;CAEf,MAAM,SAAS,YAAY,MAAM,eAAe;AAChD,KAAI,OAAQ,QAAO;AAEnB,KAAI,CAAC,GACJ,QAAO,SAAS,mBAAmB,wBAAwB,IAAI;AAGhE,KAAI,CAAC,QAAQ,GACZ,QAAO,SAAS,kBAAkB,6BAA6B,IAAI;AAGpE,KAAI;EACH,MAAM,OAAO,MAAM,kBAAkB,SAAS,kBAAkB,EAAE,CAAC;AACnE,MAAI,aAAa,KAAK,CAAE,QAAO;EAE/B,MAAM,OAAO,IAAI,gBAAgB,OAAO,GAAG;EAG3C,MAAM,WAAW,MAAM,KAAK,SAAS,GAAG;AACxC,MAAI,CAAC,SACJ,QAAO,SAAS,aAAa,yBAAyB,MAAM,IAAI;AAGjE,MAAI,SAAS,WAAW,UACvB,QAAO,SAAS,iBAAiB,8BAA8B,SAAS,UAAU,IAAI;EAIvF,MAAM,cAAc,iBACnB,MACA,SAAS,YAAY,IACrB,kBACA,iBACA;AACD,MAAI,YAAa,QAAO;AAGxB,MAAI,OAAO,SAEV;OAAI,CADW,MAAM,OAAO,QAAQ,OAAO,SAAS,WAAW,EAClD;AAEZ,UAAM,KAAK,WAAW,GAAG;AACzB,WAAO,SAAS,kBAAkB,oCAAoC,IAAI;;;EAY5E,IAAI;EACJ,IAAI;EACJ,IAAI,QAAQ,KAAK;EACjB,IAAI,SAAS,KAAK;AAClB,MAAI,OAAO,WAAW,SAAS,SAAS,WAAW,SAAS,EAAE;GAC7D,MAAM,YAAY,KAAK,QAAQ,SAAS,QAAQ;AAEhD,OAAI,EADa,aAAa,QAAQ,YAAY,gCAEjD,KAAI;IACH,MAAM,EAAE,MAAM,WAAW,MAAM,OAAO,QAAQ,SAAS,SAAS,WAAW;IAC3E,MAAM,QAAQ,IAAI,WAAW,MAAM,IAAI,SAAS,OAAO,CAAC,aAAa,CAAC;AAItE,QAAI,MAAM,aAAa,+BACtB,SAAQ,KACP,gDAAgD,SAAS,WAAW,MAAM,MAAM,WAAW,YAAY,+BAA+B,GACtI;SACK;KACN,MAAM,WAAW,MAAM,oBAAoB,OAAO,SAAS,UAAU,EACpE,iBACC,KAAK,SAAS,QAAQ,KAAK,UAAU,OAClC;MAAE,OAAO,KAAK;MAAO,QAAQ,KAAK;MAAQ,GAC1C,QACJ,CAAC;AACF,gBAAW,SAAS;AACpB,qBAAgB,SAAS;AACzB,aAAQ,SAAS,SAAS;AAC1B,cAAS,UAAU,SAAS;;YAErB,OAAO;AACf,YAAQ,MAAM,kDAAkD,MAAM;;OAGvE,SAAQ,KACP,gDAAgD,SAAS,WAAW,iBAAiB,UAAU,YAAY,+BAA+B,GAC1I;;EAKH,MAAM,OAAO,MAAM,KAAK,cAAc,IAAI;GACzC,MAAM,KAAK;GACX;GACA;GACA;GACA;GACA,CAAC;AAEF,MAAI,CAAC,KACJ,QAAO,SAAS,kBAAkB,4BAA4B,IAAI;AAMnE,SAAO,WAAW,EAAE,MAFA,cAAc,KAAK,EAEA,CAAC;UAChC,OAAO;AACf,SAAO,YAAY,OAAO,4BAA4B,gBAAgB"}
|
|
1
|
+
{"version":3,"file":"confirm.mjs","names":[],"sources":["../../../../../../src/astro/routes/api/media/[id]/confirm.ts"],"sourcesContent":["/**\n * Confirm media upload endpoint\n *\n * POST /_emdash/api/media/{id}/confirm\n *\n * Confirms that the client has successfully uploaded the file to storage.\n * Marks the media record as ready and optionally updates metadata.\n */\n\nimport type { APIRoute } from \"astro\";\nimport type { DownloadResult } from \"emdash\";\n\nimport { requireOwnerPerm, requirePerm } from \"#api/authorize.js\";\nimport { apiError, apiSuccess, handleError } from \"#api/error.js\";\nimport { isParseError, parseOptionalBody } from \"#api/parse.js\";\nimport { mediaConfirmBody } from \"#api/schemas.js\";\nimport { MediaRepository } from \"#db/repositories/media.js\";\nimport { enrichImageMetadata } from \"#media/enrich.js\";\nimport type { MediaItem } from \"#types\";\nimport { computeContentHash, MAX_CONTENT_HASH_BYTES } from \"#utils/hash.js\";\n\nexport const prerender = false;\n\n/**\n * Max raw bytes to buffer for server-side LQIP generation at confirm time. The\n * signed-URL upload flow exists so large files bypass server buffering — re-reading\n * the whole object into a Worker's 128 MB heap to compute a blurhash would OOM\n * on the very uploads that flow was designed for. LQIP is progressive\n * enhancement: large images simply ship without a server-generated placeholder.\n */\nconst MAX_PLACEHOLDER_DOWNLOAD_BYTES = MAX_CONTENT_HASH_BYTES;\n\n/**\n * Add URL to media item (relative URL for portability)\n */\nfunction addUrlToMedia(item: MediaItem): MediaItem & { url: string } {\n\treturn {\n\t\t...item,\n\t\turl: `/_emdash/api/media/file/${item.storageKey}`,\n\t};\n}\n\nasync function cancelDownload(download: DownloadResult): Promise<void> {\n\ttry {\n\t\tawait download.body.cancel();\n\t} catch (error) {\n\t\tconsole.error(\"[media] confirm download cancellation failed:\", error);\n\t}\n}\n\nasync function forgetUploadAttempt(repo: MediaRepository, storageKey: string): Promise<void> {\n\ttry {\n\t\tawait repo.deleteUploadAttempt(storageKey);\n\t} catch (error) {\n\t\tconsole.error(\"[media] confirm upload attempt cleanup failed:\", error);\n\t}\n}\n\nasync function confirmationConflict(repo: MediaRepository, id: string): Promise<Response> {\n\tconst current = await repo.findById(id);\n\tif (!current) {\n\t\treturn apiError(\"NOT_FOUND\", `Media item not found: ${id}`, 404);\n\t}\n\tif (current.status === \"ready\") {\n\t\tawait forgetUploadAttempt(repo, current.storageKey);\n\t\treturn apiSuccess({ item: addUrlToMedia(current) });\n\t}\n\tif (current.status === \"pending\") {\n\t\treturn apiError(\"INVALID_STATE\", \"Media item changed during confirmation\", 409);\n\t}\n\treturn apiError(\"INVALID_STATE\", `Media item is not pending: ${current.status}`, 400);\n}\n\nasync function consumeDownload(download: DownloadResult): Promise<Uint8Array> {\n\tconst reader = download.body.getReader();\n\ttry {\n\t\tconst bytes = new Uint8Array(download.size);\n\t\tlet receivedSize = 0;\n\t\twhile (true) {\n\t\t\tconst { done, value } = await reader.read();\n\t\t\tif (done) break;\n\t\t\tif (receivedSize + value.byteLength > bytes.byteLength) {\n\t\t\t\tthrow new Error(\"Stored file exceeds its reported size\");\n\t\t\t}\n\t\t\tbytes.set(value, receivedSize);\n\t\t\treceivedSize += value.byteLength;\n\t\t}\n\t\tif (receivedSize !== download.size) {\n\t\t\tthrow new Error(\"Stored file size does not match its reported size\");\n\t\t}\n\t\treturn bytes;\n\t} catch (error) {\n\t\ttry {\n\t\t\tawait reader.cancel(error);\n\t\t} catch (cancelError) {\n\t\t\tconsole.error(\"[media] confirm download cancellation failed:\", cancelError);\n\t\t}\n\t\tthrow error;\n\t} finally {\n\t\treader.releaseLock();\n\t}\n}\n\n/**\n * Confirm upload completion\n */\nexport const POST: APIRoute = async ({ params, request, locals }) => {\n\tconst { emdash, user } = locals;\n\tconst { id } = params;\n\n\tconst denied = requirePerm(user, \"media:upload\");\n\tif (denied) return denied;\n\n\tif (!id) {\n\t\treturn apiError(\"INVALID_REQUEST\", \"Media ID is required\", 400);\n\t}\n\n\tif (!emdash?.db) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\n\ttry {\n\t\tconst body = await parseOptionalBody(request, mediaConfirmBody, {});\n\t\tif (isParseError(body)) return body;\n\n\t\tconst repo = new MediaRepository(emdash.db);\n\n\t\t// Get the media item first to check status\n\t\tconst existing = await repo.findById(id);\n\t\tif (!existing) {\n\t\t\treturn apiError(\"NOT_FOUND\", `Media item not found: ${id}`, 404);\n\t\t}\n\n\t\t// Only the uploader or a user with media:edit_any can confirm/fail a pending upload\n\t\tconst ownerDenied = requireOwnerPerm(\n\t\t\tuser,\n\t\t\texisting.authorId ?? \"\",\n\t\t\t\"media:upload\",\n\t\t\t\"media:edit_any\",\n\t\t);\n\t\tif (ownerDenied) return ownerDenied;\n\n\t\tif (existing.status === \"ready\") {\n\t\t\tawait forgetUploadAttempt(repo, existing.storageKey);\n\t\t\treturn apiSuccess({ item: addUrlToMedia(existing) });\n\t\t}\n\t\tif (existing.status !== \"pending\") {\n\t\t\treturn apiError(\"INVALID_STATE\", `Media item is not pending: ${existing.status}`, 400);\n\t\t}\n\n\t\tif (body.size !== undefined && existing.size !== null && body.size !== existing.size) {\n\t\t\treturn apiError(\n\t\t\t\t\"UPLOAD_SIZE_MISMATCH\",\n\t\t\t\t\"Confirmed size does not match the pending media item\",\n\t\t\t\t400,\n\t\t\t);\n\t\t}\n\n\t\tlet confirmedSize = existing.size ?? body.size;\n\t\tlet contentHash = existing.contentHash;\n\t\tlet imageBytes: Uint8Array | undefined;\n\n\t\tif (emdash.storage) {\n\t\t\tconst exists = await emdash.storage.exists(existing.storageKey);\n\t\t\tif (!exists) {\n\t\t\t\tconst failed = await repo.markFailed(id, existing.storageKey);\n\t\t\t\tif (!failed) return await confirmationConflict(repo, id);\n\t\t\t\treturn apiError(\"FILE_NOT_FOUND\", \"File was not uploaded to storage\", 400);\n\t\t\t}\n\n\t\t\tconst storedFile = await emdash.storage.download(existing.storageKey);\n\t\t\tif (confirmedSize !== undefined && storedFile.size !== confirmedSize) {\n\t\t\t\tawait cancelDownload(storedFile);\n\t\t\t\treturn apiError(\n\t\t\t\t\t\"UPLOAD_SIZE_MISMATCH\",\n\t\t\t\t\t\"Stored file size does not match the pending media item\",\n\t\t\t\t\t400,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconfirmedSize = storedFile.size;\n\n\t\t\tconst isImage = existing.mimeType.startsWith(\"image/\");\n\t\t\tconst canBuffer = storedFile.size <= MAX_PLACEHOLDER_DOWNLOAD_BYTES;\n\t\t\tconst hasServerHash =\n\t\t\t\tcontentHash !== null && (await repo.hasUploadAttempt(existing.storageKey));\n\t\t\tif (canBuffer && (isImage || !hasServerHash)) {\n\t\t\t\tconst bytes = await consumeDownload(storedFile);\n\t\t\t\tcontentHash = bytes.byteLength > 0 ? await computeContentHash(bytes) : null;\n\t\t\t\tif (isImage && bytes.byteLength > 0) imageBytes = bytes;\n\t\t\t} else {\n\t\t\t\tif (!hasServerHash) contentHash = null;\n\t\t\t\tawait cancelDownload(storedFile);\n\t\t\t}\n\n\t\t\tif (isImage && !canBuffer) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[media] confirm skipping placeholder: object ${existing.storageKey} reported size ${storedFile.size} bytes (> ${MAX_PLACEHOLDER_DOWNLOAD_BYTES})`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// LQIP is best-effort; oversized images skip server-side placeholders.\n\t\tlet blurhash: string | undefined;\n\t\tlet dominantColor: string | undefined;\n\t\tlet width = body.width;\n\t\tlet height = body.height;\n\t\tif (imageBytes) {\n\t\t\ttry {\n\t\t\t\tconst enriched = await enrichImageMetadata(imageBytes, existing.mimeType, {\n\t\t\t\t\tknownDimensions:\n\t\t\t\t\t\tbody.width != null && body.height != null\n\t\t\t\t\t\t\t? { width: body.width, height: body.height }\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t});\n\t\t\t\tblurhash = enriched.blurhash;\n\t\t\t\tdominantColor = enriched.dominantColor;\n\t\t\t\twidth = width ?? enriched.width;\n\t\t\t\theight = height ?? enriched.height;\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"[media] confirm placeholder generation failed:\", error);\n\t\t\t}\n\t\t}\n\n\t\t// Confirm the upload\n\t\tconst item = await repo.confirmUpload(\n\t\t\tid,\n\t\t\t{\n\t\t\t\tsize: confirmedSize,\n\t\t\t\twidth,\n\t\t\t\theight,\n\t\t\t\tblurhash,\n\t\t\t\tdominantColor,\n\t\t\t\tcontentHash,\n\t\t\t},\n\t\t\texisting.storageKey,\n\t\t);\n\n\t\tif (!item) {\n\t\t\treturn await confirmationConflict(repo, id);\n\t\t}\n\n\t\tawait forgetUploadAttempt(repo, item.storageKey);\n\n\t\t// Add URL to the response (relative URL for portability)\n\t\tconst itemWithUrl = addUrlToMedia(item);\n\n\t\treturn apiSuccess({ item: itemWithUrl });\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to confirm upload\", \"CONFIRM_ERROR\");\n\t}\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.d.mts","names":[],"sources":["../../../../../../src/astro/routes/api/media/[id]/upload.ts"],"mappings":";;;cAWa,SAAA;AAAA,cA2DA,GAAA,EAAK,QAAA"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import "../../../../../base64-B-PsqheR.mjs";
|
|
2
|
+
import "../../../../../types-Cd8iuS1J.mjs";
|
|
3
|
+
import { t as MediaRepository } from "../../../../../media-BP7S-nHT.mjs";
|
|
4
|
+
import { n as normalizeMime } from "../../../../../mime-DfIVjNkr.mjs";
|
|
5
|
+
import { n as computeContentHash, t as MAX_CONTENT_HASH_BYTES } from "../../../../../hash-Cg0TXJPV.mjs";
|
|
6
|
+
import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-oh_9i6bR.mjs";
|
|
7
|
+
import { t as removeUploadAttempt } from "../../../../../upload-attempts-D8QAFLUJ.mjs";
|
|
8
|
+
import { n as requireOwnerPerm, r as requirePerm } from "../../../../../authorize-qRhWQnel.mjs";
|
|
9
|
+
import { ulid } from "ulidx";
|
|
10
|
+
|
|
11
|
+
//#region src/astro/routes/api/media/[id]/upload.ts
|
|
12
|
+
const prerender = false;
|
|
13
|
+
const INITIAL_HASH_BUFFER_BYTES = 64 * 1024;
|
|
14
|
+
var UploadBodyError = class extends Error {
|
|
15
|
+
code;
|
|
16
|
+
constructor(code) {
|
|
17
|
+
super(code);
|
|
18
|
+
this.code = code;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
function findUploadBodyError(error) {
|
|
22
|
+
let current = error;
|
|
23
|
+
for (let depth = 0; depth < 5 && current instanceof Error; depth++) {
|
|
24
|
+
if (current instanceof UploadBodyError) return current;
|
|
25
|
+
current = current.cause;
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
function preserveKnownLength(body, expectedLength) {
|
|
30
|
+
if (typeof FixedLengthStream === "undefined") return body;
|
|
31
|
+
return body.pipeThrough(new FixedLengthStream(expectedLength));
|
|
32
|
+
}
|
|
33
|
+
function createUploadAttemptKey(key) {
|
|
34
|
+
const pathSeparator = key.lastIndexOf("/");
|
|
35
|
+
const extensionSeparator = key.lastIndexOf(".");
|
|
36
|
+
if (extensionSeparator > pathSeparator) return `${key.slice(0, extensionSeparator)}.${ulid()}${key.slice(extensionSeparator)}`;
|
|
37
|
+
return `${key}.${ulid()}`;
|
|
38
|
+
}
|
|
39
|
+
async function getStoredSize(storage, key) {
|
|
40
|
+
if (!await storage.exists(key)) return null;
|
|
41
|
+
const download = await storage.download(key);
|
|
42
|
+
try {
|
|
43
|
+
return download.size;
|
|
44
|
+
} finally {
|
|
45
|
+
try {
|
|
46
|
+
await download.body.cancel();
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error("[media] upload download cancellation failed:", error);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const PUT = async ({ params, request, locals }) => {
|
|
53
|
+
const { emdash, user } = locals;
|
|
54
|
+
const { id } = params;
|
|
55
|
+
const denied = requirePerm(user, "media:upload");
|
|
56
|
+
if (denied) return denied;
|
|
57
|
+
if (!id) return apiError("INVALID_REQUEST", "Media ID is required", 400);
|
|
58
|
+
if (!emdash?.db) return apiError("NOT_CONFIGURED", "EmDash is not initialized", 500);
|
|
59
|
+
if (!emdash.storage) return apiError("NO_STORAGE", "Storage not configured", 500);
|
|
60
|
+
try {
|
|
61
|
+
const repo = new MediaRepository(emdash.db);
|
|
62
|
+
const media = await repo.findById(id);
|
|
63
|
+
if (!media) return apiError("NOT_FOUND", `Media item not found: ${id}`, 404);
|
|
64
|
+
if (media.status !== "pending") return apiError("INVALID_STATE", `Media item is not pending: ${media.status}`, 400);
|
|
65
|
+
const ownerDenied = requireOwnerPerm(user, media.authorId ?? "", "media:upload", "media:edit_any");
|
|
66
|
+
if (ownerDenied) return ownerDenied;
|
|
67
|
+
if (!Number.isSafeInteger(media.size) || media.size === null || media.size < 0) return apiError("INVALID_STATE", "Pending media item has no valid upload size", 400);
|
|
68
|
+
const expectedSize = media.size;
|
|
69
|
+
const contentType = request.headers.get("Content-Type");
|
|
70
|
+
if (!contentType || normalizeMime(contentType) !== media.mimeType) return apiError("INVALID_TYPE", "Upload content type does not match the media item", 400);
|
|
71
|
+
const requestBody = request.body ?? (expectedSize === 0 ? new ReadableStream({ start(controller) {
|
|
72
|
+
controller.close();
|
|
73
|
+
} }) : null);
|
|
74
|
+
if (!requestBody) return apiError("NO_FILE", "No file provided", 400);
|
|
75
|
+
const contentLength = request.headers.get("Content-Length");
|
|
76
|
+
if (contentLength !== null) {
|
|
77
|
+
const declaredSize = Number(contentLength);
|
|
78
|
+
if (!Number.isSafeInteger(declaredSize) || declaredSize < 0) return apiError("INVALID_REQUEST", "Invalid Content-Length header", 400);
|
|
79
|
+
if (declaredSize > expectedSize) return apiError("PAYLOAD_TOO_LARGE", "Upload exceeds the expected size", 413);
|
|
80
|
+
if (declaredSize !== expectedSize) return apiError("UPLOAD_SIZE_MISMATCH", "Upload size does not match the media item", 400);
|
|
81
|
+
}
|
|
82
|
+
let receivedSize = 0;
|
|
83
|
+
const shouldHash = expectedSize > 0 && expectedSize <= MAX_CONTENT_HASH_BYTES;
|
|
84
|
+
let hashBytes = null;
|
|
85
|
+
const body = preserveKnownLength(requestBody.pipeThrough(new TransformStream({
|
|
86
|
+
transform(chunk, controller) {
|
|
87
|
+
const offset = receivedSize;
|
|
88
|
+
receivedSize += chunk.byteLength;
|
|
89
|
+
if (receivedSize > expectedSize) {
|
|
90
|
+
controller.error(new UploadBodyError("PAYLOAD_TOO_LARGE"));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (shouldHash && chunk.byteLength > 0) {
|
|
94
|
+
if (!hashBytes) hashBytes = new Uint8Array(Math.min(expectedSize, Math.max(INITIAL_HASH_BUFFER_BYTES, receivedSize)));
|
|
95
|
+
else if (receivedSize > hashBytes.byteLength) {
|
|
96
|
+
const grown = new Uint8Array(Math.min(expectedSize, Math.max(receivedSize, hashBytes.byteLength * 2)));
|
|
97
|
+
grown.set(hashBytes);
|
|
98
|
+
hashBytes = grown;
|
|
99
|
+
}
|
|
100
|
+
hashBytes.set(chunk, offset);
|
|
101
|
+
}
|
|
102
|
+
controller.enqueue(chunk);
|
|
103
|
+
},
|
|
104
|
+
flush(controller) {
|
|
105
|
+
if (receivedSize !== expectedSize) controller.error(new UploadBodyError("UPLOAD_SIZE_MISMATCH"));
|
|
106
|
+
}
|
|
107
|
+
})), expectedSize);
|
|
108
|
+
const attemptKey = createUploadAttemptKey(media.storageKey);
|
|
109
|
+
await repo.createUploadAttempt(id, attemptKey);
|
|
110
|
+
let attemptSize;
|
|
111
|
+
try {
|
|
112
|
+
attemptSize = (await emdash.storage.upload({
|
|
113
|
+
key: attemptKey,
|
|
114
|
+
body,
|
|
115
|
+
contentType: media.mimeType
|
|
116
|
+
})).size;
|
|
117
|
+
} catch (error) {
|
|
118
|
+
await removeUploadAttempt(emdash.storage, repo, attemptKey);
|
|
119
|
+
const bodyError = findUploadBodyError(error);
|
|
120
|
+
if (bodyError?.code === "PAYLOAD_TOO_LARGE") return apiError("PAYLOAD_TOO_LARGE", "Upload exceeds the expected size", 413);
|
|
121
|
+
if (bodyError?.code === "UPLOAD_SIZE_MISMATCH") return apiError("UPLOAD_SIZE_MISMATCH", "Upload size does not match the media item", 400);
|
|
122
|
+
return handleError(error, "Upload failed", "UPLOAD_ERROR");
|
|
123
|
+
}
|
|
124
|
+
if (receivedSize !== expectedSize || attemptSize !== expectedSize) {
|
|
125
|
+
await removeUploadAttempt(emdash.storage, repo, attemptKey);
|
|
126
|
+
return apiError("UPLOAD_SIZE_MISMATCH", "Upload size does not match the media item", 400);
|
|
127
|
+
}
|
|
128
|
+
const contentHash = hashBytes ? await computeContentHash(hashBytes) : void 0;
|
|
129
|
+
let published;
|
|
130
|
+
try {
|
|
131
|
+
published = await repo.publishPendingStorageKey(id, media.storageKey, attemptKey, contentHash);
|
|
132
|
+
} catch (error) {
|
|
133
|
+
try {
|
|
134
|
+
const current = await repo.findById(id);
|
|
135
|
+
if (current?.storageKey === attemptKey && (current.status === "pending" || current.status === "ready") && current.size === expectedSize && await getStoredSize(emdash.storage, attemptKey) === expectedSize) return apiSuccess({
|
|
136
|
+
uploaded: true,
|
|
137
|
+
size: expectedSize
|
|
138
|
+
});
|
|
139
|
+
} catch (verificationError) {
|
|
140
|
+
console.error("[media] upload publication verification failed:", verificationError);
|
|
141
|
+
}
|
|
142
|
+
return handleError(error, "Upload failed", "UPLOAD_ERROR");
|
|
143
|
+
}
|
|
144
|
+
if (!published) {
|
|
145
|
+
const current = await repo.findById(id);
|
|
146
|
+
if (current && (current.status === "pending" || current.status === "ready") && current.size === expectedSize && (current.storageKey === attemptKey || expectedSize === 0 || contentHash !== void 0 && current.contentHash === contentHash) && await getStoredSize(emdash.storage, current.storageKey) === expectedSize) {
|
|
147
|
+
if (current.storageKey !== attemptKey) await removeUploadAttempt(emdash.storage, repo, attemptKey);
|
|
148
|
+
return apiSuccess({
|
|
149
|
+
uploaded: true,
|
|
150
|
+
size: expectedSize
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
await removeUploadAttempt(emdash.storage, repo, attemptKey);
|
|
154
|
+
return apiError("INVALID_STATE", "Media item is no longer pending", 400);
|
|
155
|
+
}
|
|
156
|
+
await removeUploadAttempt(emdash.storage, repo, media.storageKey);
|
|
157
|
+
return apiSuccess({
|
|
158
|
+
uploaded: true,
|
|
159
|
+
size: receivedSize
|
|
160
|
+
});
|
|
161
|
+
} catch (error) {
|
|
162
|
+
return handleError(error, "Upload failed", "UPLOAD_ERROR");
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
//#endregion
|
|
167
|
+
export { PUT, prerender };
|
|
168
|
+
//# sourceMappingURL=upload.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.mjs","names":[],"sources":["../../../../../../src/astro/routes/api/media/[id]/upload.ts"],"sourcesContent":["import type { APIRoute } from \"astro\";\nimport type { Storage } from \"emdash\";\nimport { ulid } from \"ulidx\";\n\nimport { requireOwnerPerm, requirePerm } from \"#api/authorize.js\";\nimport { apiError, apiSuccess, handleError } from \"#api/error.js\";\nimport { MediaRepository } from \"#db/repositories/media.js\";\nimport { normalizeMime } from \"#media/mime.js\";\nimport { removeUploadAttempt } from \"#media/upload-attempts.js\";\nimport { computeContentHash, MAX_CONTENT_HASH_BYTES } from \"#utils/hash.js\";\n\nexport const prerender = false;\n\nconst INITIAL_HASH_BUFFER_BYTES = 64 * 1024;\n\ntype FixedLengthStreamConstructor = new (\n\texpectedLength: number | bigint,\n) => TransformStream<ArrayBuffer | ArrayBufferView, Uint8Array>;\n\ndeclare const FixedLengthStream: FixedLengthStreamConstructor | undefined;\n\nclass UploadBodyError extends Error {\n\treadonly code: \"PAYLOAD_TOO_LARGE\" | \"UPLOAD_SIZE_MISMATCH\";\n\n\tconstructor(code: \"PAYLOAD_TOO_LARGE\" | \"UPLOAD_SIZE_MISMATCH\") {\n\t\tsuper(code);\n\t\tthis.code = code;\n\t}\n}\n\nfunction findUploadBodyError(error: unknown): UploadBodyError | null {\n\tlet current = error;\n\tfor (let depth = 0; depth < 5 && current instanceof Error; depth++) {\n\t\tif (current instanceof UploadBodyError) return current;\n\t\tcurrent = current.cause;\n\t}\n\treturn null;\n}\n\nfunction preserveKnownLength(\n\tbody: ReadableStream<Uint8Array>,\n\texpectedLength: number,\n): ReadableStream<Uint8Array> {\n\tif (typeof FixedLengthStream === \"undefined\") return body;\n\treturn body.pipeThrough(new FixedLengthStream(expectedLength));\n}\n\nfunction createUploadAttemptKey(key: string): string {\n\tconst pathSeparator = key.lastIndexOf(\"/\");\n\tconst extensionSeparator = key.lastIndexOf(\".\");\n\tif (extensionSeparator > pathSeparator) {\n\t\treturn `${key.slice(0, extensionSeparator)}.${ulid()}${key.slice(extensionSeparator)}`;\n\t}\n\treturn `${key}.${ulid()}`;\n}\n\nasync function getStoredSize(storage: Storage, key: string): Promise<number | null> {\n\tif (!(await storage.exists(key))) return null;\n\tconst download = await storage.download(key);\n\ttry {\n\t\treturn download.size;\n\t} finally {\n\t\ttry {\n\t\t\tawait download.body.cancel();\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[media] upload download cancellation failed:\", error);\n\t\t}\n\t}\n}\n\nexport const PUT: APIRoute = async ({ params, request, locals }) => {\n\tconst { emdash, user } = locals;\n\tconst { id } = params;\n\n\tconst denied = requirePerm(user, \"media:upload\");\n\tif (denied) return denied;\n\n\tif (!id) {\n\t\treturn apiError(\"INVALID_REQUEST\", \"Media ID is required\", 400);\n\t}\n\n\tif (!emdash?.db) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\n\tif (!emdash.storage) {\n\t\treturn apiError(\"NO_STORAGE\", \"Storage not configured\", 500);\n\t}\n\n\ttry {\n\t\tconst repo = new MediaRepository(emdash.db);\n\t\tconst media = await repo.findById(id);\n\t\tif (!media) {\n\t\t\treturn apiError(\"NOT_FOUND\", `Media item not found: ${id}`, 404);\n\t\t}\n\n\t\tif (media.status !== \"pending\") {\n\t\t\treturn apiError(\"INVALID_STATE\", `Media item is not pending: ${media.status}`, 400);\n\t\t}\n\n\t\tconst ownerDenied = requireOwnerPerm(\n\t\t\tuser,\n\t\t\tmedia.authorId ?? \"\",\n\t\t\t\"media:upload\",\n\t\t\t\"media:edit_any\",\n\t\t);\n\t\tif (ownerDenied) return ownerDenied;\n\n\t\tif (!Number.isSafeInteger(media.size) || media.size === null || media.size < 0) {\n\t\t\treturn apiError(\"INVALID_STATE\", \"Pending media item has no valid upload size\", 400);\n\t\t}\n\t\tconst expectedSize = media.size;\n\n\t\tconst contentType = request.headers.get(\"Content-Type\");\n\t\tif (!contentType || normalizeMime(contentType) !== media.mimeType) {\n\t\t\treturn apiError(\"INVALID_TYPE\", \"Upload content type does not match the media item\", 400);\n\t\t}\n\n\t\tconst requestBody =\n\t\t\trequest.body ??\n\t\t\t(expectedSize === 0\n\t\t\t\t? new ReadableStream<Uint8Array>({\n\t\t\t\t\t\tstart(controller) {\n\t\t\t\t\t\t\tcontroller.close();\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t: null);\n\t\tif (!requestBody) {\n\t\t\treturn apiError(\"NO_FILE\", \"No file provided\", 400);\n\t\t}\n\n\t\tconst contentLength = request.headers.get(\"Content-Length\");\n\t\tif (contentLength !== null) {\n\t\t\tconst declaredSize = Number(contentLength);\n\t\t\tif (!Number.isSafeInteger(declaredSize) || declaredSize < 0) {\n\t\t\t\treturn apiError(\"INVALID_REQUEST\", \"Invalid Content-Length header\", 400);\n\t\t\t}\n\t\t\tif (declaredSize > expectedSize) {\n\t\t\t\treturn apiError(\"PAYLOAD_TOO_LARGE\", \"Upload exceeds the expected size\", 413);\n\t\t\t}\n\t\t\tif (declaredSize !== expectedSize) {\n\t\t\t\treturn apiError(\"UPLOAD_SIZE_MISMATCH\", \"Upload size does not match the media item\", 400);\n\t\t\t}\n\t\t}\n\n\t\tlet receivedSize = 0;\n\t\tconst shouldHash = expectedSize > 0 && expectedSize <= MAX_CONTENT_HASH_BYTES;\n\t\tlet hashBytes: Uint8Array | null = null;\n\t\tconst checkedBody = requestBody.pipeThrough(\n\t\t\tnew TransformStream<Uint8Array, Uint8Array>({\n\t\t\t\ttransform(chunk, controller) {\n\t\t\t\t\tconst offset = receivedSize;\n\t\t\t\t\treceivedSize += chunk.byteLength;\n\t\t\t\t\tif (receivedSize > expectedSize) {\n\t\t\t\t\t\tcontroller.error(new UploadBodyError(\"PAYLOAD_TOO_LARGE\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (shouldHash && chunk.byteLength > 0) {\n\t\t\t\t\t\tif (!hashBytes) {\n\t\t\t\t\t\t\thashBytes = new Uint8Array(\n\t\t\t\t\t\t\t\tMath.min(expectedSize, Math.max(INITIAL_HASH_BUFFER_BYTES, receivedSize)),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} else if (receivedSize > hashBytes.byteLength) {\n\t\t\t\t\t\t\tconst grown = new Uint8Array(\n\t\t\t\t\t\t\t\tMath.min(expectedSize, Math.max(receivedSize, hashBytes.byteLength * 2)),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tgrown.set(hashBytes);\n\t\t\t\t\t\t\thashBytes = grown;\n\t\t\t\t\t\t}\n\t\t\t\t\t\thashBytes.set(chunk, offset);\n\t\t\t\t\t}\n\t\t\t\t\tcontroller.enqueue(chunk);\n\t\t\t\t},\n\t\t\t\tflush(controller) {\n\t\t\t\t\tif (receivedSize !== expectedSize) {\n\t\t\t\t\t\tcontroller.error(new UploadBodyError(\"UPLOAD_SIZE_MISMATCH\"));\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t}),\n\t\t);\n\t\tconst body = preserveKnownLength(checkedBody, expectedSize);\n\n\t\tconst attemptKey = createUploadAttemptKey(media.storageKey);\n\t\tawait repo.createUploadAttempt(id, attemptKey);\n\n\t\tlet attemptSize: number;\n\t\ttry {\n\t\t\tconst result = await emdash.storage.upload({\n\t\t\t\tkey: attemptKey,\n\t\t\t\tbody,\n\t\t\t\tcontentType: media.mimeType,\n\t\t\t});\n\t\t\tattemptSize = result.size;\n\t\t} catch (error) {\n\t\t\tawait removeUploadAttempt(emdash.storage, repo, attemptKey);\n\t\t\tconst bodyError = findUploadBodyError(error);\n\t\t\tif (bodyError?.code === \"PAYLOAD_TOO_LARGE\") {\n\t\t\t\treturn apiError(\"PAYLOAD_TOO_LARGE\", \"Upload exceeds the expected size\", 413);\n\t\t\t}\n\t\t\tif (bodyError?.code === \"UPLOAD_SIZE_MISMATCH\") {\n\t\t\t\treturn apiError(\"UPLOAD_SIZE_MISMATCH\", \"Upload size does not match the media item\", 400);\n\t\t\t}\n\t\t\treturn handleError(error, \"Upload failed\", \"UPLOAD_ERROR\");\n\t\t}\n\n\t\tif (receivedSize !== expectedSize || attemptSize !== expectedSize) {\n\t\t\tawait removeUploadAttempt(emdash.storage, repo, attemptKey);\n\t\t\treturn apiError(\"UPLOAD_SIZE_MISMATCH\", \"Upload size does not match the media item\", 400);\n\t\t}\n\t\tconst contentHash = hashBytes ? await computeContentHash(hashBytes) : undefined;\n\n\t\tlet published: boolean;\n\t\ttry {\n\t\t\tpublished = await repo.publishPendingStorageKey(\n\t\t\t\tid,\n\t\t\t\tmedia.storageKey,\n\t\t\t\tattemptKey,\n\t\t\t\tcontentHash,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tconst current = await repo.findById(id);\n\t\t\t\tif (\n\t\t\t\t\tcurrent?.storageKey === attemptKey &&\n\t\t\t\t\t(current.status === \"pending\" || current.status === \"ready\") &&\n\t\t\t\t\tcurrent.size === expectedSize &&\n\t\t\t\t\t(await getStoredSize(emdash.storage, attemptKey)) === expectedSize\n\t\t\t\t) {\n\t\t\t\t\treturn apiSuccess({ uploaded: true, size: expectedSize });\n\t\t\t\t}\n\t\t\t} catch (verificationError) {\n\t\t\t\tconsole.error(\"[media] upload publication verification failed:\", verificationError);\n\t\t\t}\n\t\t\treturn handleError(error, \"Upload failed\", \"UPLOAD_ERROR\");\n\t\t}\n\n\t\tif (!published) {\n\t\t\tconst current = await repo.findById(id);\n\t\t\tif (\n\t\t\t\tcurrent &&\n\t\t\t\t(current.status === \"pending\" || current.status === \"ready\") &&\n\t\t\t\tcurrent.size === expectedSize &&\n\t\t\t\t(current.storageKey === attemptKey ||\n\t\t\t\t\texpectedSize === 0 ||\n\t\t\t\t\t(contentHash !== undefined && current.contentHash === contentHash)) &&\n\t\t\t\t(await getStoredSize(emdash.storage, current.storageKey)) === expectedSize\n\t\t\t) {\n\t\t\t\tif (current.storageKey !== attemptKey) {\n\t\t\t\t\tawait removeUploadAttempt(emdash.storage, repo, attemptKey);\n\t\t\t\t}\n\t\t\t\treturn apiSuccess({ uploaded: true, size: expectedSize });\n\t\t\t}\n\t\t\tawait removeUploadAttempt(emdash.storage, repo, attemptKey);\n\t\t\treturn apiError(\"INVALID_STATE\", \"Media item is no longer pending\", 400);\n\t\t}\n\n\t\tawait removeUploadAttempt(emdash.storage, repo, media.storageKey);\n\t\treturn apiSuccess({ uploaded: true, size: receivedSize });\n\t} catch (error) {\n\t\treturn handleError(error, \"Upload failed\", \"UPLOAD_ERROR\");\n\t}\n};\n"],"mappings":"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"}
|
|
@@ -1,18 +1,20 @@
|
|
|
1
|
+
import "../../../../../config-DyF3OKg_.mjs";
|
|
1
2
|
import "../../../../../base64-B-PsqheR.mjs";
|
|
2
|
-
import "../../../../../types-
|
|
3
|
-
import "../../../../../media-
|
|
4
|
-
import "../../../../../
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import "../../../../../
|
|
10
|
-
import "../../../../../
|
|
11
|
-
import "../../../../../
|
|
3
|
+
import "../../../../../types-Cd8iuS1J.mjs";
|
|
4
|
+
import "../../../../../media-BP7S-nHT.mjs";
|
|
5
|
+
import "../../../../../hash-Cg0TXJPV.mjs";
|
|
6
|
+
import "../../../../../content-refresh-Xw7aKmou.mjs";
|
|
7
|
+
import { t as handleMediaUsageDetails } from "../../../../../media-usage-BlxfTkN0.mjs";
|
|
8
|
+
import { a as unwrapResult, t as apiError } from "../../../../../error-oh_9i6bR.mjs";
|
|
9
|
+
import { i as parseQuery, t as isParseError } from "../../../../../parse-CJKNuewM.mjs";
|
|
10
|
+
import { C as mediaUsageDetailsQuery } from "../../../../../media-OhYL-7Io.mjs";
|
|
11
|
+
import "../../../../../redirects-G8wAhWAf.mjs";
|
|
12
|
+
import "../../../../../relations-Bw_E1TXo.mjs";
|
|
13
|
+
import "../../../../../status-CIsmlC1E.mjs";
|
|
12
14
|
import "../../../../../api/schemas/index.mjs";
|
|
13
|
-
import "../../../../../api-tokens-
|
|
14
|
-
import { r as requirePerm } from "../../../../../authorize-
|
|
15
|
-
import { t as requireScope } from "../../../../../scopes-
|
|
15
|
+
import "../../../../../api-tokens-Dai9dND0.mjs";
|
|
16
|
+
import { r as requirePerm } from "../../../../../authorize-qRhWQnel.mjs";
|
|
17
|
+
import { t as requireScope } from "../../../../../scopes-CNJVT5i5.mjs";
|
|
16
18
|
|
|
17
19
|
//#region src/astro/routes/api/media/[id]/usage.ts
|
|
18
20
|
const prerender = false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usage.mjs","names":[],"sources":["../../../../../../src/astro/routes/api/media/[id]/usage.ts"],"sourcesContent":["import type { APIRoute } from \"astro\";\n\nimport { requirePerm } from \"#api/authorize.js\";\nimport { apiError, unwrapResult } from \"#api/error.js\";\nimport { handleMediaUsageDetails } from \"#api/handlers/media-usage.js\";\nimport { isParseError, parseQuery } from \"#api/parse.js\";\nimport { mediaUsageDetailsQuery } from \"#api/schemas.js\";\nimport { requireScope } from \"#auth/scopes.js\";\n\nexport const prerender = false;\n\nexport const GET: APIRoute = async ({ params, request, locals }) => {\n\tconst { emdash, user } = locals;\n\n\tconst mediaDenied = requirePerm(user, \"media:read\");\n\tif (mediaDenied) return mediaDenied;\n\tconst contentDenied = requirePerm(user, \"content:read_drafts\");\n\tif (contentDenied) return contentDenied;\n\tconst scopeDenied = requireScope(locals, \"admin\");\n\tif (scopeDenied) return scopeDenied;\n\n\tconst { id } = params;\n\tif (!id) return apiError(\"INVALID_REQUEST\", \"Media ID required\", 400);\n\tif (!emdash?.db) return apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\n\tconst query = parseQuery(new URL(request.url), mediaUsageDetailsQuery);\n\tif (isParseError(query)) return query;\n\n\treturn unwrapResult(await handleMediaUsageDetails(emdash.db, id, query));\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"usage.mjs","names":[],"sources":["../../../../../../src/astro/routes/api/media/[id]/usage.ts"],"sourcesContent":["import type { APIRoute } from \"astro\";\n\nimport { requirePerm } from \"#api/authorize.js\";\nimport { apiError, unwrapResult } from \"#api/error.js\";\nimport { handleMediaUsageDetails } from \"#api/handlers/media-usage.js\";\nimport { isParseError, parseQuery } from \"#api/parse.js\";\nimport { mediaUsageDetailsQuery } from \"#api/schemas.js\";\nimport { requireScope } from \"#auth/scopes.js\";\n\nexport const prerender = false;\n\nexport const GET: APIRoute = async ({ params, request, locals }) => {\n\tconst { emdash, user } = locals;\n\n\tconst mediaDenied = requirePerm(user, \"media:read\");\n\tif (mediaDenied) return mediaDenied;\n\tconst contentDenied = requirePerm(user, \"content:read_drafts\");\n\tif (contentDenied) return contentDenied;\n\tconst scopeDenied = requireScope(locals, \"admin\");\n\tif (scopeDenied) return scopeDenied;\n\n\tconst { id } = params;\n\tif (!id) return apiError(\"INVALID_REQUEST\", \"Media ID required\", 400);\n\tif (!emdash?.db) return apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\n\tconst query = parseQuery(new URL(request.url), mediaUsageDetailsQuery);\n\tif (isParseError(query)) return query;\n\n\treturn unwrapResult(await handleMediaUsageDetails(emdash.db, id, query));\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AASA,MAAa,YAAY;AAEzB,MAAa,MAAgB,OAAO,EAAE,QAAQ,SAAS,aAAa;CACnE,MAAM,EAAE,QAAQ,SAAS;CAEzB,MAAM,cAAc,YAAY,MAAM,aAAa;AACnD,KAAI,YAAa,QAAO;CACxB,MAAM,gBAAgB,YAAY,MAAM,sBAAsB;AAC9D,KAAI,cAAe,QAAO;CAC1B,MAAM,cAAc,aAAa,QAAQ,QAAQ;AACjD,KAAI,YAAa,QAAO;CAExB,MAAM,EAAE,OAAO;AACf,KAAI,CAAC,GAAI,QAAO,SAAS,mBAAmB,qBAAqB,IAAI;AACrE,KAAI,CAAC,QAAQ,GAAI,QAAO,SAAS,kBAAkB,6BAA6B,IAAI;CAEpF,MAAM,QAAQ,WAAW,IAAI,IAAI,QAAQ,IAAI,EAAE,uBAAuB;AACtE,KAAI,aAAa,MAAM,CAAE,QAAO;AAEhC,QAAO,aAAa,MAAM,wBAAwB,OAAO,IAAI,IAAI,MAAM,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_id_.d.mts","names":[],"sources":["../../../../../src/astro/routes/api/media/[id].ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"_id_.d.mts","names":[],"sources":["../../../../../src/astro/routes/api/media/[id].ts"],"mappings":";;;cAkBa,SAAA;;;;cAKA,GAAA,EAAK,QAAA;AAkClB;;;;;AAAA,cAAa,GAAA,EAAK,QAAA;;;;;;cAkDL,MAAA,EAAQ,QAAA"}
|
|
@@ -1,16 +1,19 @@
|
|
|
1
|
+
import "../../../../config-DyF3OKg_.mjs";
|
|
1
2
|
import "../../../../base64-B-PsqheR.mjs";
|
|
2
|
-
import "../../../../types-
|
|
3
|
-
import "../../../../media-
|
|
4
|
-
import "../../../../
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import "../../../../
|
|
10
|
-
import "../../../../
|
|
11
|
-
import "../../../../
|
|
3
|
+
import "../../../../types-Cd8iuS1J.mjs";
|
|
4
|
+
import { t as MediaRepository } from "../../../../media-BP7S-nHT.mjs";
|
|
5
|
+
import "../../../../hash-Cg0TXJPV.mjs";
|
|
6
|
+
import "../../../../content-refresh-Xw7aKmou.mjs";
|
|
7
|
+
import { r as handleMediaUsageSummaries } from "../../../../media-usage-BlxfTkN0.mjs";
|
|
8
|
+
import { a as unwrapResult, n as apiSuccess, r as handleError, t as apiError } from "../../../../error-oh_9i6bR.mjs";
|
|
9
|
+
import { i as parseQuery, n as parseBody, t as isParseError } from "../../../../parse-CJKNuewM.mjs";
|
|
10
|
+
import { s as mediaGetQuery, v as mediaUpdateBody } from "../../../../media-OhYL-7Io.mjs";
|
|
11
|
+
import "../../../../redirects-G8wAhWAf.mjs";
|
|
12
|
+
import "../../../../relations-Bw_E1TXo.mjs";
|
|
13
|
+
import "../../../../status-CIsmlC1E.mjs";
|
|
12
14
|
import "../../../../api/schemas/index.mjs";
|
|
13
|
-
import {
|
|
15
|
+
import { t as removeUploadAttempt } from "../../../../upload-attempts-D8QAFLUJ.mjs";
|
|
16
|
+
import { n as requireOwnerPerm, r as requirePerm, t as canReadMediaUsageCount } from "../../../../authorize-qRhWQnel.mjs";
|
|
14
17
|
|
|
15
18
|
//#region src/astro/routes/api/media/[id].ts
|
|
16
19
|
const prerender = false;
|
|
@@ -86,10 +89,14 @@ const DELETE = async ({ params, locals }) => {
|
|
|
86
89
|
const media = getResult.data.item;
|
|
87
90
|
const ownerDenied = requireOwnerPerm(user, media.authorId, "media:delete_own", "media:delete_any");
|
|
88
91
|
if (ownerDenied) return ownerDenied;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
const result = await emdash.handleMediaDelete(id);
|
|
93
|
+
if (!result.success) return unwrapResult(result);
|
|
94
|
+
if (typeof result.data !== "object" || result.data === null || !("storageKey" in result.data) || typeof result.data.storageKey !== "string") return apiError("MEDIA_DELETE_ERROR", "Failed to delete media", 500);
|
|
95
|
+
if (emdash.storage) {
|
|
96
|
+
const repo = new MediaRepository(emdash.db);
|
|
97
|
+
await removeUploadAttempt(emdash.storage, repo, result.data.storageKey, { allowUntracked: true });
|
|
98
|
+
}
|
|
99
|
+
return apiSuccess({ deleted: true });
|
|
93
100
|
} catch (error) {
|
|
94
101
|
return handleError(error, "Failed to delete media", "MEDIA_DELETE_ERROR");
|
|
95
102
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_id_.mjs","names":[],"sources":["../../../../../src/astro/routes/api/media/[id].ts"],"sourcesContent":["/**\n * Single media item endpoint\n *\n * GET /_emdash/api/media/:id - Get media item\n * PUT /_emdash/api/media/:id - Update media metadata\n * DELETE /_emdash/api/media/:id - Delete media item\n */\n\nimport type { APIRoute } from \"astro\";\n\nimport { canReadMediaUsageCount, requireOwnerPerm, requirePerm } from \"#api/authorize.js\";\nimport { apiError, apiSuccess, handleError, unwrapResult } from \"#api/error.js\";\nimport { handleMediaUsageSummaries } from \"#api/handlers/media-usage.js\";\nimport { isParseError, parseBody, parseQuery } from \"#api/parse.js\";\nimport { mediaGetQuery, mediaUpdateBody } from \"#api/schemas.js\";\n\nexport const prerender = false;\n\n/**\n * Get media item\n */\nexport const GET: APIRoute = async ({ params, request, locals }) => {\n\tconst { emdash, user } = locals;\n\tconst { id } = params;\n\n\tconst readDenied = requirePerm(user, \"media:read\");\n\tif (readDenied) return readDenied;\n\n\tif (!id) {\n\t\treturn apiError(\"INVALID_REQUEST\", \"Media ID required\", 400);\n\t}\n\n\tif (!emdash?.handleMediaGet) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\tconst query = parseQuery(new URL(request.url), mediaGetQuery);\n\tif (isParseError(query)) return query;\n\n\tconst result = await emdash.handleMediaGet(id);\n\tif (!result.success || query.includeUsage !== \"1\") return unwrapResult(result);\n\n\tconst includeCount = canReadMediaUsageCount(user, locals.tokenScopes);\n\tconst usageResult = await handleMediaUsageSummaries(emdash.db, [id], { includeCount });\n\tif (!usageResult.success) return unwrapResult(usageResult);\n\tconst usage = usageResult.data[id];\n\tif (!usage) return apiError(\"MEDIA_USAGE_READ_ERROR\", \"Failed to read media usage\", 500);\n\n\treturn apiSuccess({ item: { ...result.data.item, usage } });\n};\n\n/**\n * Update media metadata\n *\n * Authors can edit their own media; editors+ can edit any.\n */\nexport const PUT: APIRoute = async ({ params, request, locals }) => {\n\tconst { emdash, user } = locals;\n\tconst { id } = params;\n\n\t// Minimum permission gate — ownership checked below\n\tconst editDenied = requirePerm(user, \"media:edit_own\");\n\tif (editDenied) return editDenied;\n\n\tif (!id) {\n\t\treturn apiError(\"INVALID_REQUEST\", \"Media ID required\", 400);\n\t}\n\n\tif (!emdash?.handleMediaGet || !emdash?.handleMediaUpdate) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\n\ttry {\n\t\t// Fetch media item for ownership check\n\t\tconst getResult = await emdash.handleMediaGet(id);\n\t\tif (!getResult.success || !getResult.data?.item) {\n\t\t\treturn apiError(\"NOT_FOUND\", \"Media item not found\", 404);\n\t\t}\n\n\t\tconst media = getResult.data.item;\n\n\t\t// Ownership check: authors can edit own, editors+ can edit any\n\t\tconst ownerDenied = requireOwnerPerm(user, media.authorId, \"media:edit_own\", \"media:edit_any\");\n\t\tif (ownerDenied) return ownerDenied;\n\n\t\tconst body = await parseBody(request, mediaUpdateBody);\n\t\tif (isParseError(body)) return body;\n\n\t\tconst result = await emdash.handleMediaUpdate(id, {\n\t\t\talt: body.alt,\n\t\t\tcaption: body.caption,\n\t\t\twidth: body.width,\n\t\t\theight: body.height,\n\t\t});\n\n\t\treturn unwrapResult(result);\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to update media\", \"MEDIA_UPDATE_ERROR\");\n\t}\n};\n\n/**\n * Delete media item\n *\n * Authors can delete their own media; editors+ can delete any.\n */\nexport const DELETE: APIRoute = async ({ params, locals }) => {\n\tconst { emdash, user } = locals;\n\tconst { id } = params;\n\n\t// Minimum permission gate — ownership checked below\n\tconst deleteDenied = requirePerm(user, \"media:delete_own\");\n\tif (deleteDenied) return deleteDenied;\n\n\tif (!id) {\n\t\treturn apiError(\"INVALID_REQUEST\", \"Media ID required\", 400);\n\t}\n\n\tif (!emdash?.handleMediaGet || !emdash?.handleMediaDelete) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\n\ttry {\n\t\t// Fetch media item for ownership check and storage key\n\t\tconst getResult = await emdash.handleMediaGet(id);\n\t\tif (!getResult.success || !getResult.data?.item) {\n\t\t\treturn apiError(\"NOT_FOUND\", \"Media item not found\", 404);\n\t\t}\n\n\t\tconst media = getResult.data.item;\n\n\t\t// Ownership check: authors can delete own, editors+ can delete any\n\t\tconst ownerDenied = requireOwnerPerm(\n\t\t\tuser,\n\t\t\tmedia.authorId,\n\t\t\t\"media:delete_own\",\n\t\t\t\"media:delete_any\",\n\t\t);\n\t\tif (ownerDenied) return ownerDenied;\n\n\t\t// Delete
|
|
1
|
+
{"version":3,"file":"_id_.mjs","names":[],"sources":["../../../../../src/astro/routes/api/media/[id].ts"],"sourcesContent":["/**\n * Single media item endpoint\n *\n * GET /_emdash/api/media/:id - Get media item\n * PUT /_emdash/api/media/:id - Update media metadata\n * DELETE /_emdash/api/media/:id - Delete media item\n */\n\nimport type { APIRoute } from \"astro\";\n\nimport { canReadMediaUsageCount, requireOwnerPerm, requirePerm } from \"#api/authorize.js\";\nimport { apiError, apiSuccess, handleError, unwrapResult } from \"#api/error.js\";\nimport { handleMediaUsageSummaries } from \"#api/handlers/media-usage.js\";\nimport { isParseError, parseBody, parseQuery } from \"#api/parse.js\";\nimport { mediaGetQuery, mediaUpdateBody } from \"#api/schemas.js\";\nimport { MediaRepository } from \"#db/repositories/media.js\";\nimport { removeUploadAttempt } from \"#media/upload-attempts.js\";\n\nexport const prerender = false;\n\n/**\n * Get media item\n */\nexport const GET: APIRoute = async ({ params, request, locals }) => {\n\tconst { emdash, user } = locals;\n\tconst { id } = params;\n\n\tconst readDenied = requirePerm(user, \"media:read\");\n\tif (readDenied) return readDenied;\n\n\tif (!id) {\n\t\treturn apiError(\"INVALID_REQUEST\", \"Media ID required\", 400);\n\t}\n\n\tif (!emdash?.handleMediaGet) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\tconst query = parseQuery(new URL(request.url), mediaGetQuery);\n\tif (isParseError(query)) return query;\n\n\tconst result = await emdash.handleMediaGet(id);\n\tif (!result.success || query.includeUsage !== \"1\") return unwrapResult(result);\n\n\tconst includeCount = canReadMediaUsageCount(user, locals.tokenScopes);\n\tconst usageResult = await handleMediaUsageSummaries(emdash.db, [id], { includeCount });\n\tif (!usageResult.success) return unwrapResult(usageResult);\n\tconst usage = usageResult.data[id];\n\tif (!usage) return apiError(\"MEDIA_USAGE_READ_ERROR\", \"Failed to read media usage\", 500);\n\n\treturn apiSuccess({ item: { ...result.data.item, usage } });\n};\n\n/**\n * Update media metadata\n *\n * Authors can edit their own media; editors+ can edit any.\n */\nexport const PUT: APIRoute = async ({ params, request, locals }) => {\n\tconst { emdash, user } = locals;\n\tconst { id } = params;\n\n\t// Minimum permission gate — ownership checked below\n\tconst editDenied = requirePerm(user, \"media:edit_own\");\n\tif (editDenied) return editDenied;\n\n\tif (!id) {\n\t\treturn apiError(\"INVALID_REQUEST\", \"Media ID required\", 400);\n\t}\n\n\tif (!emdash?.handleMediaGet || !emdash?.handleMediaUpdate) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\n\ttry {\n\t\t// Fetch media item for ownership check\n\t\tconst getResult = await emdash.handleMediaGet(id);\n\t\tif (!getResult.success || !getResult.data?.item) {\n\t\t\treturn apiError(\"NOT_FOUND\", \"Media item not found\", 404);\n\t\t}\n\n\t\tconst media = getResult.data.item;\n\n\t\t// Ownership check: authors can edit own, editors+ can edit any\n\t\tconst ownerDenied = requireOwnerPerm(user, media.authorId, \"media:edit_own\", \"media:edit_any\");\n\t\tif (ownerDenied) return ownerDenied;\n\n\t\tconst body = await parseBody(request, mediaUpdateBody);\n\t\tif (isParseError(body)) return body;\n\n\t\tconst result = await emdash.handleMediaUpdate(id, {\n\t\t\talt: body.alt,\n\t\t\tcaption: body.caption,\n\t\t\twidth: body.width,\n\t\t\theight: body.height,\n\t\t});\n\n\t\treturn unwrapResult(result);\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to update media\", \"MEDIA_UPDATE_ERROR\");\n\t}\n};\n\n/**\n * Delete media item\n *\n * Authors can delete their own media; editors+ can delete any.\n */\nexport const DELETE: APIRoute = async ({ params, locals }) => {\n\tconst { emdash, user } = locals;\n\tconst { id } = params;\n\n\t// Minimum permission gate — ownership checked below\n\tconst deleteDenied = requirePerm(user, \"media:delete_own\");\n\tif (deleteDenied) return deleteDenied;\n\n\tif (!id) {\n\t\treturn apiError(\"INVALID_REQUEST\", \"Media ID required\", 400);\n\t}\n\n\tif (!emdash?.handleMediaGet || !emdash?.handleMediaDelete) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\n\ttry {\n\t\t// Fetch media item for ownership check and storage key\n\t\tconst getResult = await emdash.handleMediaGet(id);\n\t\tif (!getResult.success || !getResult.data?.item) {\n\t\t\treturn apiError(\"NOT_FOUND\", \"Media item not found\", 404);\n\t\t}\n\n\t\tconst media = getResult.data.item;\n\n\t\t// Ownership check: authors can delete own, editors+ can delete any\n\t\tconst ownerDenied = requireOwnerPerm(\n\t\t\tuser,\n\t\t\tmedia.authorId,\n\t\t\t\"media:delete_own\",\n\t\t\t\"media:delete_any\",\n\t\t);\n\t\tif (ownerDenied) return ownerDenied;\n\n\t\t// Delete from database — site-settings cache invalidation happens\n\t\t// in `EmDashRuntime.handleMediaDelete` so MCP/plugin paths inherit it.\n\t\tconst result = await emdash.handleMediaDelete(id);\n\t\tif (!result.success) return unwrapResult(result);\n\t\tif (\n\t\t\ttypeof result.data !== \"object\" ||\n\t\t\tresult.data === null ||\n\t\t\t!(\"storageKey\" in result.data) ||\n\t\t\ttypeof result.data.storageKey !== \"string\"\n\t\t) {\n\t\t\treturn apiError(\"MEDIA_DELETE_ERROR\", \"Failed to delete media\", 500);\n\t\t}\n\n\t\tif (emdash.storage) {\n\t\t\tconst repo = new MediaRepository(emdash.db);\n\t\t\tawait removeUploadAttempt(emdash.storage, repo, result.data.storageKey, {\n\t\t\t\tallowUntracked: true,\n\t\t\t});\n\t\t}\n\n\t\treturn apiSuccess({ deleted: true });\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to delete media\", \"MEDIA_DELETE_ERROR\");\n\t}\n};\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "../../../../../base64-B-PsqheR.mjs";
|
|
2
|
-
import "../../../../../types-
|
|
3
|
-
import { r as handleError, t as apiError } from "../../../../../error-
|
|
2
|
+
import "../../../../../types-Cd8iuS1J.mjs";
|
|
3
|
+
import { r as handleError, t as apiError } from "../../../../../error-oh_9i6bR.mjs";
|
|
4
4
|
|
|
5
5
|
//#region src/astro/routes/api/media/file/[...key].ts
|
|
6
6
|
const prerender = false;
|