@humaan/payload-storage-imagekit 3.0.0-canary.0 → 3.0.0-canary.1
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 +21 -2
- package/dist/components/ImageKitUploadHandler.js +30 -3
- package/dist/components/ImageKitUploadHandler.js.map +1 -1
- package/dist/endpoints/completeImageKitUpload.d.ts +10 -0
- package/dist/endpoints/completeImageKitUpload.js +84 -0
- package/dist/endpoints/completeImageKitUpload.js.map +1 -0
- package/dist/endpoints/createImageKitUploadInstructions.js +2 -1
- package/dist/endpoints/createImageKitUploadInstructions.js.map +1 -1
- package/dist/imageKitFileStore.d.ts +129 -0
- package/dist/imageKitFileStore.js +134 -0
- package/dist/imageKitFileStore.js.map +1 -0
- package/dist/index.js +72 -65
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -107,8 +107,27 @@ including records with missing or slash-prefixed prefixes, remain readable.
|
|
|
107
107
|
|
|
108
108
|
The browser still reauthorizes mislabeled raster images using content-detected
|
|
109
109
|
filename/MIME hints; the server independently validates provider metadata and bytes.
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
|
|
111
|
+
After a provider-direct upload, the browser calls the adapter's completion endpoint
|
|
112
|
+
(normally `POST /api/imagekit-upload-complete`) with the collection slug, ImageKit's
|
|
113
|
+
returned `fileId`, and the original `signedReceipt`. The server checks access and
|
|
114
|
+
the user-bound receipt, then verifies the provider's exact path, name, upload tag,
|
|
115
|
+
privacy and MIME policy before recording the ID. Document admission retrieves the
|
|
116
|
+
file by that ID and rechecks provider state and file contents. It does not depend
|
|
117
|
+
on ImageKit's asset listing becoming visible immediately after upload.
|
|
118
|
+
|
|
119
|
+
Verified path-to-ID mappings use Payload's existing KV store (`payload-kv` with the
|
|
120
|
+
default database adapter), remain available while the provider object exists, and
|
|
121
|
+
are removed when the adapter deletes that object. Server uploads and managed copies
|
|
122
|
+
also record their returned IDs, so immediate rollback/deletion does not depend on
|
|
123
|
+
listing visibility. A failed registry write triggers cleanup by the returned ID.
|
|
124
|
+
If you configure a custom KV
|
|
125
|
+
adapter, it must persist and share entries across the instances handling completion
|
|
126
|
+
and document-save requests; a process-local cache is not sufficient for serverless
|
|
127
|
+
or multi-instance deployments. No new credentials, custom collections, storage-path
|
|
128
|
+
changes or content migration are required. Existing files and older/in-flight
|
|
129
|
+
clients without a mapping retain the path-listing fallback. Custom direct-upload
|
|
130
|
+
clients should call completion before saving their original Payload reference.
|
|
112
131
|
|
|
113
132
|
Run `pnpm exec vitest run` for mock-provider REST/MongoDB, client transport, and
|
|
114
133
|
folder-migration coverage. Live provider tests require explicitly configured
|
|
@@ -4,7 +4,7 @@ import { formatAdminURL } from 'payload/shared';
|
|
|
4
4
|
import { getClientImageIdentity } from '../clientImageIdentity.js';
|
|
5
5
|
export const ImageKitUploadHandler = createClientUploadHandler({
|
|
6
6
|
name: 'imagekit',
|
|
7
|
-
handler: async ({ apiRoute, collectionSlug, data, docPrefix, file, serverURL, updateFilename })=>{
|
|
7
|
+
handler: async ({ apiRoute, collectionSlug, data, docPrefix, endpointPath, file, serverURL, updateFilename })=>{
|
|
8
8
|
let authorization = data;
|
|
9
9
|
let uploadReference;
|
|
10
10
|
const identity = await getClientImageIdentity(file);
|
|
@@ -40,6 +40,9 @@ export const ImageKitUploadHandler = createClientUploadHandler({
|
|
|
40
40
|
uploadReference = instructions.file.uploadReference;
|
|
41
41
|
updateFilename(instructions.file.filename);
|
|
42
42
|
}
|
|
43
|
+
if (!endpointPath || typeof authorization.uploadReference?.signedReceipt !== 'string') {
|
|
44
|
+
throw new Error('ImageKit upload configuration changed. Refresh the page and try again.');
|
|
45
|
+
}
|
|
43
46
|
const formData = new FormData();
|
|
44
47
|
formData.set('file', file);
|
|
45
48
|
formData.set('token', authorization.token);
|
|
@@ -54,8 +57,32 @@ export const ImageKitUploadHandler = createClientUploadHandler({
|
|
|
54
57
|
const error = await response.json().catch(()=>null);
|
|
55
58
|
throw new Error(error?.message || 'Failed to upload file to ImageKit.');
|
|
56
59
|
}
|
|
57
|
-
|
|
58
|
-
|
|
60
|
+
const uploaded = await response.json().catch(()=>null);
|
|
61
|
+
if (typeof uploaded?.fileId !== 'string' || !uploaded.fileId) {
|
|
62
|
+
throw new Error('ImageKit returned incomplete upload metadata.');
|
|
63
|
+
}
|
|
64
|
+
const completed = await fetch(formatAdminURL({
|
|
65
|
+
apiRoute,
|
|
66
|
+
path: endpointPath,
|
|
67
|
+
serverURL
|
|
68
|
+
}), {
|
|
69
|
+
body: JSON.stringify({
|
|
70
|
+
collectionSlug,
|
|
71
|
+
fileId: uploaded.fileId,
|
|
72
|
+
signedReceipt: authorization.uploadReference.signedReceipt
|
|
73
|
+
}),
|
|
74
|
+
credentials: 'include',
|
|
75
|
+
headers: {
|
|
76
|
+
'Content-Type': 'application/json'
|
|
77
|
+
},
|
|
78
|
+
method: 'POST'
|
|
79
|
+
});
|
|
80
|
+
if (!completed.ok) {
|
|
81
|
+
const error = await completed.json().catch(()=>null);
|
|
82
|
+
throw new Error(error?.errors?.[0]?.message || 'Failed to verify the ImageKit upload.');
|
|
83
|
+
}
|
|
84
|
+
// The completion callback remembers a server-verified ID, not a browser-supplied proof.
|
|
85
|
+
// Payload keeps its original signed reference and owns the original/history lifecycle.
|
|
59
86
|
return uploadReference;
|
|
60
87
|
}
|
|
61
88
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/ImageKitUploadHandler.tsx"],"sourcesContent":["'use client'\n\nimport type { UploadInstructions } from 'payload'\n\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\nimport { formatAdminURL } from 'payload/shared'\n\nimport { getClientImageIdentity } from '../clientImageIdentity.js'\n\nexport const ImageKitUploadHandler = createClientUploadHandler({\n name: 'imagekit',\n handler: async ({\n apiRoute,\n collectionSlug,\n data,\n docPrefix,\n file,\n serverURL,\n updateFilename,\n }) => {\n let authorization = data as {\n token: string\n uploadPayload: Record<string, string>\n }\n let uploadReference: Record<string, unknown> | undefined\n const identity = await getClientImageIdentity(file)\n // Payload's initial instructions use the browser's MIME hint. Re-authorize\n // mislabeled image bytes through the same shared endpoint, not a private API.\n if (identity.filename !== file.name || identity.mimeType !== file.type) {\n const response = await fetch(\n formatAdminURL({ apiRoute, path: '/upload-instructions', serverURL }),\n {\n body: JSON.stringify({\n collectionSlug,\n docPrefix,\n filename: identity.filename,\n filesize: file.size,\n mimeType: identity.mimeType,\n }),\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n method: 'POST',\n },\n )\n if (!response.ok) {\n throw new Error('Failed to authorize detected image content.')\n }\n const instructions = (await response.json()) as UploadInstructions\n if (instructions.type !== 'dispatch' || instructions.name !== 'imagekit') {\n throw new Error('Expected ImageKit upload instructions.')\n }\n authorization = instructions.data as typeof authorization\n uploadReference = instructions.file.uploadReference\n updateFilename(instructions.file.filename)\n }\n const formData = new FormData()\n formData.set('file', file)\n formData.set('token', authorization.token)\n for (const [key, value] of Object.entries(authorization.uploadPayload)) {\n formData.set(key, value)\n }\n const response = await fetch('https://upload.imagekit.io/api/v2/files/upload', {\n body: formData,\n method: 'POST',\n })\n if (!response.ok) {\n const error = (await response.json().catch(() => null)) as { message?: string } | null\n throw new Error(error?.message || 'Failed to upload file to ImageKit.')\n }\n
|
|
1
|
+
{"version":3,"sources":["../../src/components/ImageKitUploadHandler.tsx"],"sourcesContent":["'use client'\n\nimport type { UploadInstructions } from 'payload'\n\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\nimport { formatAdminURL } from 'payload/shared'\n\nimport { getClientImageIdentity } from '../clientImageIdentity.js'\n\nexport const ImageKitUploadHandler = createClientUploadHandler({\n name: 'imagekit',\n handler: async ({\n apiRoute,\n collectionSlug,\n data,\n docPrefix,\n endpointPath,\n file,\n serverURL,\n updateFilename,\n }) => {\n let authorization = data as {\n token: string\n uploadPayload: Record<string, string>\n uploadReference: Record<string, unknown>\n }\n let uploadReference: Record<string, unknown> | undefined\n const identity = await getClientImageIdentity(file)\n // Payload's initial instructions use the browser's MIME hint. Re-authorize\n // mislabeled image bytes through the same shared endpoint, not a private API.\n if (identity.filename !== file.name || identity.mimeType !== file.type) {\n const response = await fetch(\n formatAdminURL({ apiRoute, path: '/upload-instructions', serverURL }),\n {\n body: JSON.stringify({\n collectionSlug,\n docPrefix,\n filename: identity.filename,\n filesize: file.size,\n mimeType: identity.mimeType,\n }),\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n method: 'POST',\n },\n )\n if (!response.ok) {\n throw new Error('Failed to authorize detected image content.')\n }\n const instructions = (await response.json()) as UploadInstructions\n if (instructions.type !== 'dispatch' || instructions.name !== 'imagekit') {\n throw new Error('Expected ImageKit upload instructions.')\n }\n authorization = instructions.data as typeof authorization\n uploadReference = instructions.file.uploadReference\n updateFilename(instructions.file.filename)\n }\n if (!endpointPath || typeof authorization.uploadReference?.signedReceipt !== 'string') {\n throw new Error('ImageKit upload configuration changed. Refresh the page and try again.')\n }\n const formData = new FormData()\n formData.set('file', file)\n formData.set('token', authorization.token)\n for (const [key, value] of Object.entries(authorization.uploadPayload)) {\n formData.set(key, value)\n }\n const response = await fetch('https://upload.imagekit.io/api/v2/files/upload', {\n body: formData,\n method: 'POST',\n })\n if (!response.ok) {\n const error = (await response.json().catch(() => null)) as { message?: string } | null\n throw new Error(error?.message || 'Failed to upload file to ImageKit.')\n }\n const uploaded = (await response.json().catch(() => null)) as { fileId?: unknown } | null\n if (typeof uploaded?.fileId !== 'string' || !uploaded.fileId) {\n throw new Error('ImageKit returned incomplete upload metadata.')\n }\n const completed = await fetch(formatAdminURL({ apiRoute, path: endpointPath, serverURL }), {\n body: JSON.stringify({\n collectionSlug,\n fileId: uploaded.fileId,\n signedReceipt: authorization.uploadReference.signedReceipt,\n }),\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n method: 'POST',\n })\n if (!completed.ok) {\n const error = (await completed.json().catch(() => null)) as {\n errors?: { message?: string }[]\n } | null\n throw new Error(error?.errors?.[0]?.message || 'Failed to verify the ImageKit upload.')\n }\n // The completion callback remembers a server-verified ID, not a browser-supplied proof.\n // Payload keeps its original signed reference and owns the original/history lifecycle.\n return uploadReference\n },\n})\n"],"names":["createClientUploadHandler","formatAdminURL","getClientImageIdentity","ImageKitUploadHandler","name","handler","apiRoute","collectionSlug","data","docPrefix","endpointPath","file","serverURL","updateFilename","authorization","uploadReference","identity","filename","mimeType","type","response","fetch","path","body","JSON","stringify","filesize","size","credentials","headers","method","ok","Error","instructions","json","signedReceipt","formData","FormData","set","token","key","value","Object","entries","uploadPayload","error","catch","message","uploaded","fileId","completed","errors"],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { PayloadHandler, UploadInstructionsAccess } from 'payload';
|
|
2
|
+
import type { createImageKitFileStore } from '../imageKitFileStore.js';
|
|
3
|
+
export declare const completeImageKitUpload: ({ access, collections, files, }: {
|
|
4
|
+
access?: UploadInstructionsAccess;
|
|
5
|
+
collections: Record<string, {
|
|
6
|
+
isPrivate: boolean;
|
|
7
|
+
prefix: string;
|
|
8
|
+
}>;
|
|
9
|
+
files: ReturnType<typeof createImageKitFileStore>;
|
|
10
|
+
}) => PayloadHandler;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { NotFoundError } from '@imagekit/nodejs';
|
|
2
|
+
import { buildStoragePathData } from '@payloadcms/plugin-cloud-storage/utilities';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { APIError, Forbidden } from 'payload';
|
|
5
|
+
import { assertClientUploadAccess, assertClientUploadAllowed, getOriginalFilename, verifyClientUploadReceipt } from 'payload/internal';
|
|
6
|
+
import { validateMimeType } from 'payload/shared';
|
|
7
|
+
export const completeImageKitUpload = ({ access = ({ req })=>Boolean(req.user), collections, files })=>async (req)=>{
|
|
8
|
+
if (!req.json) {
|
|
9
|
+
throw new APIError('Expected an ImageKit upload completion payload.', 400);
|
|
10
|
+
}
|
|
11
|
+
const body = await req.json().catch(()=>{
|
|
12
|
+
throw new APIError('Invalid ImageKit upload completion.', 400);
|
|
13
|
+
});
|
|
14
|
+
if (!body || typeof body.collectionSlug !== 'string' || !Object.hasOwn(collections, body.collectionSlug) || typeof body.fileId !== 'string' || !body.fileId.trim()) {
|
|
15
|
+
throw new APIError('Invalid ImageKit upload completion.', 400);
|
|
16
|
+
}
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -- Consumer-generated configs narrow UploadCollectionSlug.
|
|
18
|
+
const collectionSlug = body.collectionSlug;
|
|
19
|
+
await assertClientUploadAccess({
|
|
20
|
+
collectionSlug,
|
|
21
|
+
req
|
|
22
|
+
});
|
|
23
|
+
if (!await access({
|
|
24
|
+
collectionSlug,
|
|
25
|
+
req
|
|
26
|
+
})) {
|
|
27
|
+
throw new Forbidden(req.t);
|
|
28
|
+
}
|
|
29
|
+
const receipt = verifyClientUploadReceipt({
|
|
30
|
+
collectionSlug,
|
|
31
|
+
req,
|
|
32
|
+
signedReceipt: body.signedReceipt
|
|
33
|
+
});
|
|
34
|
+
const { isPrivate, prefix } = collections[collectionSlug];
|
|
35
|
+
const expectedPath = buildStoragePathData({
|
|
36
|
+
collectionPrefix: prefix,
|
|
37
|
+
docPrefix: path.posix.join(receipt.filePrefix, receipt._objectKey ?? ''),
|
|
38
|
+
filename: getOriginalFilename({
|
|
39
|
+
filename: receipt.filename
|
|
40
|
+
})
|
|
41
|
+
}).storageFilePath;
|
|
42
|
+
if (!receipt._objectKey || receipt.allowOverwrite || expectedPath !== receipt.storageFilePath || !expectedPath.startsWith(`${prefix}/`)) {
|
|
43
|
+
throw new APIError('Invalid ImageKit upload reference.', 400);
|
|
44
|
+
}
|
|
45
|
+
let file;
|
|
46
|
+
try {
|
|
47
|
+
file = await files.getByID({
|
|
48
|
+
fileId: body.fileId,
|
|
49
|
+
key: expectedPath
|
|
50
|
+
});
|
|
51
|
+
} catch (error) {
|
|
52
|
+
if (error instanceof NotFoundError) {
|
|
53
|
+
throw new APIError('ImageKit upload was not found.', 400);
|
|
54
|
+
}
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
if (!file.mime || Boolean(file.isPrivateFile) !== isPrivate || !file.tags?.includes(`payload-upload-${receipt._objectKey}`)) {
|
|
58
|
+
throw new APIError('ImageKit upload did not match its authorization.', 400);
|
|
59
|
+
}
|
|
60
|
+
const collection = req.payload.collections[collectionSlug].config;
|
|
61
|
+
assertClientUploadAllowed({
|
|
62
|
+
collection,
|
|
63
|
+
filename: file.name,
|
|
64
|
+
mimeType: file.mime
|
|
65
|
+
});
|
|
66
|
+
if (typeof collection.upload === 'object' && !validateMimeType(file.mime, collection.upload.mimeTypes ?? [])) {
|
|
67
|
+
throw new APIError('ImageKit file type is not allowed for this collection.', 400);
|
|
68
|
+
}
|
|
69
|
+
// The browser's ID is only a lookup hint. Persist it only after checking authoritative state
|
|
70
|
+
// against the user-bound receipt. Save-time admission rechecks both the receipt and provider.
|
|
71
|
+
await files.remember({
|
|
72
|
+
fileId: file.fileId,
|
|
73
|
+
key: expectedPath,
|
|
74
|
+
req
|
|
75
|
+
});
|
|
76
|
+
return new Response(null, {
|
|
77
|
+
headers: {
|
|
78
|
+
'Cache-Control': 'no-store'
|
|
79
|
+
},
|
|
80
|
+
status: 204
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
//# sourceMappingURL=completeImageKitUpload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/endpoints/completeImageKitUpload.ts"],"sourcesContent":["import type { PayloadHandler, UploadCollectionSlug, UploadInstructionsAccess } from 'payload'\n\nimport { NotFoundError } from '@imagekit/nodejs'\nimport { buildStoragePathData } from '@payloadcms/plugin-cloud-storage/utilities'\nimport path from 'node:path'\nimport { APIError, Forbidden } from 'payload'\nimport {\n assertClientUploadAccess,\n assertClientUploadAllowed,\n getOriginalFilename,\n verifyClientUploadReceipt,\n} from 'payload/internal'\nimport { validateMimeType } from 'payload/shared'\n\nimport type { createImageKitFileStore } from '../imageKitFileStore.js'\n\nexport const completeImageKitUpload =\n ({\n access = ({ req }) => Boolean(req.user),\n collections,\n files,\n }: {\n access?: UploadInstructionsAccess\n collections: Record<string, { isPrivate: boolean; prefix: string }>\n files: ReturnType<typeof createImageKitFileStore>\n }): PayloadHandler =>\n async (req) => {\n if (!req.json) {\n throw new APIError('Expected an ImageKit upload completion payload.', 400)\n }\n const body = (await req.json().catch(() => {\n throw new APIError('Invalid ImageKit upload completion.', 400)\n })) as {\n collectionSlug?: unknown\n fileId?: unknown\n signedReceipt?: unknown\n }\n if (\n !body ||\n typeof body.collectionSlug !== 'string' ||\n !Object.hasOwn(collections, body.collectionSlug) ||\n typeof body.fileId !== 'string' ||\n !body.fileId.trim()\n ) {\n throw new APIError('Invalid ImageKit upload completion.', 400)\n }\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -- Consumer-generated configs narrow UploadCollectionSlug.\n const collectionSlug = body.collectionSlug as UploadCollectionSlug\n await assertClientUploadAccess({ collectionSlug, req })\n if (!(await access({ collectionSlug, req }))) {\n throw new Forbidden(req.t)\n }\n const receipt = verifyClientUploadReceipt({\n collectionSlug,\n req,\n signedReceipt: body.signedReceipt,\n })\n const { isPrivate, prefix } = collections[collectionSlug]\n const expectedPath = buildStoragePathData({\n collectionPrefix: prefix,\n docPrefix: path.posix.join(receipt.filePrefix, receipt._objectKey ?? ''),\n filename: getOriginalFilename({ filename: receipt.filename }),\n }).storageFilePath\n if (\n !receipt._objectKey ||\n receipt.allowOverwrite ||\n expectedPath !== receipt.storageFilePath ||\n !expectedPath.startsWith(`${prefix}/`)\n ) {\n throw new APIError('Invalid ImageKit upload reference.', 400)\n }\n let file: Awaited<ReturnType<typeof files.getByID>>\n try {\n file = await files.getByID({ fileId: body.fileId, key: expectedPath })\n } catch (error) {\n if (error instanceof NotFoundError) {\n throw new APIError('ImageKit upload was not found.', 400)\n }\n throw error\n }\n if (\n !file.mime ||\n Boolean(file.isPrivateFile) !== isPrivate ||\n !file.tags?.includes(`payload-upload-${receipt._objectKey}`)\n ) {\n throw new APIError('ImageKit upload did not match its authorization.', 400)\n }\n const collection = req.payload.collections[collectionSlug].config\n assertClientUploadAllowed({\n collection,\n filename: file.name,\n mimeType: file.mime,\n })\n if (\n typeof collection.upload === 'object' &&\n !validateMimeType(file.mime, collection.upload.mimeTypes ?? [])\n ) {\n throw new APIError('ImageKit file type is not allowed for this collection.', 400)\n }\n // The browser's ID is only a lookup hint. Persist it only after checking authoritative state\n // against the user-bound receipt. Save-time admission rechecks both the receipt and provider.\n await files.remember({ fileId: file.fileId, key: expectedPath, req })\n return new Response(null, { headers: { 'Cache-Control': 'no-store' }, status: 204 })\n }\n"],"names":["NotFoundError","buildStoragePathData","path","APIError","Forbidden","assertClientUploadAccess","assertClientUploadAllowed","getOriginalFilename","verifyClientUploadReceipt","validateMimeType","completeImageKitUpload","access","req","Boolean","user","collections","files","json","body","catch","collectionSlug","Object","hasOwn","fileId","trim","t","receipt","signedReceipt","isPrivate","prefix","expectedPath","collectionPrefix","docPrefix","posix","join","filePrefix","_objectKey","filename","storageFilePath","allowOverwrite","startsWith","file","getByID","key","error","mime","isPrivateFile","tags","includes","collection","payload","config","name","mimeType","upload","mimeTypes","remember","Response","headers","status"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/endpoints/createImageKitUploadInstructions.ts"],"sourcesContent":["import type { GenerateUploadInstructions, UploadInstructionsAccess } from 'payload'\n\nimport { buildUploadStoragePathData } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { randomUUID } from 'node:crypto'\nimport { posix } from 'node:path'\nimport { Forbidden } from 'payload'\nimport {\n assertClientUploadAccess,\n assertClientUploadAllowed,\n createClientUploadReceipt,\n getOriginalFilename,\n getSafeFileName,\n incrementName,\n} from 'payload/internal'\nimport { getSanitizedUploadFilename } from 'payload/shared'\n\nimport type { ImageKitUploadPayload } from '../clientUploads.js'\n\nimport { createImageKitUploadToken } from '../imageKitUploadToken.js'\n\n/** ImageKit enforces these constraints before accepting any provider-direct bytes. */\nexport const getUploadChecks = ({\n allowedMimeTypes,\n allowRestrictedFileTypes,\n filesize,\n}: {\n allowedMimeTypes: string[]\n allowRestrictedFileTypes: boolean\n filesize: number\n}): string => {\n const checks = [`\"file.size\" = ${filesize}`]\n const prefixes = allowedMimeTypes\n .map((mimeType) => mimeType.replace('*', ''))\n .filter((prefix) => !/[\"\\\\]/.test(prefix))\n\n if (prefixes.length && !prefixes.includes('')) {\n checks.push(`(${prefixes.map((prefix) => `\"file.mime\" : \"${prefix}\"`).join(' OR ')})`)\n }\n if (!allowRestrictedFileTypes) {\n checks.push(\n '\"file.mime\" NOT IN [\"application/atom+xml\", \"application/mathml+xml\", \"application/rdf+xml\", \"application/rss+xml\", \"application/xhtml+xml\", \"application/xml\", \"application/xslt+xml\", \"image/svg+xml\", \"text/xml\"]',\n )\n }\n return checks.join(' AND ')\n}\n\nexport const createImageKitUploadInstructions =\n ({\n access = ({ req }) => Boolean(req.user),\n isPrivate,\n prefix,\n privateKey,\n publicKey,\n }: {\n access?: UploadInstructionsAccess\n isPrivate: boolean\n prefix: string\n privateKey: string\n publicKey: string\n }): GenerateUploadInstructions =>\n async ({ collectionSlug, docPrefix, filename, filesize, mimeType, overrideAccess, req }) => {\n if (!overrideAccess) {\n await assertClientUploadAccess({ collectionSlug, req })\n if (!(await access({ collectionSlug, req }))) {\n throw new Forbidden(req.t)\n }\n }\n const collection = req.payload.collections[collectionSlug].config\n assertClientUploadAllowed({ collection, filename, mimeType })\n let safeFilename = await getSafeFileName({\n collectionSlug,\n desiredFilename: getSanitizedUploadFilename(filename).replace(/[^\\w.-]/g, '_'),\n req,\n })\n // Without a transformer, v4 exposes the retained original as the current file.\n // Reserve both names so repeated uploads cannot violate filename uniqueness.\n while (\n (await getSafeFileName({\n collectionSlug,\n desiredFilename: getOriginalFilename({ filename: safeFilename }),\n req,\n })) !== getOriginalFilename({ filename: safeFilename })\n ) {\n safeFilename = incrementName(safeFilename)\n }\n const originalFilename = getOriginalFilename({ filename: safeFilename })\n const objectKey = randomUUID()\n const resolved = buildUploadStoragePathData({\n collectionPrefix: prefix,\n docPrefix: docPrefix ?? prefix,\n filename: originalFilename,\n })\n // The admin sends an empty docPrefix for new files; the resolved destination retains the collection fallback.\n const filePrefix = posix.dirname(resolved.storageFilePath)\n const storageFilePath = posix.join(filePrefix, objectKey, originalFilename)\n const uploadReference = {\n _objectKey: objectKey,\n prefix: filePrefix,\n signedReceipt: createClientUploadReceipt({\n _objectKey: objectKey,\n collectionSlug,\n filename: safeFilename,\n filePrefix,\n req,\n storageFilePath,\n }),\n }\n const upload = typeof collection.upload === 'object' ? collection.upload : undefined\n const uploadPayload: ImageKitUploadPayload = {\n checks: getUploadChecks({\n allowedMimeTypes: upload?.mimeTypes ?? [],\n allowRestrictedFileTypes: upload?.allowRestrictedFileTypes ?? false,\n filesize,\n }),\n fileName: originalFilename,\n folder: `/${posix.dirname(storageFilePath)}`,\n isPrivateFile: isPrivate ? 'true' : 'false',\n overwriteFile: 'false',\n tags: `payload-upload-${objectKey}`,\n useUniqueFileName: 'false',\n }\n return {\n name: 'imagekit',\n type: 'dispatch',\n data: {\n token: createImageKitUploadToken({ privateKey, publicKey, uploadPayload }),\n uploadPayload,\n },\n file: { filename: safeFilename, mimeType, size: filesize, uploadReference },\n }\n }\n"],"names":["buildUploadStoragePathData","randomUUID","posix","Forbidden","assertClientUploadAccess","assertClientUploadAllowed","createClientUploadReceipt","getOriginalFilename","getSafeFileName","incrementName","getSanitizedUploadFilename","createImageKitUploadToken","getUploadChecks","allowedMimeTypes","allowRestrictedFileTypes","filesize","checks","prefixes","map","mimeType","replace","filter","prefix","test","length","includes","push","join","createImageKitUploadInstructions","access","req","Boolean","user","isPrivate","privateKey","publicKey","collectionSlug","docPrefix","filename","overrideAccess","t","collection","payload","collections","config","safeFilename","desiredFilename","originalFilename","objectKey","resolved","collectionPrefix","filePrefix","dirname","storageFilePath","uploadReference","_objectKey","signedReceipt","upload","undefined","uploadPayload","mimeTypes","fileName","folder","isPrivateFile","overwriteFile","tags","useUniqueFileName","name","type","data","token","file","size"],"mappings":"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
|
|
1
|
+
{"version":3,"sources":["../../src/endpoints/createImageKitUploadInstructions.ts"],"sourcesContent":["import type { GenerateUploadInstructions, UploadInstructionsAccess } from 'payload'\n\nimport { buildUploadStoragePathData } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { randomUUID } from 'node:crypto'\nimport { posix } from 'node:path'\nimport { Forbidden } from 'payload'\nimport {\n assertClientUploadAccess,\n assertClientUploadAllowed,\n createClientUploadReceipt,\n getOriginalFilename,\n getSafeFileName,\n incrementName,\n} from 'payload/internal'\nimport { getSanitizedUploadFilename } from 'payload/shared'\n\nimport type { ImageKitUploadPayload } from '../clientUploads.js'\n\nimport { createImageKitUploadToken } from '../imageKitUploadToken.js'\n\n/** ImageKit enforces these constraints before accepting any provider-direct bytes. */\nexport const getUploadChecks = ({\n allowedMimeTypes,\n allowRestrictedFileTypes,\n filesize,\n}: {\n allowedMimeTypes: string[]\n allowRestrictedFileTypes: boolean\n filesize: number\n}): string => {\n const checks = [`\"file.size\" = ${filesize}`]\n const prefixes = allowedMimeTypes\n .map((mimeType) => mimeType.replace('*', ''))\n .filter((prefix) => !/[\"\\\\]/.test(prefix))\n\n if (prefixes.length && !prefixes.includes('')) {\n checks.push(`(${prefixes.map((prefix) => `\"file.mime\" : \"${prefix}\"`).join(' OR ')})`)\n }\n if (!allowRestrictedFileTypes) {\n checks.push(\n '\"file.mime\" NOT IN [\"application/atom+xml\", \"application/mathml+xml\", \"application/rdf+xml\", \"application/rss+xml\", \"application/xhtml+xml\", \"application/xml\", \"application/xslt+xml\", \"image/svg+xml\", \"text/xml\"]',\n )\n }\n return checks.join(' AND ')\n}\n\nexport const createImageKitUploadInstructions =\n ({\n access = ({ req }) => Boolean(req.user),\n isPrivate,\n prefix,\n privateKey,\n publicKey,\n }: {\n access?: UploadInstructionsAccess\n isPrivate: boolean\n prefix: string\n privateKey: string\n publicKey: string\n }): GenerateUploadInstructions =>\n async ({ collectionSlug, docPrefix, filename, filesize, mimeType, overrideAccess, req }) => {\n if (!overrideAccess) {\n await assertClientUploadAccess({ collectionSlug, req })\n if (!(await access({ collectionSlug, req }))) {\n throw new Forbidden(req.t)\n }\n }\n const collection = req.payload.collections[collectionSlug].config\n assertClientUploadAllowed({ collection, filename, mimeType })\n let safeFilename = await getSafeFileName({\n collectionSlug,\n desiredFilename: getSanitizedUploadFilename(filename).replace(/[^\\w.-]/g, '_'),\n req,\n })\n // Without a transformer, v4 exposes the retained original as the current file.\n // Reserve both names so repeated uploads cannot violate filename uniqueness.\n while (\n (await getSafeFileName({\n collectionSlug,\n desiredFilename: getOriginalFilename({ filename: safeFilename }),\n req,\n })) !== getOriginalFilename({ filename: safeFilename })\n ) {\n safeFilename = incrementName(safeFilename)\n }\n const originalFilename = getOriginalFilename({ filename: safeFilename })\n const objectKey = randomUUID()\n const resolved = buildUploadStoragePathData({\n collectionPrefix: prefix,\n docPrefix: docPrefix ?? prefix,\n filename: originalFilename,\n })\n // The admin sends an empty docPrefix for new files; the resolved destination retains the collection fallback.\n const filePrefix = posix.dirname(resolved.storageFilePath)\n const storageFilePath = posix.join(filePrefix, objectKey, originalFilename)\n const uploadReference = {\n _objectKey: objectKey,\n prefix: filePrefix,\n signedReceipt: createClientUploadReceipt({\n _objectKey: objectKey,\n collectionSlug,\n filename: safeFilename,\n filePrefix,\n req,\n storageFilePath,\n }),\n }\n const upload = typeof collection.upload === 'object' ? collection.upload : undefined\n const uploadPayload: ImageKitUploadPayload = {\n checks: getUploadChecks({\n allowedMimeTypes: upload?.mimeTypes ?? [],\n allowRestrictedFileTypes: upload?.allowRestrictedFileTypes ?? false,\n filesize,\n }),\n fileName: originalFilename,\n folder: `/${posix.dirname(storageFilePath)}`,\n isPrivateFile: isPrivate ? 'true' : 'false',\n overwriteFile: 'false',\n tags: `payload-upload-${objectKey}`,\n useUniqueFileName: 'false',\n }\n return {\n name: 'imagekit',\n type: 'dispatch',\n data: {\n token: createImageKitUploadToken({ privateKey, publicKey, uploadPayload }),\n uploadPayload,\n uploadReference,\n },\n file: { filename: safeFilename, mimeType, size: filesize, uploadReference },\n }\n }\n"],"names":["buildUploadStoragePathData","randomUUID","posix","Forbidden","assertClientUploadAccess","assertClientUploadAllowed","createClientUploadReceipt","getOriginalFilename","getSafeFileName","incrementName","getSanitizedUploadFilename","createImageKitUploadToken","getUploadChecks","allowedMimeTypes","allowRestrictedFileTypes","filesize","checks","prefixes","map","mimeType","replace","filter","prefix","test","length","includes","push","join","createImageKitUploadInstructions","access","req","Boolean","user","isPrivate","privateKey","publicKey","collectionSlug","docPrefix","filename","overrideAccess","t","collection","payload","collections","config","safeFilename","desiredFilename","originalFilename","objectKey","resolved","collectionPrefix","filePrefix","dirname","storageFilePath","uploadReference","_objectKey","signedReceipt","upload","undefined","uploadPayload","mimeTypes","fileName","folder","isPrivateFile","overwriteFile","tags","useUniqueFileName","name","type","data","token","file","size"],"mappings":"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"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import type ImageKit from '@imagekit/nodejs';
|
|
2
|
+
import type { PayloadRequest } from 'payload';
|
|
3
|
+
/** Resolve new uploads by provider ID, without depending on ImageKit's eventual-consistency listing. */
|
|
4
|
+
export declare const createImageKitFileStore: (client: ImageKit, urlEndpoint: string) => {
|
|
5
|
+
find: ({ key, req }: {
|
|
6
|
+
key: string;
|
|
7
|
+
req: PayloadRequest;
|
|
8
|
+
}) => Promise<{
|
|
9
|
+
name: string;
|
|
10
|
+
fileId: string;
|
|
11
|
+
size: number;
|
|
12
|
+
url: string;
|
|
13
|
+
AITags?: Array<ImageKit.Files.File.AITag> | null;
|
|
14
|
+
audioCodec?: string;
|
|
15
|
+
bitRate?: number;
|
|
16
|
+
createdAt?: string;
|
|
17
|
+
customCoordinates?: string | null;
|
|
18
|
+
customMetadata?: {
|
|
19
|
+
[key: string]: unknown;
|
|
20
|
+
};
|
|
21
|
+
description?: string;
|
|
22
|
+
duration?: number;
|
|
23
|
+
embeddedMetadata?: {
|
|
24
|
+
[key: string]: unknown;
|
|
25
|
+
};
|
|
26
|
+
filePath?: string;
|
|
27
|
+
fileType?: string;
|
|
28
|
+
hasAlpha?: boolean;
|
|
29
|
+
height?: number;
|
|
30
|
+
isPrivateFile?: boolean;
|
|
31
|
+
isPublished?: boolean;
|
|
32
|
+
mime?: string;
|
|
33
|
+
selectedFieldsSchema?: {
|
|
34
|
+
[key: string]: ImageKit.Files.File.SelectedFieldsSchema;
|
|
35
|
+
};
|
|
36
|
+
tags?: Array<string> | null;
|
|
37
|
+
thumbnail?: string;
|
|
38
|
+
type?: "file" | "file-version";
|
|
39
|
+
updatedAt?: string;
|
|
40
|
+
versionInfo?: ImageKit.Files.File.VersionInfo;
|
|
41
|
+
videoCodec?: string;
|
|
42
|
+
width?: number;
|
|
43
|
+
} | undefined>;
|
|
44
|
+
getByID: ({ fileId, key }: {
|
|
45
|
+
fileId: string;
|
|
46
|
+
key: string;
|
|
47
|
+
}) => Promise<{
|
|
48
|
+
name: string;
|
|
49
|
+
fileId: string;
|
|
50
|
+
size: number;
|
|
51
|
+
url: string;
|
|
52
|
+
AITags?: Array<ImageKit.Files.File.AITag> | null;
|
|
53
|
+
audioCodec?: string;
|
|
54
|
+
bitRate?: number;
|
|
55
|
+
createdAt?: string;
|
|
56
|
+
customCoordinates?: string | null;
|
|
57
|
+
customMetadata?: {
|
|
58
|
+
[key: string]: unknown;
|
|
59
|
+
};
|
|
60
|
+
description?: string;
|
|
61
|
+
duration?: number;
|
|
62
|
+
embeddedMetadata?: {
|
|
63
|
+
[key: string]: unknown;
|
|
64
|
+
};
|
|
65
|
+
filePath?: string;
|
|
66
|
+
fileType?: string;
|
|
67
|
+
hasAlpha?: boolean;
|
|
68
|
+
height?: number;
|
|
69
|
+
isPrivateFile?: boolean;
|
|
70
|
+
isPublished?: boolean;
|
|
71
|
+
mime?: string;
|
|
72
|
+
selectedFieldsSchema?: {
|
|
73
|
+
[key: string]: ImageKit.Files.File.SelectedFieldsSchema;
|
|
74
|
+
};
|
|
75
|
+
tags?: Array<string> | null;
|
|
76
|
+
thumbnail?: string;
|
|
77
|
+
type?: "file" | "file-version";
|
|
78
|
+
updatedAt?: string;
|
|
79
|
+
versionInfo?: ImageKit.Files.File.VersionInfo;
|
|
80
|
+
videoCodec?: string;
|
|
81
|
+
width?: number;
|
|
82
|
+
}>;
|
|
83
|
+
remember: ({ fileId, key, req, }: {
|
|
84
|
+
fileId: string;
|
|
85
|
+
key: string;
|
|
86
|
+
req: PayloadRequest;
|
|
87
|
+
}) => Promise<void>;
|
|
88
|
+
remove: ({ key, req }: {
|
|
89
|
+
key: string;
|
|
90
|
+
req: PayloadRequest;
|
|
91
|
+
}) => Promise<void>;
|
|
92
|
+
upload: ({ body, key, req, }: {
|
|
93
|
+
body: Parameters<ImageKit["files"]["upload"]>[0];
|
|
94
|
+
key: string;
|
|
95
|
+
req: PayloadRequest;
|
|
96
|
+
}) => Promise<{
|
|
97
|
+
fileId: string;
|
|
98
|
+
url: string;
|
|
99
|
+
AITags?: Array<ImageKit.Files.FileUploadResponse.AITag> | null;
|
|
100
|
+
audioCodec?: string;
|
|
101
|
+
bitRate?: number;
|
|
102
|
+
customCoordinates?: string | null;
|
|
103
|
+
customMetadata?: {
|
|
104
|
+
[key: string]: unknown;
|
|
105
|
+
};
|
|
106
|
+
description?: string;
|
|
107
|
+
duration?: number;
|
|
108
|
+
embeddedMetadata?: {
|
|
109
|
+
[key: string]: unknown;
|
|
110
|
+
};
|
|
111
|
+
extensionStatus?: ImageKit.Files.FileUploadResponse.ExtensionStatus;
|
|
112
|
+
filePath?: string;
|
|
113
|
+
fileType?: string;
|
|
114
|
+
height?: number;
|
|
115
|
+
isPrivateFile?: boolean;
|
|
116
|
+
isPublished?: boolean;
|
|
117
|
+
metadata?: ImageKit.Files.Metadata;
|
|
118
|
+
name?: string;
|
|
119
|
+
selectedFieldsSchema?: {
|
|
120
|
+
[key: string]: ImageKit.Files.FileUploadResponse.SelectedFieldsSchema;
|
|
121
|
+
};
|
|
122
|
+
size?: number;
|
|
123
|
+
tags?: Array<string> | null;
|
|
124
|
+
thumbnailUrl?: string;
|
|
125
|
+
versionInfo?: ImageKit.Files.FileUploadResponse.VersionInfo;
|
|
126
|
+
videoCodec?: string;
|
|
127
|
+
width?: number;
|
|
128
|
+
}>;
|
|
129
|
+
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { NotFoundError } from '@imagekit/nodejs';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { APIError } from 'payload';
|
|
5
|
+
class FileLocationMismatch extends APIError {
|
|
6
|
+
constructor(){
|
|
7
|
+
super('ImageKit file does not match the authorized destination.', 400);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
/** Resolve new uploads by provider ID, without depending on ImageKit's eventual-consistency listing. */ export const createImageKitFileStore = (client, urlEndpoint)=>{
|
|
11
|
+
const registryKey = (key)=>`imagekit:file:${createHash('sha256').update(JSON.stringify([
|
|
12
|
+
urlEndpoint,
|
|
13
|
+
key
|
|
14
|
+
])).digest('hex')}`;
|
|
15
|
+
const getByID = async ({ fileId, key })=>{
|
|
16
|
+
const filePath = `/${key.replace(/^\/+/, '')}`;
|
|
17
|
+
const file = await client.files.get(fileId);
|
|
18
|
+
if (file.fileId !== fileId || typeof file.name !== 'string' || typeof file.filePath !== 'string' || typeof file.size !== 'number' || !Number.isSafeInteger(file.size) || file.size < 0 || !file.url) {
|
|
19
|
+
throw new APIError('ImageKit returned incomplete file metadata.', 502);
|
|
20
|
+
}
|
|
21
|
+
if (file.name !== path.posix.basename(filePath) || file.filePath !== filePath) {
|
|
22
|
+
throw new FileLocationMismatch();
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
...file,
|
|
26
|
+
name: file.name,
|
|
27
|
+
fileId,
|
|
28
|
+
size: file.size,
|
|
29
|
+
url: file.url
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
const remember = async ({ fileId, key, req })=>{
|
|
33
|
+
// This is a provider-object index, not an authorization or an upload session.
|
|
34
|
+
// Keep it for the object's lifetime (including retained versions), then remove it on delete.
|
|
35
|
+
await req.payload.kv.set(registryKey(key), fileId);
|
|
36
|
+
};
|
|
37
|
+
const upload = async ({ body, key, req })=>{
|
|
38
|
+
const uploaded = await client.files.upload(body);
|
|
39
|
+
try {
|
|
40
|
+
if (!uploaded.fileId || !uploaded.url) {
|
|
41
|
+
throw new APIError('ImageKit returned incomplete upload metadata.', 502);
|
|
42
|
+
}
|
|
43
|
+
await remember({
|
|
44
|
+
fileId: uploaded.fileId,
|
|
45
|
+
key,
|
|
46
|
+
req
|
|
47
|
+
});
|
|
48
|
+
return {
|
|
49
|
+
...uploaded,
|
|
50
|
+
fileId: uploaded.fileId,
|
|
51
|
+
url: uploaded.url
|
|
52
|
+
};
|
|
53
|
+
} catch (error) {
|
|
54
|
+
// The provider write already succeeded, but the framework may not yet have registered
|
|
55
|
+
// its rollback action (notably for copies). Compensate by the returned ID, never a listing.
|
|
56
|
+
if (uploaded.fileId) {
|
|
57
|
+
try {
|
|
58
|
+
await client.files.delete(uploaded.fileId);
|
|
59
|
+
await req.payload.kv.delete(registryKey(key));
|
|
60
|
+
} catch (cleanupError) {
|
|
61
|
+
req.payload.logger.error({
|
|
62
|
+
err: cleanupError,
|
|
63
|
+
msg: 'Failed to roll back an unregistered ImageKit upload.'
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const find = async ({ key, req })=>{
|
|
71
|
+
const rememberedID = await req.payload.kv.get(registryKey(key));
|
|
72
|
+
if (rememberedID) {
|
|
73
|
+
try {
|
|
74
|
+
return await getByID({
|
|
75
|
+
fileId: rememberedID,
|
|
76
|
+
key
|
|
77
|
+
});
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if (!(error instanceof NotFoundError) && !(error instanceof FileLocationMismatch)) {
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
// External deletes or moves can stale the index. Never delete the mismatched object;
|
|
83
|
+
// forget only its mapping and resolve the requested path normally.
|
|
84
|
+
await req.payload.kv.delete(registryKey(key));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Existing installations and uploads started by older admin bundles have no registry entry.
|
|
88
|
+
const filePath = `/${key.replace(/^\/+/, '')}`;
|
|
89
|
+
const name = path.posix.basename(filePath);
|
|
90
|
+
for(let skip = 0;; skip += 100){
|
|
91
|
+
const assets = await client.assets.list({
|
|
92
|
+
type: 'file',
|
|
93
|
+
limit: 100,
|
|
94
|
+
path: `${path.posix.dirname(filePath)}/`,
|
|
95
|
+
skip
|
|
96
|
+
});
|
|
97
|
+
const found = assets.find((asset)=>asset.filePath === filePath && asset.name === name);
|
|
98
|
+
if (found?.fileId) {
|
|
99
|
+
return getByID({
|
|
100
|
+
fileId: found.fileId,
|
|
101
|
+
key
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
if (assets.length < 100) {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
const remove = async ({ key, req })=>{
|
|
110
|
+
const file = await find({
|
|
111
|
+
key,
|
|
112
|
+
req
|
|
113
|
+
});
|
|
114
|
+
if (file) {
|
|
115
|
+
try {
|
|
116
|
+
await client.files.delete(file.fileId);
|
|
117
|
+
} catch (error) {
|
|
118
|
+
if (!(error instanceof NotFoundError)) {
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
await req.payload.kv.delete(registryKey(key));
|
|
124
|
+
};
|
|
125
|
+
return {
|
|
126
|
+
find,
|
|
127
|
+
getByID,
|
|
128
|
+
remember,
|
|
129
|
+
remove,
|
|
130
|
+
upload
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
//# sourceMappingURL=imageKitFileStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/imageKitFileStore.ts"],"sourcesContent":["import type ImageKit from '@imagekit/nodejs'\nimport type { PayloadRequest } from 'payload'\n\nimport { NotFoundError } from '@imagekit/nodejs'\nimport { createHash } from 'node:crypto'\nimport path from 'node:path'\nimport { APIError } from 'payload'\n\nclass FileLocationMismatch extends APIError {\n constructor() {\n super('ImageKit file does not match the authorized destination.', 400)\n }\n}\n\n/** Resolve new uploads by provider ID, without depending on ImageKit's eventual-consistency listing. */\nexport const createImageKitFileStore = (client: ImageKit, urlEndpoint: string) => {\n const registryKey = (key: string) =>\n `imagekit:file:${createHash('sha256')\n .update(JSON.stringify([urlEndpoint, key]))\n .digest('hex')}`\n\n const getByID = async ({ fileId, key }: { fileId: string; key: string }) => {\n const filePath = `/${key.replace(/^\\/+/, '')}`\n const file = await client.files.get(fileId)\n if (\n file.fileId !== fileId ||\n typeof file.name !== 'string' ||\n typeof file.filePath !== 'string' ||\n typeof file.size !== 'number' ||\n !Number.isSafeInteger(file.size) ||\n file.size < 0 ||\n !file.url\n ) {\n throw new APIError('ImageKit returned incomplete file metadata.', 502)\n }\n if (file.name !== path.posix.basename(filePath) || file.filePath !== filePath) {\n throw new FileLocationMismatch()\n }\n return { ...file, name: file.name, fileId, size: file.size, url: file.url }\n }\n\n const remember = async ({\n fileId,\n key,\n req,\n }: {\n fileId: string\n key: string\n req: PayloadRequest\n }) => {\n // This is a provider-object index, not an authorization or an upload session.\n // Keep it for the object's lifetime (including retained versions), then remove it on delete.\n await req.payload.kv.set(registryKey(key), fileId)\n }\n\n const upload = async ({\n body,\n key,\n req,\n }: {\n body: Parameters<ImageKit['files']['upload']>[0]\n key: string\n req: PayloadRequest\n }) => {\n const uploaded = await client.files.upload(body)\n try {\n if (!uploaded.fileId || !uploaded.url) {\n throw new APIError('ImageKit returned incomplete upload metadata.', 502)\n }\n await remember({ fileId: uploaded.fileId, key, req })\n return { ...uploaded, fileId: uploaded.fileId, url: uploaded.url }\n } catch (error) {\n // The provider write already succeeded, but the framework may not yet have registered\n // its rollback action (notably for copies). Compensate by the returned ID, never a listing.\n if (uploaded.fileId) {\n try {\n await client.files.delete(uploaded.fileId)\n await req.payload.kv.delete(registryKey(key))\n } catch (cleanupError) {\n req.payload.logger.error({\n err: cleanupError,\n msg: 'Failed to roll back an unregistered ImageKit upload.',\n })\n }\n }\n throw error\n }\n }\n\n const find = async ({ key, req }: { key: string; req: PayloadRequest }) => {\n const rememberedID = await req.payload.kv.get<string>(registryKey(key))\n if (rememberedID) {\n try {\n return await getByID({ fileId: rememberedID, key })\n } catch (error) {\n if (!(error instanceof NotFoundError) && !(error instanceof FileLocationMismatch)) {\n throw error\n }\n // External deletes or moves can stale the index. Never delete the mismatched object;\n // forget only its mapping and resolve the requested path normally.\n await req.payload.kv.delete(registryKey(key))\n }\n }\n // Existing installations and uploads started by older admin bundles have no registry entry.\n const filePath = `/${key.replace(/^\\/+/, '')}`\n const name = path.posix.basename(filePath)\n for (let skip = 0; ; skip += 100) {\n const assets = await client.assets.list({\n type: 'file',\n limit: 100,\n path: `${path.posix.dirname(filePath)}/`,\n skip,\n })\n const found = assets.find((asset) => asset.filePath === filePath && asset.name === name)\n if (found?.fileId) {\n return getByID({ fileId: found.fileId, key })\n }\n if (assets.length < 100) {\n return undefined\n }\n }\n }\n\n const remove = async ({ key, req }: { key: string; req: PayloadRequest }) => {\n const file = await find({ key, req })\n if (file) {\n try {\n await client.files.delete(file.fileId)\n } catch (error) {\n if (!(error instanceof NotFoundError)) {\n throw error\n }\n }\n }\n await req.payload.kv.delete(registryKey(key))\n }\n\n return { find, getByID, remember, remove, upload }\n}\n"],"names":["NotFoundError","createHash","path","APIError","FileLocationMismatch","createImageKitFileStore","client","urlEndpoint","registryKey","key","update","JSON","stringify","digest","getByID","fileId","filePath","replace","file","files","get","name","size","Number","isSafeInteger","url","posix","basename","remember","req","payload","kv","set","upload","body","uploaded","error","delete","cleanupError","logger","err","msg","find","rememberedID","skip","assets","list","type","limit","dirname","found","asset","length","undefined","remove"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -5,8 +5,10 @@ import { openAsBlob } from 'node:fs';
|
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { APIError } from 'payload';
|
|
7
7
|
import { assertClientUploadAllowed, getRangeRequestInfo, isXmlMimeType, uploadContentSecurityPolicy, verifyClientUploadReceipt } from 'payload/internal';
|
|
8
|
+
import { completeImageKitUpload } from './endpoints/completeImageKitUpload.js';
|
|
8
9
|
import { createImageKitUploadInstructions } from './endpoints/createImageKitUploadInstructions.js';
|
|
9
10
|
import { getAdminThumbnailURL, getPrivateAdminThumbnailURL, isPdfMimeType, isVideoMimeType } from './getAdminThumbnailURL.js';
|
|
11
|
+
import { createImageKitFileStore } from './imageKitFileStore.js';
|
|
10
12
|
import { getImageKitThumbnailSrc, getVideoPosterSrc } from './imageTransforms.js';
|
|
11
13
|
/** Payload 4 storage capability. Register under config.storage, not config.plugins. */ export const imagekitStorage = (options)=>({
|
|
12
14
|
name: 'imagekit',
|
|
@@ -46,6 +48,18 @@ function initializeStorage(config, options) {
|
|
|
46
48
|
const value = collections[slug];
|
|
47
49
|
return value !== true && value?.private === true;
|
|
48
50
|
};
|
|
51
|
+
const files = createImageKitFileStore(client, options.urlEndpoint);
|
|
52
|
+
const completeUpload = completeImageKitUpload({
|
|
53
|
+
access: typeof options.clientUploads === 'object' ? options.clientUploads.access : undefined,
|
|
54
|
+
collections: Object.fromEntries(Object.keys(collections).map((slug)=>[
|
|
55
|
+
slug,
|
|
56
|
+
{
|
|
57
|
+
isPrivate: collectionIsPrivate(slug),
|
|
58
|
+
prefix: prefixFor(slug)
|
|
59
|
+
}
|
|
60
|
+
])),
|
|
61
|
+
files
|
|
62
|
+
});
|
|
49
63
|
const sourceConfig = {
|
|
50
64
|
...config,
|
|
51
65
|
collections: (config.collections ?? []).map((collection)=>{
|
|
@@ -116,33 +130,41 @@ function initializeStorage(config, options) {
|
|
|
116
130
|
] : undefined,
|
|
117
131
|
urlEndpoint: options.urlEndpoint
|
|
118
132
|
});
|
|
119
|
-
const deleteFile =
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
};
|
|
133
|
+
const deleteFile = ({ req, storageFilePath })=>files.remove({
|
|
134
|
+
key: storageFilePath,
|
|
135
|
+
req
|
|
136
|
+
});
|
|
125
137
|
return {
|
|
126
138
|
name: 'imagekit',
|
|
127
|
-
copyFile: async ({ from, to })=>{
|
|
128
|
-
if (await
|
|
139
|
+
copyFile: async ({ from, req, to })=>{
|
|
140
|
+
if (await files.find({
|
|
141
|
+
key: to,
|
|
142
|
+
req
|
|
143
|
+
})) {
|
|
129
144
|
throw new Error(`ImageKit destination already exists: ${to}`);
|
|
130
145
|
}
|
|
131
|
-
const source = await
|
|
146
|
+
const source = await files.find({
|
|
147
|
+
key: from,
|
|
148
|
+
req
|
|
149
|
+
});
|
|
132
150
|
if (!source) {
|
|
133
151
|
throw new Error(`ImageKit source not found: ${from}`);
|
|
134
152
|
}
|
|
135
153
|
// Upload by URL copies the exact original without buffering it in the app process.
|
|
136
154
|
// overwriteFile:false protects a destination created concurrently with the lookup.
|
|
137
|
-
await
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
155
|
+
await files.upload({
|
|
156
|
+
body: {
|
|
157
|
+
customMetadata: source.customMetadata ?? undefined,
|
|
158
|
+
file: buildURL(from, Boolean(source.isPrivateFile), 'orig-true'),
|
|
159
|
+
fileName: path.posix.basename(to),
|
|
160
|
+
folder: `/${path.posix.dirname(to)}`,
|
|
161
|
+
isPrivateFile: source.isPrivateFile ?? false,
|
|
162
|
+
overwriteFile: false,
|
|
163
|
+
tags: source.tags ?? undefined,
|
|
164
|
+
useUniqueFileName: false
|
|
165
|
+
},
|
|
166
|
+
key: to,
|
|
167
|
+
req
|
|
146
168
|
});
|
|
147
169
|
},
|
|
148
170
|
deleteFile,
|
|
@@ -158,23 +180,24 @@ function initializeStorage(config, options) {
|
|
|
158
180
|
filename
|
|
159
181
|
}).storageFilePath, false),
|
|
160
182
|
handleDelete: deleteFile,
|
|
161
|
-
handleUpload: async ({ data, file, storageFilePath })=>{
|
|
183
|
+
handleUpload: async ({ data, file, req, storageFilePath })=>{
|
|
162
184
|
const uploadSource = file.tempFilePath ? await openAsBlob(file.tempFilePath, {
|
|
163
185
|
type: file.mimeType
|
|
164
186
|
}) : file.buffer;
|
|
165
|
-
const uploaded = await
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
187
|
+
const uploaded = await files.upload({
|
|
188
|
+
body: {
|
|
189
|
+
file: await toFile(uploadSource, file.filename, {
|
|
190
|
+
type: file.mimeType
|
|
191
|
+
}),
|
|
192
|
+
fileName: path.posix.basename(storageFilePath),
|
|
193
|
+
folder: `/${path.posix.dirname(storageFilePath)}`,
|
|
194
|
+
isPrivateFile: isPrivate,
|
|
195
|
+
overwriteFile: false,
|
|
196
|
+
useUniqueFileName: false
|
|
197
|
+
},
|
|
198
|
+
key: storageFilePath,
|
|
199
|
+
req
|
|
174
200
|
});
|
|
175
|
-
if (!uploaded.fileId || !uploaded.url) {
|
|
176
|
-
throw new Error('ImageKit returned incomplete upload metadata.');
|
|
177
|
-
}
|
|
178
201
|
return applyMetadata(data, {
|
|
179
202
|
name: path.posix.basename(storageFilePath),
|
|
180
203
|
fileId: uploaded.fileId,
|
|
@@ -202,7 +225,7 @@ function initializeStorage(config, options) {
|
|
|
202
225
|
if (!receipt._objectKey || expectedPath !== receipt.storageFilePath || !expectedPath.startsWith(`${prefix.replace(/^\/+/, '')}/`)) {
|
|
203
226
|
throw new APIError('Invalid ImageKit upload reference.', 400);
|
|
204
227
|
}
|
|
205
|
-
const uploaded = await findUploadedFile(
|
|
228
|
+
const uploaded = await findUploadedFile(files, expectedPath, req);
|
|
206
229
|
if (!uploaded || !uploaded.mime || Boolean(uploaded.isPrivateFile) !== isPrivate) {
|
|
207
230
|
throw new APIError('ImageKit upload did not match its authorization.', 400);
|
|
208
231
|
}
|
|
@@ -246,7 +269,10 @@ function initializeStorage(config, options) {
|
|
|
246
269
|
}).storageFilePath;
|
|
247
270
|
const privateFile = isPrivateDocument(document, isPrivate);
|
|
248
271
|
if (params.operation === 'transform' || isXmlMimeType(source.mimeType ?? '')) {
|
|
249
|
-
const file = await
|
|
272
|
+
const file = await files.find({
|
|
273
|
+
key,
|
|
274
|
+
req
|
|
275
|
+
});
|
|
250
276
|
if (!file?.mime) {
|
|
251
277
|
throw new APIError('ImageKit source not found.', 404);
|
|
252
278
|
}
|
|
@@ -276,6 +302,10 @@ function initializeStorage(config, options) {
|
|
|
276
302
|
path: '@humaan/payload-storage-imagekit/client#ImageKitUploadHandler'
|
|
277
303
|
},
|
|
278
304
|
enabled: options.enabled !== false && Boolean(options.clientUploads),
|
|
305
|
+
endpoint: {
|
|
306
|
+
handler: completeUpload,
|
|
307
|
+
path: '/imagekit-upload-complete'
|
|
308
|
+
},
|
|
279
309
|
generate: createImageKitUploadInstructions({
|
|
280
310
|
access: typeof options.clientUploads === 'object' ? options.clientUploads.access : undefined,
|
|
281
311
|
isPrivate,
|
|
@@ -311,39 +341,13 @@ function initializeStorage(config, options) {
|
|
|
311
341
|
});
|
|
312
342
|
return output;
|
|
313
343
|
}
|
|
314
|
-
async function
|
|
315
|
-
const filePath = `/${storageFilePath.replace(/^\/+/, '')}`;
|
|
316
|
-
const name = path.posix.basename(filePath);
|
|
317
|
-
for(let skip = 0;; skip += 100){
|
|
318
|
-
const assets = await client.assets.list({
|
|
319
|
-
type: 'file',
|
|
320
|
-
limit: 100,
|
|
321
|
-
path: `${path.posix.dirname(filePath)}/`,
|
|
322
|
-
skip
|
|
323
|
-
});
|
|
324
|
-
const found = assets.find((asset)=>'fileId' in asset && asset.filePath === filePath && asset.name === name);
|
|
325
|
-
if (found && 'fileId' in found && found.fileId) {
|
|
326
|
-
const file = await client.files.get(found.fileId);
|
|
327
|
-
if (file.fileId !== found.fileId || file.name !== name || file.filePath !== filePath || typeof file.size !== 'number' || !Number.isSafeInteger(file.size) || file.size < 0 || !file.url) {
|
|
328
|
-
throw new Error('ImageKit returned incomplete file metadata.');
|
|
329
|
-
}
|
|
330
|
-
return {
|
|
331
|
-
...file,
|
|
332
|
-
name: file.name,
|
|
333
|
-
fileId: file.fileId,
|
|
334
|
-
size: file.size,
|
|
335
|
-
url: file.url
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
if (assets.length < 100) {
|
|
339
|
-
return undefined;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
async function findUploadedFile(client, key) {
|
|
344
|
+
async function findUploadedFile(files, key, req) {
|
|
344
345
|
// ImageKit asset listings can lag a successful upload. Only admission retries;
|
|
345
346
|
// ordinary missing reads/deletes and collision lookups must stay immediate.
|
|
346
|
-
let file = await
|
|
347
|
+
let file = await files.find({
|
|
348
|
+
key,
|
|
349
|
+
req
|
|
350
|
+
});
|
|
347
351
|
for (const delay of [
|
|
348
352
|
250,
|
|
349
353
|
500,
|
|
@@ -354,7 +358,10 @@ async function findUploadedFile(client, key) {
|
|
|
354
358
|
break;
|
|
355
359
|
}
|
|
356
360
|
await new Promise((resolve)=>setTimeout(resolve, delay));
|
|
357
|
-
file = await
|
|
361
|
+
file = await files.find({
|
|
362
|
+
key,
|
|
363
|
+
req
|
|
364
|
+
});
|
|
358
365
|
}
|
|
359
366
|
return file;
|
|
360
367
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Adapter, ClientUploadsConfig } from '@payloadcms/plugin-cloud-storage/types'\nimport type {\n CollectionConfig,\n CollectionSlug,\n Config,\n Field,\n PayloadRequest,\n StorageAdapter,\n UploadCollectionSlug,\n} from 'payload'\n\nimport ImageKit, { toFile } from '@imagekit/nodejs'\nimport { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage'\nimport { buildStoragePathData } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { openAsBlob } from 'node:fs'\nimport path from 'node:path'\nimport { APIError } from 'payload'\nimport {\n assertClientUploadAllowed,\n getRangeRequestInfo,\n isXmlMimeType,\n uploadContentSecurityPolicy,\n verifyClientUploadReceipt,\n} from 'payload/internal'\n\nimport { createImageKitUploadInstructions } from './endpoints/createImageKitUploadInstructions.js'\nimport {\n getAdminThumbnailURL,\n getPrivateAdminThumbnailURL,\n isPdfMimeType,\n isVideoMimeType,\n} from './getAdminThumbnailURL.js'\nimport { getImageKitThumbnailSrc, getVideoPosterSrc } from './imageTransforms.js'\n\nexport type ImageKitCollectionOptions = { focalPoint?: boolean; private?: boolean }\nexport type PayloadStorageImageKitConfig = {\n clientUploads?: ClientUploadsConfig\n collections: Partial<Record<UploadCollectionSlug, ImageKitCollectionOptions | true>>\n disablePayloadAccessControl?: true\n enabled?: boolean\n focalPoint?: boolean\n folder: string\n privateKey: string\n publicKey: string\n signedUrlExpiresIn?: number\n urlEndpoint: string\n}\n\n/** Payload 4 storage capability. Register under config.storage, not config.plugins. */\nexport const imagekitStorage = (options: PayloadStorageImageKitConfig): StorageAdapter => ({\n name: 'imagekit',\n collections: Object.keys(options.collections),\n init: (config) => initializeStorage(config, options),\n})\n\nexport { migrateFolder } from './migrate.js'\nexport type { MigrateFolderOptions, MigrationJobResult, MigrationLocation } from './migrate.js'\nexport const payloadStorageImagekit = imagekitStorage\nexport const payloadStorageImageKit = imagekitStorage\nexport type PayloadStorageImagekitConfig = PayloadStorageImageKitConfig\n\ninterface ProviderFile {\n fileId: string\n isPrivateFile?: boolean\n mime?: string\n name: string\n size: number\n thumbnail?: string\n url: string\n}\n\ninterface FileLocation {\n _objectKey?: string\n filename?: string\n filesize?: number\n mimeType?: string\n prefix?: string\n}\n\nfunction initializeStorage(config: Config, options: PayloadStorageImageKitConfig): Config {\n for (const key of ['privateKey', 'publicKey', 'urlEndpoint', 'folder'] as const) {\n if (!options[key]?.trim()) {\n throw new Error(`imagekitStorage: \\`${key}\\` is required.`)\n }\n }\n const folder = options.folder.trim().replace(/^\\/+|\\/+$/g, '')\n if (folder.split('/').some((segment) => !segment || segment === '.' || segment === '..')) {\n throw new Error('imagekitStorage: `folder` must not contain empty or dot segments.')\n }\n const client = new ImageKit({ privateKey: options.privateKey })\n const collections: Partial<Record<string, ImageKitCollectionOptions | true>> = options.collections\n if (!Object.keys(collections).length) {\n throw new Error('imagekitStorage: `collections` must not be empty.')\n }\n const configured = new Set(Object.keys(collections))\n const apiRoute = config.routes?.api ?? '/api'\n const expiresIn = options.signedUrlExpiresIn ?? 900\n const prefixFor = (slug: string) => path.posix.join(folder, slug)\n const collectionIsPrivate = (slug: string) => {\n const value = collections[slug]\n return value !== true && value?.private === true\n }\n const sourceConfig: Config = {\n ...config,\n collections: (config.collections ?? []).map((collection) => {\n if (!configured.has(collection.slug)) {\n return collection\n }\n const isPrivate = collectionIsPrivate(collection.slug)\n if (options.disablePayloadAccessControl && (isPrivate || canStoreXml(collection))) {\n throw new Error(\n 'imagekitStorage: private and XML-capable collections require Payload access control.',\n )\n }\n const setting = collections[collection.slug]\n const focalPoint = setting !== true ? setting?.focalPoint : undefined\n const description = collection.admin?.description\n return {\n ...collection,\n admin: isPrivate\n ? {\n ...collection.admin,\n description:\n typeof description === 'string'\n ? `${description} Files in this collection are uploaded to ImageKit as private files and delivered through short-lived signed URLs.`\n : (description ??\n 'Files in this collection are uploaded to ImageKit as private files and delivered through short-lived signed URLs.'),\n }\n : collection.admin,\n hooks: {\n ...collection.hooks,\n beforeChange: [\n ...(collection.hooks?.beforeChange ?? []),\n async ({ data, operation, originalDoc, req }) => {\n if (operation === 'validate') {\n return data\n }\n const verified = req.context.imagekitVerifiedUpload as ProviderFile | undefined\n if (!verified) {\n return data\n }\n const existing = await req.payload.db.findOne({\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -- Generated consumer configs narrow CollectionSlug, unlike the standalone package build.\n collection: collection.slug as CollectionSlug,\n req,\n where: { 'imagekit.fileId': { equals: verified.fileId } },\n })\n if (existing && existing.id !== originalDoc?.id) {\n throw new APIError('This ImageKit upload reference has already been used.', 400)\n }\n return applyMetadata(data, verified, isPrivate, collection.slug, apiRoute)\n },\n ],\n },\n upload: {\n focalPoint: focalPoint ?? options.focalPoint ?? false,\n ...(typeof collection.upload === 'object' ? collection.upload : {}),\n adminThumbnail: ({ doc }) =>\n isPrivateDocument(doc, isPrivate)\n ? getPrivateAdminThumbnailURL(doc, collection.slug, apiRoute)\n : getAdminThumbnailURL(doc),\n crop: false,\n },\n }\n }),\n }\n const adapter: Adapter = ({ collection, prefix = prefixFor(collection.slug) }) => {\n const isPrivate = collectionIsPrivate(collection.slug)\n const buildURL = (key: string, signed: boolean, transformation?: null | string) =>\n client.helper.buildSrc({\n expiresIn: signed ? expiresIn : undefined,\n signed,\n src: `/${key.replace(/^\\/+/, '')}`,\n transformation: transformation ? [{ raw: transformation }] : undefined,\n urlEndpoint: options.urlEndpoint,\n })\n const deleteFile = async ({ storageFilePath }: { storageFilePath: string }) => {\n const file = await findFile(client, storageFilePath)\n if (file) {\n await client.files.delete(file.fileId)\n }\n }\n return {\n name: 'imagekit',\n copyFile: async ({ from, to }) => {\n if (await findFile(client, to)) {\n throw new Error(`ImageKit destination already exists: ${to}`)\n }\n const source = await findFile(client, from)\n if (!source) {\n throw new Error(`ImageKit source not found: ${from}`)\n }\n // Upload by URL copies the exact original without buffering it in the app process.\n // overwriteFile:false protects a destination created concurrently with the lookup.\n await client.files.upload({\n customMetadata: source.customMetadata ?? undefined,\n file: buildURL(from, Boolean(source.isPrivateFile), 'orig-true'),\n fileName: path.posix.basename(to),\n folder: `/${path.posix.dirname(to)}`,\n isPrivateFile: source.isPrivateFile ?? false,\n overwriteFile: false,\n tags: source.tags ?? undefined,\n useUniqueFileName: false,\n })\n },\n deleteFile,\n fields: storageFields({ buildURL, expiresIn, isPrivate, prefix }),\n generateURL: ({ filename, prefix: docPrefix }) =>\n buildURL(\n buildStoragePathData({\n collectionPrefix: prefix,\n docPrefix,\n filename,\n }).storageFilePath,\n false,\n ),\n handleDelete: deleteFile,\n handleUpload: async ({ data, file, storageFilePath }) => {\n const uploadSource = file.tempFilePath\n ? await openAsBlob(file.tempFilePath, { type: file.mimeType })\n : file.buffer\n const uploaded = await client.files.upload({\n file: await toFile(uploadSource, file.filename, { type: file.mimeType }),\n fileName: path.posix.basename(storageFilePath),\n folder: `/${path.posix.dirname(storageFilePath)}`,\n isPrivateFile: isPrivate,\n overwriteFile: false,\n useUniqueFileName: false,\n })\n if (!uploaded.fileId || !uploaded.url) {\n throw new Error('ImageKit returned incomplete upload metadata.')\n }\n return applyMetadata(\n data,\n {\n name: path.posix.basename(storageFilePath),\n fileId: uploaded.fileId,\n isPrivateFile: isPrivate,\n mime: file.mimeType,\n size: file.filesize,\n thumbnail: uploaded.thumbnailUrl,\n url: uploaded.url,\n },\n isPrivate,\n collection.slug,\n apiRoute,\n )\n },\n staticHandler: async (req, { doc, params }) => {\n if (params.uploadReference) {\n try {\n const reference = params.uploadReference as { signedReceipt?: unknown }\n const receipt = verifyClientUploadReceipt({\n collectionSlug: collection.slug,\n req,\n signedReceipt: reference.signedReceipt,\n })\n const expectedPath = buildStoragePathData({\n collectionPrefix: prefix,\n docPrefix: path.posix.join(receipt.filePrefix, receipt._objectKey ?? ''),\n filename: params.filename,\n }).storageFilePath\n if (\n !receipt._objectKey ||\n expectedPath !== receipt.storageFilePath ||\n !expectedPath.startsWith(`${prefix.replace(/^\\/+/, '')}/`)\n ) {\n throw new APIError('Invalid ImageKit upload reference.', 400)\n }\n const uploaded = await findUploadedFile(client, expectedPath)\n if (!uploaded || !uploaded.mime || Boolean(uploaded.isPrivateFile) !== isPrivate) {\n throw new APIError('ImageKit upload did not match its authorization.', 400)\n }\n assertClientUploadAllowed({\n collection,\n filename: uploaded.name,\n mimeType: uploaded.mime,\n })\n req.context.imagekitVerifiedUpload = uploaded\n return await fetchOriginal({\n file: uploaded,\n req,\n url: buildURL(expectedPath, isPrivate, 'orig-true'),\n })\n } catch (error) {\n if (error instanceof APIError) {\n return Response.json({ message: error.message }, { status: error.status })\n }\n throw error\n }\n }\n const document =\n doc ??\n (await req.payload.db.findOne({\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -- Generated consumer configs narrow CollectionSlug, unlike the standalone package build.\n collection: collection.slug as CollectionSlug,\n req,\n where: { filename: { equals: params.filename } },\n }))\n const source = getFileLocation(document, params.filename)\n const key = buildStoragePathData({\n collectionPrefix: prefix,\n docPrefix: path.posix.join(\n params.prefix ?? source.prefix ?? prefix,\n source._objectKey ?? '',\n ),\n filename: params.filename,\n }).storageFilePath\n const privateFile = isPrivateDocument(document, isPrivate)\n if (params.operation === 'transform' || isXmlMimeType(source.mimeType ?? '')) {\n const file = await findFile(client, key)\n if (!file?.mime) {\n throw new APIError('ImageKit source not found.', 404)\n }\n return fetchOriginal({ file, req, url: buildURL(key, privateFile, 'orig-true') })\n }\n const tr = new URL(req.url ?? 'http://localhost').searchParams.get('tr')\n let src = key\n if (tr && isVideoMimeType(source.mimeType)) {\n src = getVideoPosterSrc(src)\n } else if (tr && isPdfMimeType(source.mimeType)) {\n src = getImageKitThumbnailSrc(src)\n }\n // Existing v3 files may live under an earlier folder or delivery endpoint.\n const storedURL = (document as { imagekit?: { url?: string }; original?: unknown } | null)\n ?.imagekit?.url\n if (\n !privateFile &&\n !tr &&\n !(document && 'original' in document && document.original) &&\n storedURL\n ) {\n return Response.redirect(storedURL, 302)\n }\n return Response.redirect(buildURL(src, privateFile, tr), 302)\n },\n supportsTempFiles: true,\n uploadInstructions: {\n adminHandler: { path: '@humaan/payload-storage-imagekit/client#ImageKitUploadHandler' },\n enabled: options.enabled !== false && Boolean(options.clientUploads),\n generate: createImageKitUploadInstructions({\n access:\n typeof options.clientUploads === 'object' ? options.clientUploads.access : undefined,\n isPrivate,\n prefix,\n privateKey: options.privateKey,\n publicKey: options.publicKey,\n }),\n requiresUploadReceipt: true,\n useInAdmin: true,\n },\n }\n }\n const output = cloudStoragePlugin({\n collections: Object.fromEntries(\n Object.keys(collections).map((slug) => [\n slug,\n {\n adapter,\n disableLocalStorage: true,\n disablePayloadAccessControl: options.disablePayloadAccessControl,\n prefix: prefixFor(slug),\n },\n ]),\n ),\n enabled: options.enabled,\n })(sourceConfig)\n // Do not invent a prefix on reads of legacy documents. A stored prefix is retained;\n // new uploads get their normalized destination from the file-operation pipeline.\n output.collections = output.collections?.map((collection) =>\n !configured.has(collection.slug)\n ? collection\n : {\n ...collection,\n fields: collection.fields.map((field) =>\n field.type === 'text' && field.name === 'prefix'\n ? {\n ...field,\n defaultValue: ({ req }) =>\n req.file && typeof field.defaultValue === 'string' ? field.defaultValue : '',\n }\n : field,\n ),\n },\n )\n return output\n}\n\nasync function findFile(client: ImageKit, storageFilePath: string) {\n const filePath = `/${storageFilePath.replace(/^\\/+/, '')}`\n const name = path.posix.basename(filePath)\n for (let skip = 0; ; skip += 100) {\n const assets = await client.assets.list({\n type: 'file',\n limit: 100,\n path: `${path.posix.dirname(filePath)}/`,\n skip,\n })\n const found = assets.find(\n (asset) => 'fileId' in asset && asset.filePath === filePath && asset.name === name,\n )\n if (found && 'fileId' in found && found.fileId) {\n const file = await client.files.get(found.fileId)\n if (\n file.fileId !== found.fileId ||\n file.name !== name ||\n file.filePath !== filePath ||\n typeof file.size !== 'number' ||\n !Number.isSafeInteger(file.size) ||\n file.size < 0 ||\n !file.url\n ) {\n throw new Error('ImageKit returned incomplete file metadata.')\n }\n return { ...file, name: file.name, fileId: file.fileId, size: file.size, url: file.url }\n }\n if (assets.length < 100) {\n return undefined\n }\n }\n}\n\nasync function findUploadedFile(client: ImageKit, key: string) {\n // ImageKit asset listings can lag a successful upload. Only admission retries;\n // ordinary missing reads/deletes and collision lookups must stay immediate.\n let file = await findFile(client, key)\n for (const delay of [250, 500, 1000, 2000]) {\n if (file) {\n break\n }\n await new Promise((resolve) => setTimeout(resolve, delay))\n file = await findFile(client, key)\n }\n return file\n}\n\nfunction isPrivateDocument(doc: unknown, fallback: boolean): boolean {\n const value = (doc as { imagekit?: { isPrivate?: boolean } } | null)?.imagekit?.isPrivate\n return typeof value === 'boolean' ? value : fallback\n}\n\nfunction getFileLocation(doc: unknown, filename: string): FileLocation {\n const value = (doc ?? {}) as {\n original?: FileLocation\n variants?: Record<string, FileLocation>\n } & FileLocation\n return (\n [value, value.original, ...Object.values(value.variants ?? {})].find(\n (file) => file?.filename === filename,\n ) ?? value\n )\n}\n\nfunction applyMetadata(\n data: Record<string, unknown>,\n file: ProviderFile,\n isPrivate: boolean,\n slug: string,\n apiRoute: string,\n) {\n data.imagekit = {\n fileId: file.fileId,\n isPrivate,\n thumbnailUrl: file.thumbnail,\n url: file.url,\n }\n data.thumbnailURL = isPrivate\n ? getPrivateAdminThumbnailURL(data, slug, apiRoute)\n : getAdminThumbnailURL(data)\n return data\n}\n\nasync function fetchOriginal({\n file,\n req,\n url,\n}: {\n file: ProviderFile\n req: PayloadRequest\n url: string\n}): Promise<Response> {\n const range = getRangeRequestInfo({ fileSize: file.size, rangeHeader: req.headers.get('Range') })\n if (range.type === 'invalid') {\n return new Response(null, { headers: range.headers, status: range.status })\n }\n const response = await fetch(url, {\n cache: 'no-store',\n headers:\n range.type === 'partial'\n ? { Range: `bytes=${range.rangeStart}-${range.rangeEnd}` }\n : undefined,\n signal: new AbortController().signal,\n })\n if (\n response.status !== range.status ||\n response.headers.get('Content-Length') !== range.headers['Content-Length'] ||\n (range.type === 'partial' &&\n response.headers.get('Content-Range') !== range.headers['Content-Range'])\n ) {\n void response.body?.cancel().catch(() => undefined)\n throw new APIError('ImageKit returned mismatched file content metadata.', 502)\n }\n const headers = new Headers(range.headers)\n headers.set('Content-Type', file.mime ?? 'application/octet-stream')\n if (isXmlMimeType(file.mime ?? '')) {\n headers.set('Content-Security-Policy', uploadContentSecurityPolicy)\n }\n return new Response(response.body, { headers, status: range.status })\n}\n\nfunction canStoreXml(collection: CollectionConfig): boolean {\n const upload = typeof collection.upload === 'object' ? collection.upload : undefined\n return (\n Boolean(upload?.allowRestrictedFileTypes) ||\n !upload?.mimeTypes?.length ||\n upload.mimeTypes.some((mime) => mime.includes('*') || isXmlMimeType(mime))\n )\n}\n\nfunction storageFields({\n buildURL,\n expiresIn,\n isPrivate,\n prefix,\n}: {\n buildURL: (key: string, signed: boolean, transformation?: null | string) => string\n expiresIn: number\n isPrivate: boolean\n prefix: string\n}): Field[] {\n return [\n {\n type: 'collapsible',\n admin: {\n condition: (data) => Boolean(data?.imagekit?.url),\n initCollapsed: true,\n },\n fields: [\n {\n name: 'imagekit',\n type: 'group',\n admin: { disabled: { column: true, filter: true } },\n fields: [\n { name: 'fileId', type: 'text', admin: { hidden: true }, unique: true },\n { name: 'url', type: 'text', admin: { readOnly: true }, label: 'URL' },\n {\n name: 'isPrivate',\n type: 'checkbox',\n admin: { readOnly: true },\n label: 'Private file',\n },\n {\n name: 'signedUrl',\n type: 'text',\n admin: {\n condition: (_data, siblingData) =>\n isPrivateDocument({ imagekit: siblingData }, isPrivate),\n description: `Temporary signed URL. Expires ${expiresIn} seconds after load. Anyone with this URL can access the file until it expires.`,\n readOnly: true,\n },\n hooks: {\n afterRead: [\n ({ data, siblingData }) => {\n if (\n !data?.filename ||\n !isPrivateDocument({ imagekit: siblingData }, isPrivate)\n ) {\n return undefined\n }\n const key = buildStoragePathData({\n collectionPrefix: prefix,\n docPrefix: path.posix.join(data.prefix || prefix, data._objectKey || ''),\n filename: data.filename,\n }).storageFilePath\n return buildURL(key, true)\n },\n ],\n },\n label: 'Signed URL',\n virtual: true,\n },\n { name: 'thumbnailUrl', type: 'text', admin: { hidden: true } },\n ],\n interfaceName: 'imagekitFields',\n label: false,\n },\n ],\n label: 'Storage: ImageKit',\n },\n ]\n}\n"],"names":["ImageKit","toFile","cloudStoragePlugin","buildStoragePathData","openAsBlob","path","APIError","assertClientUploadAllowed","getRangeRequestInfo","isXmlMimeType","uploadContentSecurityPolicy","verifyClientUploadReceipt","createImageKitUploadInstructions","getAdminThumbnailURL","getPrivateAdminThumbnailURL","isPdfMimeType","isVideoMimeType","getImageKitThumbnailSrc","getVideoPosterSrc","imagekitStorage","options","name","collections","Object","keys","init","config","initializeStorage","migrateFolder","payloadStorageImagekit","payloadStorageImageKit","key","trim","Error","folder","replace","split","some","segment","client","privateKey","length","configured","Set","apiRoute","routes","api","expiresIn","signedUrlExpiresIn","prefixFor","slug","posix","join","collectionIsPrivate","value","private","sourceConfig","map","collection","has","isPrivate","disablePayloadAccessControl","canStoreXml","setting","focalPoint","undefined","description","admin","hooks","beforeChange","data","operation","originalDoc","req","verified","context","imagekitVerifiedUpload","existing","payload","db","findOne","where","equals","fileId","id","applyMetadata","upload","adminThumbnail","doc","isPrivateDocument","crop","adapter","prefix","buildURL","signed","transformation","helper","buildSrc","src","raw","urlEndpoint","deleteFile","storageFilePath","file","findFile","files","delete","copyFile","from","to","source","customMetadata","Boolean","isPrivateFile","fileName","basename","dirname","overwriteFile","tags","useUniqueFileName","fields","storageFields","generateURL","filename","docPrefix","collectionPrefix","handleDelete","handleUpload","uploadSource","tempFilePath","type","mimeType","buffer","uploaded","url","mime","size","filesize","thumbnail","thumbnailUrl","staticHandler","params","uploadReference","reference","receipt","collectionSlug","signedReceipt","expectedPath","filePrefix","_objectKey","startsWith","findUploadedFile","fetchOriginal","error","Response","json","message","status","document","getFileLocation","privateFile","tr","URL","searchParams","get","storedURL","imagekit","original","redirect","supportsTempFiles","uploadInstructions","adminHandler","enabled","clientUploads","generate","access","publicKey","requiresUploadReceipt","useInAdmin","output","fromEntries","disableLocalStorage","field","defaultValue","filePath","skip","assets","list","limit","found","find","asset","Number","isSafeInteger","delay","Promise","resolve","setTimeout","fallback","values","variants","thumbnailURL","range","fileSize","rangeHeader","headers","response","fetch","cache","Range","rangeStart","rangeEnd","signal","AbortController","body","cancel","catch","Headers","set","allowRestrictedFileTypes","mimeTypes","includes","condition","initCollapsed","disabled","column","filter","hidden","unique","readOnly","label","_data","siblingData","afterRead","virtual","interfaceName"],"mappings":"AAWA,OAAOA,YAAYC,MAAM,QAAQ,mBAAkB;AACnD,SAASC,kBAAkB,QAAQ,mCAAkC;AACrE,SAASC,oBAAoB,QAAQ,6CAA4C;AACjF,SAASC,UAAU,QAAQ,UAAS;AACpC,OAAOC,UAAU,YAAW;AAC5B,SAASC,QAAQ,QAAQ,UAAS;AAClC,SACEC,yBAAyB,EACzBC,mBAAmB,EACnBC,aAAa,EACbC,2BAA2B,EAC3BC,yBAAyB,QACpB,mBAAkB;AAEzB,SAASC,gCAAgC,QAAQ,kDAAiD;AAClG,SACEC,oBAAoB,EACpBC,2BAA2B,EAC3BC,aAAa,EACbC,eAAe,QACV,4BAA2B;AAClC,SAASC,uBAAuB,EAAEC,iBAAiB,QAAQ,uBAAsB;AAgBjF,qFAAqF,GACrF,OAAO,MAAMC,kBAAkB,CAACC,UAA2D,CAAA;QACzFC,MAAM;QACNC,aAAaC,OAAOC,IAAI,CAACJ,QAAQE,WAAW;QAC5CG,MAAM,CAACC,SAAWC,kBAAkBD,QAAQN;IAC9C,CAAA,EAAE;AAEF,SAASQ,aAAa,QAAQ,eAAc;AAE5C,OAAO,MAAMC,yBAAyBV,gBAAe;AACrD,OAAO,MAAMW,yBAAyBX,gBAAe;AAqBrD,SAASQ,kBAAkBD,MAAc,EAAEN,OAAqC;IAC9E,KAAK,MAAMW,OAAO;QAAC;QAAc;QAAa;QAAe;KAAS,CAAW;QAC/E,IAAI,CAACX,OAAO,CAACW,IAAI,EAAEC,QAAQ;YACzB,MAAM,IAAIC,MAAM,CAAC,mBAAmB,EAAEF,IAAI,eAAe,CAAC;QAC5D;IACF;IACA,MAAMG,SAASd,QAAQc,MAAM,CAACF,IAAI,GAAGG,OAAO,CAAC,cAAc;IAC3D,IAAID,OAAOE,KAAK,CAAC,KAAKC,IAAI,CAAC,CAACC,UAAY,CAACA,WAAWA,YAAY,OAAOA,YAAY,OAAO;QACxF,MAAM,IAAIL,MAAM;IAClB;IACA,MAAMM,SAAS,IAAIvC,SAAS;QAAEwC,YAAYpB,QAAQoB,UAAU;IAAC;IAC7D,MAAMlB,cAAyEF,QAAQE,WAAW;IAClG,IAAI,CAACC,OAAOC,IAAI,CAACF,aAAamB,MAAM,EAAE;QACpC,MAAM,IAAIR,MAAM;IAClB;IACA,MAAMS,aAAa,IAAIC,IAAIpB,OAAOC,IAAI,CAACF;IACvC,MAAMsB,WAAWlB,OAAOmB,MAAM,EAAEC,OAAO;IACvC,MAAMC,YAAY3B,QAAQ4B,kBAAkB,IAAI;IAChD,MAAMC,YAAY,CAACC,OAAiB7C,KAAK8C,KAAK,CAACC,IAAI,CAAClB,QAAQgB;IAC5D,MAAMG,sBAAsB,CAACH;QAC3B,MAAMI,QAAQhC,WAAW,CAAC4B,KAAK;QAC/B,OAAOI,UAAU,QAAQA,OAAOC,YAAY;IAC9C;IACA,MAAMC,eAAuB;QAC3B,GAAG9B,MAAM;QACTJ,aAAa,AAACI,CAAAA,OAAOJ,WAAW,IAAI,EAAE,AAAD,EAAGmC,GAAG,CAAC,CAACC;YAC3C,IAAI,CAAChB,WAAWiB,GAAG,CAACD,WAAWR,IAAI,GAAG;gBACpC,OAAOQ;YACT;YACA,MAAME,YAAYP,oBAAoBK,WAAWR,IAAI;YACrD,IAAI9B,QAAQyC,2BAA2B,IAAKD,CAAAA,aAAaE,YAAYJ,WAAU,GAAI;gBACjF,MAAM,IAAIzB,MACR;YAEJ;YACA,MAAM8B,UAAUzC,WAAW,CAACoC,WAAWR,IAAI,CAAC;YAC5C,MAAMc,aAAaD,YAAY,OAAOA,SAASC,aAAaC;YAC5D,MAAMC,cAAcR,WAAWS,KAAK,EAAED;YACtC,OAAO;gBACL,GAAGR,UAAU;gBACbS,OAAOP,YACH;oBACE,GAAGF,WAAWS,KAAK;oBACnBD,aACE,OAAOA,gBAAgB,WACnB,GAAGA,YAAY,kHAAkH,CAAC,GACjIA,eACD;gBACR,IACAR,WAAWS,KAAK;gBACpBC,OAAO;oBACL,GAAGV,WAAWU,KAAK;oBACnBC,cAAc;2BACRX,WAAWU,KAAK,EAAEC,gBAAgB,EAAE;wBACxC,OAAO,EAAEC,IAAI,EAAEC,SAAS,EAAEC,WAAW,EAAEC,GAAG,EAAE;4BAC1C,IAAIF,cAAc,YAAY;gCAC5B,OAAOD;4BACT;4BACA,MAAMI,WAAWD,IAAIE,OAAO,CAACC,sBAAsB;4BACnD,IAAI,CAACF,UAAU;gCACb,OAAOJ;4BACT;4BACA,MAAMO,WAAW,MAAMJ,IAAIK,OAAO,CAACC,EAAE,CAACC,OAAO,CAAC;gCAC5C,sKAAsK;gCACtKtB,YAAYA,WAAWR,IAAI;gCAC3BuB;gCACAQ,OAAO;oCAAE,mBAAmB;wCAAEC,QAAQR,SAASS,MAAM;oCAAC;gCAAE;4BAC1D;4BACA,IAAIN,YAAYA,SAASO,EAAE,KAAKZ,aAAaY,IAAI;gCAC/C,MAAM,IAAI9E,SAAS,yDAAyD;4BAC9E;4BACA,OAAO+E,cAAcf,MAAMI,UAAUd,WAAWF,WAAWR,IAAI,EAAEN;wBACnE;qBACD;gBACH;gBACA0C,QAAQ;oBACNtB,YAAYA,cAAc5C,QAAQ4C,UAAU,IAAI;oBAChD,GAAI,OAAON,WAAW4B,MAAM,KAAK,WAAW5B,WAAW4B,MAAM,GAAG,CAAC,CAAC;oBAClEC,gBAAgB,CAAC,EAAEC,GAAG,EAAE,GACtBC,kBAAkBD,KAAK5B,aACnB9C,4BAA4B0E,KAAK9B,WAAWR,IAAI,EAAEN,YAClD/B,qBAAqB2E;oBAC3BE,MAAM;gBACR;YACF;QACF;IACF;IACA,MAAMC,UAAmB,CAAC,EAAEjC,UAAU,EAAEkC,SAAS3C,UAAUS,WAAWR,IAAI,CAAC,EAAE;QAC3E,MAAMU,YAAYP,oBAAoBK,WAAWR,IAAI;QACrD,MAAM2C,WAAW,CAAC9D,KAAa+D,QAAiBC,iBAC9CxD,OAAOyD,MAAM,CAACC,QAAQ,CAAC;gBACrBlD,WAAW+C,SAAS/C,YAAYkB;gBAChC6B;gBACAI,KAAK,CAAC,CAAC,EAAEnE,IAAII,OAAO,CAAC,QAAQ,KAAK;gBAClC4D,gBAAgBA,iBAAiB;oBAAC;wBAAEI,KAAKJ;oBAAe;iBAAE,GAAG9B;gBAC7DmC,aAAahF,QAAQgF,WAAW;YAClC;QACF,MAAMC,aAAa,OAAO,EAAEC,eAAe,EAA+B;YACxE,MAAMC,OAAO,MAAMC,SAASjE,QAAQ+D;YACpC,IAAIC,MAAM;gBACR,MAAMhE,OAAOkE,KAAK,CAACC,MAAM,CAACH,KAAKpB,MAAM;YACvC;QACF;QACA,OAAO;YACL9D,MAAM;YACNsF,UAAU,OAAO,EAAEC,IAAI,EAAEC,EAAE,EAAE;gBAC3B,IAAI,MAAML,SAASjE,QAAQsE,KAAK;oBAC9B,MAAM,IAAI5E,MAAM,CAAC,qCAAqC,EAAE4E,IAAI;gBAC9D;gBACA,MAAMC,SAAS,MAAMN,SAASjE,QAAQqE;gBACtC,IAAI,CAACE,QAAQ;oBACX,MAAM,IAAI7E,MAAM,CAAC,2BAA2B,EAAE2E,MAAM;gBACtD;gBACA,mFAAmF;gBACnF,mFAAmF;gBACnF,MAAMrE,OAAOkE,KAAK,CAACnB,MAAM,CAAC;oBACxByB,gBAAgBD,OAAOC,cAAc,IAAI9C;oBACzCsC,MAAMV,SAASe,MAAMI,QAAQF,OAAOG,aAAa,GAAG;oBACpDC,UAAU7G,KAAK8C,KAAK,CAACgE,QAAQ,CAACN;oBAC9B3E,QAAQ,CAAC,CAAC,EAAE7B,KAAK8C,KAAK,CAACiE,OAAO,CAACP,KAAK;oBACpCI,eAAeH,OAAOG,aAAa,IAAI;oBACvCI,eAAe;oBACfC,MAAMR,OAAOQ,IAAI,IAAIrD;oBACrBsD,mBAAmB;gBACrB;YACF;YACAlB;YACAmB,QAAQC,cAAc;gBAAE5B;gBAAU9C;gBAAWa;gBAAWgC;YAAO;YAC/D8B,aAAa,CAAC,EAAEC,QAAQ,EAAE/B,QAAQgC,SAAS,EAAE,GAC3C/B,SACE1F,qBAAqB;oBACnB0H,kBAAkBjC;oBAClBgC;oBACAD;gBACF,GAAGrB,eAAe,EAClB;YAEJwB,cAAczB;YACd0B,cAAc,OAAO,EAAEzD,IAAI,EAAEiC,IAAI,EAAED,eAAe,EAAE;gBAClD,MAAM0B,eAAezB,KAAK0B,YAAY,GAClC,MAAM7H,WAAWmG,KAAK0B,YAAY,EAAE;oBAAEC,MAAM3B,KAAK4B,QAAQ;gBAAC,KAC1D5B,KAAK6B,MAAM;gBACf,MAAMC,WAAW,MAAM9F,OAAOkE,KAAK,CAACnB,MAAM,CAAC;oBACzCiB,MAAM,MAAMtG,OAAO+H,cAAczB,KAAKoB,QAAQ,EAAE;wBAAEO,MAAM3B,KAAK4B,QAAQ;oBAAC;oBACtEjB,UAAU7G,KAAK8C,KAAK,CAACgE,QAAQ,CAACb;oBAC9BpE,QAAQ,CAAC,CAAC,EAAE7B,KAAK8C,KAAK,CAACiE,OAAO,CAACd,kBAAkB;oBACjDW,eAAerD;oBACfyD,eAAe;oBACfE,mBAAmB;gBACrB;gBACA,IAAI,CAACc,SAASlD,MAAM,IAAI,CAACkD,SAASC,GAAG,EAAE;oBACrC,MAAM,IAAIrG,MAAM;gBAClB;gBACA,OAAOoD,cACLf,MACA;oBACEjD,MAAMhB,KAAK8C,KAAK,CAACgE,QAAQ,CAACb;oBAC1BnB,QAAQkD,SAASlD,MAAM;oBACvB8B,eAAerD;oBACf2E,MAAMhC,KAAK4B,QAAQ;oBACnBK,MAAMjC,KAAKkC,QAAQ;oBACnBC,WAAWL,SAASM,YAAY;oBAChCL,KAAKD,SAASC,GAAG;gBACnB,GACA1E,WACAF,WAAWR,IAAI,EACfN;YAEJ;YACAgG,eAAe,OAAOnE,KAAK,EAAEe,GAAG,EAAEqD,MAAM,EAAE;gBACxC,IAAIA,OAAOC,eAAe,EAAE;oBAC1B,IAAI;wBACF,MAAMC,YAAYF,OAAOC,eAAe;wBACxC,MAAME,UAAUrI,0BAA0B;4BACxCsI,gBAAgBvF,WAAWR,IAAI;4BAC/BuB;4BACAyE,eAAeH,UAAUG,aAAa;wBACxC;wBACA,MAAMC,eAAehJ,qBAAqB;4BACxC0H,kBAAkBjC;4BAClBgC,WAAWvH,KAAK8C,KAAK,CAACC,IAAI,CAAC4F,QAAQI,UAAU,EAAEJ,QAAQK,UAAU,IAAI;4BACrE1B,UAAUkB,OAAOlB,QAAQ;wBAC3B,GAAGrB,eAAe;wBAClB,IACE,CAAC0C,QAAQK,UAAU,IACnBF,iBAAiBH,QAAQ1C,eAAe,IACxC,CAAC6C,aAAaG,UAAU,CAAC,GAAG1D,OAAOzD,OAAO,CAAC,QAAQ,IAAI,CAAC,CAAC,GACzD;4BACA,MAAM,IAAI7B,SAAS,sCAAsC;wBAC3D;wBACA,MAAM+H,WAAW,MAAMkB,iBAAiBhH,QAAQ4G;wBAChD,IAAI,CAACd,YAAY,CAACA,SAASE,IAAI,IAAIvB,QAAQqB,SAASpB,aAAa,MAAMrD,WAAW;4BAChF,MAAM,IAAItD,SAAS,oDAAoD;wBACzE;wBACAC,0BAA0B;4BACxBmD;4BACAiE,UAAUU,SAAShH,IAAI;4BACvB8G,UAAUE,SAASE,IAAI;wBACzB;wBACA9D,IAAIE,OAAO,CAACC,sBAAsB,GAAGyD;wBACrC,OAAO,MAAMmB,cAAc;4BACzBjD,MAAM8B;4BACN5D;4BACA6D,KAAKzC,SAASsD,cAAcvF,WAAW;wBACzC;oBACF,EAAE,OAAO6F,OAAO;wBACd,IAAIA,iBAAiBnJ,UAAU;4BAC7B,OAAOoJ,SAASC,IAAI,CAAC;gCAAEC,SAASH,MAAMG,OAAO;4BAAC,GAAG;gCAAEC,QAAQJ,MAAMI,MAAM;4BAAC;wBAC1E;wBACA,MAAMJ;oBACR;gBACF;gBACA,MAAMK,WACJtE,OACC,MAAMf,IAAIK,OAAO,CAACC,EAAE,CAACC,OAAO,CAAC;oBAC5B,sKAAsK;oBACtKtB,YAAYA,WAAWR,IAAI;oBAC3BuB;oBACAQ,OAAO;wBAAE0C,UAAU;4BAAEzC,QAAQ2D,OAAOlB,QAAQ;wBAAC;oBAAE;gBACjD;gBACF,MAAMb,SAASiD,gBAAgBD,UAAUjB,OAAOlB,QAAQ;gBACxD,MAAM5F,MAAM5B,qBAAqB;oBAC/B0H,kBAAkBjC;oBAClBgC,WAAWvH,KAAK8C,KAAK,CAACC,IAAI,CACxByF,OAAOjD,MAAM,IAAIkB,OAAOlB,MAAM,IAAIA,QAClCkB,OAAOuC,UAAU,IAAI;oBAEvB1B,UAAUkB,OAAOlB,QAAQ;gBAC3B,GAAGrB,eAAe;gBAClB,MAAM0D,cAAcvE,kBAAkBqE,UAAUlG;gBAChD,IAAIiF,OAAOtE,SAAS,KAAK,eAAe9D,cAAcqG,OAAOqB,QAAQ,IAAI,KAAK;oBAC5E,MAAM5B,OAAO,MAAMC,SAASjE,QAAQR;oBACpC,IAAI,CAACwE,MAAMgC,MAAM;wBACf,MAAM,IAAIjI,SAAS,8BAA8B;oBACnD;oBACA,OAAOkJ,cAAc;wBAAEjD;wBAAM9B;wBAAK6D,KAAKzC,SAAS9D,KAAKiI,aAAa;oBAAa;gBACjF;gBACA,MAAMC,KAAK,IAAIC,IAAIzF,IAAI6D,GAAG,IAAI,oBAAoB6B,YAAY,CAACC,GAAG,CAAC;gBACnE,IAAIlE,MAAMnE;gBACV,IAAIkI,MAAMjJ,gBAAgB8F,OAAOqB,QAAQ,GAAG;oBAC1CjC,MAAMhF,kBAAkBgF;gBAC1B,OAAO,IAAI+D,MAAMlJ,cAAc+F,OAAOqB,QAAQ,GAAG;oBAC/CjC,MAAMjF,wBAAwBiF;gBAChC;gBACA,2EAA2E;gBAC3E,MAAMmE,YAAaP,UACfQ,UAAUhC;gBACd,IACE,CAAC0B,eACD,CAACC,MACD,CAAEH,CAAAA,YAAY,cAAcA,YAAYA,SAASS,QAAQ,AAAD,KACxDF,WACA;oBACA,OAAOX,SAASc,QAAQ,CAACH,WAAW;gBACtC;gBACA,OAAOX,SAASc,QAAQ,CAAC3E,SAASK,KAAK8D,aAAaC,KAAK;YAC3D;YACAQ,mBAAmB;YACnBC,oBAAoB;gBAClBC,cAAc;oBAAEtK,MAAM;gBAAgE;gBACtFuK,SAASxJ,QAAQwJ,OAAO,KAAK,SAAS5D,QAAQ5F,QAAQyJ,aAAa;gBACnEC,UAAUlK,iCAAiC;oBACzCmK,QACE,OAAO3J,QAAQyJ,aAAa,KAAK,WAAWzJ,QAAQyJ,aAAa,CAACE,MAAM,GAAG9G;oBAC7EL;oBACAgC;oBACApD,YAAYpB,QAAQoB,UAAU;oBAC9BwI,WAAW5J,QAAQ4J,SAAS;gBAC9B;gBACAC,uBAAuB;gBACvBC,YAAY;YACd;QACF;IACF;IACA,MAAMC,SAASjL,mBAAmB;QAChCoB,aAAaC,OAAO6J,WAAW,CAC7B7J,OAAOC,IAAI,CAACF,aAAamC,GAAG,CAAC,CAACP,OAAS;gBACrCA;gBACA;oBACEyC;oBACA0F,qBAAqB;oBACrBxH,6BAA6BzC,QAAQyC,2BAA2B;oBAChE+B,QAAQ3C,UAAUC;gBACpB;aACD;QAEH0H,SAASxJ,QAAQwJ,OAAO;IAC1B,GAAGpH;IACH,oFAAoF;IACpF,iFAAiF;IACjF2H,OAAO7J,WAAW,GAAG6J,OAAO7J,WAAW,EAAEmC,IAAI,CAACC,aAC5C,CAAChB,WAAWiB,GAAG,CAACD,WAAWR,IAAI,IAC3BQ,aACA;YACE,GAAGA,UAAU;YACb8D,QAAQ9D,WAAW8D,MAAM,CAAC/D,GAAG,CAAC,CAAC6H,QAC7BA,MAAMpD,IAAI,KAAK,UAAUoD,MAAMjK,IAAI,KAAK,WACpC;oBACE,GAAGiK,KAAK;oBACRC,cAAc,CAAC,EAAE9G,GAAG,EAAE,GACpBA,IAAI8B,IAAI,IAAI,OAAO+E,MAAMC,YAAY,KAAK,WAAWD,MAAMC,YAAY,GAAG;gBAC9E,IACAD;QAER;IAEN,OAAOH;AACT;AAEA,eAAe3E,SAASjE,MAAgB,EAAE+D,eAAuB;IAC/D,MAAMkF,WAAW,CAAC,CAAC,EAAElF,gBAAgBnE,OAAO,CAAC,QAAQ,KAAK;IAC1D,MAAMd,OAAOhB,KAAK8C,KAAK,CAACgE,QAAQ,CAACqE;IACjC,IAAK,IAAIC,OAAO,IAAKA,QAAQ,IAAK;QAChC,MAAMC,SAAS,MAAMnJ,OAAOmJ,MAAM,CAACC,IAAI,CAAC;YACtCzD,MAAM;YACN0D,OAAO;YACPvL,MAAM,GAAGA,KAAK8C,KAAK,CAACiE,OAAO,CAACoE,UAAU,CAAC,CAAC;YACxCC;QACF;QACA,MAAMI,QAAQH,OAAOI,IAAI,CACvB,CAACC,QAAU,YAAYA,SAASA,MAAMP,QAAQ,KAAKA,YAAYO,MAAM1K,IAAI,KAAKA;QAEhF,IAAIwK,SAAS,YAAYA,SAASA,MAAM1G,MAAM,EAAE;YAC9C,MAAMoB,OAAO,MAAMhE,OAAOkE,KAAK,CAAC2D,GAAG,CAACyB,MAAM1G,MAAM;YAChD,IACEoB,KAAKpB,MAAM,KAAK0G,MAAM1G,MAAM,IAC5BoB,KAAKlF,IAAI,KAAKA,QACdkF,KAAKiF,QAAQ,KAAKA,YAClB,OAAOjF,KAAKiC,IAAI,KAAK,YACrB,CAACwD,OAAOC,aAAa,CAAC1F,KAAKiC,IAAI,KAC/BjC,KAAKiC,IAAI,GAAG,KACZ,CAACjC,KAAK+B,GAAG,EACT;gBACA,MAAM,IAAIrG,MAAM;YAClB;YACA,OAAO;gBAAE,GAAGsE,IAAI;gBAAElF,MAAMkF,KAAKlF,IAAI;gBAAE8D,QAAQoB,KAAKpB,MAAM;gBAAEqD,MAAMjC,KAAKiC,IAAI;gBAAEF,KAAK/B,KAAK+B,GAAG;YAAC;QACzF;QACA,IAAIoD,OAAOjJ,MAAM,GAAG,KAAK;YACvB,OAAOwB;QACT;IACF;AACF;AAEA,eAAesF,iBAAiBhH,MAAgB,EAAER,GAAW;IAC3D,+EAA+E;IAC/E,4EAA4E;IAC5E,IAAIwE,OAAO,MAAMC,SAASjE,QAAQR;IAClC,KAAK,MAAMmK,SAAS;QAAC;QAAK;QAAK;QAAM;KAAK,CAAE;QAC1C,IAAI3F,MAAM;YACR;QACF;QACA,MAAM,IAAI4F,QAAQ,CAACC,UAAYC,WAAWD,SAASF;QACnD3F,OAAO,MAAMC,SAASjE,QAAQR;IAChC;IACA,OAAOwE;AACT;AAEA,SAASd,kBAAkBD,GAAY,EAAE8G,QAAiB;IACxD,MAAMhJ,QAASkC,KAAuD8E,UAAU1G;IAChF,OAAO,OAAON,UAAU,YAAYA,QAAQgJ;AAC9C;AAEA,SAASvC,gBAAgBvE,GAAY,EAAEmC,QAAgB;IACrD,MAAMrE,QAASkC,OAAO,CAAC;IAIvB,OACE;QAAClC;QAAOA,MAAMiH,QAAQ;WAAKhJ,OAAOgL,MAAM,CAACjJ,MAAMkJ,QAAQ,IAAI,CAAC;KAAG,CAACV,IAAI,CAClE,CAACvF,OAASA,MAAMoB,aAAaA,aAC1BrE;AAET;AAEA,SAAS+B,cACPf,IAA6B,EAC7BiC,IAAkB,EAClB3C,SAAkB,EAClBV,IAAY,EACZN,QAAgB;IAEhB0B,KAAKgG,QAAQ,GAAG;QACdnF,QAAQoB,KAAKpB,MAAM;QACnBvB;QACA+E,cAAcpC,KAAKmC,SAAS;QAC5BJ,KAAK/B,KAAK+B,GAAG;IACf;IACAhE,KAAKmI,YAAY,GAAG7I,YAChB9C,4BAA4BwD,MAAMpB,MAAMN,YACxC/B,qBAAqByD;IACzB,OAAOA;AACT;AAEA,eAAekF,cAAc,EAC3BjD,IAAI,EACJ9B,GAAG,EACH6D,GAAG,EAKJ;IACC,MAAMoE,QAAQlM,oBAAoB;QAAEmM,UAAUpG,KAAKiC,IAAI;QAAEoE,aAAanI,IAAIoI,OAAO,CAACzC,GAAG,CAAC;IAAS;IAC/F,IAAIsC,MAAMxE,IAAI,KAAK,WAAW;QAC5B,OAAO,IAAIwB,SAAS,MAAM;YAAEmD,SAASH,MAAMG,OAAO;YAAEhD,QAAQ6C,MAAM7C,MAAM;QAAC;IAC3E;IACA,MAAMiD,WAAW,MAAMC,MAAMzE,KAAK;QAChC0E,OAAO;QACPH,SACEH,MAAMxE,IAAI,KAAK,YACX;YAAE+E,OAAO,CAAC,MAAM,EAAEP,MAAMQ,UAAU,CAAC,CAAC,EAAER,MAAMS,QAAQ,EAAE;QAAC,IACvDlJ;QACNmJ,QAAQ,IAAIC,kBAAkBD,MAAM;IACtC;IACA,IACEN,SAASjD,MAAM,KAAK6C,MAAM7C,MAAM,IAChCiD,SAASD,OAAO,CAACzC,GAAG,CAAC,sBAAsBsC,MAAMG,OAAO,CAAC,iBAAiB,IACzEH,MAAMxE,IAAI,KAAK,aACd4E,SAASD,OAAO,CAACzC,GAAG,CAAC,qBAAqBsC,MAAMG,OAAO,CAAC,gBAAgB,EAC1E;QACA,KAAKC,SAASQ,IAAI,EAAEC,SAASC,MAAM,IAAMvJ;QACzC,MAAM,IAAI3D,SAAS,uDAAuD;IAC5E;IACA,MAAMuM,UAAU,IAAIY,QAAQf,MAAMG,OAAO;IACzCA,QAAQa,GAAG,CAAC,gBAAgBnH,KAAKgC,IAAI,IAAI;IACzC,IAAI9H,cAAc8F,KAAKgC,IAAI,IAAI,KAAK;QAClCsE,QAAQa,GAAG,CAAC,2BAA2BhN;IACzC;IACA,OAAO,IAAIgJ,SAASoD,SAASQ,IAAI,EAAE;QAAET;QAAShD,QAAQ6C,MAAM7C,MAAM;IAAC;AACrE;AAEA,SAAS/F,YAAYJ,UAA4B;IAC/C,MAAM4B,SAAS,OAAO5B,WAAW4B,MAAM,KAAK,WAAW5B,WAAW4B,MAAM,GAAGrB;IAC3E,OACE+C,QAAQ1B,QAAQqI,6BAChB,CAACrI,QAAQsI,WAAWnL,UACpB6C,OAAOsI,SAAS,CAACvL,IAAI,CAAC,CAACkG,OAASA,KAAKsF,QAAQ,CAAC,QAAQpN,cAAc8H;AAExE;AAEA,SAASd,cAAc,EACrB5B,QAAQ,EACR9C,SAAS,EACTa,SAAS,EACTgC,MAAM,EAMP;IACC,OAAO;QACL;YACEsC,MAAM;YACN/D,OAAO;gBACL2J,WAAW,CAACxJ,OAAS0C,QAAQ1C,MAAMgG,UAAUhC;gBAC7CyF,eAAe;YACjB;YACAvG,QAAQ;gBACN;oBACEnG,MAAM;oBACN6G,MAAM;oBACN/D,OAAO;wBAAE6J,UAAU;4BAAEC,QAAQ;4BAAMC,QAAQ;wBAAK;oBAAE;oBAClD1G,QAAQ;wBACN;4BAAEnG,MAAM;4BAAU6G,MAAM;4BAAQ/D,OAAO;gCAAEgK,QAAQ;4BAAK;4BAAGC,QAAQ;wBAAK;wBACtE;4BAAE/M,MAAM;4BAAO6G,MAAM;4BAAQ/D,OAAO;gCAAEkK,UAAU;4BAAK;4BAAGC,OAAO;wBAAM;wBACrE;4BACEjN,MAAM;4BACN6G,MAAM;4BACN/D,OAAO;gCAAEkK,UAAU;4BAAK;4BACxBC,OAAO;wBACT;wBACA;4BACEjN,MAAM;4BACN6G,MAAM;4BACN/D,OAAO;gCACL2J,WAAW,CAACS,OAAOC,cACjB/I,kBAAkB;wCAAE6E,UAAUkE;oCAAY,GAAG5K;gCAC/CM,aAAa,CAAC,8BAA8B,EAAEnB,UAAU,+EAA+E,CAAC;gCACxIsL,UAAU;4BACZ;4BACAjK,OAAO;gCACLqK,WAAW;oCACT,CAAC,EAAEnK,IAAI,EAAEkK,WAAW,EAAE;wCACpB,IACE,CAAClK,MAAMqD,YACP,CAAClC,kBAAkB;4CAAE6E,UAAUkE;wCAAY,GAAG5K,YAC9C;4CACA,OAAOK;wCACT;wCACA,MAAMlC,MAAM5B,qBAAqB;4CAC/B0H,kBAAkBjC;4CAClBgC,WAAWvH,KAAK8C,KAAK,CAACC,IAAI,CAACkB,KAAKsB,MAAM,IAAIA,QAAQtB,KAAK+E,UAAU,IAAI;4CACrE1B,UAAUrD,KAAKqD,QAAQ;wCACzB,GAAGrB,eAAe;wCAClB,OAAOT,SAAS9D,KAAK;oCACvB;iCACD;4BACH;4BACAuM,OAAO;4BACPI,SAAS;wBACX;wBACA;4BAAErN,MAAM;4BAAgB6G,MAAM;4BAAQ/D,OAAO;gCAAEgK,QAAQ;4BAAK;wBAAE;qBAC/D;oBACDQ,eAAe;oBACfL,OAAO;gBACT;aACD;YACDA,OAAO;QACT;KACD;AACH"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Adapter, ClientUploadsConfig } from '@payloadcms/plugin-cloud-storage/types'\nimport type {\n CollectionConfig,\n CollectionSlug,\n Config,\n Field,\n PayloadRequest,\n StorageAdapter,\n UploadCollectionSlug,\n} from 'payload'\n\nimport ImageKit, { toFile } from '@imagekit/nodejs'\nimport { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage'\nimport { buildStoragePathData } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { openAsBlob } from 'node:fs'\nimport path from 'node:path'\nimport { APIError } from 'payload'\nimport {\n assertClientUploadAllowed,\n getRangeRequestInfo,\n isXmlMimeType,\n uploadContentSecurityPolicy,\n verifyClientUploadReceipt,\n} from 'payload/internal'\n\nimport { completeImageKitUpload } from './endpoints/completeImageKitUpload.js'\nimport { createImageKitUploadInstructions } from './endpoints/createImageKitUploadInstructions.js'\nimport {\n getAdminThumbnailURL,\n getPrivateAdminThumbnailURL,\n isPdfMimeType,\n isVideoMimeType,\n} from './getAdminThumbnailURL.js'\nimport { createImageKitFileStore } from './imageKitFileStore.js'\nimport { getImageKitThumbnailSrc, getVideoPosterSrc } from './imageTransforms.js'\n\nexport type ImageKitCollectionOptions = { focalPoint?: boolean; private?: boolean }\nexport type PayloadStorageImageKitConfig = {\n clientUploads?: ClientUploadsConfig\n collections: Partial<Record<UploadCollectionSlug, ImageKitCollectionOptions | true>>\n disablePayloadAccessControl?: true\n enabled?: boolean\n focalPoint?: boolean\n folder: string\n privateKey: string\n publicKey: string\n signedUrlExpiresIn?: number\n urlEndpoint: string\n}\n\n/** Payload 4 storage capability. Register under config.storage, not config.plugins. */\nexport const imagekitStorage = (options: PayloadStorageImageKitConfig): StorageAdapter => ({\n name: 'imagekit',\n collections: Object.keys(options.collections),\n init: (config) => initializeStorage(config, options),\n})\n\nexport { migrateFolder } from './migrate.js'\nexport type { MigrateFolderOptions, MigrationJobResult, MigrationLocation } from './migrate.js'\nexport const payloadStorageImagekit = imagekitStorage\nexport const payloadStorageImageKit = imagekitStorage\nexport type PayloadStorageImagekitConfig = PayloadStorageImageKitConfig\n\ninterface ProviderFile {\n fileId: string\n isPrivateFile?: boolean\n mime?: string\n name: string\n size: number\n thumbnail?: string\n url: string\n}\n\ninterface FileLocation {\n _objectKey?: string\n filename?: string\n filesize?: number\n mimeType?: string\n prefix?: string\n}\n\nfunction initializeStorage(config: Config, options: PayloadStorageImageKitConfig): Config {\n for (const key of ['privateKey', 'publicKey', 'urlEndpoint', 'folder'] as const) {\n if (!options[key]?.trim()) {\n throw new Error(`imagekitStorage: \\`${key}\\` is required.`)\n }\n }\n const folder = options.folder.trim().replace(/^\\/+|\\/+$/g, '')\n if (folder.split('/').some((segment) => !segment || segment === '.' || segment === '..')) {\n throw new Error('imagekitStorage: `folder` must not contain empty or dot segments.')\n }\n const client = new ImageKit({ privateKey: options.privateKey })\n const collections: Partial<Record<string, ImageKitCollectionOptions | true>> = options.collections\n if (!Object.keys(collections).length) {\n throw new Error('imagekitStorage: `collections` must not be empty.')\n }\n const configured = new Set(Object.keys(collections))\n const apiRoute = config.routes?.api ?? '/api'\n const expiresIn = options.signedUrlExpiresIn ?? 900\n const prefixFor = (slug: string) => path.posix.join(folder, slug)\n const collectionIsPrivate = (slug: string) => {\n const value = collections[slug]\n return value !== true && value?.private === true\n }\n const files = createImageKitFileStore(client, options.urlEndpoint)\n const completeUpload = completeImageKitUpload({\n access: typeof options.clientUploads === 'object' ? options.clientUploads.access : undefined,\n collections: Object.fromEntries(\n Object.keys(collections).map((slug) => [\n slug,\n { isPrivate: collectionIsPrivate(slug), prefix: prefixFor(slug) },\n ]),\n ),\n files,\n })\n const sourceConfig: Config = {\n ...config,\n collections: (config.collections ?? []).map((collection) => {\n if (!configured.has(collection.slug)) {\n return collection\n }\n const isPrivate = collectionIsPrivate(collection.slug)\n if (options.disablePayloadAccessControl && (isPrivate || canStoreXml(collection))) {\n throw new Error(\n 'imagekitStorage: private and XML-capable collections require Payload access control.',\n )\n }\n const setting = collections[collection.slug]\n const focalPoint = setting !== true ? setting?.focalPoint : undefined\n const description = collection.admin?.description\n return {\n ...collection,\n admin: isPrivate\n ? {\n ...collection.admin,\n description:\n typeof description === 'string'\n ? `${description} Files in this collection are uploaded to ImageKit as private files and delivered through short-lived signed URLs.`\n : (description ??\n 'Files in this collection are uploaded to ImageKit as private files and delivered through short-lived signed URLs.'),\n }\n : collection.admin,\n hooks: {\n ...collection.hooks,\n beforeChange: [\n ...(collection.hooks?.beforeChange ?? []),\n async ({ data, operation, originalDoc, req }) => {\n if (operation === 'validate') {\n return data\n }\n const verified = req.context.imagekitVerifiedUpload as ProviderFile | undefined\n if (!verified) {\n return data\n }\n const existing = await req.payload.db.findOne({\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -- Generated consumer configs narrow CollectionSlug, unlike the standalone package build.\n collection: collection.slug as CollectionSlug,\n req,\n where: { 'imagekit.fileId': { equals: verified.fileId } },\n })\n if (existing && existing.id !== originalDoc?.id) {\n throw new APIError('This ImageKit upload reference has already been used.', 400)\n }\n return applyMetadata(data, verified, isPrivate, collection.slug, apiRoute)\n },\n ],\n },\n upload: {\n focalPoint: focalPoint ?? options.focalPoint ?? false,\n ...(typeof collection.upload === 'object' ? collection.upload : {}),\n adminThumbnail: ({ doc }) =>\n isPrivateDocument(doc, isPrivate)\n ? getPrivateAdminThumbnailURL(doc, collection.slug, apiRoute)\n : getAdminThumbnailURL(doc),\n crop: false,\n },\n }\n }),\n }\n const adapter: Adapter = ({ collection, prefix = prefixFor(collection.slug) }) => {\n const isPrivate = collectionIsPrivate(collection.slug)\n const buildURL = (key: string, signed: boolean, transformation?: null | string) =>\n client.helper.buildSrc({\n expiresIn: signed ? expiresIn : undefined,\n signed,\n src: `/${key.replace(/^\\/+/, '')}`,\n transformation: transformation ? [{ raw: transformation }] : undefined,\n urlEndpoint: options.urlEndpoint,\n })\n const deleteFile = ({\n req,\n storageFilePath,\n }: {\n req: PayloadRequest\n storageFilePath: string\n }) => files.remove({ key: storageFilePath, req })\n return {\n name: 'imagekit',\n copyFile: async ({ from, req, to }) => {\n if (await files.find({ key: to, req })) {\n throw new Error(`ImageKit destination already exists: ${to}`)\n }\n const source = await files.find({ key: from, req })\n if (!source) {\n throw new Error(`ImageKit source not found: ${from}`)\n }\n // Upload by URL copies the exact original without buffering it in the app process.\n // overwriteFile:false protects a destination created concurrently with the lookup.\n await files.upload({\n body: {\n customMetadata: source.customMetadata ?? undefined,\n file: buildURL(from, Boolean(source.isPrivateFile), 'orig-true'),\n fileName: path.posix.basename(to),\n folder: `/${path.posix.dirname(to)}`,\n isPrivateFile: source.isPrivateFile ?? false,\n overwriteFile: false,\n tags: source.tags ?? undefined,\n useUniqueFileName: false,\n },\n key: to,\n req,\n })\n },\n deleteFile,\n fields: storageFields({ buildURL, expiresIn, isPrivate, prefix }),\n generateURL: ({ filename, prefix: docPrefix }) =>\n buildURL(\n buildStoragePathData({\n collectionPrefix: prefix,\n docPrefix,\n filename,\n }).storageFilePath,\n false,\n ),\n handleDelete: deleteFile,\n handleUpload: async ({ data, file, req, storageFilePath }) => {\n const uploadSource = file.tempFilePath\n ? await openAsBlob(file.tempFilePath, { type: file.mimeType })\n : file.buffer\n const uploaded = await files.upload({\n body: {\n file: await toFile(uploadSource, file.filename, { type: file.mimeType }),\n fileName: path.posix.basename(storageFilePath),\n folder: `/${path.posix.dirname(storageFilePath)}`,\n isPrivateFile: isPrivate,\n overwriteFile: false,\n useUniqueFileName: false,\n },\n key: storageFilePath,\n req,\n })\n return applyMetadata(\n data,\n {\n name: path.posix.basename(storageFilePath),\n fileId: uploaded.fileId,\n isPrivateFile: isPrivate,\n mime: file.mimeType,\n size: file.filesize,\n thumbnail: uploaded.thumbnailUrl,\n url: uploaded.url,\n },\n isPrivate,\n collection.slug,\n apiRoute,\n )\n },\n staticHandler: async (req, { doc, params }) => {\n if (params.uploadReference) {\n try {\n const reference = params.uploadReference as { signedReceipt?: unknown }\n const receipt = verifyClientUploadReceipt({\n collectionSlug: collection.slug,\n req,\n signedReceipt: reference.signedReceipt,\n })\n const expectedPath = buildStoragePathData({\n collectionPrefix: prefix,\n docPrefix: path.posix.join(receipt.filePrefix, receipt._objectKey ?? ''),\n filename: params.filename,\n }).storageFilePath\n if (\n !receipt._objectKey ||\n expectedPath !== receipt.storageFilePath ||\n !expectedPath.startsWith(`${prefix.replace(/^\\/+/, '')}/`)\n ) {\n throw new APIError('Invalid ImageKit upload reference.', 400)\n }\n const uploaded = await findUploadedFile(files, expectedPath, req)\n if (!uploaded || !uploaded.mime || Boolean(uploaded.isPrivateFile) !== isPrivate) {\n throw new APIError('ImageKit upload did not match its authorization.', 400)\n }\n assertClientUploadAllowed({\n collection,\n filename: uploaded.name,\n mimeType: uploaded.mime,\n })\n req.context.imagekitVerifiedUpload = uploaded\n return await fetchOriginal({\n file: uploaded,\n req,\n url: buildURL(expectedPath, isPrivate, 'orig-true'),\n })\n } catch (error) {\n if (error instanceof APIError) {\n return Response.json({ message: error.message }, { status: error.status })\n }\n throw error\n }\n }\n const document =\n doc ??\n (await req.payload.db.findOne({\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -- Generated consumer configs narrow CollectionSlug, unlike the standalone package build.\n collection: collection.slug as CollectionSlug,\n req,\n where: { filename: { equals: params.filename } },\n }))\n const source = getFileLocation(document, params.filename)\n const key = buildStoragePathData({\n collectionPrefix: prefix,\n docPrefix: path.posix.join(\n params.prefix ?? source.prefix ?? prefix,\n source._objectKey ?? '',\n ),\n filename: params.filename,\n }).storageFilePath\n const privateFile = isPrivateDocument(document, isPrivate)\n if (params.operation === 'transform' || isXmlMimeType(source.mimeType ?? '')) {\n const file = await files.find({ key, req })\n if (!file?.mime) {\n throw new APIError('ImageKit source not found.', 404)\n }\n return fetchOriginal({ file, req, url: buildURL(key, privateFile, 'orig-true') })\n }\n const tr = new URL(req.url ?? 'http://localhost').searchParams.get('tr')\n let src = key\n if (tr && isVideoMimeType(source.mimeType)) {\n src = getVideoPosterSrc(src)\n } else if (tr && isPdfMimeType(source.mimeType)) {\n src = getImageKitThumbnailSrc(src)\n }\n // Existing v3 files may live under an earlier folder or delivery endpoint.\n const storedURL = (document as { imagekit?: { url?: string }; original?: unknown } | null)\n ?.imagekit?.url\n if (\n !privateFile &&\n !tr &&\n !(document && 'original' in document && document.original) &&\n storedURL\n ) {\n return Response.redirect(storedURL, 302)\n }\n return Response.redirect(buildURL(src, privateFile, tr), 302)\n },\n supportsTempFiles: true,\n uploadInstructions: {\n adminHandler: { path: '@humaan/payload-storage-imagekit/client#ImageKitUploadHandler' },\n enabled: options.enabled !== false && Boolean(options.clientUploads),\n endpoint: { handler: completeUpload, path: '/imagekit-upload-complete' },\n generate: createImageKitUploadInstructions({\n access:\n typeof options.clientUploads === 'object' ? options.clientUploads.access : undefined,\n isPrivate,\n prefix,\n privateKey: options.privateKey,\n publicKey: options.publicKey,\n }),\n requiresUploadReceipt: true,\n useInAdmin: true,\n },\n }\n }\n const output = cloudStoragePlugin({\n collections: Object.fromEntries(\n Object.keys(collections).map((slug) => [\n slug,\n {\n adapter,\n disableLocalStorage: true,\n disablePayloadAccessControl: options.disablePayloadAccessControl,\n prefix: prefixFor(slug),\n },\n ]),\n ),\n enabled: options.enabled,\n })(sourceConfig)\n // Do not invent a prefix on reads of legacy documents. A stored prefix is retained;\n // new uploads get their normalized destination from the file-operation pipeline.\n output.collections = output.collections?.map((collection) =>\n !configured.has(collection.slug)\n ? collection\n : {\n ...collection,\n fields: collection.fields.map((field) =>\n field.type === 'text' && field.name === 'prefix'\n ? {\n ...field,\n defaultValue: ({ req }) =>\n req.file && typeof field.defaultValue === 'string' ? field.defaultValue : '',\n }\n : field,\n ),\n },\n )\n return output\n}\n\nasync function findUploadedFile(\n files: ReturnType<typeof createImageKitFileStore>,\n key: string,\n req: PayloadRequest,\n) {\n // ImageKit asset listings can lag a successful upload. Only admission retries;\n // ordinary missing reads/deletes and collision lookups must stay immediate.\n let file = await files.find({ key, req })\n for (const delay of [250, 500, 1000, 2000]) {\n if (file) {\n break\n }\n await new Promise((resolve) => setTimeout(resolve, delay))\n file = await files.find({ key, req })\n }\n return file\n}\n\nfunction isPrivateDocument(doc: unknown, fallback: boolean): boolean {\n const value = (doc as { imagekit?: { isPrivate?: boolean } } | null)?.imagekit?.isPrivate\n return typeof value === 'boolean' ? value : fallback\n}\n\nfunction getFileLocation(doc: unknown, filename: string): FileLocation {\n const value = (doc ?? {}) as {\n original?: FileLocation\n variants?: Record<string, FileLocation>\n } & FileLocation\n return (\n [value, value.original, ...Object.values(value.variants ?? {})].find(\n (file) => file?.filename === filename,\n ) ?? value\n )\n}\n\nfunction applyMetadata(\n data: Record<string, unknown>,\n file: ProviderFile,\n isPrivate: boolean,\n slug: string,\n apiRoute: string,\n) {\n data.imagekit = {\n fileId: file.fileId,\n isPrivate,\n thumbnailUrl: file.thumbnail,\n url: file.url,\n }\n data.thumbnailURL = isPrivate\n ? getPrivateAdminThumbnailURL(data, slug, apiRoute)\n : getAdminThumbnailURL(data)\n return data\n}\n\nasync function fetchOriginal({\n file,\n req,\n url,\n}: {\n file: ProviderFile\n req: PayloadRequest\n url: string\n}): Promise<Response> {\n const range = getRangeRequestInfo({ fileSize: file.size, rangeHeader: req.headers.get('Range') })\n if (range.type === 'invalid') {\n return new Response(null, { headers: range.headers, status: range.status })\n }\n const response = await fetch(url, {\n cache: 'no-store',\n headers:\n range.type === 'partial'\n ? { Range: `bytes=${range.rangeStart}-${range.rangeEnd}` }\n : undefined,\n signal: new AbortController().signal,\n })\n if (\n response.status !== range.status ||\n response.headers.get('Content-Length') !== range.headers['Content-Length'] ||\n (range.type === 'partial' &&\n response.headers.get('Content-Range') !== range.headers['Content-Range'])\n ) {\n void response.body?.cancel().catch(() => undefined)\n throw new APIError('ImageKit returned mismatched file content metadata.', 502)\n }\n const headers = new Headers(range.headers)\n headers.set('Content-Type', file.mime ?? 'application/octet-stream')\n if (isXmlMimeType(file.mime ?? '')) {\n headers.set('Content-Security-Policy', uploadContentSecurityPolicy)\n }\n return new Response(response.body, { headers, status: range.status })\n}\n\nfunction canStoreXml(collection: CollectionConfig): boolean {\n const upload = typeof collection.upload === 'object' ? collection.upload : undefined\n return (\n Boolean(upload?.allowRestrictedFileTypes) ||\n !upload?.mimeTypes?.length ||\n upload.mimeTypes.some((mime) => mime.includes('*') || isXmlMimeType(mime))\n )\n}\n\nfunction storageFields({\n buildURL,\n expiresIn,\n isPrivate,\n prefix,\n}: {\n buildURL: (key: string, signed: boolean, transformation?: null | string) => string\n expiresIn: number\n isPrivate: boolean\n prefix: string\n}): Field[] {\n return [\n {\n type: 'collapsible',\n admin: {\n condition: (data) => Boolean(data?.imagekit?.url),\n initCollapsed: true,\n },\n fields: [\n {\n name: 'imagekit',\n type: 'group',\n admin: { disabled: { column: true, filter: true } },\n fields: [\n { name: 'fileId', type: 'text', admin: { hidden: true }, unique: true },\n { name: 'url', type: 'text', admin: { readOnly: true }, label: 'URL' },\n {\n name: 'isPrivate',\n type: 'checkbox',\n admin: { readOnly: true },\n label: 'Private file',\n },\n {\n name: 'signedUrl',\n type: 'text',\n admin: {\n condition: (_data, siblingData) =>\n isPrivateDocument({ imagekit: siblingData }, isPrivate),\n description: `Temporary signed URL. Expires ${expiresIn} seconds after load. Anyone with this URL can access the file until it expires.`,\n readOnly: true,\n },\n hooks: {\n afterRead: [\n ({ data, siblingData }) => {\n if (\n !data?.filename ||\n !isPrivateDocument({ imagekit: siblingData }, isPrivate)\n ) {\n return undefined\n }\n const key = buildStoragePathData({\n collectionPrefix: prefix,\n docPrefix: path.posix.join(data.prefix || prefix, data._objectKey || ''),\n filename: data.filename,\n }).storageFilePath\n return buildURL(key, true)\n },\n ],\n },\n label: 'Signed URL',\n virtual: true,\n },\n { name: 'thumbnailUrl', type: 'text', admin: { hidden: true } },\n ],\n interfaceName: 'imagekitFields',\n label: false,\n },\n ],\n label: 'Storage: ImageKit',\n },\n ]\n}\n"],"names":["ImageKit","toFile","cloudStoragePlugin","buildStoragePathData","openAsBlob","path","APIError","assertClientUploadAllowed","getRangeRequestInfo","isXmlMimeType","uploadContentSecurityPolicy","verifyClientUploadReceipt","completeImageKitUpload","createImageKitUploadInstructions","getAdminThumbnailURL","getPrivateAdminThumbnailURL","isPdfMimeType","isVideoMimeType","createImageKitFileStore","getImageKitThumbnailSrc","getVideoPosterSrc","imagekitStorage","options","name","collections","Object","keys","init","config","initializeStorage","migrateFolder","payloadStorageImagekit","payloadStorageImageKit","key","trim","Error","folder","replace","split","some","segment","client","privateKey","length","configured","Set","apiRoute","routes","api","expiresIn","signedUrlExpiresIn","prefixFor","slug","posix","join","collectionIsPrivate","value","private","files","urlEndpoint","completeUpload","access","clientUploads","undefined","fromEntries","map","isPrivate","prefix","sourceConfig","collection","has","disablePayloadAccessControl","canStoreXml","setting","focalPoint","description","admin","hooks","beforeChange","data","operation","originalDoc","req","verified","context","imagekitVerifiedUpload","existing","payload","db","findOne","where","equals","fileId","id","applyMetadata","upload","adminThumbnail","doc","isPrivateDocument","crop","adapter","buildURL","signed","transformation","helper","buildSrc","src","raw","deleteFile","storageFilePath","remove","copyFile","from","to","find","source","body","customMetadata","file","Boolean","isPrivateFile","fileName","basename","dirname","overwriteFile","tags","useUniqueFileName","fields","storageFields","generateURL","filename","docPrefix","collectionPrefix","handleDelete","handleUpload","uploadSource","tempFilePath","type","mimeType","buffer","uploaded","mime","size","filesize","thumbnail","thumbnailUrl","url","staticHandler","params","uploadReference","reference","receipt","collectionSlug","signedReceipt","expectedPath","filePrefix","_objectKey","startsWith","findUploadedFile","fetchOriginal","error","Response","json","message","status","document","getFileLocation","privateFile","tr","URL","searchParams","get","storedURL","imagekit","original","redirect","supportsTempFiles","uploadInstructions","adminHandler","enabled","endpoint","handler","generate","publicKey","requiresUploadReceipt","useInAdmin","output","disableLocalStorage","field","defaultValue","delay","Promise","resolve","setTimeout","fallback","values","variants","thumbnailURL","range","fileSize","rangeHeader","headers","response","fetch","cache","Range","rangeStart","rangeEnd","signal","AbortController","cancel","catch","Headers","set","allowRestrictedFileTypes","mimeTypes","includes","condition","initCollapsed","disabled","column","filter","hidden","unique","readOnly","label","_data","siblingData","afterRead","virtual","interfaceName"],"mappings":"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"}
|