nuxt-media-library 0.0.60 → 0.0.61
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/README.md +106 -57
- package/dist/module.d.mts +12 -6
- package/dist/module.d.ts +12 -6
- package/dist/module.json +1 -1
- package/dist/module.mjs +7 -16
- package/dist/runtime/components/MLMedia/Editor.d.vue.ts +1 -0
- package/dist/runtime/components/MLMedia/Editor.vue +3 -1
- package/dist/runtime/components/MLMedia/Editor.vue.d.ts +1 -0
- package/dist/runtime/components/MLMedia/EditorPanel.d.vue.ts +3 -0
- package/dist/runtime/components/MLMedia/EditorPanel.vue +3 -2
- package/dist/runtime/components/MLMedia/EditorPanel.vue.d.ts +3 -0
- package/dist/runtime/components/MLMedia/Field.d.vue.ts +2 -2
- package/dist/runtime/components/MLMedia/Field.vue +2 -1
- package/dist/runtime/components/MLMedia/Field.vue.d.ts +2 -2
- package/dist/runtime/components/MLMedia/FilePickerPanel.vue +1 -1
- package/dist/runtime/components/MLMedia/Gallery.d.vue.ts +2 -2
- package/dist/runtime/components/MLMedia/Gallery.vue +4 -1
- package/dist/runtime/components/MLMedia/Gallery.vue.d.ts +2 -2
- package/dist/runtime/components/MLMedia/GallerySection.d.vue.ts +1 -1
- package/dist/runtime/components/MLMedia/GallerySection.vue +2 -1
- package/dist/runtime/components/MLMedia/GallerySection.vue.d.ts +1 -1
- package/dist/runtime/components/MLMedia/MediaTile.d.vue.ts +1 -1
- package/dist/runtime/components/MLMedia/MediaTile.vue.d.ts +1 -1
- package/dist/runtime/components/MLMedia/PickerCard.d.vue.ts +1 -1
- package/dist/runtime/components/MLMedia/PickerCard.vue.d.ts +1 -1
- package/dist/runtime/components/MLMedia/PickerRow.d.vue.ts +1 -1
- package/dist/runtime/components/MLMedia/PickerRow.vue.d.ts +1 -1
- package/dist/runtime/components/MLMedia/ProgressVeil.d.vue.ts +1 -1
- package/dist/runtime/components/MLMedia/ProgressVeil.vue.d.ts +1 -1
- package/dist/runtime/components/MLMedia/UploadStep.vue +4 -3
- package/dist/runtime/components/MLProcessing/PipelineStepItem.vue +7 -9
- package/dist/runtime/components/MLProcessing/StepperItem.vue +3 -3
- package/dist/runtime/composables/useMLMediaAssets.d.ts +11 -3
- package/dist/runtime/composables/useMLMediaAssets.js +29 -5
- package/dist/runtime/composables/useMLMediaEditor.d.ts +2 -0
- package/dist/runtime/composables/useMLMediaField.js +1 -1
- package/dist/runtime/composables/useMLMediaGallery.js +1 -1
- package/dist/runtime/composables/useMLMediaUpload.d.ts +3 -3
- package/dist/runtime/composables/useMLMediaUpload.js +2 -2
- package/dist/runtime/composables/useMLPermalinks.d.ts +20 -0
- package/dist/runtime/composables/useMLPermalinks.js +42 -0
- package/dist/runtime/composables/useMLProcessingPipeline.js +2 -1
- package/dist/runtime/composables/useMLProcessingStrategies.js +29 -17
- package/dist/runtime/composables/useMLUploadQueue.js +7 -3
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.js +3 -0
- package/dist/runtime/server/create-ml-proxy.d.ts +1 -17
- package/dist/runtime/server/create-ml-proxy.js +35 -114
- package/dist/runtime/server/delivery-proxy.js +4 -3
- package/dist/runtime/server/index.d.ts +2 -2
- package/dist/runtime/server/index.js +1 -7
- package/dist/runtime/server/permalinks-proxy.d.ts +2 -0
- package/dist/runtime/server/permalinks-proxy.js +2 -0
- package/dist/runtime/types/asset.d.ts +12 -4
- package/dist/runtime/types/permalink.d.ts +12 -0
- package/dist/runtime/types/permalink.js +0 -0
- package/dist/runtime/utils/backends.d.ts +28 -2
- package/dist/runtime/utils/backends.js +20 -3
- package/dist/runtime/utils/credentialHeaders.d.ts +19 -0
- package/dist/runtime/utils/credentialHeaders.js +26 -0
- package/dist/runtime/utils/permalinks.d.ts +11 -0
- package/dist/runtime/utils/permalinks.js +16 -0
- package/dist/runtime/utils/processingSteps.d.ts +14 -3
- package/dist/runtime/utils/processingSteps.js +15 -12
- package/package.json +2 -2
- package/dist/runtime/server/grants.d.ts +0 -124
- package/dist/runtime/server/grants.js +0 -164
package/README.md
CHANGED
|
@@ -53,7 +53,7 @@ export default defineNuxtConfig({
|
|
|
53
53
|
// Only for direct browser uploads (proxyUploads: false): a public address.
|
|
54
54
|
// publicBaseURL: 'https://media.example.com/v1',
|
|
55
55
|
// false when your own server/api/media-library/[...].ts serves /manage
|
|
56
|
-
// (
|
|
56
|
+
// (below). Default true: the module's /manage proxies.
|
|
57
57
|
// manageProxy: false,
|
|
58
58
|
// Uploads always go through Nuxt server (/api/media-library/uploads/*)
|
|
59
59
|
routes: {
|
|
@@ -66,17 +66,20 @@ export default defineNuxtConfig({
|
|
|
66
66
|
partSize: 8 * 1024 * 1024,
|
|
67
67
|
concurrency: 3,
|
|
68
68
|
checksum: { algorithm: 'SHA-256', perPart: true, final: true },
|
|
69
|
-
// Sent to browsers too, except credentials: Authorization,
|
|
70
|
-
// Proxy-Authorization and X-Api-Key stay server-side (the
|
|
71
|
-
|
|
69
|
+
// Optional. Sent to browsers too, except credentials: Authorization,
|
|
70
|
+
// Cookie, Proxy-Authorization and X-Api-Key stay server-side (only the
|
|
71
|
+
// /manage proxies add them, never /delivery). X-Tenant is not needed
|
|
72
|
+
// (the media library ignores it); it is still sent when set.
|
|
73
|
+
// headers: { 'X-Tenant': 'acme' },
|
|
72
74
|
},
|
|
73
75
|
})
|
|
74
76
|
```
|
|
75
77
|
|
|
76
78
|
Nuxt puts the public runtime config in every page, so the module never copies
|
|
77
79
|
`baseURL` or credential headers there. Set the media library's internal
|
|
78
|
-
address
|
|
79
|
-
|
|
80
|
+
address only server-side (`NUXT_MEDIA_LIBRARY_BASE_URL`). The Nuxt server
|
|
81
|
+
issues no media grants and needs no service token: the MLMedia widgets read
|
|
82
|
+
through each service's `resolve` backend (below).
|
|
80
83
|
|
|
81
84
|
### Nuxt usage
|
|
82
85
|
|
|
@@ -95,21 +98,63 @@ If you use the uploader component in Nuxt, provide config via the Nuxt plugin
|
|
|
95
98
|
</template>
|
|
96
99
|
```
|
|
97
100
|
|
|
98
|
-
###
|
|
101
|
+
### Reading files through your services (`resolve` backends)
|
|
102
|
+
|
|
103
|
+
The MLMedia widgets (`MLMediaGallery`, `MLMediaField`, the file picker, the
|
|
104
|
+
editor) and the upload queue read files through the service that owns them.
|
|
105
|
+
The service decides who may see what; the Nuxt server holds no media
|
|
106
|
+
credential and issues no grants.
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
// nuxt.config.ts (or the service's layer)
|
|
110
|
+
export default defineNuxtConfig({
|
|
111
|
+
mediaLibrary: {
|
|
112
|
+
backends: {
|
|
113
|
+
videos: {
|
|
114
|
+
upload: { start: '/api/videos/uploads' },
|
|
115
|
+
resolve: '/api/videos/media/resolve',
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
})
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```vue
|
|
123
|
+
<MLMediaGallery v-model="lesson.mediaIds" backend="videos" />
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
# the videos service (pkg_media[fastapi])
|
|
128
|
+
gate = AccessGate(client, can_access=lambda user, ids: videos.visible_media(user, ids))
|
|
129
|
+
app.include_router(create_media_router(gate, get_current_user, prefix="/videos/media"))
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
- Request: `POST {resolve}` from the browser, same origin, with the user's
|
|
133
|
+
session (cookies), body `{ "assetIds": ["…"] }`. At most 100 ids per
|
|
134
|
+
request; the widgets split longer lists.
|
|
135
|
+
- Response: `{ "items": [{ "assetId", "status", "asset", "error" }] }`, the
|
|
136
|
+
shape of the media library's `/manage/assets/resolve`. `status` is `ok`,
|
|
137
|
+
`not_found`, `forbidden` (the service's `can_access` refused) or `error`;
|
|
138
|
+
`asset` is set when `ok`. HLS/DASH links are reloaded before
|
|
139
|
+
`streamUrlExpiresIn` runs out, as before.
|
|
140
|
+
- `backend` picks the route: the field, the gallery (also when it places
|
|
141
|
+
picked files by kind), the picker's already-attached files, the editor
|
|
142
|
+
(`useMLMediaEditor().open(id, { backend })`), `useMLMediaAssets(ids, { backend })`
|
|
143
|
+
and the upload queue's status polling all read there.
|
|
144
|
+
- Without `backend`, or for a backend without `resolve`, they read through
|
|
145
|
+
Nuxt's own `/api/media-library/manage/assets/resolve` as before. An
|
|
146
|
+
unknown name shows the files as errors with a message naming it.
|
|
147
|
+
- Still through Nuxt's own `/manage` proxy: the picker's library list and
|
|
148
|
+
its preview of listed files, and the processing stream.
|
|
149
|
+
|
|
150
|
+
### Serving `/manage` yourself
|
|
99
151
|
|
|
100
152
|
The module's server proxies forward `/api/media-library/manage/*` to the
|
|
101
|
-
media library
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
endpoints, and no long-lived credential reaches the media library from the
|
|
107
|
-
browser.
|
|
108
|
-
|
|
109
|
-
Turn off the module's built-in `/manage` proxies so your handler gets the
|
|
110
|
-
requests. Their routes (`/api/media-library/manage/assets/**`…) are more
|
|
111
|
-
specific than a catch-all file and would otherwise answer first, without
|
|
112
|
-
`authorize` (the module warns at startup when it sees a shadowed catch-all):
|
|
153
|
+
media library with the browser's credentials and your static `headers`.
|
|
154
|
+
To add your own per-request credential, turn off the built-in `/manage`
|
|
155
|
+
proxies (their routes are more specific than a catch-all file and would
|
|
156
|
+
answer first; the module warns at startup when it sees a shadowed
|
|
157
|
+
catch-all) and serve them from one file:
|
|
113
158
|
|
|
114
159
|
```ts
|
|
115
160
|
// nuxt.config.ts
|
|
@@ -120,40 +165,25 @@ export default defineNuxtConfig({
|
|
|
120
165
|
|
|
121
166
|
```ts
|
|
122
167
|
// server/api/media-library/[...].ts
|
|
123
|
-
import { createMLCatchAllProxy
|
|
124
|
-
|
|
125
|
-
const COVER = { kind: 'image', visibility: 'public', allowedMimeTypes: ['image/*'], maxSizeBytes: 5_000_000 }
|
|
168
|
+
import { createMLCatchAllProxy } from 'nuxt-media-library/server'
|
|
126
169
|
|
|
127
170
|
export default createMLCatchAllProxy({
|
|
128
|
-
async
|
|
129
|
-
const user = await requireUser(event) // your session
|
|
130
|
-
const subject = String(user.id)
|
|
131
|
-
if (need.scope === 'upload' && !need.sessionId) { // creating an upload
|
|
132
|
-
return user.canUpload ? { scopes: ['upload'], profile: COVER, subject } : false
|
|
133
|
-
}
|
|
134
|
-
if (need.taskId) return false // or look up the task's asset
|
|
135
|
-
if (need.scope === 'manage:*') return user.isAdmin // tenant-wide list, stream
|
|
136
|
-
return (need.assetIds ?? []).every(id => user.canEdit(id))
|
|
137
|
-
? { ...specFromNeed(need), subject }
|
|
138
|
-
: false
|
|
139
|
-
},
|
|
171
|
+
requestHeaders: async event => ({ Authorization: `Bearer ${await getAccessToken(event)}` }),
|
|
140
172
|
})
|
|
141
173
|
```
|
|
142
174
|
|
|
143
|
-
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
`
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
that created the session.
|
|
156
|
-
- `/delivery` and the processing metadata routes are not affected.
|
|
175
|
+
- Grant mode is gone: `createMLCatchAllProxy` and `createMLProxy` no longer
|
|
176
|
+
take `authorize` or `issueGrant`, and the private `mediaLibrary.serviceToken`
|
|
177
|
+
config is not read. Passing either option throws, so an app that relied
|
|
178
|
+
on them fails loudly instead of forwarding unchecked. Read files through
|
|
179
|
+
`resolve` backends instead.
|
|
180
|
+
- `/delivery` never gets a credential from any source: not the static
|
|
181
|
+
credentials (a service token in `headers`), not the browser's own
|
|
182
|
+
`Authorization`, `Cookie`, `Proxy-Authorization` or `X-Api-Key` (a
|
|
183
|
+
signed-in user's token, which the media library refuses), and not one a
|
|
184
|
+
`requestHeaders` factory adds. Grants travel in `X-Media-Grants`; other
|
|
185
|
+
non-credential headers such as `X-Tenant` pass. `requestHeaders` still
|
|
186
|
+
sees the request's cookies, and `/manage` is unchanged.
|
|
157
187
|
- With `manageProxy: false` the module still serves `/api/media-library/delivery/**`
|
|
158
188
|
and, when `serviceUploads.proxy` is on, the service-upload pass-through.
|
|
159
189
|
Your catch-all handles everything else under `/api/media-library`.
|
|
@@ -218,13 +248,10 @@ await $fetch(`/api/videos/lessons/${lessonId}/video`, { method: 'PUT', body: { s
|
|
|
218
248
|
session's `GET`, parts `PUT`, `finalize` and `abort` to the private
|
|
219
249
|
`mediaLibrary.baseURL`. It requires the browser's `Bearer mlg_…` grant and
|
|
220
250
|
forwards it and `X-Tenant` untouched (the static `X-Tenant` when the
|
|
221
|
-
browser sends none). It never adds
|
|
222
|
-
|
|
251
|
+
browser sends none, and only if one is configured). It never adds static
|
|
252
|
+
auth headers, drops cookies, and streams request bodies. Mount it
|
|
223
253
|
yourself with `createMLServiceUploadsProxy()` from
|
|
224
254
|
`nuxt-media-library/server` for another path.
|
|
225
|
-
- Don't send these uploads through `createMLCatchAllProxy` with `authorize`:
|
|
226
|
-
grant mode replaces the browser's grant with its own, and the service's
|
|
227
|
-
`claim_upload` rejects the session.
|
|
228
255
|
|
|
229
256
|
#### One layer per service, widgets by backend name
|
|
230
257
|
|
|
@@ -240,7 +267,8 @@ export default defineNuxtConfig({
|
|
|
240
267
|
backends: {
|
|
241
268
|
videos: {
|
|
242
269
|
upload: { start: '/api/videos/uploads' }, // refresh defaults to `${start}/:sessionId/grant`
|
|
243
|
-
claim: '/api/videos/uploads/:sessionId/attach', //
|
|
270
|
+
claim: '/api/videos/uploads/:sessionId/attach', // pkg_media on_attach
|
|
271
|
+
resolve: '/api/videos/media/resolve', // pkg_media create_media_router
|
|
244
272
|
},
|
|
245
273
|
},
|
|
246
274
|
},
|
|
@@ -288,9 +316,10 @@ export default defineNuxtConfig({
|
|
|
288
316
|
`(assetId, role, sessionId)`, field: `(assetId, sessionId)`), including
|
|
289
317
|
files uploaded from the picker and chosen. Send both to the service's
|
|
290
318
|
attach endpoint: its `claim_upload` needs them.
|
|
291
|
-
- Processing progress uses
|
|
292
|
-
|
|
293
|
-
user
|
|
319
|
+
- Processing progress uses the processing stream and, when it is not
|
|
320
|
+
available, polls the backend's `resolve` route (Nuxt's own route without
|
|
321
|
+
one), so the service's `can_access` must let the user see a file they
|
|
322
|
+
just uploaded.
|
|
294
323
|
- There is no default backend: each layer's widgets name theirs. The module
|
|
295
324
|
only stores the paths; each layer's own routes reach its service.
|
|
296
325
|
|
|
@@ -316,6 +345,26 @@ name, extension, MIME type, kind, size, duration, width, height and pages.
|
|
|
316
345
|
has none yet.
|
|
317
346
|
- `useMLMediaMeta(ids, getAsset)` gives the same map for your own lists.
|
|
318
347
|
|
|
348
|
+
### Permalinks
|
|
349
|
+
|
|
350
|
+
A permalink is a stable public address `{media host}/p/{name}` for a
|
|
351
|
+
public asset (server `docs/api/permalinks.md`). Store the name, e.g.
|
|
352
|
+
`users/42/avatar`, and re-point it when the file changes:
|
|
353
|
+
|
|
354
|
+
```ts
|
|
355
|
+
const { set, get, remove } = useMLPermalinks()
|
|
356
|
+
const link = await set('users/42/avatar', assetId) // { name, assetId, url, createdAt, updatedAt }
|
|
357
|
+
await get('users/42/avatar') // null when unknown
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
- The media library takes a **service token only**. The module's
|
|
361
|
+
`/api/media-library/manage/permalinks/{name}` proxy works when
|
|
362
|
+
`mediaLibrary.headers` carries one. Otherwise call
|
|
363
|
+
`PUT/GET/DELETE {baseURL}/manage/permalinks/{name}` from your server
|
|
364
|
+
(`permalinkPath(name)` from `/server`).
|
|
365
|
+
- `isValidPermalinkName` checks a name the way the server does;
|
|
366
|
+
`permalinkUrl(mediaHost, name)` builds the public address.
|
|
367
|
+
|
|
319
368
|
### Error messages, in the editor's language
|
|
320
369
|
|
|
321
370
|
When a backend refuses a request it answers `{ detail, reason }`: `reason`
|
package/dist/module.d.mts
CHANGED
|
@@ -36,6 +36,11 @@ interface ModuleOptions {
|
|
|
36
36
|
perPart?: boolean;
|
|
37
37
|
final?: boolean;
|
|
38
38
|
};
|
|
39
|
+
/**
|
|
40
|
+
* Static headers the `/manage` proxies add upstream. Optional. Credential
|
|
41
|
+
* headers stay server-side and never go to `/delivery`. `X-Tenant` is not
|
|
42
|
+
* needed (the media library ignores it); it is still sent when set.
|
|
43
|
+
*/
|
|
39
44
|
headers?: Record<string, string> | (() => Record<string, string>);
|
|
40
45
|
/**
|
|
41
46
|
* Uploads started by the app's backend services (`useMLServiceUpload`).
|
|
@@ -56,19 +61,20 @@ interface ModuleOptions {
|
|
|
56
61
|
/**
|
|
57
62
|
* The built-in `/api/media-library/manage/**` proxies (default `true`).
|
|
58
63
|
* Set `false` when the app serves `/manage` itself, e.g. with
|
|
59
|
-
* `createMLCatchAllProxy({
|
|
64
|
+
* `createMLCatchAllProxy({ requestHeaders })` in
|
|
60
65
|
* `server/api/media-library/[...].ts`: the built-in routes are more
|
|
61
|
-
* specific than an app's catch-all and would otherwise answer first
|
|
62
|
-
*
|
|
63
|
-
* pass-through are still registered.
|
|
66
|
+
* specific than an app's catch-all and would otherwise answer first.
|
|
67
|
+
* `/delivery/**` and the service-upload pass-through are still registered.
|
|
64
68
|
*/
|
|
65
69
|
manageProxy?: boolean;
|
|
66
70
|
/**
|
|
67
71
|
* The backend registry: named places widgets upload to and read from
|
|
68
72
|
* (`<MLMediaGallery backend="videos" />`). Each entry has a `kind`
|
|
69
73
|
* (`media-library`, the default, or `media-kit`) and its routes: `upload`
|
|
70
|
-
* (`{ start, refresh? }`,
|
|
71
|
-
* (attach a finished upload, `/…/:sessionId/attach`)
|
|
74
|
+
* (`{ start, refresh? }`, pkg_media `create_upload_router`), `claim`
|
|
75
|
+
* (attach a finished upload, `/…/:sessionId/attach`) and `resolve` (where
|
|
76
|
+
* widgets read its assets: `POST { assetIds }` with the user's session,
|
|
77
|
+
* pkg_media `create_media_router`). Each service's Nuxt
|
|
72
78
|
* layer registers its own; Nuxt merges them. Paths only: each layer serves
|
|
73
79
|
* its routes.
|
|
74
80
|
*/
|
package/dist/module.d.ts
CHANGED
|
@@ -36,6 +36,11 @@ interface ModuleOptions {
|
|
|
36
36
|
perPart?: boolean;
|
|
37
37
|
final?: boolean;
|
|
38
38
|
};
|
|
39
|
+
/**
|
|
40
|
+
* Static headers the `/manage` proxies add upstream. Optional. Credential
|
|
41
|
+
* headers stay server-side and never go to `/delivery`. `X-Tenant` is not
|
|
42
|
+
* needed (the media library ignores it); it is still sent when set.
|
|
43
|
+
*/
|
|
39
44
|
headers?: Record<string, string> | (() => Record<string, string>);
|
|
40
45
|
/**
|
|
41
46
|
* Uploads started by the app's backend services (`useMLServiceUpload`).
|
|
@@ -56,19 +61,20 @@ interface ModuleOptions {
|
|
|
56
61
|
/**
|
|
57
62
|
* The built-in `/api/media-library/manage/**` proxies (default `true`).
|
|
58
63
|
* Set `false` when the app serves `/manage` itself, e.g. with
|
|
59
|
-
* `createMLCatchAllProxy({
|
|
64
|
+
* `createMLCatchAllProxy({ requestHeaders })` in
|
|
60
65
|
* `server/api/media-library/[...].ts`: the built-in routes are more
|
|
61
|
-
* specific than an app's catch-all and would otherwise answer first
|
|
62
|
-
*
|
|
63
|
-
* pass-through are still registered.
|
|
66
|
+
* specific than an app's catch-all and would otherwise answer first.
|
|
67
|
+
* `/delivery/**` and the service-upload pass-through are still registered.
|
|
64
68
|
*/
|
|
65
69
|
manageProxy?: boolean;
|
|
66
70
|
/**
|
|
67
71
|
* The backend registry: named places widgets upload to and read from
|
|
68
72
|
* (`<MLMediaGallery backend="videos" />`). Each entry has a `kind`
|
|
69
73
|
* (`media-library`, the default, or `media-kit`) and its routes: `upload`
|
|
70
|
-
* (`{ start, refresh? }`,
|
|
71
|
-
* (attach a finished upload, `/…/:sessionId/attach`)
|
|
74
|
+
* (`{ start, refresh? }`, pkg_media `create_upload_router`), `claim`
|
|
75
|
+
* (attach a finished upload, `/…/:sessionId/attach`) and `resolve` (where
|
|
76
|
+
* widgets read its assets: `POST { assetIds }` with the user's session,
|
|
77
|
+
* pkg_media `create_media_router`). Each service's Nuxt
|
|
72
78
|
* layer registers its own; Nuxt merges them. Paths only: each layer serves
|
|
73
79
|
* its routes.
|
|
74
80
|
*/
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,23 +1,13 @@
|
|
|
1
1
|
import { existsSync, readdirSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { defineNuxtModule, createResolver, addImportsDir, addComponentsDir, addServerHandler, useLogger, addPlugin } from '@nuxt/kit';
|
|
4
|
+
import { browserHeaders } from '../dist/runtime/utils/credentialHeaders.js';
|
|
4
5
|
import { normalizeBackends } from '../dist/runtime/utils/backends.js';
|
|
5
6
|
export * from '../dist/runtime/types/resourcePolicy.js';
|
|
6
7
|
export * from '../dist/runtime/types/uploader.js';
|
|
7
8
|
export * from '../dist/runtime/types/preview.js';
|
|
8
9
|
export * from '../dist/runtime/types/asset.js';
|
|
9
10
|
|
|
10
|
-
const CREDENTIAL_HEADERS = ["authorization", "proxy-authorization", "cookie", "x-api-key"];
|
|
11
|
-
function browserHeaders(headers) {
|
|
12
|
-
if (!headers || typeof headers !== "object") return { headers: {}, dropped: [] };
|
|
13
|
-
const kept = {};
|
|
14
|
-
const dropped = [];
|
|
15
|
-
for (const [name, value] of Object.entries(headers)) {
|
|
16
|
-
if (CREDENTIAL_HEADERS.includes(name.toLowerCase())) dropped.push(name);
|
|
17
|
-
else kept[name] = value;
|
|
18
|
-
}
|
|
19
|
-
return { headers: kept, dropped };
|
|
20
|
-
}
|
|
21
11
|
function browserBaseURL(existingPublic, publicBaseURL) {
|
|
22
12
|
return existingPublic ?? publicBaseURL ?? "";
|
|
23
13
|
}
|
|
@@ -128,9 +118,13 @@ const module$1 = defineNuxtModule({
|
|
|
128
118
|
route: "/api/media-library/manage/processing/**",
|
|
129
119
|
handler: resolver.resolve("./runtime/server/processing-proxy")
|
|
130
120
|
});
|
|
121
|
+
addServerHandler({
|
|
122
|
+
route: "/api/media-library/manage/permalinks/**",
|
|
123
|
+
handler: resolver.resolve("./runtime/server/permalinks-proxy")
|
|
124
|
+
});
|
|
131
125
|
const shadowed = appCatchAlls(nuxt.options._layers);
|
|
132
126
|
if (shadowed.length) {
|
|
133
|
-
logger.warn(`${shadowed.join(", ")} will not receive /api/media-library/manage requests: the module's own /manage routes answer first. Set mediaLibrary.manageProxy: false to serve /manage from your handler (e.g. createMLCatchAllProxy({
|
|
127
|
+
logger.warn(`${shadowed.join(", ")} will not receive /api/media-library/manage requests: the module's own /manage routes answer first. Set mediaLibrary.manageProxy: false to serve /manage from your handler (e.g. createMLCatchAllProxy({ requestHeaders })).`);
|
|
134
128
|
}
|
|
135
129
|
}
|
|
136
130
|
if (options.serviceUploads?.proxy) {
|
|
@@ -143,10 +137,7 @@ const module$1 = defineNuxtModule({
|
|
|
143
137
|
...existingPrivate,
|
|
144
138
|
baseURL: existingPrivate.baseURL ?? envBaseURL ?? options.baseURL ?? "",
|
|
145
139
|
routes: existingPrivate.routes ?? options.routes ?? routes,
|
|
146
|
-
headers: resolvedHeaders
|
|
147
|
-
// Only read when a proxy has `authorize` and no `issueGrant` (media
|
|
148
|
-
// grants). Declared so NUXT_MEDIA_LIBRARY_SERVICE_TOKEN applies.
|
|
149
|
-
serviceToken: existingPrivate.serviceToken ?? ""
|
|
140
|
+
headers: resolvedHeaders
|
|
150
141
|
};
|
|
151
142
|
const publicHeaders = browserHeaders(existingPublic.headers ?? options.headers);
|
|
152
143
|
if (publicHeaders.dropped.length) {
|
|
@@ -13,6 +13,7 @@ type __VLS_Props = {
|
|
|
13
13
|
readonly?: boolean;
|
|
14
14
|
removeLabel?: string;
|
|
15
15
|
usages?: Readonly<Record<string, MediaUsage[]>>;
|
|
16
|
+
backend?: string | null;
|
|
16
17
|
};
|
|
17
18
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
18
19
|
close: (result: MediaEditorResult | null) => any;
|
|
@@ -5,7 +5,8 @@ defineProps({
|
|
|
5
5
|
alt: { type: Object, required: false },
|
|
6
6
|
readonly: { type: Boolean, required: false },
|
|
7
7
|
removeLabel: { type: String, required: false },
|
|
8
|
-
usages: { type: Object, required: false }
|
|
8
|
+
usages: { type: Object, required: false },
|
|
9
|
+
backend: { type: [String, null], required: false }
|
|
9
10
|
});
|
|
10
11
|
const emit = defineEmits(["close"]);
|
|
11
12
|
</script>
|
|
@@ -25,6 +26,7 @@ const emit = defineEmits(["close"]);
|
|
|
25
26
|
:readonly="readonly"
|
|
26
27
|
:remove-label="removeLabel"
|
|
27
28
|
:usages="usages"
|
|
29
|
+
:backend="backend"
|
|
28
30
|
@save="({ alt: a }) => emit('close', { action: 'save', alt: a })"
|
|
29
31
|
@remove="(id) => emit('close', { action: 'remove', assetId: id })"
|
|
30
32
|
@cancel="emit('close', null)"
|
|
@@ -13,6 +13,7 @@ type __VLS_Props = {
|
|
|
13
13
|
readonly?: boolean;
|
|
14
14
|
removeLabel?: string;
|
|
15
15
|
usages?: Readonly<Record<string, MediaUsage[]>>;
|
|
16
|
+
backend?: string | null;
|
|
16
17
|
};
|
|
17
18
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
18
19
|
close: (result: MediaEditorResult | null) => any;
|
|
@@ -12,6 +12,8 @@ type __VLS_Props = {
|
|
|
12
12
|
removeLabel?: string;
|
|
13
13
|
/** Where each file is used, per asset ID. Omit to hide the section. */
|
|
14
14
|
usages?: Readonly<Record<string, MediaUsage[]>>;
|
|
15
|
+
/** The backend (`mediaLibrary.backends`) whose `resolve` route reads the files. */
|
|
16
|
+
backend?: string | null;
|
|
15
17
|
};
|
|
16
18
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
|
|
17
19
|
currentId: import("vue").ComputedRef<string>;
|
|
@@ -28,6 +30,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
|
|
|
28
30
|
alt: Record<string, string>;
|
|
29
31
|
}) => any) | undefined;
|
|
30
32
|
}>, {
|
|
33
|
+
backend: string | null;
|
|
31
34
|
usages: Readonly<Record<string, MediaUsage[]>>;
|
|
32
35
|
siblings: readonly string[];
|
|
33
36
|
alt: Readonly<Record<string, string>>;
|
|
@@ -8,7 +8,8 @@ const props = defineProps({
|
|
|
8
8
|
alt: { type: Object, required: false, default: () => ({}) },
|
|
9
9
|
readonly: { type: Boolean, required: false, default: false },
|
|
10
10
|
removeLabel: { type: String, required: false, default: "" },
|
|
11
|
-
usages: { type: Object, required: false, default: void 0 }
|
|
11
|
+
usages: { type: Object, required: false, default: void 0 },
|
|
12
|
+
backend: { type: [String, null], required: false, default: null }
|
|
12
13
|
});
|
|
13
14
|
const emit = defineEmits(["save", "remove", "cancel"]);
|
|
14
15
|
const list = computed(() => {
|
|
@@ -17,7 +18,7 @@ const list = computed(() => {
|
|
|
17
18
|
});
|
|
18
19
|
const index = ref(Math.max(0, list.value.indexOf(props.assetId)));
|
|
19
20
|
const currentId = computed(() => list.value[index.value] ?? props.assetId);
|
|
20
|
-
const library = useMLMediaAssets(list);
|
|
21
|
+
const library = useMLMediaAssets(list, { backend: () => props.backend });
|
|
21
22
|
const asset = computed(() => library.get(currentId.value));
|
|
22
23
|
const lookup = computed(() => library.lookup(currentId.value));
|
|
23
24
|
const name = computed(() => asset.value ? assetName(asset.value) : "");
|
|
@@ -12,6 +12,8 @@ type __VLS_Props = {
|
|
|
12
12
|
removeLabel?: string;
|
|
13
13
|
/** Where each file is used, per asset ID. Omit to hide the section. */
|
|
14
14
|
usages?: Readonly<Record<string, MediaUsage[]>>;
|
|
15
|
+
/** The backend (`mediaLibrary.backends`) whose `resolve` route reads the files. */
|
|
16
|
+
backend?: string | null;
|
|
15
17
|
};
|
|
16
18
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
|
|
17
19
|
currentId: import("vue").ComputedRef<string>;
|
|
@@ -28,6 +30,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
|
|
|
28
30
|
alt: Record<string, string>;
|
|
29
31
|
}) => any) | undefined;
|
|
30
32
|
}>, {
|
|
33
|
+
backend: string | null;
|
|
31
34
|
usages: Readonly<Record<string, MediaUsage[]>>;
|
|
32
35
|
siblings: readonly string[];
|
|
33
36
|
alt: Readonly<Record<string, string>>;
|
|
@@ -63,9 +63,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
63
63
|
"onUpdate:alt"?: ((value: string | undefined) => any) | undefined;
|
|
64
64
|
"onUpdate:meta"?: ((value: MediaFileMeta | null) => any) | undefined;
|
|
65
65
|
}>, {
|
|
66
|
-
visibility: "private" | "
|
|
67
|
-
disabled: boolean;
|
|
66
|
+
visibility: "private" | "restricted" | "public";
|
|
68
67
|
label: string;
|
|
68
|
+
disabled: boolean;
|
|
69
69
|
backend: string | null;
|
|
70
70
|
service: string | import("../../index.js").MediaServiceUpload | null;
|
|
71
71
|
profile: string | null;
|
|
@@ -120,7 +120,8 @@ async function openEditor() {
|
|
|
120
120
|
const result = await mediaEditor.open(id, {
|
|
121
121
|
alt: alt.value ?? "",
|
|
122
122
|
readonly: props.disabled,
|
|
123
|
-
removeLabel: props.removeLabel
|
|
123
|
+
removeLabel: props.removeLabel,
|
|
124
|
+
backend: props.backend
|
|
124
125
|
});
|
|
125
126
|
if (result?.action === "save") alt.value = result.alt[id] ?? "";
|
|
126
127
|
else if (result?.action === "remove") field.clear();
|
|
@@ -63,9 +63,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
63
63
|
"onUpdate:alt"?: ((value: string | undefined) => any) | undefined;
|
|
64
64
|
"onUpdate:meta"?: ((value: MediaFileMeta | null) => any) | undefined;
|
|
65
65
|
}>, {
|
|
66
|
-
visibility: "private" | "
|
|
67
|
-
disabled: boolean;
|
|
66
|
+
visibility: "private" | "restricted" | "public";
|
|
68
67
|
label: string;
|
|
68
|
+
disabled: boolean;
|
|
69
69
|
backend: string | null;
|
|
70
70
|
service: string | import("../../index.js").MediaServiceUpload | null;
|
|
71
71
|
profile: string | null;
|
|
@@ -62,7 +62,7 @@ function toggle(id) {
|
|
|
62
62
|
choose(id);
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
|
-
const pinned = useMLMediaAssets(() => initial);
|
|
65
|
+
const pinned = useMLMediaAssets(() => initial, { backend: () => props.upload?.backend });
|
|
66
66
|
const pinnedAssets = computed(() => initial.map((id) => pinned.get(id)).filter((a) => !!a));
|
|
67
67
|
const uploader = useMLMediaUpload(props.upload);
|
|
68
68
|
const rejectNote = ref(null);
|
|
@@ -76,10 +76,10 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
76
76
|
"onUpdate:profiles"?: ((profiles: MediaGalleryProfiles) => any) | undefined;
|
|
77
77
|
}>, {
|
|
78
78
|
title: string;
|
|
79
|
-
visibility: "private" | "
|
|
79
|
+
visibility: "private" | "restricted" | "public";
|
|
80
80
|
disabled: boolean;
|
|
81
81
|
backend: string | null;
|
|
82
|
-
service: string | import("
|
|
82
|
+
service: string | import("#imports").MediaServiceUpload | null;
|
|
83
83
|
profile: string | null;
|
|
84
84
|
processingConfig: {
|
|
85
85
|
stepOptions?: Record<string, Record<string, unknown>> | null;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { computed, ref, watch } from "vue";
|
|
3
3
|
import { $fetch } from "ofetch";
|
|
4
|
+
import { useRuntimeConfig } from "#imports";
|
|
4
5
|
import { watchMediaMeta } from "../../composables/useMLMediaMeta";
|
|
5
6
|
import { kindForFile } from "../../utils/fileAccept";
|
|
7
|
+
import { backendsFromConfig, resolveAssetsUrl } from "../../utils/backends";
|
|
6
8
|
import { GALLERY_TILE_TYPE } from "../../utils/galleryDrag";
|
|
7
9
|
import GallerySection from "./GallerySection.vue";
|
|
8
10
|
const props = defineProps({
|
|
@@ -147,11 +149,12 @@ function routeFiles(files) {
|
|
|
147
149
|
}
|
|
148
150
|
if (left.length) notice.value = `${left[0].name}${left.length > 1 ? ` and ${left.length - 1} more` : ""} can't be used in any role here.`;
|
|
149
151
|
}
|
|
152
|
+
const backends = backendsFromConfig(useRuntimeConfig().public?.mediaLibrary);
|
|
150
153
|
async function placeByKind(ids) {
|
|
151
154
|
let kinds = /* @__PURE__ */ new Map();
|
|
152
155
|
try {
|
|
153
156
|
const res = await $fetch(
|
|
154
|
-
|
|
157
|
+
resolveAssetsUrl(props.backend, backends),
|
|
155
158
|
{ method: "POST", body: { assetIds: ids } }
|
|
156
159
|
);
|
|
157
160
|
kinds = new Map(res.items.map((it, i) => [it.asset?.id ?? it.assetId ?? ids[i], it.asset?.kind?.toLowerCase() ?? null]));
|
|
@@ -76,10 +76,10 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
76
76
|
"onUpdate:profiles"?: ((profiles: MediaGalleryProfiles) => any) | undefined;
|
|
77
77
|
}>, {
|
|
78
78
|
title: string;
|
|
79
|
-
visibility: "private" | "
|
|
79
|
+
visibility: "private" | "restricted" | "public";
|
|
80
80
|
disabled: boolean;
|
|
81
81
|
backend: string | null;
|
|
82
|
-
service: string | import("
|
|
82
|
+
service: string | import("#imports").MediaServiceUpload | null;
|
|
83
83
|
profile: string | null;
|
|
84
84
|
processingConfig: {
|
|
85
85
|
stepOptions?: Record<string, Record<string, unknown>> | null;
|
|
@@ -80,7 +80,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
80
80
|
"onUpdate:alt"?: ((value: Record<string, string> | undefined) => any) | undefined;
|
|
81
81
|
}>, {
|
|
82
82
|
title: string;
|
|
83
|
-
visibility: "private" | "
|
|
83
|
+
visibility: "private" | "restricted" | "public";
|
|
84
84
|
disabled: boolean;
|
|
85
85
|
highlight: ReadonlySet<string> | null;
|
|
86
86
|
backend: string | null;
|
|
@@ -161,7 +161,8 @@ async function openEditor(id) {
|
|
|
161
161
|
siblings: model.value,
|
|
162
162
|
alt: alt.value ?? {},
|
|
163
163
|
readonly: props.disabled,
|
|
164
|
-
removeLabel: removeLabel.value
|
|
164
|
+
removeLabel: removeLabel.value,
|
|
165
|
+
backend: props.backend
|
|
165
166
|
});
|
|
166
167
|
if (result?.action === "save") alt.value = { ...alt.value ?? {}, ...result.alt };
|
|
167
168
|
else if (result?.action === "remove") gallery.remove([result.assetId]);
|
|
@@ -80,7 +80,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
80
80
|
"onUpdate:alt"?: ((value: Record<string, string> | undefined) => any) | undefined;
|
|
81
81
|
}>, {
|
|
82
82
|
title: string;
|
|
83
|
-
visibility: "private" | "
|
|
83
|
+
visibility: "private" | "restricted" | "public";
|
|
84
84
|
disabled: boolean;
|
|
85
85
|
highlight: ReadonlySet<string> | null;
|
|
86
86
|
backend: string | null;
|
|
@@ -41,9 +41,9 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
|
|
|
41
41
|
asset: AssetDetail | DeliveryAsset | null;
|
|
42
42
|
src: string | null;
|
|
43
43
|
progress: number | null;
|
|
44
|
+
name: string;
|
|
44
45
|
duration: number | null;
|
|
45
46
|
disabled: boolean;
|
|
46
|
-
name: string;
|
|
47
47
|
phase: ProgressVeilPhase | null;
|
|
48
48
|
selected: boolean;
|
|
49
49
|
missingAlt: boolean;
|
|
@@ -41,9 +41,9 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
|
|
|
41
41
|
asset: AssetDetail | DeliveryAsset | null;
|
|
42
42
|
src: string | null;
|
|
43
43
|
progress: number | null;
|
|
44
|
+
name: string;
|
|
44
45
|
duration: number | null;
|
|
45
46
|
disabled: boolean;
|
|
46
|
-
name: string;
|
|
47
47
|
phase: ProgressVeilPhase | null;
|
|
48
48
|
selected: boolean;
|
|
49
49
|
missingAlt: boolean;
|
|
@@ -25,8 +25,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
|
|
|
25
25
|
kind: string | null;
|
|
26
26
|
asset: AssetDetail | DeliveryAsset | null;
|
|
27
27
|
src: string | null;
|
|
28
|
-
disabled: boolean;
|
|
29
28
|
name: string;
|
|
29
|
+
disabled: boolean;
|
|
30
30
|
meta: string;
|
|
31
31
|
selected: boolean;
|
|
32
32
|
disabledReason: string;
|