Skip to content

feat(media): scope foundation for the media management API#67

Merged
ABB65 merged 1 commit into
mainfrom
feat/media-api-foundation
Jun 18, 2026
Merged

feat(media): scope foundation for the media management API#67
ABB65 merged 1 commit into
mainfrom
feat/media-api-foundation

Conversation

@ABB65

@ABB65 ABB65 commented Jun 18, 2026

Copy link
Copy Markdown
Member

Phase 1 — Media management API foundation (backend only)

REST-first media CRUD API groundwork. No new public route is exposed yet — this lays the scope/key model + shared ingest helper the next phases build on. No user-visible behaviour change beyond a security hardening.

What's in

  • migration 010cdn_api_keys.scopes text[] (default {delivery}); create_cdn_key_if_allowed gains p_scopes. Pre-migration keys default to delivery → CDN serving untouched, no key gains media access implicitly.
  • cdn-keys.tsvalidateCDNKey surfaces scopes; new requireScope(scopes, needed) + CDN_KEY_SCOPES (delivery | media:read | media:write).
  • DB + create route — scopes plumbed through validate/create; CDN key-create accepts + validates a scopes body field.
  • middleware/api/media/ added to PUBLIC_PATHS (Bearer-key path; same pattern as mcp/forms/conversation/cdn).
  • media-ingest.ts — single SSRF-hardened fetchRemoteMedia (MIME whitelist + size cap). URL-import route refactored onto it; chat upload_media gains the missing SSRF guard (was fetching arbitrary URLs without isAllowedWebhookUrl).

Tests

tests/unit/cdn-key-scopes.test.ts (requireScope + scope passthrough/default); existing cdn-keys test updated for the new field. Full suite green: 681.

Notes

  • Migration applies in the release gate (supabase start + test:rls); PR CI is lint/typecheck/build.
  • New error keys (cdn.scope_insufficient, cdn.scope_invalid, media.url_blocked) fall back to their key string until added to the dictionary alongside the UI strings (next phase).

Next

  • UI — scope checkboxes in CDNPanel.vue key creation + scope badges on the keys list + strings.
  • Public CRUD routes/api/media/v1/{projectId}/assets/... (list/get/upload/update/delete/bulk) + media-url.ts.

Lays the Bearer-key scope model that the upcoming public media CRUD API
(`/api/media/v1/...`) will enforce, without exposing any new route yet.

- migration 010: `cdn_api_keys.scopes text[]` (default `{delivery}`) +
  `create_cdn_key_if_allowed` gains a `p_scopes` argument. Existing keys
  default to delivery so CDN serving is untouched.
- `validateCDNKey` now surfaces `scopes`; new `requireScope` helper +
  `CDN_KEY_SCOPES` (`delivery` | `media:read` | `media:write`).
- DB plumbing (validate select + create insert/RPC) carries scopes; the
  CDN key-create route accepts + validates a `scopes` body field.
- `/api/media/` exempted from the session middleware (Bearer-key path,
  same pattern as mcp/forms/conversation/cdn).
- `media-ingest.ts`: one SSRF-hardened `fetchRemoteMedia` (MIME + size
  enforced). URL-import route refactored onto it; the chat `upload_media`
  tool gains the missing SSRF guard.

UI for scope selection (CDNPanel checkboxes) and the public CRUD routes
land in follow-up phases. New error keys (cdn.scope_*, media.url_blocked)
fall back to their key string until added to the dictionary with the UI
strings.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant