@airdraft/plugin-media 0.2.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/CHANGELOG.md +5 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/withMedia.d.ts +59 -0
- package/dist/withMedia.d.ts.map +1 -0
- package/dist/withMedia.js +510 -0
- package/dist/withMedia.js.map +1 -0
- package/package.json +41 -0
package/CHANGELOG.md
ADDED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { Plugin, MediaAdapter, StorageAdapter, MediaUploadResult } from '@airdraft/core';
|
|
2
|
+
export interface WithMediaOptions {
|
|
3
|
+
/** Required. The media adapter instance (handles binary storage). */
|
|
4
|
+
adapter: MediaAdapter;
|
|
5
|
+
/**
|
|
6
|
+
* Optional. A content StorageAdapter used to persist JSON sidecar files at
|
|
7
|
+
* `_media/{key}.json`. Activates the full media library feature set.
|
|
8
|
+
* Omitting this field leaves withMedia() fully backwards-compatible with v0.1.
|
|
9
|
+
*/
|
|
10
|
+
storageAdapter?: StorageAdapter;
|
|
11
|
+
/** Storage key prefix. Default: 'uploads' */
|
|
12
|
+
prefix?: string;
|
|
13
|
+
/** Override key generation. Receives file metadata, returns the full key. */
|
|
14
|
+
generateKey?: (file: {
|
|
15
|
+
name: string;
|
|
16
|
+
type: string;
|
|
17
|
+
size: number;
|
|
18
|
+
}) => string;
|
|
19
|
+
/** MIME type allowlist. Checked before the adapter is called. Default: all types allowed. */
|
|
20
|
+
allowedTypes?: string[];
|
|
21
|
+
/** Max file size in bytes. Checked before the adapter is called. Default: unlimited. */
|
|
22
|
+
maxSize?: number;
|
|
23
|
+
}
|
|
24
|
+
export type { MediaUploadResult };
|
|
25
|
+
declare function sanitizeName(name: string): string;
|
|
26
|
+
declare function hash4(input: string): string;
|
|
27
|
+
declare function defaultGenerateKey(file: {
|
|
28
|
+
name: string;
|
|
29
|
+
type: string;
|
|
30
|
+
size: number;
|
|
31
|
+
}, prefix: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Adds media management to the Airdraft CMS.
|
|
34
|
+
*
|
|
35
|
+
* Registers four routes:
|
|
36
|
+
* POST /media/upload
|
|
37
|
+
* GET /media
|
|
38
|
+
* DELETE /media/:key
|
|
39
|
+
* GET /media/:key/url
|
|
40
|
+
*
|
|
41
|
+
* Also implements `hooks.transformEntry` to inject `{field}_url` companion
|
|
42
|
+
* fields for every `image`-type field in a collection.
|
|
43
|
+
*
|
|
44
|
+
* ```ts
|
|
45
|
+
* import { withMedia } from '@airdraft/plugin-media'
|
|
46
|
+
* import { LocalMediaAdapter } from '@airdraft/media-adapter-local'
|
|
47
|
+
*
|
|
48
|
+
* plugins: [
|
|
49
|
+
* withMedia({
|
|
50
|
+
* adapter: new LocalMediaAdapter({ root: process.cwd() + '/public' }),
|
|
51
|
+
* allowedTypes: ['image/jpeg', 'image/png', 'image/webp'],
|
|
52
|
+
* maxSize: 5 * 1024 * 1024,
|
|
53
|
+
* }),
|
|
54
|
+
* ]
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare function withMedia(options: WithMediaOptions): Plugin;
|
|
58
|
+
export { defaultGenerateKey, sanitizeName, hash4 };
|
|
59
|
+
//# sourceMappingURL=withMedia.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"withMedia.d.ts","sourceRoot":"","sources":["../src/withMedia.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,MAAM,EACN,YAAY,EACZ,cAAc,EACd,iBAAiB,EAIlB,MAAM,gBAAgB,CAAA;AAMvB,MAAM,WAAW,gBAAgB;IAC/B,qEAAqE;IACrE,OAAO,EAAE,YAAY,CAAA;IACrB;;;;OAIG;IACH,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,6EAA6E;IAC7E,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,MAAM,CAAA;IAC5E,6FAA6F;IAC7F,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,wFAAwF;IACxF,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAGD,YAAY,EAAE,iBAAiB,EAAE,CAAA;AA2BjC,iBAAS,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK1C;AAED,iBAAS,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpC;AAED,iBAAS,kBAAkB,CACzB,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAClD,MAAM,EAAE,MAAM,GACb,MAAM,CAcR;AAwBD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CA6b3D;AAMD,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,KAAK,EAAE,CAAA"}
|
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// Sidecar helpers
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
function sidecarPath(key) {
|
|
6
|
+
return '_media/' + key + '.json';
|
|
7
|
+
}
|
|
8
|
+
/** Normalise tag values: trim, lowercase, spaces/underscores → hyphens, max 50 chars, dedupe. */
|
|
9
|
+
function normalizeTags(tags) {
|
|
10
|
+
if (!Array.isArray(tags))
|
|
11
|
+
return undefined;
|
|
12
|
+
const seen = new Set();
|
|
13
|
+
const result = [];
|
|
14
|
+
for (const t of tags) {
|
|
15
|
+
if (typeof t !== 'string')
|
|
16
|
+
continue;
|
|
17
|
+
const normalized = t.trim().toLowerCase().replace(/[\s_]+/g, '-').slice(0, 50);
|
|
18
|
+
if (normalized && !seen.has(normalized)) {
|
|
19
|
+
seen.add(normalized);
|
|
20
|
+
result.push(normalized);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return result.length > 0 ? result : undefined;
|
|
24
|
+
}
|
|
25
|
+
function sanitizeName(name) {
|
|
26
|
+
return name
|
|
27
|
+
.replace(/[/\\]/g, '') // strip path separators
|
|
28
|
+
.replace(/[^a-zA-Z0-9._-]/g, '_') // replace unsafe chars
|
|
29
|
+
.toLowerCase();
|
|
30
|
+
}
|
|
31
|
+
function hash4(input) {
|
|
32
|
+
return createHash('sha1').update(input).digest('hex').slice(0, 4);
|
|
33
|
+
}
|
|
34
|
+
function defaultGenerateKey(file, prefix) {
|
|
35
|
+
const now = new Date();
|
|
36
|
+
const yyyy = now.getUTCFullYear();
|
|
37
|
+
const mm = String(now.getUTCMonth() + 1).padStart(2, '0');
|
|
38
|
+
const lastDot = file.name.lastIndexOf('.');
|
|
39
|
+
const stem = lastDot === -1 ? file.name : file.name.slice(0, lastDot);
|
|
40
|
+
const ext = lastDot === -1 ? '' : file.name.slice(lastDot + 1).toLowerCase();
|
|
41
|
+
const safeStem = sanitizeName(stem);
|
|
42
|
+
const h = hash4(file.name + Date.now());
|
|
43
|
+
const filename = ext ? `${safeStem}-${h}.${ext}` : `${safeStem}-${h}`;
|
|
44
|
+
return `${prefix}/${yyyy}/${mm}/${filename}`;
|
|
45
|
+
}
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Error helpers
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
function errorRes(status, code, message) {
|
|
50
|
+
return new Response(JSON.stringify({ error: { code, message, details: {} } }), { status, headers: { 'Content-Type': 'application/json' } });
|
|
51
|
+
}
|
|
52
|
+
function jsonRes(data, status = 200) {
|
|
53
|
+
return new Response(JSON.stringify(data), {
|
|
54
|
+
status,
|
|
55
|
+
headers: { 'Content-Type': 'application/json' },
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// withMedia
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
/**
|
|
62
|
+
* Adds media management to the Airdraft CMS.
|
|
63
|
+
*
|
|
64
|
+
* Registers four routes:
|
|
65
|
+
* POST /media/upload
|
|
66
|
+
* GET /media
|
|
67
|
+
* DELETE /media/:key
|
|
68
|
+
* GET /media/:key/url
|
|
69
|
+
*
|
|
70
|
+
* Also implements `hooks.transformEntry` to inject `{field}_url` companion
|
|
71
|
+
* fields for every `image`-type field in a collection.
|
|
72
|
+
*
|
|
73
|
+
* ```ts
|
|
74
|
+
* import { withMedia } from '@airdraft/plugin-media'
|
|
75
|
+
* import { LocalMediaAdapter } from '@airdraft/media-adapter-local'
|
|
76
|
+
*
|
|
77
|
+
* plugins: [
|
|
78
|
+
* withMedia({
|
|
79
|
+
* adapter: new LocalMediaAdapter({ root: process.cwd() + '/public' }),
|
|
80
|
+
* allowedTypes: ['image/jpeg', 'image/png', 'image/webp'],
|
|
81
|
+
* maxSize: 5 * 1024 * 1024,
|
|
82
|
+
* }),
|
|
83
|
+
* ]
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export function withMedia(options) {
|
|
87
|
+
const { adapter, storageAdapter, prefix = 'uploads', allowedTypes, maxSize } = options;
|
|
88
|
+
const genKey = options.generateKey
|
|
89
|
+
? (file) => options.generateKey(file)
|
|
90
|
+
: (file) => defaultGenerateKey(file, prefix);
|
|
91
|
+
// ── POST /media/upload ───────────────────────────────────────────────────
|
|
92
|
+
async function handleUpload(req) {
|
|
93
|
+
if (req.method !== 'POST') {
|
|
94
|
+
return errorRes(405, 'METHOD_NOT_ALLOWED', 'Use POST /media/upload');
|
|
95
|
+
}
|
|
96
|
+
let formData;
|
|
97
|
+
try {
|
|
98
|
+
formData = await req.formData();
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return errorRes(400, 'INVALID_REQUEST', 'Request must be multipart/form-data');
|
|
102
|
+
}
|
|
103
|
+
const file = formData.get('file');
|
|
104
|
+
if (!file || !(file instanceof File)) {
|
|
105
|
+
return errorRes(400, 'INVALID_REQUEST', 'Missing or invalid "file" field in form data');
|
|
106
|
+
}
|
|
107
|
+
if (allowedTypes && !allowedTypes.includes(file.type)) {
|
|
108
|
+
return errorRes(415, 'UNSUPPORTED_TYPE', `File type "${file.type}" is not allowed`);
|
|
109
|
+
}
|
|
110
|
+
if (maxSize !== undefined && file.size > maxSize) {
|
|
111
|
+
return errorRes(413, 'FILE_TOO_LARGE', `File exceeds maximum size of ${maxSize} bytes`);
|
|
112
|
+
}
|
|
113
|
+
const key = genKey({ name: file.name, type: file.type, size: file.size });
|
|
114
|
+
let result;
|
|
115
|
+
try {
|
|
116
|
+
result = await adapter.upload(key, file, { mimeType: file.type });
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
console.error('[airdraft/plugin-media] Upload failed:', err);
|
|
120
|
+
return errorRes(502, 'UPLOAD_FAILED', 'Upload to storage adapter failed');
|
|
121
|
+
}
|
|
122
|
+
// Library mode: write sidecar (best-effort)
|
|
123
|
+
if (storageAdapter) {
|
|
124
|
+
const warnings = [];
|
|
125
|
+
const uploadedAt = new Date().toISOString();
|
|
126
|
+
const sidecarData = {
|
|
127
|
+
key,
|
|
128
|
+
name: file.name,
|
|
129
|
+
mimeType: file.type,
|
|
130
|
+
size: file.size,
|
|
131
|
+
uploadedAt,
|
|
132
|
+
...(result.width != null ? { width: result.width } : {}),
|
|
133
|
+
...(result.height != null ? { height: result.height } : {}),
|
|
134
|
+
};
|
|
135
|
+
try {
|
|
136
|
+
await storageAdapter.write(sidecarPath(key), JSON.stringify(sidecarData, null, 2), {
|
|
137
|
+
message: `chore: add media metadata for ${file.name}`,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
console.error('[airdraft/plugin-media] Sidecar write failed:', err);
|
|
142
|
+
warnings.push('metadata_save_failed');
|
|
143
|
+
}
|
|
144
|
+
const item = {
|
|
145
|
+
key,
|
|
146
|
+
name: file.name,
|
|
147
|
+
mimeType: file.type,
|
|
148
|
+
size: file.size,
|
|
149
|
+
uploadedAt,
|
|
150
|
+
url: result.url,
|
|
151
|
+
isIndexed: warnings.length === 0,
|
|
152
|
+
...(result.width != null ? { width: result.width } : {}),
|
|
153
|
+
...(result.height != null ? { height: result.height } : {}),
|
|
154
|
+
};
|
|
155
|
+
const body = { data: item };
|
|
156
|
+
if (warnings.length > 0)
|
|
157
|
+
body.warnings = warnings;
|
|
158
|
+
return jsonRes(body, 201);
|
|
159
|
+
}
|
|
160
|
+
return jsonRes({ data: result }, 201);
|
|
161
|
+
}
|
|
162
|
+
// ── GET /media ───────────────────────────────────────────────────────────
|
|
163
|
+
async function handleList(req) {
|
|
164
|
+
if (req.method !== 'GET') {
|
|
165
|
+
return errorRes(405, 'METHOD_NOT_ALLOWED', 'Use GET /media');
|
|
166
|
+
}
|
|
167
|
+
const url = new URL(req.url);
|
|
168
|
+
const listPrefix = url.searchParams.get('prefix') ?? undefined;
|
|
169
|
+
const limit = url.searchParams.has('limit')
|
|
170
|
+
? parseInt(url.searchParams.get('limit'), 10)
|
|
171
|
+
: undefined;
|
|
172
|
+
const cursor = url.searchParams.get('cursor') ?? undefined;
|
|
173
|
+
const search = url.searchParams.get('search') ?? undefined;
|
|
174
|
+
const mimeTypeFilter = url.searchParams.get('mimeType') ?? undefined;
|
|
175
|
+
let result;
|
|
176
|
+
try {
|
|
177
|
+
result = await adapter.list({ prefix: listPrefix, limit, cursor });
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
const msg = err instanceof Error ? err.message : 'Failed to list media';
|
|
181
|
+
return errorRes(502, 'ADAPTER_ERROR', msg);
|
|
182
|
+
}
|
|
183
|
+
// Without library mode: return MediaListItem-shaped items with isIndexed: false
|
|
184
|
+
if (!storageAdapter) {
|
|
185
|
+
return jsonRes({
|
|
186
|
+
data: result.items.map(item => ({
|
|
187
|
+
...item,
|
|
188
|
+
uploadedAt: item.lastModified ?? '',
|
|
189
|
+
isIndexed: false,
|
|
190
|
+
})),
|
|
191
|
+
meta: { ...(result.cursor ? { cursor: result.cursor } : {}) },
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
// Library mode: enrich each item with sidecar data in parallel
|
|
195
|
+
const settled = await Promise.allSettled(result.items.map(async (item) => {
|
|
196
|
+
try {
|
|
197
|
+
const sidecarFile = await storageAdapter.read(sidecarPath(item.key));
|
|
198
|
+
if (sidecarFile) {
|
|
199
|
+
const meta = JSON.parse(sidecarFile.content);
|
|
200
|
+
return {
|
|
201
|
+
key: item.key,
|
|
202
|
+
name: typeof meta.name === 'string' ? meta.name : item.name,
|
|
203
|
+
mimeType: item.mimeType,
|
|
204
|
+
size: item.size,
|
|
205
|
+
uploadedAt: typeof meta.uploadedAt === 'string' ? meta.uploadedAt : (item.lastModified ?? ''),
|
|
206
|
+
url: item.url,
|
|
207
|
+
isIndexed: true,
|
|
208
|
+
...(typeof meta.alt === 'string' ? { alt: meta.alt } : {}),
|
|
209
|
+
...(typeof meta.caption === 'string' ? { caption: meta.caption } : {}),
|
|
210
|
+
...(typeof meta.title === 'string' ? { title: meta.title } : {}),
|
|
211
|
+
...(Array.isArray(meta.tags) ? { tags: meta.tags } : {}),
|
|
212
|
+
...(typeof meta.width === 'number' ? { width: meta.width } : {}),
|
|
213
|
+
...(typeof meta.height === 'number' ? { height: meta.height } : {}),
|
|
214
|
+
...(typeof meta.uploadedBy === 'string' ? { uploadedBy: meta.uploadedBy } : {}),
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
// sidecar read failure → treat as ghost
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
key: item.key,
|
|
223
|
+
name: item.name,
|
|
224
|
+
mimeType: item.mimeType,
|
|
225
|
+
size: item.size,
|
|
226
|
+
uploadedAt: item.lastModified ?? '',
|
|
227
|
+
url: item.url,
|
|
228
|
+
isIndexed: false,
|
|
229
|
+
};
|
|
230
|
+
}));
|
|
231
|
+
let items = settled
|
|
232
|
+
.map(r => (r.status === 'fulfilled' ? r.value : null))
|
|
233
|
+
.filter((i) => i !== null);
|
|
234
|
+
if (mimeTypeFilter) {
|
|
235
|
+
items = items.filter(i => mimeTypeFilter.endsWith('/')
|
|
236
|
+
? i.mimeType.startsWith(mimeTypeFilter)
|
|
237
|
+
: i.mimeType === mimeTypeFilter);
|
|
238
|
+
}
|
|
239
|
+
if (search) {
|
|
240
|
+
const lower = search.toLowerCase();
|
|
241
|
+
items = items.filter(i => i.name.toLowerCase().includes(lower) ||
|
|
242
|
+
(i.alt ?? '').toLowerCase().includes(lower) ||
|
|
243
|
+
(i.caption ?? '').toLowerCase().includes(lower) ||
|
|
244
|
+
(i.title ?? '').toLowerCase().includes(lower));
|
|
245
|
+
}
|
|
246
|
+
return jsonRes({
|
|
247
|
+
data: items,
|
|
248
|
+
meta: { ...(result.cursor ? { cursor: result.cursor } : {}) },
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
// ── GET /media/:key ──────────────────────────────────────────────────────
|
|
252
|
+
async function handleGet(_req, key) {
|
|
253
|
+
if (!storageAdapter) {
|
|
254
|
+
return errorRes(501, 'NOT_IMPLEMENTED', 'Library mode requires storageAdapter to be configured');
|
|
255
|
+
}
|
|
256
|
+
let sidecarFile = null;
|
|
257
|
+
try {
|
|
258
|
+
sidecarFile = await storageAdapter.read(sidecarPath(key));
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
// no sidecar — ghost file
|
|
262
|
+
}
|
|
263
|
+
let resolvedUrl;
|
|
264
|
+
try {
|
|
265
|
+
resolvedUrl = await adapter.url(key);
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
return errorRes(404, 'NOT_FOUND', `Media key "${key}" not found`);
|
|
269
|
+
}
|
|
270
|
+
if (!sidecarFile) {
|
|
271
|
+
const item = {
|
|
272
|
+
key,
|
|
273
|
+
name: key.split('/').pop() ?? key,
|
|
274
|
+
mimeType: 'application/octet-stream',
|
|
275
|
+
size: 0,
|
|
276
|
+
uploadedAt: '',
|
|
277
|
+
url: resolvedUrl,
|
|
278
|
+
isIndexed: false,
|
|
279
|
+
};
|
|
280
|
+
return jsonRes({ data: item });
|
|
281
|
+
}
|
|
282
|
+
const meta = JSON.parse(sidecarFile.content);
|
|
283
|
+
const item = {
|
|
284
|
+
key,
|
|
285
|
+
name: typeof meta.name === 'string' ? meta.name : key,
|
|
286
|
+
mimeType: typeof meta.mimeType === 'string' ? meta.mimeType : 'application/octet-stream',
|
|
287
|
+
size: typeof meta.size === 'number' ? meta.size : 0,
|
|
288
|
+
uploadedAt: typeof meta.uploadedAt === 'string' ? meta.uploadedAt : '',
|
|
289
|
+
url: resolvedUrl,
|
|
290
|
+
isIndexed: true,
|
|
291
|
+
...(typeof meta.alt === 'string' ? { alt: meta.alt } : {}),
|
|
292
|
+
...(typeof meta.caption === 'string' ? { caption: meta.caption } : {}),
|
|
293
|
+
...(typeof meta.title === 'string' ? { title: meta.title } : {}),
|
|
294
|
+
...(Array.isArray(meta.tags) ? { tags: meta.tags } : {}),
|
|
295
|
+
...(typeof meta.width === 'number' ? { width: meta.width } : {}),
|
|
296
|
+
...(typeof meta.height === 'number' ? { height: meta.height } : {}),
|
|
297
|
+
...(typeof meta.uploadedBy === 'string' ? { uploadedBy: meta.uploadedBy } : {}),
|
|
298
|
+
};
|
|
299
|
+
return jsonRes({ data: item });
|
|
300
|
+
}
|
|
301
|
+
// ── PATCH /media/:key ────────────────────────────────────────────────────
|
|
302
|
+
async function handlePatch(req, key) {
|
|
303
|
+
if (!storageAdapter) {
|
|
304
|
+
return errorRes(501, 'NOT_IMPLEMENTED', 'Library mode requires storageAdapter to be configured');
|
|
305
|
+
}
|
|
306
|
+
let patch;
|
|
307
|
+
try {
|
|
308
|
+
patch = await req.json();
|
|
309
|
+
}
|
|
310
|
+
catch {
|
|
311
|
+
return errorRes(400, 'INVALID_REQUEST', 'Request body must be valid JSON');
|
|
312
|
+
}
|
|
313
|
+
// Read existing sidecar for read-modify-write (GitHubAdapter requires sha)
|
|
314
|
+
let existing = null;
|
|
315
|
+
try {
|
|
316
|
+
existing = await storageAdapter.read(sidecarPath(key));
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
// no existing sidecar — creating from scratch
|
|
320
|
+
}
|
|
321
|
+
const current = existing
|
|
322
|
+
? JSON.parse(existing.content)
|
|
323
|
+
: { key, uploadedAt: new Date().toISOString() };
|
|
324
|
+
// Apply patch — only editable fields; null clears the field
|
|
325
|
+
const editableFields = ['alt', 'caption', 'title', 'tags'];
|
|
326
|
+
for (const field of editableFields) {
|
|
327
|
+
if (!(field in patch))
|
|
328
|
+
continue;
|
|
329
|
+
if (patch[field] === null) {
|
|
330
|
+
delete current[field];
|
|
331
|
+
}
|
|
332
|
+
else if (field === 'tags') {
|
|
333
|
+
const normalized = normalizeTags(patch[field]);
|
|
334
|
+
if (normalized)
|
|
335
|
+
current[field] = normalized;
|
|
336
|
+
else
|
|
337
|
+
delete current[field];
|
|
338
|
+
}
|
|
339
|
+
else if (typeof patch[field] === 'string') {
|
|
340
|
+
current[field] = patch[field];
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
try {
|
|
344
|
+
await storageAdapter.write(sidecarPath(key), JSON.stringify(current, null, 2), {
|
|
345
|
+
message: `chore: update media metadata for ${key}`,
|
|
346
|
+
sha: existing?.sha,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
catch (err) {
|
|
350
|
+
console.error('[airdraft/plugin-media] Sidecar patch failed:', err);
|
|
351
|
+
return errorRes(502, 'UPLOAD_FAILED', 'Failed to persist metadata update');
|
|
352
|
+
}
|
|
353
|
+
let resolvedUrl;
|
|
354
|
+
try {
|
|
355
|
+
resolvedUrl = await adapter.url(key);
|
|
356
|
+
}
|
|
357
|
+
catch {
|
|
358
|
+
return errorRes(404, 'NOT_FOUND', `Media key "${key}" not found`);
|
|
359
|
+
}
|
|
360
|
+
const item = {
|
|
361
|
+
key,
|
|
362
|
+
name: typeof current.name === 'string' ? current.name : key,
|
|
363
|
+
mimeType: typeof current.mimeType === 'string' ? current.mimeType : 'application/octet-stream',
|
|
364
|
+
size: typeof current.size === 'number' ? current.size : 0,
|
|
365
|
+
uploadedAt: typeof current.uploadedAt === 'string' ? current.uploadedAt : '',
|
|
366
|
+
url: resolvedUrl,
|
|
367
|
+
isIndexed: true,
|
|
368
|
+
...(typeof current.alt === 'string' ? { alt: current.alt } : {}),
|
|
369
|
+
...(typeof current.caption === 'string' ? { caption: current.caption } : {}),
|
|
370
|
+
...(typeof current.title === 'string' ? { title: current.title } : {}),
|
|
371
|
+
...(Array.isArray(current.tags) ? { tags: current.tags } : {}),
|
|
372
|
+
...(typeof current.width === 'number' ? { width: current.width } : {}),
|
|
373
|
+
...(typeof current.height === 'number' ? { height: current.height } : {}),
|
|
374
|
+
...(typeof current.uploadedBy === 'string' ? { uploadedBy: current.uploadedBy } : {}),
|
|
375
|
+
};
|
|
376
|
+
return jsonRes({ data: item });
|
|
377
|
+
}
|
|
378
|
+
// ── DELETE /media/:key ───────────────────────────────────────────────────
|
|
379
|
+
async function handleDelete(_req, key) {
|
|
380
|
+
// Read sidecar sha before deletion (GitHubAdapter requires sha for delete)
|
|
381
|
+
let sidecarFile = null;
|
|
382
|
+
if (storageAdapter) {
|
|
383
|
+
try {
|
|
384
|
+
sidecarFile = await storageAdapter.read(sidecarPath(key));
|
|
385
|
+
}
|
|
386
|
+
catch {
|
|
387
|
+
// no sidecar — nothing to delete
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
try {
|
|
391
|
+
await adapter.delete(key);
|
|
392
|
+
}
|
|
393
|
+
catch (err) {
|
|
394
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
395
|
+
if (msg.includes('not found') || msg.includes('ENOENT') || msg.includes('NoSuchKey')) {
|
|
396
|
+
return errorRes(404, 'NOT_FOUND', `Media key "${key}" not found`);
|
|
397
|
+
}
|
|
398
|
+
console.error('[airdraft/plugin-media] Delete failed:', err);
|
|
399
|
+
return errorRes(502, 'DELETE_FAILED', 'Delete from storage adapter failed');
|
|
400
|
+
}
|
|
401
|
+
// Best-effort sidecar delete
|
|
402
|
+
if (storageAdapter && sidecarFile) {
|
|
403
|
+
try {
|
|
404
|
+
await storageAdapter.delete(sidecarPath(key), {
|
|
405
|
+
message: `chore: remove media metadata for ${key}`,
|
|
406
|
+
sha: sidecarFile.sha,
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
catch (err) {
|
|
410
|
+
console.error('[airdraft/plugin-media] Sidecar delete failed:', err);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return new Response(null, { status: 204 });
|
|
414
|
+
}
|
|
415
|
+
// ── GET /media/:key/url ──────────────────────────────────────────────────
|
|
416
|
+
async function handleUrl(req, key) {
|
|
417
|
+
if (req.method !== 'GET') {
|
|
418
|
+
return errorRes(405, 'METHOD_NOT_ALLOWED', 'Use GET /media/:key/url');
|
|
419
|
+
}
|
|
420
|
+
const url = await adapter.url(key);
|
|
421
|
+
return new Response(JSON.stringify({ data: { url } }), {
|
|
422
|
+
status: 200,
|
|
423
|
+
headers: { 'Content-Type': 'application/json' },
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
// ── transformEntry hook ──────────────────────────────────────────────────
|
|
427
|
+
async function transformEntry(entry, collection) {
|
|
428
|
+
const imageFields = Object.entries(collection.fields)
|
|
429
|
+
.filter(([, f]) => f.type === 'image')
|
|
430
|
+
.map(([name]) => name);
|
|
431
|
+
if (imageFields.length === 0)
|
|
432
|
+
return entry;
|
|
433
|
+
const updatedData = { ...entry.data };
|
|
434
|
+
for (const field of imageFields) {
|
|
435
|
+
const value = entry.data[field];
|
|
436
|
+
if (typeof value !== 'string' || !value)
|
|
437
|
+
continue;
|
|
438
|
+
// URL detection: already-resolved values pass through unchanged
|
|
439
|
+
const isResolved = value.startsWith('http://') ||
|
|
440
|
+
value.startsWith('https://') ||
|
|
441
|
+
value.startsWith('/');
|
|
442
|
+
const resolvedUrl = isResolved ? value : await adapter.url(value);
|
|
443
|
+
updatedData[`${field}_url`] = resolvedUrl;
|
|
444
|
+
// Sidecar enrichment — library mode only
|
|
445
|
+
if (!isResolved && storageAdapter) {
|
|
446
|
+
try {
|
|
447
|
+
const sidecarFile = await storageAdapter.read(sidecarPath(value));
|
|
448
|
+
if (sidecarFile) {
|
|
449
|
+
const meta = JSON.parse(sidecarFile.content);
|
|
450
|
+
const mediaObj = {
|
|
451
|
+
key: value,
|
|
452
|
+
url: resolvedUrl,
|
|
453
|
+
name: typeof meta.name === 'string' ? meta.name : value,
|
|
454
|
+
mimeType: typeof meta.mimeType === 'string' ? meta.mimeType : '',
|
|
455
|
+
size: typeof meta.size === 'number' ? meta.size : 0,
|
|
456
|
+
uploadedAt: typeof meta.uploadedAt === 'string' ? meta.uploadedAt : '',
|
|
457
|
+
};
|
|
458
|
+
if (typeof meta.alt === 'string')
|
|
459
|
+
mediaObj.alt = meta.alt;
|
|
460
|
+
if (typeof meta.caption === 'string')
|
|
461
|
+
mediaObj.caption = meta.caption;
|
|
462
|
+
if (typeof meta.title === 'string')
|
|
463
|
+
mediaObj.title = meta.title;
|
|
464
|
+
if (Array.isArray(meta.tags))
|
|
465
|
+
mediaObj.tags = meta.tags;
|
|
466
|
+
if (typeof meta.width === 'number')
|
|
467
|
+
mediaObj.width = meta.width;
|
|
468
|
+
if (typeof meta.height === 'number')
|
|
469
|
+
mediaObj.height = meta.height;
|
|
470
|
+
updatedData[`${field}_media`] = mediaObj;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
catch {
|
|
474
|
+
// sidecar read failure is non-fatal; {field}_url is still injected
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return { ...entry, data: updatedData };
|
|
479
|
+
}
|
|
480
|
+
// ── Plugin registration ──────────────────────────────────────────────────
|
|
481
|
+
return {
|
|
482
|
+
name: 'media',
|
|
483
|
+
routes: {
|
|
484
|
+
'/media/upload': async (req) => handleUpload(req),
|
|
485
|
+
'/media': async (req) => handleList(req),
|
|
486
|
+
'/media/:key/url': async (req) => {
|
|
487
|
+
const key = decodeURIComponent(new URL(req.url).pathname.split('/media/')[1]?.replace(/\/url$/, '') ?? '');
|
|
488
|
+
return handleUrl(req, key);
|
|
489
|
+
},
|
|
490
|
+
'/media/:key': async (req) => {
|
|
491
|
+
const key = decodeURIComponent(new URL(req.url).pathname.split('/media/')[1] ?? '');
|
|
492
|
+
if (req.method === 'GET')
|
|
493
|
+
return handleGet(req, key);
|
|
494
|
+
if (req.method === 'PATCH')
|
|
495
|
+
return handlePatch(req, key);
|
|
496
|
+
if (req.method === 'DELETE')
|
|
497
|
+
return handleDelete(req, key);
|
|
498
|
+
return errorRes(405, 'METHOD_NOT_ALLOWED', 'Allowed: GET, PATCH, DELETE');
|
|
499
|
+
},
|
|
500
|
+
},
|
|
501
|
+
hooks: {
|
|
502
|
+
transformEntry,
|
|
503
|
+
},
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
// ---------------------------------------------------------------------------
|
|
507
|
+
// Helpers (exported for testing)
|
|
508
|
+
// ---------------------------------------------------------------------------
|
|
509
|
+
export { defaultGenerateKey, sanitizeName, hash4 };
|
|
510
|
+
//# sourceMappingURL=withMedia.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"withMedia.js","sourceRoot":"","sources":["../src/withMedia.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@airdraft/plugin-media",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Airdraft media plugin — image upload, optimisation, CDN",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": ["dist", "README.md", "CHANGELOG.md"],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"dev": "tsc --watch",
|
|
18
|
+
"clean": "rm -rf dist",
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"test:watch": "vitest",
|
|
21
|
+
"typecheck": "tsc --noEmit",
|
|
22
|
+
"prepublishOnly": "npm run build",
|
|
23
|
+
"release": "standard-version",
|
|
24
|
+
"release:patch": "standard-version --release-as patch",
|
|
25
|
+
"release:minor": "standard-version --release-as minor",
|
|
26
|
+
"release:major": "standard-version --release-as major"
|
|
27
|
+
},
|
|
28
|
+
"publishConfig": { "access": "public" },
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^20.0.0",
|
|
32
|
+
"standard-version": "^9.5.0",
|
|
33
|
+
"tsx": "^4.7.0",
|
|
34
|
+
"typescript": "^5.4.0",
|
|
35
|
+
"vitest": "^1.5.0"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@airdraft/core": "*"
|
|
39
|
+
},
|
|
40
|
+
"engines": { "node": ">=18.0.0" }
|
|
41
|
+
}
|