@humaan/payload-storage-imagekit 2.8.0 → 2.8.2
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 +7 -2
- package/dist/clientUploadVerification.js +3 -1
- package/dist/clientUploadVerification.js.map +1 -1
- package/dist/endpoints/createImageKitUploadAuthHandler.d.ts +10 -0
- package/dist/endpoints/createImageKitUploadAuthHandler.js +36 -1
- package/dist/endpoints/createImageKitUploadAuthHandler.js.map +1 -1
- package/dist/index.js +17 -1
- package/dist/index.js.map +1 -1
- package/package.json +16 -16
package/README.md
CHANGED
|
@@ -171,8 +171,13 @@ were not affected by this adapter issue.
|
|
|
171
171
|
Client uploads now use ImageKit Upload API V2 and Payload's signed upload receipts.
|
|
172
172
|
Payload chooses the sanitized filename and adds numeric suffixes for duplicates.
|
|
173
173
|
For PNG, JPEG, GIF, and WebP images, the browser handler checks the file signature
|
|
174
|
-
before authorization and corrects a mismatched MIME label or extension.
|
|
175
|
-
|
|
174
|
+
before authorization and corrects a mismatched MIME label or extension. The signed
|
|
175
|
+
ImageKit checks bind the exact size and require ImageKit's detected content type to
|
|
176
|
+
match the collection's `upload.mimeTypes` (using Payload's prefix matching) and not
|
|
177
|
+
be an XML-family type. The browser's MIME type is not required to match ImageKit's
|
|
178
|
+
detection, because browsers derive it from the file extension and the two can
|
|
179
|
+
legitimately disagree (a macro-enabled `.xltm` template is detected as `.xlsx`, for
|
|
180
|
+
example). The detected type is checked again on completion, and is what Payload stores.
|
|
176
181
|
Uploads go directly into the collection folder. Authorization binds the collection,
|
|
177
182
|
user, path, filename, MIME type, size, privacy mode, expiry, a per-upload ImageKit tag,
|
|
178
183
|
and create-only upload options. ImageKit metadata is checked on every completed upload,
|
|
@@ -77,13 +77,15 @@ export const createImageKitClientUploadVerifier = ({ client, collections })=>{
|
|
|
77
77
|
const uploadedFile = await client.files.get(fileId);
|
|
78
78
|
const uploadedMime = typeof uploadedFile.mime === 'string' ? getMimeTypeEssence(uploadedFile.mime) : '';
|
|
79
79
|
const uploadedSize = uploadedFile.size;
|
|
80
|
+
// ImageKit's detected type is authoritative. It may differ from the browser's
|
|
81
|
+
// extension-based type, so validate it against the collection, not the claim.
|
|
80
82
|
assertClientUploadAllowed({
|
|
81
83
|
collection: collectionSecurity.collection,
|
|
82
84
|
filename: context.filename,
|
|
83
85
|
mimeType: uploadedMime
|
|
84
86
|
});
|
|
85
87
|
const allowedMimeTypes = typeof collectionSecurity.collection.upload === 'object' && Array.isArray(collectionSecurity.collection.upload.mimeTypes) ? collectionSecurity.collection.upload.mimeTypes : [];
|
|
86
|
-
if (uploadedFile.fileId !== fileId || uploadedFile.filePath !== expectedFilePath || uploadedFile.name !== context.filename || context.uploadTag !== undefined && !uploadedFile.tags?.includes(context.uploadTag) || !uploadedMime ||
|
|
88
|
+
if (uploadedFile.fileId !== fileId || uploadedFile.filePath !== expectedFilePath || uploadedFile.name !== context.filename || context.uploadTag !== undefined && !uploadedFile.tags?.includes(context.uploadTag) || !uploadedMime || typeof uploadedSize !== 'number' || !Number.isSafeInteger(uploadedSize) || uploadedSize !== context.expectedSize || allowedMimeTypes.length > 0 && !validateMimeType(uploadedMime, allowedMimeTypes) || Boolean(uploadedFile.isPrivateFile) !== collectionSecurity.isPrivate || typeof uploadedFile.url !== 'string' || uploadedFile.url.length === 0) {
|
|
87
89
|
throw new APIError('ImageKit client upload metadata did not match its authorization.', 400);
|
|
88
90
|
}
|
|
89
91
|
const verified = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/clientUploadVerification.ts"],"sourcesContent":["import type ImageKit from '@imagekit/nodejs'\nimport type { CollectionConfig } from 'payload'\n\nimport { buildStoragePathData } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { APIError } from 'payload'\nimport { assertClientUploadAllowed } from 'payload/internal'\nimport { validateMimeType } from 'payload/shared'\n\nimport type { ImageKitReceiptContext } from './clientUploads.js'\n\nimport { isImageKitReceiptContext } from './clientUploads.js'\n\nexport type VerifiedImageKitUpload = {\n fileId: string\n filePath: string\n fileType?: string\n mimeType: string\n name: string\n size: number\n thumbnailUrl?: string\n url: string\n}\n\ntype CollectionSecurityConfig = {\n collection: CollectionConfig\n isPrivate: boolean\n prefix: string\n}\n\nconst getMimeTypeEssence = (mimeType: string): string =>\n (mimeType.split(';', 1)[0] || '').trim().toLowerCase()\n\nconst toImageKitPath = (storageFilePath: string): string =>\n `/${storageFilePath.replace(/^\\/+/, '')}`\n\nconst assetLookupRetryDelays = [250, 500, 1000, 2000]\n\nconst findFileIDByPath = async (\n client: ImageKit,\n filePath: string,\n): Promise<string | undefined> => {\n const parentPath = `${filePath.slice(0, filePath.lastIndexOf('/') + 1)}`\n const name = filePath.slice(filePath.lastIndexOf('/') + 1)\n const limit = 100\n\n for (let skip = 0; ; skip += limit) {\n const assets = await client.assets.list({\n type: 'file',\n limit,\n path: parentPath,\n skip,\n })\n const found = assets.find(\n (asset) => 'fileId' in asset && asset.filePath === filePath && asset.name === name,\n )\n\n if (found && 'fileId' in found) {\n return found.fileId\n }\n\n if (assets.length < limit) {\n return undefined\n }\n }\n}\n\nexport const getClientUploadStorageFilePath = ({\n collectionPrefix,\n context,\n}: {\n collectionPrefix: string\n context: ImageKitReceiptContext\n}): string =>\n buildStoragePathData({\n collectionPrefix,\n docPrefix: [context.prefix, context._objectKey].filter(Boolean).join('/'),\n filename: context.filename,\n }).storageFilePath\n\nexport const createImageKitClientUploadVerifier = ({\n client,\n collections,\n}: {\n client: ImageKit\n collections: Record<string, CollectionSecurityConfig>\n}) => {\n const verifiedContexts = new WeakMap<object, VerifiedImageKitUpload>()\n\n return async ({\n collectionSlug,\n context,\n }: {\n collectionSlug: string\n context: unknown\n }): Promise<VerifiedImageKitUpload> => {\n if (!isImageKitReceiptContext(context)) {\n throw new APIError('Invalid ImageKit client upload context.', 400)\n }\n\n const collectionSecurity = collections[collectionSlug]\n if (\n !collectionSecurity ||\n context.collectionSlug !== collectionSlug ||\n context.isPrivate !== collectionSecurity.isPrivate\n ) {\n throw new APIError('ImageKit client upload reference does not match this collection.', 400)\n }\n\n const cached = verifiedContexts.get(context)\n if (cached) {\n return cached\n }\n\n const storageFilePath = getClientUploadStorageFilePath({\n collectionPrefix: collectionSecurity.prefix,\n context,\n })\n const expectedFilePath = toImageKitPath(storageFilePath)\n let fileId = context.fileId\n\n // Receipts without a file ID can still be verified by their signed path and binding.\n // The completion handshake avoids depending on asset listing visibility.\n if (!fileId) {\n fileId = await findFileIDByPath(client, expectedFilePath)\n for (const delay of assetLookupRetryDelays) {\n if (fileId) {\n break\n }\n await new Promise((resolve) => setTimeout(resolve, delay))\n fileId = await findFileIDByPath(client, expectedFilePath)\n }\n }\n\n if (!fileId) {\n throw new APIError('ImageKit client upload was not found at its authorized path.', 400)\n }\n\n const uploadedFile = await client.files.get(fileId)\n const uploadedMime =\n typeof uploadedFile.mime === 'string' ? getMimeTypeEssence(uploadedFile.mime) : ''\n const uploadedSize = uploadedFile.size\n\n assertClientUploadAllowed({\n collection: collectionSecurity.collection,\n filename: context.filename,\n mimeType: uploadedMime,\n })\n const allowedMimeTypes =\n typeof collectionSecurity.collection.upload === 'object' &&\n Array.isArray(collectionSecurity.collection.upload.mimeTypes)\n ? collectionSecurity.collection.upload.mimeTypes\n : []\n\n if (\n uploadedFile.fileId !== fileId ||\n uploadedFile.filePath !== expectedFilePath ||\n uploadedFile.name !== context.filename ||\n (context.uploadTag !== undefined && !uploadedFile.tags?.includes(context.uploadTag)) ||\n !uploadedMime ||\n uploadedMime !== context.expectedMimeType ||\n typeof uploadedSize !== 'number' ||\n !Number.isSafeInteger(uploadedSize) ||\n uploadedSize !== context.expectedSize ||\n (allowedMimeTypes.length > 0 && !validateMimeType(uploadedMime, allowedMimeTypes)) ||\n Boolean(uploadedFile.isPrivateFile) !== collectionSecurity.isPrivate ||\n typeof uploadedFile.url !== 'string' ||\n uploadedFile.url.length === 0\n ) {\n throw new APIError('ImageKit client upload metadata did not match its authorization.', 400)\n }\n\n const verified: VerifiedImageKitUpload = {\n name: uploadedFile.name,\n fileId,\n filePath: expectedFilePath,\n fileType: uploadedFile.fileType,\n mimeType: uploadedMime,\n size: uploadedSize,\n thumbnailUrl: typeof uploadedFile.thumbnail === 'string' ? uploadedFile.thumbnail : undefined,\n url: uploadedFile.url,\n }\n\n verifiedContexts.set(context, verified)\n return verified\n }\n}\n"],"names":["buildStoragePathData","APIError","assertClientUploadAllowed","validateMimeType","isImageKitReceiptContext","getMimeTypeEssence","mimeType","split","trim","toLowerCase","toImageKitPath","storageFilePath","replace","assetLookupRetryDelays","findFileIDByPath","client","filePath","parentPath","slice","lastIndexOf","name","limit","skip","assets","list","type","path","found","find","asset","fileId","length","undefined","getClientUploadStorageFilePath","collectionPrefix","context","docPrefix","prefix","_objectKey","filter","Boolean","join","filename","createImageKitClientUploadVerifier","collections","verifiedContexts","WeakMap","collectionSlug","collectionSecurity","isPrivate","cached","get","expectedFilePath","delay","Promise","resolve","setTimeout","uploadedFile","files","uploadedMime","mime","uploadedSize","size","collection","allowedMimeTypes","upload","Array","isArray","mimeTypes","uploadTag","tags","includes","expectedMimeType","Number","isSafeInteger","expectedSize","isPrivateFile","url","verified","fileType","thumbnailUrl","thumbnail","set"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/clientUploadVerification.ts"],"sourcesContent":["import type ImageKit from '@imagekit/nodejs'\nimport type { CollectionConfig } from 'payload'\n\nimport { buildStoragePathData } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { APIError } from 'payload'\nimport { assertClientUploadAllowed } from 'payload/internal'\nimport { validateMimeType } from 'payload/shared'\n\nimport type { ImageKitReceiptContext } from './clientUploads.js'\n\nimport { isImageKitReceiptContext } from './clientUploads.js'\n\nexport type VerifiedImageKitUpload = {\n fileId: string\n filePath: string\n fileType?: string\n mimeType: string\n name: string\n size: number\n thumbnailUrl?: string\n url: string\n}\n\ntype CollectionSecurityConfig = {\n collection: CollectionConfig\n isPrivate: boolean\n prefix: string\n}\n\nconst getMimeTypeEssence = (mimeType: string): string =>\n (mimeType.split(';', 1)[0] || '').trim().toLowerCase()\n\nconst toImageKitPath = (storageFilePath: string): string =>\n `/${storageFilePath.replace(/^\\/+/, '')}`\n\nconst assetLookupRetryDelays = [250, 500, 1000, 2000]\n\nconst findFileIDByPath = async (\n client: ImageKit,\n filePath: string,\n): Promise<string | undefined> => {\n const parentPath = `${filePath.slice(0, filePath.lastIndexOf('/') + 1)}`\n const name = filePath.slice(filePath.lastIndexOf('/') + 1)\n const limit = 100\n\n for (let skip = 0; ; skip += limit) {\n const assets = await client.assets.list({\n type: 'file',\n limit,\n path: parentPath,\n skip,\n })\n const found = assets.find(\n (asset) => 'fileId' in asset && asset.filePath === filePath && asset.name === name,\n )\n\n if (found && 'fileId' in found) {\n return found.fileId\n }\n\n if (assets.length < limit) {\n return undefined\n }\n }\n}\n\nexport const getClientUploadStorageFilePath = ({\n collectionPrefix,\n context,\n}: {\n collectionPrefix: string\n context: ImageKitReceiptContext\n}): string =>\n buildStoragePathData({\n collectionPrefix,\n docPrefix: [context.prefix, context._objectKey].filter(Boolean).join('/'),\n filename: context.filename,\n }).storageFilePath\n\nexport const createImageKitClientUploadVerifier = ({\n client,\n collections,\n}: {\n client: ImageKit\n collections: Record<string, CollectionSecurityConfig>\n}) => {\n const verifiedContexts = new WeakMap<object, VerifiedImageKitUpload>()\n\n return async ({\n collectionSlug,\n context,\n }: {\n collectionSlug: string\n context: unknown\n }): Promise<VerifiedImageKitUpload> => {\n if (!isImageKitReceiptContext(context)) {\n throw new APIError('Invalid ImageKit client upload context.', 400)\n }\n\n const collectionSecurity = collections[collectionSlug]\n if (\n !collectionSecurity ||\n context.collectionSlug !== collectionSlug ||\n context.isPrivate !== collectionSecurity.isPrivate\n ) {\n throw new APIError('ImageKit client upload reference does not match this collection.', 400)\n }\n\n const cached = verifiedContexts.get(context)\n if (cached) {\n return cached\n }\n\n const storageFilePath = getClientUploadStorageFilePath({\n collectionPrefix: collectionSecurity.prefix,\n context,\n })\n const expectedFilePath = toImageKitPath(storageFilePath)\n let fileId = context.fileId\n\n // Receipts without a file ID can still be verified by their signed path and binding.\n // The completion handshake avoids depending on asset listing visibility.\n if (!fileId) {\n fileId = await findFileIDByPath(client, expectedFilePath)\n for (const delay of assetLookupRetryDelays) {\n if (fileId) {\n break\n }\n await new Promise((resolve) => setTimeout(resolve, delay))\n fileId = await findFileIDByPath(client, expectedFilePath)\n }\n }\n\n if (!fileId) {\n throw new APIError('ImageKit client upload was not found at its authorized path.', 400)\n }\n\n const uploadedFile = await client.files.get(fileId)\n const uploadedMime =\n typeof uploadedFile.mime === 'string' ? getMimeTypeEssence(uploadedFile.mime) : ''\n const uploadedSize = uploadedFile.size\n\n // ImageKit's detected type is authoritative. It may differ from the browser's\n // extension-based type, so validate it against the collection, not the claim.\n assertClientUploadAllowed({\n collection: collectionSecurity.collection,\n filename: context.filename,\n mimeType: uploadedMime,\n })\n const allowedMimeTypes =\n typeof collectionSecurity.collection.upload === 'object' &&\n Array.isArray(collectionSecurity.collection.upload.mimeTypes)\n ? collectionSecurity.collection.upload.mimeTypes\n : []\n\n if (\n uploadedFile.fileId !== fileId ||\n uploadedFile.filePath !== expectedFilePath ||\n uploadedFile.name !== context.filename ||\n (context.uploadTag !== undefined && !uploadedFile.tags?.includes(context.uploadTag)) ||\n !uploadedMime ||\n typeof uploadedSize !== 'number' ||\n !Number.isSafeInteger(uploadedSize) ||\n uploadedSize !== context.expectedSize ||\n (allowedMimeTypes.length > 0 && !validateMimeType(uploadedMime, allowedMimeTypes)) ||\n Boolean(uploadedFile.isPrivateFile) !== collectionSecurity.isPrivate ||\n typeof uploadedFile.url !== 'string' ||\n uploadedFile.url.length === 0\n ) {\n throw new APIError('ImageKit client upload metadata did not match its authorization.', 400)\n }\n\n const verified: VerifiedImageKitUpload = {\n name: uploadedFile.name,\n fileId,\n filePath: expectedFilePath,\n fileType: uploadedFile.fileType,\n mimeType: uploadedMime,\n size: uploadedSize,\n thumbnailUrl: typeof uploadedFile.thumbnail === 'string' ? uploadedFile.thumbnail : undefined,\n url: uploadedFile.url,\n }\n\n verifiedContexts.set(context, verified)\n return verified\n }\n}\n"],"names":["buildStoragePathData","APIError","assertClientUploadAllowed","validateMimeType","isImageKitReceiptContext","getMimeTypeEssence","mimeType","split","trim","toLowerCase","toImageKitPath","storageFilePath","replace","assetLookupRetryDelays","findFileIDByPath","client","filePath","parentPath","slice","lastIndexOf","name","limit","skip","assets","list","type","path","found","find","asset","fileId","length","undefined","getClientUploadStorageFilePath","collectionPrefix","context","docPrefix","prefix","_objectKey","filter","Boolean","join","filename","createImageKitClientUploadVerifier","collections","verifiedContexts","WeakMap","collectionSlug","collectionSecurity","isPrivate","cached","get","expectedFilePath","delay","Promise","resolve","setTimeout","uploadedFile","files","uploadedMime","mime","uploadedSize","size","collection","allowedMimeTypes","upload","Array","isArray","mimeTypes","uploadTag","tags","includes","Number","isSafeInteger","expectedSize","isPrivateFile","url","verified","fileType","thumbnailUrl","thumbnail","set"],"mappings":"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"}
|
|
@@ -12,5 +12,15 @@ type CreateImageKitUploadAuthHandlerArgs = {
|
|
|
12
12
|
publicKey: string;
|
|
13
13
|
verifyClientUpload: ReturnType<typeof createImageKitClientUploadVerifier>;
|
|
14
14
|
};
|
|
15
|
+
/**
|
|
16
|
+
* ImageKit evaluates `file.mime` against its own content detection, which can differ from
|
|
17
|
+
* the browser's extension-based type (e.g. `.xltm` is detected as `.xlsx`). Constrain the
|
|
18
|
+
* detected type with the rules completion verification applies, not the browser's claim.
|
|
19
|
+
*/
|
|
20
|
+
export declare const getUploadChecks: ({ allowedMimeTypes, allowRestrictedFileTypes, filesize, }: {
|
|
21
|
+
allowedMimeTypes: string[];
|
|
22
|
+
allowRestrictedFileTypes: boolean;
|
|
23
|
+
filesize: number;
|
|
24
|
+
}) => string;
|
|
15
25
|
export declare const createImageKitUploadAuthHandler: ({ access, collections, privateKey, publicKey, verifyClientUpload, }: CreateImageKitUploadAuthHandlerArgs) => PayloadHandler;
|
|
16
26
|
export {};
|
|
@@ -10,6 +10,37 @@ const defaultAccess = ({ req })=>Boolean(req.user);
|
|
|
10
10
|
const bytesToMB = (bytes)=>(bytes / 1024 / 1024).toFixed(2);
|
|
11
11
|
const getMimeTypeEssence = (mimeType)=>(mimeType.split(';', 1)[0] || '').trim().toLowerCase();
|
|
12
12
|
const getImageKitSafeFilename = (filename)=>getSanitizedUploadFilename(filename).replace(/[^\w.-]/g, '_');
|
|
13
|
+
// ImageKit can't match Payload's `+xml` suffix rule, so name the XML-family types it may detect.
|
|
14
|
+
const XML_FAMILY_MIME_TYPES = [
|
|
15
|
+
'application/atom+xml',
|
|
16
|
+
'application/mathml+xml',
|
|
17
|
+
'application/rdf+xml',
|
|
18
|
+
'application/rss+xml',
|
|
19
|
+
'application/xhtml+xml',
|
|
20
|
+
'application/xml',
|
|
21
|
+
'application/xslt+xml',
|
|
22
|
+
'image/svg+xml',
|
|
23
|
+
'text/xml'
|
|
24
|
+
];
|
|
25
|
+
/**
|
|
26
|
+
* ImageKit evaluates `file.mime` against its own content detection, which can differ from
|
|
27
|
+
* the browser's extension-based type (e.g. `.xltm` is detected as `.xlsx`). Constrain the
|
|
28
|
+
* detected type with the rules completion verification applies, not the browser's claim.
|
|
29
|
+
*/ export const getUploadChecks = ({ allowedMimeTypes, allowRestrictedFileTypes, filesize })=>{
|
|
30
|
+
const checks = [
|
|
31
|
+
`"file.size" = ${filesize}`
|
|
32
|
+
];
|
|
33
|
+
// Mirror Payload's validateMimeType, which drops the first `*` and matches by prefix.
|
|
34
|
+
// Entries with quotes or backslashes can't prefix a valid MIME type, so omit them.
|
|
35
|
+
const prefixes = allowedMimeTypes.map((mimeType)=>mimeType.replace('*', '')).filter((prefix)=>!/["\\]/.test(prefix));
|
|
36
|
+
if (allowedMimeTypes.length > 0 && !prefixes.includes('')) {
|
|
37
|
+
checks.push(`(${prefixes.map((prefix)=>`"file.mime" : "${prefix}"`).join(' OR ')})`);
|
|
38
|
+
}
|
|
39
|
+
if (!allowRestrictedFileTypes) {
|
|
40
|
+
checks.push(`"file.mime" NOT IN [${XML_FAMILY_MIME_TYPES.map((type)=>`"${type}"`).join(', ')}]`);
|
|
41
|
+
}
|
|
42
|
+
return checks.join(' AND ');
|
|
43
|
+
};
|
|
13
44
|
// Continue Payload's numeric suffix convention when the conflict exists only in ImageKit.
|
|
14
45
|
const nextFilename = (filename)=>{
|
|
15
46
|
const { name, ext } = posix.parse(filename);
|
|
@@ -134,7 +165,11 @@ export const createImageKitUploadAuthHandler = ({ access = defaultAccess, collec
|
|
|
134
165
|
const folder = `/${resolved.storageFilePath.slice(0, -(sanitizedFilename.length + 1))}`;
|
|
135
166
|
const uploadTag = `payload-upload-${randomUUID()}`;
|
|
136
167
|
const uploadPayload = {
|
|
137
|
-
checks:
|
|
168
|
+
checks: getUploadChecks({
|
|
169
|
+
allowedMimeTypes,
|
|
170
|
+
allowRestrictedFileTypes: typeof collectionConfig?.upload === 'object' && Boolean(collectionConfig.upload.allowRestrictedFileTypes),
|
|
171
|
+
filesize: requestedSize
|
|
172
|
+
}),
|
|
138
173
|
fileName: sanitizedFilename,
|
|
139
174
|
folder,
|
|
140
175
|
isPrivateFile: imageKitCollection.isPrivate ? 'true' : 'false',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/endpoints/createImageKitUploadAuthHandler.ts"],"sourcesContent":["import type { ClientUploadsAccess } from '@payloadcms/plugin-cloud-storage/types'\nimport type { PayloadHandler, UploadCollectionSlug } from 'payload'\n\nimport { buildUploadStoragePathData } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { randomUUID } from 'node:crypto'\nimport { posix } from 'node:path'\nimport { APIError, Forbidden } from 'payload'\nimport {\n assertClientUploadAccess,\n assertClientUploadAllowed,\n assertClientUploadFileSize,\n createClientUploadReceipt,\n getSafeFileName,\n verifyClientUploadReceipt,\n} from 'payload/internal'\nimport { getSanitizedUploadFilename, validateMimeType } from 'payload/shared'\n\nimport type {\n ImageKitReceiptContext,\n ImageKitUploadAuthorization,\n ImageKitUploadCompletion,\n ImageKitUploadPayload,\n} from '../clientUploads.js'\nimport type { createImageKitClientUploadVerifier } from '../clientUploadVerification.js'\n\nimport { isImageKitReceiptContext } from '../clientUploads.js'\nimport { createImageKitUploadToken } from '../imageKitUploadToken.js'\n\ntype ImageKitUploadCollection = {\n isPrivate: boolean\n prefix: string\n}\n\ntype CreateImageKitUploadAuthHandlerArgs = {\n access?: ClientUploadsAccess\n collections: Partial<Record<UploadCollectionSlug, ImageKitUploadCollection>>\n privateKey: string\n publicKey: string\n verifyClientUpload: ReturnType<typeof createImageKitClientUploadVerifier>\n}\n\nconst defaultAccess: ClientUploadsAccess = ({ req }) => Boolean(req.user)\n\nconst bytesToMB = (bytes: number): string => (bytes / 1024 / 1024).toFixed(2)\n\nconst getMimeTypeEssence = (mimeType: string): string =>\n (mimeType.split(';', 1)[0] || '').trim().toLowerCase()\n\nconst getImageKitSafeFilename = (filename: string): string =>\n getSanitizedUploadFilename(filename).replace(/[^\\w.-]/g, '_')\n\n// Continue Payload's numeric suffix convention when the conflict exists only in ImageKit.\nconst nextFilename = (filename: string): string => {\n const { name, ext } = posix.parse(filename)\n const suffix = /^(.*)-(\\d+)$/.exec(name)\n return `${suffix ? `${suffix[1]}-${Number(suffix[2]) + 1}` : `${name}-1`}${ext}`\n}\n\nexport const createImageKitUploadAuthHandler = ({\n access = defaultAccess,\n collections,\n privateKey,\n publicKey,\n verifyClientUpload,\n}: CreateImageKitUploadAuthHandlerArgs): PayloadHandler => {\n return async (req) => {\n if (!req.json) {\n throw new APIError('Content-Type expected to be application/json', 400)\n }\n\n const body = (await req.json()) as {\n action?: unknown\n collectionSlug?: unknown\n fileId?: unknown\n filename?: unknown\n filesize?: unknown\n mimeType?: unknown\n signedReceipt?: unknown\n }\n const { action, collectionSlug, fileId, signedReceipt } = body\n let { filename, filesize, mimeType } = body\n\n if (typeof collectionSlug !== 'string' || !Object.hasOwn(collections, collectionSlug)) {\n throw new APIError('Invalid upload payload.', 400)\n }\n\n await assertClientUploadAccess({ collectionSlug, req })\n\n const imageKitCollection = collections[collectionSlug]\n if (!imageKitCollection) {\n throw new APIError(`Collection ${collectionSlug} was not found in ImageKit options.`, 400)\n }\n\n if (!(await access({ collectionSlug, req }))) {\n throw new Forbidden(req.t)\n }\n\n if (action === 'complete') {\n if (typeof fileId !== 'string' || !fileId.trim() || typeof signedReceipt !== 'string') {\n throw new APIError('Invalid ImageKit upload completion payload.', 400)\n }\n const receipt = verifyClientUploadReceipt({ collectionSlug, req, signedReceipt })\n if (\n !isImageKitReceiptContext(receipt.context) ||\n receipt.filename !== receipt.context.filename ||\n (receipt.context.fileId !== undefined && receipt.context.fileId !== fileId)\n ) {\n throw new APIError('Invalid ImageKit client upload context.', 400)\n }\n\n // The browser supplies a lookup ID, not proof. Check provider state against the\n // original signed authorization before including that ID in a new signed receipt.\n const context: ImageKitReceiptContext = { ...receipt.context, fileId }\n await verifyClientUpload({ collectionSlug, context })\n delete context.uploadTag\n const response: ImageKitUploadCompletion = {\n clientUploadContext: {\n _objectKey: context._objectKey,\n prefix: context.prefix,\n signedReceipt: createClientUploadReceipt({\n collectionSlug,\n context,\n filename: context.filename,\n req,\n }),\n },\n }\n return Response.json(response, { headers: { 'Cache-Control': 'no-store' } })\n }\n\n if (action === 'retry') {\n if (typeof signedReceipt !== 'string') {\n throw new APIError('Invalid ImageKit upload retry payload.', 400)\n }\n const receipt = verifyClientUploadReceipt({ collectionSlug, req, signedReceipt })\n if (\n !isImageKitReceiptContext(receipt.context) ||\n receipt.filename !== receipt.context.filename ||\n receipt.context.fileId !== undefined\n ) {\n throw new APIError('Invalid ImageKit upload retry payload.', 400)\n }\n filename = nextFilename(receipt.context.filename)\n filesize = receipt.context.expectedSize\n mimeType = receipt.context.expectedMimeType\n }\n\n if (\n (action !== undefined && action !== 'retry') ||\n typeof filename !== 'string' ||\n typeof mimeType !== 'string'\n ) {\n throw new APIError('Invalid upload payload.', 400)\n }\n\n assertClientUploadAllowed({\n collection: req.payload.collections[collectionSlug]?.config,\n filename,\n mimeType,\n })\n assertClientUploadFileSize(filesize)\n\n const requestedSize = filesize as number\n const expectedMimeType = getMimeTypeEssence(mimeType)\n if (!expectedMimeType) {\n throw new APIError('A valid MIME type is required for ImageKit client uploads.', 400)\n }\n\n const collectionConfig = req.payload.collections[collectionSlug]?.config\n const allowedMimeTypes =\n collectionConfig &&\n typeof collectionConfig.upload === 'object' &&\n Array.isArray(collectionConfig.upload.mimeTypes)\n ? collectionConfig.upload.mimeTypes\n : []\n\n if (allowedMimeTypes.length > 0 && !validateMimeType(expectedMimeType, allowedMimeTypes)) {\n throw new APIError(`File type ${expectedMimeType} is not allowed.`, 400)\n }\n\n const filesizeLimit = req.payload.config.upload.limits?.fileSize\n\n if (\n typeof filesizeLimit === 'number' &&\n Number.isFinite(filesizeLimit) &&\n requestedSize > filesizeLimit\n ) {\n throw new APIError(\n `Exceeded file size limit. Limit: ${bytesToMB(filesizeLimit)}MB, got: ${bytesToMB(requestedSize)}MB`,\n 400,\n )\n }\n\n const sanitizedFilename = await getSafeFileName({\n collectionSlug,\n desiredFilename: getImageKitSafeFilename(filename),\n req,\n })\n const resolved = buildUploadStoragePathData({\n collectionPrefix: imageKitCollection.prefix,\n docPrefix: imageKitCollection.prefix,\n filename: sanitizedFilename,\n })\n const folder = `/${resolved.storageFilePath.slice(0, -(sanitizedFilename.length + 1))}`\n const uploadTag = `payload-upload-${randomUUID()}`\n const uploadPayload: ImageKitUploadPayload = {\n checks: `\"file.size\" = ${requestedSize} AND \"file.mime\" = \"${expectedMimeType}\"`,\n fileName: sanitizedFilename,\n folder,\n isPrivateFile: imageKitCollection.isPrivate ? 'true' : 'false',\n overwriteFile: 'false',\n tags: uploadTag,\n useUniqueFileName: 'false',\n }\n const receiptContext: ImageKitReceiptContext = {\n collectionSlug,\n expectedMimeType,\n expectedSize: requestedSize,\n filename: sanitizedFilename,\n isPrivate: imageKitCollection.isPrivate,\n prefix: resolved.sanitizedDocPrefix,\n uploadTag,\n }\n const response: ImageKitUploadAuthorization = {\n clientUploadContext: {\n prefix: resolved.sanitizedDocPrefix,\n signedReceipt: createClientUploadReceipt({\n collectionSlug,\n context: receiptContext,\n filename: sanitizedFilename,\n req,\n }),\n },\n filename: sanitizedFilename,\n token: createImageKitUploadToken({ privateKey, publicKey, uploadPayload }),\n uploadPayload,\n }\n\n return Response.json(response, {\n headers: {\n 'Cache-Control': 'no-store',\n },\n })\n }\n}\n"],"names":["buildUploadStoragePathData","randomUUID","posix","APIError","Forbidden","assertClientUploadAccess","assertClientUploadAllowed","assertClientUploadFileSize","createClientUploadReceipt","getSafeFileName","verifyClientUploadReceipt","getSanitizedUploadFilename","validateMimeType","isImageKitReceiptContext","createImageKitUploadToken","defaultAccess","req","Boolean","user","bytesToMB","bytes","toFixed","getMimeTypeEssence","mimeType","split","trim","toLowerCase","getImageKitSafeFilename","filename","replace","nextFilename","name","ext","parse","suffix","exec","Number","createImageKitUploadAuthHandler","access","collections","privateKey","publicKey","verifyClientUpload","json","body","action","collectionSlug","fileId","signedReceipt","filesize","Object","hasOwn","imageKitCollection","t","receipt","context","undefined","uploadTag","response","clientUploadContext","_objectKey","prefix","Response","headers","expectedSize","expectedMimeType","collection","payload","config","requestedSize","collectionConfig","allowedMimeTypes","upload","Array","isArray","mimeTypes","length","filesizeLimit","limits","fileSize","isFinite","sanitizedFilename","desiredFilename","resolved","collectionPrefix","docPrefix","folder","storageFilePath","slice","uploadPayload","checks","fileName","isPrivateFile","isPrivate","overwriteFile","tags","useUniqueFileName","receiptContext","sanitizedDocPrefix","token"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/endpoints/createImageKitUploadAuthHandler.ts"],"sourcesContent":["import type { ClientUploadsAccess } from '@payloadcms/plugin-cloud-storage/types'\nimport type { PayloadHandler, UploadCollectionSlug } from 'payload'\n\nimport { buildUploadStoragePathData } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { randomUUID } from 'node:crypto'\nimport { posix } from 'node:path'\nimport { APIError, Forbidden } from 'payload'\nimport {\n assertClientUploadAccess,\n assertClientUploadAllowed,\n assertClientUploadFileSize,\n createClientUploadReceipt,\n getSafeFileName,\n verifyClientUploadReceipt,\n} from 'payload/internal'\nimport { getSanitizedUploadFilename, validateMimeType } from 'payload/shared'\n\nimport type {\n ImageKitReceiptContext,\n ImageKitUploadAuthorization,\n ImageKitUploadCompletion,\n ImageKitUploadPayload,\n} from '../clientUploads.js'\nimport type { createImageKitClientUploadVerifier } from '../clientUploadVerification.js'\n\nimport { isImageKitReceiptContext } from '../clientUploads.js'\nimport { createImageKitUploadToken } from '../imageKitUploadToken.js'\n\ntype ImageKitUploadCollection = {\n isPrivate: boolean\n prefix: string\n}\n\ntype CreateImageKitUploadAuthHandlerArgs = {\n access?: ClientUploadsAccess\n collections: Partial<Record<UploadCollectionSlug, ImageKitUploadCollection>>\n privateKey: string\n publicKey: string\n verifyClientUpload: ReturnType<typeof createImageKitClientUploadVerifier>\n}\n\nconst defaultAccess: ClientUploadsAccess = ({ req }) => Boolean(req.user)\n\nconst bytesToMB = (bytes: number): string => (bytes / 1024 / 1024).toFixed(2)\n\nconst getMimeTypeEssence = (mimeType: string): string =>\n (mimeType.split(';', 1)[0] || '').trim().toLowerCase()\n\nconst getImageKitSafeFilename = (filename: string): string =>\n getSanitizedUploadFilename(filename).replace(/[^\\w.-]/g, '_')\n\n// ImageKit can't match Payload's `+xml` suffix rule, so name the XML-family types it may detect.\nconst XML_FAMILY_MIME_TYPES = [\n 'application/atom+xml',\n 'application/mathml+xml',\n 'application/rdf+xml',\n 'application/rss+xml',\n 'application/xhtml+xml',\n 'application/xml',\n 'application/xslt+xml',\n 'image/svg+xml',\n 'text/xml',\n]\n\n/**\n * ImageKit evaluates `file.mime` against its own content detection, which can differ from\n * the browser's extension-based type (e.g. `.xltm` is detected as `.xlsx`). Constrain the\n * detected type with the rules completion verification applies, not the browser's claim.\n */\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 // Mirror Payload's validateMimeType, which drops the first `*` and matches by prefix.\n // Entries with quotes or backslashes can't prefix a valid MIME type, so omit them.\n const prefixes = allowedMimeTypes\n .map((mimeType) => mimeType.replace('*', ''))\n .filter((prefix) => !/[\"\\\\]/.test(prefix))\n\n if (allowedMimeTypes.length > 0 && !prefixes.includes('')) {\n checks.push(`(${prefixes.map((prefix) => `\"file.mime\" : \"${prefix}\"`).join(' OR ')})`)\n }\n\n if (!allowRestrictedFileTypes) {\n checks.push(`\"file.mime\" NOT IN [${XML_FAMILY_MIME_TYPES.map((type) => `\"${type}\"`).join(', ')}]`)\n }\n\n return checks.join(' AND ')\n}\n\n// Continue Payload's numeric suffix convention when the conflict exists only in ImageKit.\nconst nextFilename = (filename: string): string => {\n const { name, ext } = posix.parse(filename)\n const suffix = /^(.*)-(\\d+)$/.exec(name)\n return `${suffix ? `${suffix[1]}-${Number(suffix[2]) + 1}` : `${name}-1`}${ext}`\n}\n\nexport const createImageKitUploadAuthHandler = ({\n access = defaultAccess,\n collections,\n privateKey,\n publicKey,\n verifyClientUpload,\n}: CreateImageKitUploadAuthHandlerArgs): PayloadHandler => {\n return async (req) => {\n if (!req.json) {\n throw new APIError('Content-Type expected to be application/json', 400)\n }\n\n const body = (await req.json()) as {\n action?: unknown\n collectionSlug?: unknown\n fileId?: unknown\n filename?: unknown\n filesize?: unknown\n mimeType?: unknown\n signedReceipt?: unknown\n }\n const { action, collectionSlug, fileId, signedReceipt } = body\n let { filename, filesize, mimeType } = body\n\n if (typeof collectionSlug !== 'string' || !Object.hasOwn(collections, collectionSlug)) {\n throw new APIError('Invalid upload payload.', 400)\n }\n\n await assertClientUploadAccess({ collectionSlug, req })\n\n const imageKitCollection = collections[collectionSlug]\n if (!imageKitCollection) {\n throw new APIError(`Collection ${collectionSlug} was not found in ImageKit options.`, 400)\n }\n\n if (!(await access({ collectionSlug, req }))) {\n throw new Forbidden(req.t)\n }\n\n if (action === 'complete') {\n if (typeof fileId !== 'string' || !fileId.trim() || typeof signedReceipt !== 'string') {\n throw new APIError('Invalid ImageKit upload completion payload.', 400)\n }\n const receipt = verifyClientUploadReceipt({ collectionSlug, req, signedReceipt })\n if (\n !isImageKitReceiptContext(receipt.context) ||\n receipt.filename !== receipt.context.filename ||\n (receipt.context.fileId !== undefined && receipt.context.fileId !== fileId)\n ) {\n throw new APIError('Invalid ImageKit client upload context.', 400)\n }\n\n // The browser supplies a lookup ID, not proof. Check provider state against the\n // original signed authorization before including that ID in a new signed receipt.\n const context: ImageKitReceiptContext = { ...receipt.context, fileId }\n await verifyClientUpload({ collectionSlug, context })\n delete context.uploadTag\n const response: ImageKitUploadCompletion = {\n clientUploadContext: {\n _objectKey: context._objectKey,\n prefix: context.prefix,\n signedReceipt: createClientUploadReceipt({\n collectionSlug,\n context,\n filename: context.filename,\n req,\n }),\n },\n }\n return Response.json(response, { headers: { 'Cache-Control': 'no-store' } })\n }\n\n if (action === 'retry') {\n if (typeof signedReceipt !== 'string') {\n throw new APIError('Invalid ImageKit upload retry payload.', 400)\n }\n const receipt = verifyClientUploadReceipt({ collectionSlug, req, signedReceipt })\n if (\n !isImageKitReceiptContext(receipt.context) ||\n receipt.filename !== receipt.context.filename ||\n receipt.context.fileId !== undefined\n ) {\n throw new APIError('Invalid ImageKit upload retry payload.', 400)\n }\n filename = nextFilename(receipt.context.filename)\n filesize = receipt.context.expectedSize\n mimeType = receipt.context.expectedMimeType\n }\n\n if (\n (action !== undefined && action !== 'retry') ||\n typeof filename !== 'string' ||\n typeof mimeType !== 'string'\n ) {\n throw new APIError('Invalid upload payload.', 400)\n }\n\n assertClientUploadAllowed({\n collection: req.payload.collections[collectionSlug]?.config,\n filename,\n mimeType,\n })\n assertClientUploadFileSize(filesize)\n\n const requestedSize = filesize as number\n const expectedMimeType = getMimeTypeEssence(mimeType)\n if (!expectedMimeType) {\n throw new APIError('A valid MIME type is required for ImageKit client uploads.', 400)\n }\n\n const collectionConfig = req.payload.collections[collectionSlug]?.config\n const allowedMimeTypes =\n collectionConfig &&\n typeof collectionConfig.upload === 'object' &&\n Array.isArray(collectionConfig.upload.mimeTypes)\n ? collectionConfig.upload.mimeTypes\n : []\n\n if (allowedMimeTypes.length > 0 && !validateMimeType(expectedMimeType, allowedMimeTypes)) {\n throw new APIError(`File type ${expectedMimeType} is not allowed.`, 400)\n }\n\n const filesizeLimit = req.payload.config.upload.limits?.fileSize\n\n if (\n typeof filesizeLimit === 'number' &&\n Number.isFinite(filesizeLimit) &&\n requestedSize > filesizeLimit\n ) {\n throw new APIError(\n `Exceeded file size limit. Limit: ${bytesToMB(filesizeLimit)}MB, got: ${bytesToMB(requestedSize)}MB`,\n 400,\n )\n }\n\n const sanitizedFilename = await getSafeFileName({\n collectionSlug,\n desiredFilename: getImageKitSafeFilename(filename),\n req,\n })\n const resolved = buildUploadStoragePathData({\n collectionPrefix: imageKitCollection.prefix,\n docPrefix: imageKitCollection.prefix,\n filename: sanitizedFilename,\n })\n const folder = `/${resolved.storageFilePath.slice(0, -(sanitizedFilename.length + 1))}`\n const uploadTag = `payload-upload-${randomUUID()}`\n const uploadPayload: ImageKitUploadPayload = {\n checks: getUploadChecks({\n allowedMimeTypes,\n allowRestrictedFileTypes:\n typeof collectionConfig?.upload === 'object' &&\n Boolean(collectionConfig.upload.allowRestrictedFileTypes),\n filesize: requestedSize,\n }),\n fileName: sanitizedFilename,\n folder,\n isPrivateFile: imageKitCollection.isPrivate ? 'true' : 'false',\n overwriteFile: 'false',\n tags: uploadTag,\n useUniqueFileName: 'false',\n }\n const receiptContext: ImageKitReceiptContext = {\n collectionSlug,\n expectedMimeType,\n expectedSize: requestedSize,\n filename: sanitizedFilename,\n isPrivate: imageKitCollection.isPrivate,\n prefix: resolved.sanitizedDocPrefix,\n uploadTag,\n }\n const response: ImageKitUploadAuthorization = {\n clientUploadContext: {\n prefix: resolved.sanitizedDocPrefix,\n signedReceipt: createClientUploadReceipt({\n collectionSlug,\n context: receiptContext,\n filename: sanitizedFilename,\n req,\n }),\n },\n filename: sanitizedFilename,\n token: createImageKitUploadToken({ privateKey, publicKey, uploadPayload }),\n uploadPayload,\n }\n\n return Response.json(response, {\n headers: {\n 'Cache-Control': 'no-store',\n },\n })\n }\n}\n"],"names":["buildUploadStoragePathData","randomUUID","posix","APIError","Forbidden","assertClientUploadAccess","assertClientUploadAllowed","assertClientUploadFileSize","createClientUploadReceipt","getSafeFileName","verifyClientUploadReceipt","getSanitizedUploadFilename","validateMimeType","isImageKitReceiptContext","createImageKitUploadToken","defaultAccess","req","Boolean","user","bytesToMB","bytes","toFixed","getMimeTypeEssence","mimeType","split","trim","toLowerCase","getImageKitSafeFilename","filename","replace","XML_FAMILY_MIME_TYPES","getUploadChecks","allowedMimeTypes","allowRestrictedFileTypes","filesize","checks","prefixes","map","filter","prefix","test","length","includes","push","join","type","nextFilename","name","ext","parse","suffix","exec","Number","createImageKitUploadAuthHandler","access","collections","privateKey","publicKey","verifyClientUpload","json","body","action","collectionSlug","fileId","signedReceipt","Object","hasOwn","imageKitCollection","t","receipt","context","undefined","uploadTag","response","clientUploadContext","_objectKey","Response","headers","expectedSize","expectedMimeType","collection","payload","config","requestedSize","collectionConfig","upload","Array","isArray","mimeTypes","filesizeLimit","limits","fileSize","isFinite","sanitizedFilename","desiredFilename","resolved","collectionPrefix","docPrefix","folder","storageFilePath","slice","uploadPayload","fileName","isPrivateFile","isPrivate","overwriteFile","tags","useUniqueFileName","receiptContext","sanitizedDocPrefix","token"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -538,7 +538,7 @@ export const imagekitStorage = (pluginOptions)=>(config)=>{
|
|
|
538
538
|
}
|
|
539
539
|
const context = req.file.clientUploadContext;
|
|
540
540
|
const requestMimeType = req.file.mimetype.split(';', 1)[0]?.trim().toLowerCase();
|
|
541
|
-
if (!isImageKitReceiptContext(context) || context.filename !== data.filename || context.filename !== req.file.name ||
|
|
541
|
+
if (!isImageKitReceiptContext(context) || context.filename !== data.filename || context.filename !== req.file.name || // Payload can clear req.file.size when preparing an in-memory image.
|
|
542
542
|
// generateFileData preserves the original size on the document.
|
|
543
543
|
context.expectedSize !== data.filesize) {
|
|
544
544
|
throw new APIError('ImageKit client upload reference does not match this request.', 400);
|
|
@@ -547,6 +547,11 @@ export const imagekitStorage = (pluginOptions)=>(config)=>{
|
|
|
547
547
|
collectionSlug: collection.slug,
|
|
548
548
|
context
|
|
549
549
|
});
|
|
550
|
+
// Payload keeps the browser's MIME unless it fetched the file content, in which
|
|
551
|
+
// case it uses the verified ImageKit-detected type the static handler served.
|
|
552
|
+
if (requestMimeType !== context.expectedMimeType && requestMimeType !== uploaded.mimeType) {
|
|
553
|
+
throw new APIError('ImageKit client upload reference does not match this request.', 400);
|
|
554
|
+
}
|
|
550
555
|
// A provider file can only be claimed once, including through legacy receipts.
|
|
551
556
|
// The unique file ID index also rejects concurrent claims.
|
|
552
557
|
const existing = await req.payload.db.findOne({
|
|
@@ -635,7 +640,18 @@ export const imagekitStorage = (pluginOptions)=>(config)=>{
|
|
|
635
640
|
if (!configuredSlugs.has(collection.slug)) {
|
|
636
641
|
return collection;
|
|
637
642
|
}
|
|
643
|
+
const originalPrefixField = (config.collections || []).find((original)=>original.slug === collection.slug)?.fields.find((field)=>'name' in field && field.name === 'prefix');
|
|
638
644
|
const withUniqueIdentity = (field)=>{
|
|
645
|
+
if (field.type === 'text' && 'name' in field && field.name === 'prefix' && (!originalPrefixField || !('defaultValue' in originalPrefixField) || originalPrefixField.defaultValue == null)) {
|
|
646
|
+
// Cloud storage's collection-prefix default is applied on reads of old
|
|
647
|
+
// documents that have no stored prefix. Its URL hook then appends that
|
|
648
|
+
// prefix, but Payload's file access check cannot find it in the DB.
|
|
649
|
+
// Upload hooks still persist the collection prefix for new files.
|
|
650
|
+
return {
|
|
651
|
+
...field,
|
|
652
|
+
defaultValue: ({ req })=>req.file && typeof field.defaultValue === 'string' ? field.defaultValue : ''
|
|
653
|
+
};
|
|
654
|
+
}
|
|
639
655
|
if ('name' in field && field.name === '_objectKey') {
|
|
640
656
|
return {
|
|
641
657
|
...field,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type {\n Adapter,\n ClientUploadsConfig,\n CollectionOptions,\n} from '@payloadcms/plugin-cloud-storage/types'\nimport type {\n CollectionConfig,\n CollectionSlug,\n Config,\n Field,\n PayloadRequest,\n TypeWithID,\n UploadCollectionSlug,\n Where,\n} from 'payload'\n\nimport ImageKit, { toFile } from '@imagekit/nodejs'\nimport { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage'\nimport {\n buildStoragePathData,\n buildUploadStoragePathData,\n getStorageFilePath,\n initClientUploads,\n} from '@payloadcms/plugin-cloud-storage/utilities'\nimport { openAsBlob } from 'node:fs'\nimport path from 'node:path'\nimport { APIError } from 'payload'\nimport {\n getRangeRequestInfo,\n isXmlMimeType,\n UPLOAD_CONTENT_SECURITY_POLICY,\n} from 'payload/internal'\nimport { validateMimeType } from 'payload/shared'\n\nimport type { VerifiedImageKitUpload } from './clientUploadVerification.js'\n\nimport { imageKitUploadAuthPath, isImageKitReceiptContext } from './clientUploads.js'\nimport {\n createImageKitClientUploadVerifier,\n getClientUploadStorageFilePath,\n} from './clientUploadVerification.js'\nimport { createImageKitUploadAuthHandler } from './endpoints/createImageKitUploadAuthHandler.js'\nimport {\n getAdminThumbnailURL,\n getPrivateAdminThumbnailURL,\n isPdfMimeType,\n isVideoMimeType,\n} from './getAdminThumbnailURL.js'\nimport { getImageKitThumbnailSrc, getVideoPosterSrc } from './imageTransforms.js'\n\nexport type ImageKitCollectionOptions = {\n /**\n * Override Payload's native focal-point picker (`upload.focalPoint`) for this\n * collection.\n */\n focalPoint?: boolean\n /**\n * Upload files in this collection as ImageKit private files (`isPrivateFile: true`)\n * and deliver them only through short-lived signed URLs, behind Payload access control.\n */\n private?: boolean\n}\ntype ImageKitCollectionConfig = ImageKitCollectionOptions | true\ntype ImageKitCollectionMap = Partial<Record<UploadCollectionSlug, ImageKitCollectionConfig>>\n\nconst DEFAULT_SIGNED_URL_EXPIRES_IN = 900\n/**\n * ImageKit re-encodes some uploads and serves the smaller derivative from the plain\n * delivery URL — a 1.9 MB MP4 can come back as 868 KB. That is exactly what we want\n * for visitors, but not when reading the file back to verify a client upload: the\n * bytes and the byte count would both belong to a file ImageKit's API never reported.\n * `orig-true` asks for the untouched original and does not disable optimisation for\n * anyone else.\n */\nconst ORIGINAL_FILE_TRANSFORMATION = 'orig-true'\n\nconst PRIVATE_COLLECTION_DESCRIPTION =\n 'Files in this collection are uploaded to ImageKit as private files and delivered through short-lived signed URLs.'\n\nconst isPrivateCollectionConfig = (value: ImageKitCollectionConfig | undefined): boolean =>\n value !== true && typeof value === 'object' && value?.private === true\n\nconst XML_FAMILY_MIME_TYPES = ['application/xml', 'image/svg+xml', 'text/xml']\n\nconst collectionCanStoreXml = (collection: CollectionConfig): boolean => {\n const upload = collection.upload\n\n if (!upload || upload === true) {\n return Boolean(upload)\n }\n\n // Payload skips MIME and content restrictions when this escape hatch is enabled.\n if (upload.allowRestrictedFileTypes) {\n return true\n }\n\n const allowedMimeTypes = upload.mimeTypes\n\n return (\n !allowedMimeTypes?.length ||\n allowedMimeTypes.some((mimeType) => mimeType.includes('*') || isXmlMimeType(mimeType)) ||\n XML_FAMILY_MIME_TYPES.some((mimeType) => validateMimeType(mimeType, allowedMimeTypes))\n )\n}\n\n/**\n * Whether a specific document is private. The flag stored on the document at upload time\n * (`imagekit.isPrivate`) is the source of truth — a file's privacy on ImageKit is fixed\n * when it is uploaded and does not change if the collection's `private` setting changes\n * later. Falls back to the collection setting for documents uploaded before this flag existed.\n */\nconst isDocumentPrivate = (doc: unknown, collectionIsPrivate: boolean): boolean => {\n const stored = (doc as { imagekit?: { isPrivate?: boolean } } | undefined)?.imagekit?.isPrivate\n\n return typeof stored === 'boolean' ? stored : collectionIsPrivate\n}\ntype ImageKitReference = {\n imagekit?: {\n fileId?: string\n isPrivate?: boolean\n thumbnailUrl?: string\n url?: string\n }\n}\n\ntype UploadDocumentData = {\n thumbnailURL?: string\n} & ImageKitReference\n\nexport type PayloadStorageImageKitConfig = {\n alwaysInsertFields?: boolean\n clientUploads?: ClientUploadsConfig\n collections: ImageKitCollectionMap\n disablePayloadAccessControl?: true\n enabled?: boolean\n /**\n * Broad default for Payload's native focal-point picker (`upload.focalPoint`)\n * on every configured collection. Focal points are disabled by default for\n * configured collections, so prefer collection-level `focalPoint: true` for\n * image collections. A collection's own `upload.focalPoint` still takes\n * precedence.\n */\n focalPoint?: boolean\n folder: string\n privateKey: string\n publicKey: string\n /**\n * Expiry (in seconds) applied to signed URLs generated for private collections.\n * Defaults to 900 (15 minutes). URLs are signed fresh on every request, so a short\n * window is safe.\n */\n signedUrlExpiresIn?: number\n urlEndpoint: string\n}\n\nconst normalizeFolder = (folder: string): string => {\n const normalized = folder.trim().replace(/^\\/+|\\/+$/g, '')\n\n if (!normalized) {\n throw new Error('imagekitStorage: `folder` is required.')\n }\n\n return `/${normalized}`\n}\n\nconst getCollectionFolder = (rootFolder: string, collectionSlug: string): string =>\n path.posix.join(rootFolder, collectionSlug)\n\nconst findImageKitFileId = async (\n imagekit: ImageKit,\n filePath: string,\n): Promise<string | undefined> => {\n const parentPath = path.posix.dirname(filePath)\n const name = path.posix.basename(filePath)\n\n const limit = 100\n\n for (let skip = 0; ; skip += limit) {\n const assets = await imagekit.assets.list({\n type: 'file',\n limit,\n path: parentPath === '/' ? '/' : `${parentPath}/`,\n skip,\n })\n\n const found = assets.find(\n (asset) => 'fileId' in asset && asset.filePath === filePath && asset.name === name,\n )\n\n if (found && 'fileId' in found) {\n return found.fileId\n }\n\n if (assets.length < limit) {\n break\n }\n }\n\n return undefined\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n !!value && typeof value === 'object'\n\nconst getStoredImageKitURL = (doc: unknown): string | undefined => {\n const storedURL = (doc as ImageKitReference | undefined)?.imagekit?.url\n\n return typeof storedURL === 'string' && storedURL.trim().length > 0 ? storedURL : undefined\n}\n\nconst hasStoredImageKitURL = (doc: unknown): boolean => !!getStoredImageKitURL(doc)\n\nconst findUploadDocumentByFilename = async ({\n collection,\n filename,\n prefix,\n req,\n}: {\n collection: string\n filename: string\n prefix?: string\n req: PayloadRequest\n}): Promise<null | TypeWithID> => {\n const constraints: Where[] = [\n {\n filename: {\n equals: filename,\n },\n },\n ]\n\n if (typeof prefix === 'string') {\n constraints.push({\n prefix: {\n equals: prefix,\n },\n })\n }\n\n return req.payload.db.findOne({\n collection,\n req,\n where: {\n and: constraints,\n },\n })\n}\n\nconst applyVerifiedClientUploadToData = <T extends UploadDocumentData>(\n data: T,\n uploaded: VerifiedImageKitUpload,\n buildThumbnailURL: (data: T) => string = getAdminThumbnailURL,\n isPrivate: boolean,\n): T => {\n data.imagekit = {\n fileId: uploaded.fileId,\n isPrivate,\n thumbnailUrl: uploaded.thumbnailUrl,\n url: uploaded.url,\n }\n\n // Preserve the admin preview when the original file was already uploaded client-side.\n data.thumbnailURL = buildThumbnailURL(data)\n\n return data\n}\n\nconst imagekitAdapter = ({\n apiRoute,\n client,\n clientUploads,\n privateSlugs,\n rootFolder,\n signedUrlExpiresIn,\n urlEndpoint,\n verifyClientUpload,\n}: {\n apiRoute: string\n client: ImageKit\n clientUploads?: ClientUploadsConfig\n privateSlugs: Set<string>\n rootFolder: string\n signedUrlExpiresIn: number\n urlEndpoint: string\n verifyClientUpload: ReturnType<typeof createImageKitClientUploadVerifier>\n}): Adapter => {\n const fetchUploadedFile = async ({\n fileSize,\n fileURL,\n mimeType,\n range,\n }: {\n fileSize: number\n fileURL: string\n mimeType: string\n range?: null | string\n }): Promise<Response> => {\n const rangeResult = getRangeRequestInfo({ fileSize, rangeHeader: range ?? null })\n\n if (rangeResult.type === 'invalid') {\n return new Response(null, {\n headers: new Headers(rangeResult.headers),\n status: rangeResult.status,\n })\n }\n\n const normalizedRange =\n rangeResult.type === 'partial'\n ? `bytes=${rangeResult.rangeStart}-${rangeResult.rangeEnd}`\n : undefined\n // Payload cancels image probes after reading a bounded prefix. Next.js memoizes\n // GET responses by teeing their streams; cancelling one branch can then wait\n // forever for its unread cached sibling. An explicit signal opts out of that\n // memoization, while no-store also excludes these reads from the data cache.\n const response = await fetch(fileURL, {\n cache: 'no-store',\n ...(normalizedRange ? { headers: { Range: normalizedRange } } : {}),\n signal: new AbortController().signal,\n })\n\n if (!response.ok || response.status !== rangeResult.status) {\n void response.body?.cancel().catch(() => undefined)\n throw new Error(\n `imagekitStorage: ImageKit returned an unexpected file response (${response.status}).`,\n )\n }\n\n const upstreamLength = response.headers.get('Content-Length')\n const expectedLength = rangeResult.headers['Content-Length']\n const upstreamRange = response.headers.get('Content-Range')\n const expectedRange =\n rangeResult.type === 'partial' ? rangeResult.headers['Content-Range'] : undefined\n\n if (\n (upstreamLength !== null && upstreamLength !== expectedLength) ||\n (expectedRange && upstreamRange !== expectedRange)\n ) {\n void response.body?.cancel().catch(() => undefined)\n throw new Error('imagekitStorage: ImageKit returned mismatched file content metadata.')\n }\n\n const headers = new Headers(rangeResult.headers)\n headers.set('Content-Type', mimeType)\n if (isXmlMimeType(mimeType)) {\n headers.set('Content-Security-Policy', UPLOAD_CONTENT_SECURITY_POLICY)\n }\n\n return new Response(response.body, {\n headers,\n status: response.status,\n })\n }\n\n return ({ collection, prefix = getCollectionFolder(rootFolder, collection.slug) }) => {\n const isPrivate = privateSlugs.has(collection.slug)\n\n // Sign a relative ImageKit path against the configured urlEndpoint. We always sign\n // a computed path (never a stored absolute URL) because ImageKit's signature is\n // derived from the path after stripping urlEndpoint, so the host must match.\n const buildSignedURL = (src: string, tr?: null | string): string =>\n client.helper.buildSrc({\n expiresIn: signedUrlExpiresIn,\n signed: true,\n src,\n transformation: tr ? [{ raw: tr }] : undefined,\n urlEndpoint,\n })\n\n const buildThumbnailURL = (doc: UploadDocumentData): string =>\n isDocumentPrivate(doc, isPrivate)\n ? getPrivateAdminThumbnailURL(doc, collection.slug, apiRoute)\n : getAdminThumbnailURL(doc)\n\n return {\n name: 'imagekit',\n clientUploads,\n fields: [\n {\n // Presentational wrapper: tucks the ImageKit details into a closed panel without\n // changing the `imagekit.*` data paths.\n type: 'collapsible',\n admin: {\n condition: (data) => hasStoredImageKitURL(data),\n initCollapsed: true,\n },\n fields: [\n {\n name: 'imagekit',\n type: 'group',\n admin: {\n disableListColumn: true,\n disableListFilter: true,\n },\n fields: [\n {\n name: 'fileId',\n type: 'text',\n admin: {\n hidden: true,\n },\n },\n {\n name: 'url',\n type: 'text',\n admin: {\n readOnly: true,\n },\n label: 'URL',\n },\n {\n name: 'isPrivate',\n type: 'checkbox',\n admin: {\n condition: (_data, siblingData) =>\n isDocumentPrivate({ imagekit: siblingData }, isPrivate),\n description:\n \"Indicates whether this file was uploaded to ImageKit as private. Private files are delivered through signed URLs. Changes to this collection's private option in payload.config only affect future uploads.\",\n readOnly: true,\n },\n label: 'Private file',\n },\n {\n name: 'signedUrl',\n type: 'text',\n admin: {\n // Only relevant for private files; the plain `url` works directly for public ones.\n condition: (_data, siblingData) =>\n isDocumentPrivate({ imagekit: siblingData }, isPrivate),\n description: `Temporary signed URL for reaching this private file directly on ImageKit. Expires ${signedUrlExpiresIn} seconds after load and is regenerated on every read. Note: anyone with this URL can access the file until it expires — it is not gated by Payload access control.`,\n readOnly: true,\n },\n hooks: {\n // Computed server-side on read so it is always freshly signed and never persisted.\n afterRead: [\n ({ data, siblingData }) => {\n if (!isDocumentPrivate({ imagekit: siblingData }, isPrivate)) {\n return undefined\n }\n\n const filename = (data as { filename?: string } | undefined)?.filename\n\n if (!filename) {\n return undefined\n }\n\n const source = (data || {}) as { _objectKey?: string; prefix?: string }\n const documentPrefix = path.posix.join(\n source.prefix || prefix,\n source._objectKey || '',\n )\n const storageFilePath = buildStoragePathData({\n collectionPrefix: prefix,\n docPrefix: documentPrefix,\n filename,\n }).storageFilePath\n\n return buildSignedURL(`/${storageFilePath}`)\n },\n ],\n },\n label: 'Signed URL',\n virtual: true,\n },\n {\n name: 'thumbnailUrl',\n type: 'text',\n admin: {\n hidden: true,\n },\n },\n ],\n interfaceName: 'imagekitFields',\n label: false,\n },\n ],\n label: 'Storage: ImageKit',\n },\n ],\n generateURL: ({ filename, prefix: documentPrefix = '' }) =>\n client.helper.buildSrc({\n src: `/${buildStoragePathData({\n collectionPrefix: prefix,\n docPrefix: documentPrefix,\n filename,\n }).storageFilePath}`,\n urlEndpoint,\n }),\n handleDelete: async ({ doc, storageFilePath }) => {\n const storedFileId = (doc as ImageKitReference & typeof doc).imagekit?.fileId\n const fileId =\n storedFileId || (await findImageKitFileId(client, `/${storageFilePath.replace(/^\\/+/, '')}`))\n\n if (!fileId) {\n return\n }\n\n await client.files.delete(fileId)\n },\n handleUpload: async ({ clientUploadContext, data, file, storageFilePath }) => {\n if (clientUploadContext) {\n const uploaded = await verifyClientUpload({\n collectionSlug: collection.slug,\n context: clientUploadContext,\n })\n\n return applyVerifiedClientUploadToData(data, uploaded, buildThumbnailURL, isPrivate)\n }\n\n // ImageKit's `toFile()` consumes async iterables before starting the request. A file-backed\n // Blob keeps temporary-file uploads lazy so FormData can stream them from disk instead.\n const uploadSource = file.tempFilePath\n ? await openAsBlob(file.tempFilePath, { type: file.mimeType })\n : file.buffer\n const uploadFile = await toFile(uploadSource, file.filename, { type: file.mimeType })\n const uploadFolder = path.posix.dirname(`/${storageFilePath.replace(/^\\/+/, '')}`)\n const uploadFilename = path.posix.basename(storageFilePath)\n\n const uploaded = await client.files.upload({\n file: uploadFile,\n fileName: uploadFilename,\n folder: uploadFolder,\n useUniqueFileName: false,\n ...(isPrivate ? { isPrivateFile: true } : {}),\n })\n\n data.imagekit = {\n fileId: uploaded.fileId,\n isPrivate,\n thumbnailUrl: uploaded.thumbnailUrl,\n url: uploaded.url,\n }\n\n // Set the root-level thumbnailURL that Payload's admin interface expects.\n // `buildThumbnailURL` needs the filename, which Payload may not have copied onto\n // `data` yet at this point, so fall back to the uploaded file's name.\n data.thumbnailURL = buildThumbnailURL({\n ...data,\n filename: (data as { filename?: string }).filename || uploadFilename,\n })\n\n return data\n },\n requiresClientUploadReceipt: true,\n staticHandler: async (req, args) => {\n const requestURL = new URL(req.url || 'http://localhost')\n const tr = requestURL.searchParams.get('tr')\n const hasClientUploadContext = 'clientUploadContext' in args.params\n\n if (hasClientUploadContext) {\n const clientUploadContext = args.params.clientUploadContext\n\n if (!isImageKitReceiptContext(clientUploadContext)) {\n throw new APIError('Invalid ImageKit client upload context.', 400)\n }\n\n const uploadedFile = await verifyClientUpload({\n collectionSlug: collection.slug,\n context: clientUploadContext,\n })\n const expectedFilePath = getClientUploadStorageFilePath({\n collectionPrefix: prefix,\n context: clientUploadContext,\n })\n const verifiedFileURL = isPrivate\n ? buildSignedURL(`/${expectedFilePath}`, ORIGINAL_FILE_TRANSFORMATION)\n : client.helper.buildSrc({\n src: `/${expectedFilePath}`,\n transformation: [{ raw: ORIGINAL_FILE_TRANSFORMATION }],\n urlEndpoint,\n })\n\n return fetchUploadedFile({\n fileSize: uploadedFile.size,\n fileURL: verifiedFileURL,\n mimeType: uploadedFile.mimeType,\n range: req.headers.get('Range'),\n })\n }\n\n const doc =\n args.doc ||\n (await findUploadDocumentByFilename({\n collection: args.params.collection,\n filename: args.params.filename,\n prefix: args.params.prefix,\n req,\n }))\n\n const storageFilePath = await getStorageFilePath({\n collection,\n collectionPrefix: prefix,\n doc: doc || undefined,\n filename: args.params.filename,\n prefixQueryParam: args.params.prefix,\n req,\n })\n const imageKitPath = `/${storageFilePath.replace(/^\\/+/, '')}`\n const mimeType = (doc as { mimeType?: string } | null)?.mimeType\n\n // XML-family uploads (including SVG) must pass through Payload so its CSP is\n // present even when ImageKit uses a custom, same-origin delivery domain.\n if (typeof mimeType === 'string' && isXmlMimeType(mimeType)) {\n const filesize = (doc as { filesize?: number } | null)?.filesize\n\n if (typeof filesize !== 'number' || !Number.isSafeInteger(filesize) || filesize < 0) {\n throw new APIError('Stored ImageKit file metadata is incomplete.', 500)\n }\n\n const fileURL = isDocumentPrivate(doc, isPrivate)\n ? buildSignedURL(imageKitPath, ORIGINAL_FILE_TRANSFORMATION)\n : client.helper.buildSrc({\n src: imageKitPath,\n transformation: [{ raw: ORIGINAL_FILE_TRANSFORMATION }],\n urlEndpoint,\n })\n\n return fetchUploadedFile({\n fileSize: filesize,\n fileURL,\n mimeType,\n range: req.headers.get('Range'),\n })\n }\n\n // Sign based on the document's stored status, not the collection's current\n // setting, so files keep working if the collection's privacy is changed later.\n if (isDocumentPrivate(doc, isPrivate)) {\n let signSrc = imageKitPath\n\n // A `tr` request for a video/PDF is a thumbnail request (the plain transform\n // can't render a preview); serve ImageKit's generated thumbnail instead.\n if (tr && isVideoMimeType(mimeType)) {\n signSrc = getVideoPosterSrc(signSrc)\n } else if (tr && isPdfMimeType(mimeType)) {\n signSrc = getImageKitThumbnailSrc(signSrc)\n }\n\n return Response.redirect(buildSignedURL(signSrc, tr), 302)\n }\n\n const imageKitURL = client.helper.buildSrc({\n src:\n getStoredImageKitURL(doc) ||\n imageKitPath,\n transformation: tr ? [{ raw: tr }] : undefined,\n urlEndpoint,\n })\n\n return Response.redirect(imageKitURL, 302)\n },\n }\n }\n}\n\nconst toCloudStorageCollections = (\n collections: ImageKitCollectionMap,\n adapter: Adapter,\n privateSlugs: Set<string>,\n rootFolder: string,\n disablePayloadAccessControl?: true,\n): Partial<Record<CollectionSlug, CollectionOptions>> => {\n const mappedCollections: Partial<Record<CollectionSlug, CollectionOptions>> = {}\n\n for (const collectionSlug of Object.keys(collections)) {\n mappedCollections[collectionSlug] = {\n adapter,\n disableLocalStorage: true,\n // Private files require Payload access control so URLs can be signed at serve time.\n disablePayloadAccessControl: privateSlugs.has(collectionSlug)\n ? undefined\n : disablePayloadAccessControl,\n prefix: getCollectionFolder(rootFolder, collectionSlug),\n }\n }\n\n return mappedCollections\n}\n\nexport const imagekitStorage =\n (pluginOptions: PayloadStorageImageKitConfig) =>\n (config: Config): Config => {\n const {\n alwaysInsertFields,\n clientUploads,\n collections,\n disablePayloadAccessControl,\n enabled,\n focalPoint,\n folder,\n privateKey,\n publicKey,\n signedUrlExpiresIn: signedUrlExpiresInOption,\n urlEndpoint,\n } = pluginOptions\n\n if (!privateKey?.trim()) {\n throw new Error('imagekitStorage: `privateKey` is required.')\n }\n\n if (!publicKey?.trim()) {\n throw new Error('imagekitStorage: `publicKey` is required.')\n }\n\n if (!urlEndpoint?.trim()) {\n throw new Error('imagekitStorage: `urlEndpoint` is required.')\n }\n\n if (!collections || Object.keys(collections).length === 0) {\n throw new Error('imagekitStorage: `collections` must include at least one upload collection.')\n }\n\n const privateSlugs = new Set(\n Object.entries(collections)\n .filter(([, value]) => isPrivateCollectionConfig(value))\n .map(([slug]) => slug),\n )\n const configuredSlugs = new Set(Object.keys(collections))\n\n if (disablePayloadAccessControl && privateSlugs.size > 0) {\n throw new Error(\n 'imagekitStorage: `disablePayloadAccessControl` cannot be used with private collections. Private files require Payload access control to remain enabled so URLs can be signed at serve time.',\n )\n }\n\n if (disablePayloadAccessControl) {\n const xmlCollectionSlugs = (config.collections || [])\n .filter(\n (collection) =>\n configuredSlugs.has(collection.slug) && collectionCanStoreXml(collection),\n )\n .map((collection) => collection.slug)\n\n if (xmlCollectionSlugs.length > 0) {\n throw new Error(\n `imagekitStorage: \\`disablePayloadAccessControl\\` cannot be used with collections that can store XML-family files (${xmlCollectionSlugs.join(', ')}). XML-family files require Payload's file route so its Content Security Policy is applied. Configure a MIME allow list that excludes SVG/XML, or keep Payload access control enabled.`,\n )\n }\n }\n\n const signedUrlExpiresIn =\n typeof signedUrlExpiresInOption === 'number' && signedUrlExpiresInOption > 0\n ? signedUrlExpiresInOption\n : DEFAULT_SIGNED_URL_EXPIRES_IN\n const apiRoute = config.routes?.api ?? '/api'\n\n const rootFolder = normalizeFolder(folder)\n\n const imageKitClient = new ImageKit({ privateKey })\n const collectionSecurity = Object.fromEntries(\n (config.collections || [])\n .filter((collection) => configuredSlugs.has(collection.slug))\n .map((collection) => [\n collection.slug,\n {\n collection,\n isPrivate: privateSlugs.has(collection.slug),\n prefix: getCollectionFolder(rootFolder, collection.slug),\n },\n ]),\n )\n const verifyClientUpload = createImageKitClientUploadVerifier({\n client: imageKitClient,\n collections: collectionSecurity,\n })\n const adapter = imagekitAdapter({\n apiRoute,\n client: imageKitClient,\n clientUploads,\n privateSlugs,\n rootFolder,\n signedUrlExpiresIn,\n urlEndpoint,\n verifyClientUpload,\n })\n\n const configWithCropDisabled: Config = {\n ...config,\n collections: (config.collections || []).map((collection) => {\n if (!configuredSlugs.has(collection.slug)) {\n return collection\n }\n\n const isCollectionPrivate = privateSlugs.has(collection.slug)\n const buildThumbnailURL = (doc: unknown): string =>\n isDocumentPrivate(doc, isCollectionPrivate)\n ? getPrivateAdminThumbnailURL(doc, collection.slug, apiRoute)\n : getAdminThumbnailURL(doc)\n\n const collectionOptions = collections[collection.slug]\n const collectionFocalPoint =\n collectionOptions !== true &&\n typeof collectionOptions === 'object' &&\n typeof collectionOptions.focalPoint === 'boolean'\n ? collectionOptions.focalPoint\n : undefined\n\n // Surface a \"this is a private collection\" note in the list and edit views.\n // Preserve an existing string description by appending; leave custom\n // component descriptions untouched.\n const existingDescription = collection.admin?.description\n const baseAdmin = isCollectionPrivate\n ? {\n ...(collection.admin || {}),\n description:\n typeof existingDescription === 'string' && existingDescription.trim().length > 0\n ? `${existingDescription} ${PRIVATE_COLLECTION_DESCRIPTION}`\n : (existingDescription ?? PRIVATE_COLLECTION_DESCRIPTION),\n }\n : collection.admin\n\n return {\n ...collection,\n admin: baseAdmin,\n hooks: {\n ...(collection.hooks || {}),\n beforeChange: [\n ...(collection.hooks?.beforeChange || []),\n async ({ data, req }) => {\n if (!isRecord(data) || !req.file?.clientUploadContext) {\n return data\n }\n\n const context = req.file.clientUploadContext\n const requestMimeType = req.file.mimetype.split(';', 1)[0]?.trim().toLowerCase()\n if (\n !isImageKitReceiptContext(context) ||\n context.filename !== data.filename ||\n context.filename !== req.file.name ||\n context.expectedMimeType !== requestMimeType ||\n // Payload can clear req.file.size when preparing an in-memory image.\n // generateFileData preserves the original size on the document.\n context.expectedSize !== data.filesize\n ) {\n throw new APIError('ImageKit client upload reference does not match this request.', 400)\n }\n\n const uploaded = await verifyClientUpload({\n collectionSlug: collection.slug,\n context,\n })\n // A provider file can only be claimed once, including through legacy receipts.\n // The unique file ID index also rejects concurrent claims.\n const existing = await req.payload.db.findOne({\n collection: collection.slug,\n req,\n where: {\n 'imagekit.fileId': {\n equals: uploaded.fileId,\n },\n },\n })\n if (existing) {\n throw new APIError('This ImageKit client upload receipt has already been used.', 400)\n }\n\n data.filename = uploaded.name\n data.filesize = uploaded.size\n data.mimeType = uploaded.mimeType\n\n return applyVerifiedClientUploadToData(\n data,\n uploaded,\n buildThumbnailURL,\n isCollectionPrivate,\n )\n },\n ],\n beforeOperation: [\n ...(collection.hooks?.beforeOperation || []),\n ({ args, operation, req }) => {\n if (\n (operation !== 'create' && operation !== 'update') ||\n req.context?.skipCloudStorage ||\n !req.file ||\n req.file.clientUploadContext ||\n !('data' in args) ||\n !isRecord(args.data) ||\n typeof args.data.prefix !== 'string'\n ) {\n return args\n }\n\n // Payload checks filename uniqueness before cloud storage's beforeChange\n // normalizes the prefix. Use the persisted form during that lookup too.\n args.data.prefix = buildUploadStoragePathData({\n collectionPrefix: getCollectionFolder(rootFolder, collection.slug),\n docPrefix: args.data.prefix,\n filename: req.file.name,\n }).sanitizedDocPrefix\n return args\n },\n ],\n },\n upload: {\n // Focal points are opt-in for configured collections. Plugin-level\n // and collection-level options can override this default; a\n // collection's own `upload.focalPoint` still wins because it is\n // spread in afterwards.\n focalPoint: false,\n ...(typeof focalPoint === 'boolean' ? { focalPoint } : {}),\n ...(typeof collectionFocalPoint === 'boolean'\n ? { focalPoint: collectionFocalPoint }\n : {}),\n ...(typeof collection.upload === 'object' ? collection.upload : {}),\n adminThumbnail: ({ doc }) => buildThumbnailURL(doc),\n crop: false,\n },\n }\n }),\n }\n\n const uploadCollections = Object.fromEntries(\n Object.keys(collections).map((collectionSlug) => [\n collectionSlug,\n {\n isPrivate: privateSlugs.has(collectionSlug),\n prefix: getCollectionFolder(rootFolder, collectionSlug),\n },\n ]),\n ) as Partial<\n Record<UploadCollectionSlug, { isPrivate: boolean; prefix: string }>\n >\n\n initClientUploads({\n clientHandler: '@humaan/payload-storage-imagekit/client#ImageKitUploadHandler',\n collections: uploadCollections,\n config: configWithCropDisabled,\n enabled: enabled !== false && Boolean(clientUploads),\n serverHandler: createImageKitUploadAuthHandler({\n access: typeof clientUploads === 'object' ? clientUploads.access : undefined,\n collections: uploadCollections,\n privateKey,\n publicKey,\n verifyClientUpload,\n }),\n serverHandlerPath: imageKitUploadAuthPath,\n })\n\n const output = cloudStoragePlugin({\n alwaysInsertFields,\n collections: toCloudStorageCollections(\n collections,\n adapter,\n privateSlugs,\n rootFolder,\n disablePayloadAccessControl,\n ),\n enabled,\n })(configWithCropDisabled)\n\n // Make the provider identity unique at persistence time to reject concurrent receipt replays.\n // Keep the legacy object key index for existing uploads.\n output.collections = (output.collections || []).map((collection) => {\n if (!configuredSlugs.has(collection.slug)) {\n return collection\n }\n\n const withUniqueIdentity = (field: Field): Field => {\n if ('name' in field && field.name === '_objectKey') {\n return { ...field, unique: true } as Field\n }\n if (field.type === 'group' && 'name' in field && field.name === 'imagekit') {\n return {\n ...field,\n fields: field.fields.map((metadataField) =>\n 'name' in metadataField && metadataField.name === 'fileId'\n ? { ...metadataField, unique: true }\n : metadataField,\n ),\n }\n }\n // The imagekit group sits inside the adapter's presentational \"Storage: ImageKit\" panel.\n if (field.type === 'collapsible') {\n return { ...field, fields: field.fields.map(withUniqueIdentity) }\n }\n return field\n }\n\n return {\n ...collection,\n fields: collection.fields.map(withUniqueIdentity),\n }\n })\n\n return output\n }\n\nexport { migrateFolder } from './migrate.js'\nexport type { MigrateFolderOptions, MigrationJobResult } from './migrate.js'\n\n// Backward-compatible alias\nexport const payloadStorageImagekit = imagekitStorage\nexport const payloadStorageImageKit = imagekitStorage\n\nexport type PayloadStorageImagekitConfig = PayloadStorageImageKitConfig\n"],"names":["ImageKit","toFile","cloudStoragePlugin","buildStoragePathData","buildUploadStoragePathData","getStorageFilePath","initClientUploads","openAsBlob","path","APIError","getRangeRequestInfo","isXmlMimeType","UPLOAD_CONTENT_SECURITY_POLICY","validateMimeType","imageKitUploadAuthPath","isImageKitReceiptContext","createImageKitClientUploadVerifier","getClientUploadStorageFilePath","createImageKitUploadAuthHandler","getAdminThumbnailURL","getPrivateAdminThumbnailURL","isPdfMimeType","isVideoMimeType","getImageKitThumbnailSrc","getVideoPosterSrc","DEFAULT_SIGNED_URL_EXPIRES_IN","ORIGINAL_FILE_TRANSFORMATION","PRIVATE_COLLECTION_DESCRIPTION","isPrivateCollectionConfig","value","private","XML_FAMILY_MIME_TYPES","collectionCanStoreXml","collection","upload","Boolean","allowRestrictedFileTypes","allowedMimeTypes","mimeTypes","length","some","mimeType","includes","isDocumentPrivate","doc","collectionIsPrivate","stored","imagekit","isPrivate","normalizeFolder","folder","normalized","trim","replace","Error","getCollectionFolder","rootFolder","collectionSlug","posix","join","findImageKitFileId","filePath","parentPath","dirname","name","basename","limit","skip","assets","list","type","found","find","asset","fileId","undefined","isRecord","getStoredImageKitURL","storedURL","url","hasStoredImageKitURL","findUploadDocumentByFilename","filename","prefix","req","constraints","equals","push","payload","db","findOne","where","and","applyVerifiedClientUploadToData","data","uploaded","buildThumbnailURL","thumbnailUrl","thumbnailURL","imagekitAdapter","apiRoute","client","clientUploads","privateSlugs","signedUrlExpiresIn","urlEndpoint","verifyClientUpload","fetchUploadedFile","fileSize","fileURL","range","rangeResult","rangeHeader","Response","headers","Headers","status","normalizedRange","rangeStart","rangeEnd","response","fetch","cache","Range","signal","AbortController","ok","body","cancel","catch","upstreamLength","get","expectedLength","upstreamRange","expectedRange","set","slug","has","buildSignedURL","src","tr","helper","buildSrc","expiresIn","signed","transformation","raw","fields","admin","condition","initCollapsed","disableListColumn","disableListFilter","hidden","readOnly","label","_data","siblingData","description","hooks","afterRead","source","documentPrefix","_objectKey","storageFilePath","collectionPrefix","docPrefix","virtual","interfaceName","generateURL","handleDelete","storedFileId","files","delete","handleUpload","clientUploadContext","file","context","uploadSource","tempFilePath","buffer","uploadFile","uploadFolder","uploadFilename","fileName","useUniqueFileName","isPrivateFile","requiresClientUploadReceipt","staticHandler","args","requestURL","URL","searchParams","hasClientUploadContext","params","uploadedFile","expectedFilePath","verifiedFileURL","size","prefixQueryParam","imageKitPath","filesize","Number","isSafeInteger","signSrc","redirect","imageKitURL","toCloudStorageCollections","collections","adapter","disablePayloadAccessControl","mappedCollections","Object","keys","disableLocalStorage","imagekitStorage","pluginOptions","config","alwaysInsertFields","enabled","focalPoint","privateKey","publicKey","signedUrlExpiresInOption","Set","entries","filter","map","configuredSlugs","xmlCollectionSlugs","routes","api","imageKitClient","collectionSecurity","fromEntries","configWithCropDisabled","isCollectionPrivate","collectionOptions","collectionFocalPoint","existingDescription","baseAdmin","beforeChange","requestMimeType","mimetype","split","toLowerCase","expectedMimeType","expectedSize","existing","beforeOperation","operation","skipCloudStorage","sanitizedDocPrefix","adminThumbnail","crop","uploadCollections","clientHandler","serverHandler","access","serverHandlerPath","output","withUniqueIdentity","field","unique","metadataField","migrateFolder","payloadStorageImagekit","payloadStorageImageKit"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type {\n Adapter,\n ClientUploadsConfig,\n CollectionOptions,\n} from '@payloadcms/plugin-cloud-storage/types'\nimport type {\n CollectionConfig,\n CollectionSlug,\n Config,\n Field,\n PayloadRequest,\n TypeWithID,\n UploadCollectionSlug,\n Where,\n} from 'payload'\n\nimport ImageKit, { toFile } from '@imagekit/nodejs'\nimport { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage'\nimport {\n buildStoragePathData,\n buildUploadStoragePathData,\n getStorageFilePath,\n initClientUploads,\n} from '@payloadcms/plugin-cloud-storage/utilities'\nimport { openAsBlob } from 'node:fs'\nimport path from 'node:path'\nimport { APIError } from 'payload'\nimport {\n getRangeRequestInfo,\n isXmlMimeType,\n UPLOAD_CONTENT_SECURITY_POLICY,\n} from 'payload/internal'\nimport { validateMimeType } from 'payload/shared'\n\nimport type { VerifiedImageKitUpload } from './clientUploadVerification.js'\n\nimport { imageKitUploadAuthPath, isImageKitReceiptContext } from './clientUploads.js'\nimport {\n createImageKitClientUploadVerifier,\n getClientUploadStorageFilePath,\n} from './clientUploadVerification.js'\nimport { createImageKitUploadAuthHandler } from './endpoints/createImageKitUploadAuthHandler.js'\nimport {\n getAdminThumbnailURL,\n getPrivateAdminThumbnailURL,\n isPdfMimeType,\n isVideoMimeType,\n} from './getAdminThumbnailURL.js'\nimport { getImageKitThumbnailSrc, getVideoPosterSrc } from './imageTransforms.js'\n\nexport type ImageKitCollectionOptions = {\n /**\n * Override Payload's native focal-point picker (`upload.focalPoint`) for this\n * collection.\n */\n focalPoint?: boolean\n /**\n * Upload files in this collection as ImageKit private files (`isPrivateFile: true`)\n * and deliver them only through short-lived signed URLs, behind Payload access control.\n */\n private?: boolean\n}\ntype ImageKitCollectionConfig = ImageKitCollectionOptions | true\ntype ImageKitCollectionMap = Partial<Record<UploadCollectionSlug, ImageKitCollectionConfig>>\n\nconst DEFAULT_SIGNED_URL_EXPIRES_IN = 900\n/**\n * ImageKit re-encodes some uploads and serves the smaller derivative from the plain\n * delivery URL — a 1.9 MB MP4 can come back as 868 KB. That is exactly what we want\n * for visitors, but not when reading the file back to verify a client upload: the\n * bytes and the byte count would both belong to a file ImageKit's API never reported.\n * `orig-true` asks for the untouched original and does not disable optimisation for\n * anyone else.\n */\nconst ORIGINAL_FILE_TRANSFORMATION = 'orig-true'\n\nconst PRIVATE_COLLECTION_DESCRIPTION =\n 'Files in this collection are uploaded to ImageKit as private files and delivered through short-lived signed URLs.'\n\nconst isPrivateCollectionConfig = (value: ImageKitCollectionConfig | undefined): boolean =>\n value !== true && typeof value === 'object' && value?.private === true\n\nconst XML_FAMILY_MIME_TYPES = ['application/xml', 'image/svg+xml', 'text/xml']\n\nconst collectionCanStoreXml = (collection: CollectionConfig): boolean => {\n const upload = collection.upload\n\n if (!upload || upload === true) {\n return Boolean(upload)\n }\n\n // Payload skips MIME and content restrictions when this escape hatch is enabled.\n if (upload.allowRestrictedFileTypes) {\n return true\n }\n\n const allowedMimeTypes = upload.mimeTypes\n\n return (\n !allowedMimeTypes?.length ||\n allowedMimeTypes.some((mimeType) => mimeType.includes('*') || isXmlMimeType(mimeType)) ||\n XML_FAMILY_MIME_TYPES.some((mimeType) => validateMimeType(mimeType, allowedMimeTypes))\n )\n}\n\n/**\n * Whether a specific document is private. The flag stored on the document at upload time\n * (`imagekit.isPrivate`) is the source of truth — a file's privacy on ImageKit is fixed\n * when it is uploaded and does not change if the collection's `private` setting changes\n * later. Falls back to the collection setting for documents uploaded before this flag existed.\n */\nconst isDocumentPrivate = (doc: unknown, collectionIsPrivate: boolean): boolean => {\n const stored = (doc as { imagekit?: { isPrivate?: boolean } } | undefined)?.imagekit?.isPrivate\n\n return typeof stored === 'boolean' ? stored : collectionIsPrivate\n}\ntype ImageKitReference = {\n imagekit?: {\n fileId?: string\n isPrivate?: boolean\n thumbnailUrl?: string\n url?: string\n }\n}\n\ntype UploadDocumentData = {\n thumbnailURL?: string\n} & ImageKitReference\n\nexport type PayloadStorageImageKitConfig = {\n alwaysInsertFields?: boolean\n clientUploads?: ClientUploadsConfig\n collections: ImageKitCollectionMap\n disablePayloadAccessControl?: true\n enabled?: boolean\n /**\n * Broad default for Payload's native focal-point picker (`upload.focalPoint`)\n * on every configured collection. Focal points are disabled by default for\n * configured collections, so prefer collection-level `focalPoint: true` for\n * image collections. A collection's own `upload.focalPoint` still takes\n * precedence.\n */\n focalPoint?: boolean\n folder: string\n privateKey: string\n publicKey: string\n /**\n * Expiry (in seconds) applied to signed URLs generated for private collections.\n * Defaults to 900 (15 minutes). URLs are signed fresh on every request, so a short\n * window is safe.\n */\n signedUrlExpiresIn?: number\n urlEndpoint: string\n}\n\nconst normalizeFolder = (folder: string): string => {\n const normalized = folder.trim().replace(/^\\/+|\\/+$/g, '')\n\n if (!normalized) {\n throw new Error('imagekitStorage: `folder` is required.')\n }\n\n return `/${normalized}`\n}\n\nconst getCollectionFolder = (rootFolder: string, collectionSlug: string): string =>\n path.posix.join(rootFolder, collectionSlug)\n\nconst findImageKitFileId = async (\n imagekit: ImageKit,\n filePath: string,\n): Promise<string | undefined> => {\n const parentPath = path.posix.dirname(filePath)\n const name = path.posix.basename(filePath)\n\n const limit = 100\n\n for (let skip = 0; ; skip += limit) {\n const assets = await imagekit.assets.list({\n type: 'file',\n limit,\n path: parentPath === '/' ? '/' : `${parentPath}/`,\n skip,\n })\n\n const found = assets.find(\n (asset) => 'fileId' in asset && asset.filePath === filePath && asset.name === name,\n )\n\n if (found && 'fileId' in found) {\n return found.fileId\n }\n\n if (assets.length < limit) {\n break\n }\n }\n\n return undefined\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n !!value && typeof value === 'object'\n\nconst getStoredImageKitURL = (doc: unknown): string | undefined => {\n const storedURL = (doc as ImageKitReference | undefined)?.imagekit?.url\n\n return typeof storedURL === 'string' && storedURL.trim().length > 0 ? storedURL : undefined\n}\n\nconst hasStoredImageKitURL = (doc: unknown): boolean => !!getStoredImageKitURL(doc)\n\nconst findUploadDocumentByFilename = async ({\n collection,\n filename,\n prefix,\n req,\n}: {\n collection: string\n filename: string\n prefix?: string\n req: PayloadRequest\n}): Promise<null | TypeWithID> => {\n const constraints: Where[] = [\n {\n filename: {\n equals: filename,\n },\n },\n ]\n\n if (typeof prefix === 'string') {\n constraints.push({\n prefix: {\n equals: prefix,\n },\n })\n }\n\n return req.payload.db.findOne({\n collection,\n req,\n where: {\n and: constraints,\n },\n })\n}\n\nconst applyVerifiedClientUploadToData = <T extends UploadDocumentData>(\n data: T,\n uploaded: VerifiedImageKitUpload,\n buildThumbnailURL: (data: T) => string = getAdminThumbnailURL,\n isPrivate: boolean,\n): T => {\n data.imagekit = {\n fileId: uploaded.fileId,\n isPrivate,\n thumbnailUrl: uploaded.thumbnailUrl,\n url: uploaded.url,\n }\n\n // Preserve the admin preview when the original file was already uploaded client-side.\n data.thumbnailURL = buildThumbnailURL(data)\n\n return data\n}\n\nconst imagekitAdapter = ({\n apiRoute,\n client,\n clientUploads,\n privateSlugs,\n rootFolder,\n signedUrlExpiresIn,\n urlEndpoint,\n verifyClientUpload,\n}: {\n apiRoute: string\n client: ImageKit\n clientUploads?: ClientUploadsConfig\n privateSlugs: Set<string>\n rootFolder: string\n signedUrlExpiresIn: number\n urlEndpoint: string\n verifyClientUpload: ReturnType<typeof createImageKitClientUploadVerifier>\n}): Adapter => {\n const fetchUploadedFile = async ({\n fileSize,\n fileURL,\n mimeType,\n range,\n }: {\n fileSize: number\n fileURL: string\n mimeType: string\n range?: null | string\n }): Promise<Response> => {\n const rangeResult = getRangeRequestInfo({ fileSize, rangeHeader: range ?? null })\n\n if (rangeResult.type === 'invalid') {\n return new Response(null, {\n headers: new Headers(rangeResult.headers),\n status: rangeResult.status,\n })\n }\n\n const normalizedRange =\n rangeResult.type === 'partial'\n ? `bytes=${rangeResult.rangeStart}-${rangeResult.rangeEnd}`\n : undefined\n // Payload cancels image probes after reading a bounded prefix. Next.js memoizes\n // GET responses by teeing their streams; cancelling one branch can then wait\n // forever for its unread cached sibling. An explicit signal opts out of that\n // memoization, while no-store also excludes these reads from the data cache.\n const response = await fetch(fileURL, {\n cache: 'no-store',\n ...(normalizedRange ? { headers: { Range: normalizedRange } } : {}),\n signal: new AbortController().signal,\n })\n\n if (!response.ok || response.status !== rangeResult.status) {\n void response.body?.cancel().catch(() => undefined)\n throw new Error(\n `imagekitStorage: ImageKit returned an unexpected file response (${response.status}).`,\n )\n }\n\n const upstreamLength = response.headers.get('Content-Length')\n const expectedLength = rangeResult.headers['Content-Length']\n const upstreamRange = response.headers.get('Content-Range')\n const expectedRange =\n rangeResult.type === 'partial' ? rangeResult.headers['Content-Range'] : undefined\n\n if (\n (upstreamLength !== null && upstreamLength !== expectedLength) ||\n (expectedRange && upstreamRange !== expectedRange)\n ) {\n void response.body?.cancel().catch(() => undefined)\n throw new Error('imagekitStorage: ImageKit returned mismatched file content metadata.')\n }\n\n const headers = new Headers(rangeResult.headers)\n headers.set('Content-Type', mimeType)\n if (isXmlMimeType(mimeType)) {\n headers.set('Content-Security-Policy', UPLOAD_CONTENT_SECURITY_POLICY)\n }\n\n return new Response(response.body, {\n headers,\n status: response.status,\n })\n }\n\n return ({ collection, prefix = getCollectionFolder(rootFolder, collection.slug) }) => {\n const isPrivate = privateSlugs.has(collection.slug)\n\n // Sign a relative ImageKit path against the configured urlEndpoint. We always sign\n // a computed path (never a stored absolute URL) because ImageKit's signature is\n // derived from the path after stripping urlEndpoint, so the host must match.\n const buildSignedURL = (src: string, tr?: null | string): string =>\n client.helper.buildSrc({\n expiresIn: signedUrlExpiresIn,\n signed: true,\n src,\n transformation: tr ? [{ raw: tr }] : undefined,\n urlEndpoint,\n })\n\n const buildThumbnailURL = (doc: UploadDocumentData): string =>\n isDocumentPrivate(doc, isPrivate)\n ? getPrivateAdminThumbnailURL(doc, collection.slug, apiRoute)\n : getAdminThumbnailURL(doc)\n\n return {\n name: 'imagekit',\n clientUploads,\n fields: [\n {\n // Presentational wrapper: tucks the ImageKit details into a closed panel without\n // changing the `imagekit.*` data paths.\n type: 'collapsible',\n admin: {\n condition: (data) => hasStoredImageKitURL(data),\n initCollapsed: true,\n },\n fields: [\n {\n name: 'imagekit',\n type: 'group',\n admin: {\n disableListColumn: true,\n disableListFilter: true,\n },\n fields: [\n {\n name: 'fileId',\n type: 'text',\n admin: {\n hidden: true,\n },\n },\n {\n name: 'url',\n type: 'text',\n admin: {\n readOnly: true,\n },\n label: 'URL',\n },\n {\n name: 'isPrivate',\n type: 'checkbox',\n admin: {\n condition: (_data, siblingData) =>\n isDocumentPrivate({ imagekit: siblingData }, isPrivate),\n description:\n \"Indicates whether this file was uploaded to ImageKit as private. Private files are delivered through signed URLs. Changes to this collection's private option in payload.config only affect future uploads.\",\n readOnly: true,\n },\n label: 'Private file',\n },\n {\n name: 'signedUrl',\n type: 'text',\n admin: {\n // Only relevant for private files; the plain `url` works directly for public ones.\n condition: (_data, siblingData) =>\n isDocumentPrivate({ imagekit: siblingData }, isPrivate),\n description: `Temporary signed URL for reaching this private file directly on ImageKit. Expires ${signedUrlExpiresIn} seconds after load and is regenerated on every read. Note: anyone with this URL can access the file until it expires — it is not gated by Payload access control.`,\n readOnly: true,\n },\n hooks: {\n // Computed server-side on read so it is always freshly signed and never persisted.\n afterRead: [\n ({ data, siblingData }) => {\n if (!isDocumentPrivate({ imagekit: siblingData }, isPrivate)) {\n return undefined\n }\n\n const filename = (data as { filename?: string } | undefined)?.filename\n\n if (!filename) {\n return undefined\n }\n\n const source = (data || {}) as { _objectKey?: string; prefix?: string }\n const documentPrefix = path.posix.join(\n source.prefix || prefix,\n source._objectKey || '',\n )\n const storageFilePath = buildStoragePathData({\n collectionPrefix: prefix,\n docPrefix: documentPrefix,\n filename,\n }).storageFilePath\n\n return buildSignedURL(`/${storageFilePath}`)\n },\n ],\n },\n label: 'Signed URL',\n virtual: true,\n },\n {\n name: 'thumbnailUrl',\n type: 'text',\n admin: {\n hidden: true,\n },\n },\n ],\n interfaceName: 'imagekitFields',\n label: false,\n },\n ],\n label: 'Storage: ImageKit',\n },\n ],\n generateURL: ({ filename, prefix: documentPrefix = '' }) =>\n client.helper.buildSrc({\n src: `/${buildStoragePathData({\n collectionPrefix: prefix,\n docPrefix: documentPrefix,\n filename,\n }).storageFilePath}`,\n urlEndpoint,\n }),\n handleDelete: async ({ doc, storageFilePath }) => {\n const storedFileId = (doc as ImageKitReference & typeof doc).imagekit?.fileId\n const fileId =\n storedFileId || (await findImageKitFileId(client, `/${storageFilePath.replace(/^\\/+/, '')}`))\n\n if (!fileId) {\n return\n }\n\n await client.files.delete(fileId)\n },\n handleUpload: async ({ clientUploadContext, data, file, storageFilePath }) => {\n if (clientUploadContext) {\n const uploaded = await verifyClientUpload({\n collectionSlug: collection.slug,\n context: clientUploadContext,\n })\n\n return applyVerifiedClientUploadToData(data, uploaded, buildThumbnailURL, isPrivate)\n }\n\n // ImageKit's `toFile()` consumes async iterables before starting the request. A file-backed\n // Blob keeps temporary-file uploads lazy so FormData can stream them from disk instead.\n const uploadSource = file.tempFilePath\n ? await openAsBlob(file.tempFilePath, { type: file.mimeType })\n : file.buffer\n const uploadFile = await toFile(uploadSource, file.filename, { type: file.mimeType })\n const uploadFolder = path.posix.dirname(`/${storageFilePath.replace(/^\\/+/, '')}`)\n const uploadFilename = path.posix.basename(storageFilePath)\n\n const uploaded = await client.files.upload({\n file: uploadFile,\n fileName: uploadFilename,\n folder: uploadFolder,\n useUniqueFileName: false,\n ...(isPrivate ? { isPrivateFile: true } : {}),\n })\n\n data.imagekit = {\n fileId: uploaded.fileId,\n isPrivate,\n thumbnailUrl: uploaded.thumbnailUrl,\n url: uploaded.url,\n }\n\n // Set the root-level thumbnailURL that Payload's admin interface expects.\n // `buildThumbnailURL` needs the filename, which Payload may not have copied onto\n // `data` yet at this point, so fall back to the uploaded file's name.\n data.thumbnailURL = buildThumbnailURL({\n ...data,\n filename: (data as { filename?: string }).filename || uploadFilename,\n })\n\n return data\n },\n requiresClientUploadReceipt: true,\n staticHandler: async (req, args) => {\n const requestURL = new URL(req.url || 'http://localhost')\n const tr = requestURL.searchParams.get('tr')\n const hasClientUploadContext = 'clientUploadContext' in args.params\n\n if (hasClientUploadContext) {\n const clientUploadContext = args.params.clientUploadContext\n\n if (!isImageKitReceiptContext(clientUploadContext)) {\n throw new APIError('Invalid ImageKit client upload context.', 400)\n }\n\n const uploadedFile = await verifyClientUpload({\n collectionSlug: collection.slug,\n context: clientUploadContext,\n })\n const expectedFilePath = getClientUploadStorageFilePath({\n collectionPrefix: prefix,\n context: clientUploadContext,\n })\n const verifiedFileURL = isPrivate\n ? buildSignedURL(`/${expectedFilePath}`, ORIGINAL_FILE_TRANSFORMATION)\n : client.helper.buildSrc({\n src: `/${expectedFilePath}`,\n transformation: [{ raw: ORIGINAL_FILE_TRANSFORMATION }],\n urlEndpoint,\n })\n\n return fetchUploadedFile({\n fileSize: uploadedFile.size,\n fileURL: verifiedFileURL,\n mimeType: uploadedFile.mimeType,\n range: req.headers.get('Range'),\n })\n }\n\n const doc =\n args.doc ||\n (await findUploadDocumentByFilename({\n collection: args.params.collection,\n filename: args.params.filename,\n prefix: args.params.prefix,\n req,\n }))\n\n const storageFilePath = await getStorageFilePath({\n collection,\n collectionPrefix: prefix,\n doc: doc || undefined,\n filename: args.params.filename,\n prefixQueryParam: args.params.prefix,\n req,\n })\n const imageKitPath = `/${storageFilePath.replace(/^\\/+/, '')}`\n const mimeType = (doc as { mimeType?: string } | null)?.mimeType\n\n // XML-family uploads (including SVG) must pass through Payload so its CSP is\n // present even when ImageKit uses a custom, same-origin delivery domain.\n if (typeof mimeType === 'string' && isXmlMimeType(mimeType)) {\n const filesize = (doc as { filesize?: number } | null)?.filesize\n\n if (typeof filesize !== 'number' || !Number.isSafeInteger(filesize) || filesize < 0) {\n throw new APIError('Stored ImageKit file metadata is incomplete.', 500)\n }\n\n const fileURL = isDocumentPrivate(doc, isPrivate)\n ? buildSignedURL(imageKitPath, ORIGINAL_FILE_TRANSFORMATION)\n : client.helper.buildSrc({\n src: imageKitPath,\n transformation: [{ raw: ORIGINAL_FILE_TRANSFORMATION }],\n urlEndpoint,\n })\n\n return fetchUploadedFile({\n fileSize: filesize,\n fileURL,\n mimeType,\n range: req.headers.get('Range'),\n })\n }\n\n // Sign based on the document's stored status, not the collection's current\n // setting, so files keep working if the collection's privacy is changed later.\n if (isDocumentPrivate(doc, isPrivate)) {\n let signSrc = imageKitPath\n\n // A `tr` request for a video/PDF is a thumbnail request (the plain transform\n // can't render a preview); serve ImageKit's generated thumbnail instead.\n if (tr && isVideoMimeType(mimeType)) {\n signSrc = getVideoPosterSrc(signSrc)\n } else if (tr && isPdfMimeType(mimeType)) {\n signSrc = getImageKitThumbnailSrc(signSrc)\n }\n\n return Response.redirect(buildSignedURL(signSrc, tr), 302)\n }\n\n const imageKitURL = client.helper.buildSrc({\n src:\n getStoredImageKitURL(doc) ||\n imageKitPath,\n transformation: tr ? [{ raw: tr }] : undefined,\n urlEndpoint,\n })\n\n return Response.redirect(imageKitURL, 302)\n },\n }\n }\n}\n\nconst toCloudStorageCollections = (\n collections: ImageKitCollectionMap,\n adapter: Adapter,\n privateSlugs: Set<string>,\n rootFolder: string,\n disablePayloadAccessControl?: true,\n): Partial<Record<CollectionSlug, CollectionOptions>> => {\n const mappedCollections: Partial<Record<CollectionSlug, CollectionOptions>> = {}\n\n for (const collectionSlug of Object.keys(collections)) {\n mappedCollections[collectionSlug] = {\n adapter,\n disableLocalStorage: true,\n // Private files require Payload access control so URLs can be signed at serve time.\n disablePayloadAccessControl: privateSlugs.has(collectionSlug)\n ? undefined\n : disablePayloadAccessControl,\n prefix: getCollectionFolder(rootFolder, collectionSlug),\n }\n }\n\n return mappedCollections\n}\n\nexport const imagekitStorage =\n (pluginOptions: PayloadStorageImageKitConfig) =>\n (config: Config): Config => {\n const {\n alwaysInsertFields,\n clientUploads,\n collections,\n disablePayloadAccessControl,\n enabled,\n focalPoint,\n folder,\n privateKey,\n publicKey,\n signedUrlExpiresIn: signedUrlExpiresInOption,\n urlEndpoint,\n } = pluginOptions\n\n if (!privateKey?.trim()) {\n throw new Error('imagekitStorage: `privateKey` is required.')\n }\n\n if (!publicKey?.trim()) {\n throw new Error('imagekitStorage: `publicKey` is required.')\n }\n\n if (!urlEndpoint?.trim()) {\n throw new Error('imagekitStorage: `urlEndpoint` is required.')\n }\n\n if (!collections || Object.keys(collections).length === 0) {\n throw new Error('imagekitStorage: `collections` must include at least one upload collection.')\n }\n\n const privateSlugs = new Set(\n Object.entries(collections)\n .filter(([, value]) => isPrivateCollectionConfig(value))\n .map(([slug]) => slug),\n )\n const configuredSlugs = new Set(Object.keys(collections))\n\n if (disablePayloadAccessControl && privateSlugs.size > 0) {\n throw new Error(\n 'imagekitStorage: `disablePayloadAccessControl` cannot be used with private collections. Private files require Payload access control to remain enabled so URLs can be signed at serve time.',\n )\n }\n\n if (disablePayloadAccessControl) {\n const xmlCollectionSlugs = (config.collections || [])\n .filter(\n (collection) =>\n configuredSlugs.has(collection.slug) && collectionCanStoreXml(collection),\n )\n .map((collection) => collection.slug)\n\n if (xmlCollectionSlugs.length > 0) {\n throw new Error(\n `imagekitStorage: \\`disablePayloadAccessControl\\` cannot be used with collections that can store XML-family files (${xmlCollectionSlugs.join(', ')}). XML-family files require Payload's file route so its Content Security Policy is applied. Configure a MIME allow list that excludes SVG/XML, or keep Payload access control enabled.`,\n )\n }\n }\n\n const signedUrlExpiresIn =\n typeof signedUrlExpiresInOption === 'number' && signedUrlExpiresInOption > 0\n ? signedUrlExpiresInOption\n : DEFAULT_SIGNED_URL_EXPIRES_IN\n const apiRoute = config.routes?.api ?? '/api'\n\n const rootFolder = normalizeFolder(folder)\n\n const imageKitClient = new ImageKit({ privateKey })\n const collectionSecurity = Object.fromEntries(\n (config.collections || [])\n .filter((collection) => configuredSlugs.has(collection.slug))\n .map((collection) => [\n collection.slug,\n {\n collection,\n isPrivate: privateSlugs.has(collection.slug),\n prefix: getCollectionFolder(rootFolder, collection.slug),\n },\n ]),\n )\n const verifyClientUpload = createImageKitClientUploadVerifier({\n client: imageKitClient,\n collections: collectionSecurity,\n })\n const adapter = imagekitAdapter({\n apiRoute,\n client: imageKitClient,\n clientUploads,\n privateSlugs,\n rootFolder,\n signedUrlExpiresIn,\n urlEndpoint,\n verifyClientUpload,\n })\n\n const configWithCropDisabled: Config = {\n ...config,\n collections: (config.collections || []).map((collection) => {\n if (!configuredSlugs.has(collection.slug)) {\n return collection\n }\n\n const isCollectionPrivate = privateSlugs.has(collection.slug)\n const buildThumbnailURL = (doc: unknown): string =>\n isDocumentPrivate(doc, isCollectionPrivate)\n ? getPrivateAdminThumbnailURL(doc, collection.slug, apiRoute)\n : getAdminThumbnailURL(doc)\n\n const collectionOptions = collections[collection.slug]\n const collectionFocalPoint =\n collectionOptions !== true &&\n typeof collectionOptions === 'object' &&\n typeof collectionOptions.focalPoint === 'boolean'\n ? collectionOptions.focalPoint\n : undefined\n\n // Surface a \"this is a private collection\" note in the list and edit views.\n // Preserve an existing string description by appending; leave custom\n // component descriptions untouched.\n const existingDescription = collection.admin?.description\n const baseAdmin = isCollectionPrivate\n ? {\n ...(collection.admin || {}),\n description:\n typeof existingDescription === 'string' && existingDescription.trim().length > 0\n ? `${existingDescription} ${PRIVATE_COLLECTION_DESCRIPTION}`\n : (existingDescription ?? PRIVATE_COLLECTION_DESCRIPTION),\n }\n : collection.admin\n\n return {\n ...collection,\n admin: baseAdmin,\n hooks: {\n ...(collection.hooks || {}),\n beforeChange: [\n ...(collection.hooks?.beforeChange || []),\n async ({ data, req }) => {\n if (!isRecord(data) || !req.file?.clientUploadContext) {\n return data\n }\n\n const context = req.file.clientUploadContext\n const requestMimeType = req.file.mimetype.split(';', 1)[0]?.trim().toLowerCase()\n if (\n !isImageKitReceiptContext(context) ||\n context.filename !== data.filename ||\n context.filename !== req.file.name ||\n // Payload can clear req.file.size when preparing an in-memory image.\n // generateFileData preserves the original size on the document.\n context.expectedSize !== data.filesize\n ) {\n throw new APIError('ImageKit client upload reference does not match this request.', 400)\n }\n\n const uploaded = await verifyClientUpload({\n collectionSlug: collection.slug,\n context,\n })\n // Payload keeps the browser's MIME unless it fetched the file content, in which\n // case it uses the verified ImageKit-detected type the static handler served.\n if (\n requestMimeType !== context.expectedMimeType &&\n requestMimeType !== uploaded.mimeType\n ) {\n throw new APIError('ImageKit client upload reference does not match this request.', 400)\n }\n // A provider file can only be claimed once, including through legacy receipts.\n // The unique file ID index also rejects concurrent claims.\n const existing = await req.payload.db.findOne({\n collection: collection.slug,\n req,\n where: {\n 'imagekit.fileId': {\n equals: uploaded.fileId,\n },\n },\n })\n if (existing) {\n throw new APIError('This ImageKit client upload receipt has already been used.', 400)\n }\n\n data.filename = uploaded.name\n data.filesize = uploaded.size\n data.mimeType = uploaded.mimeType\n\n return applyVerifiedClientUploadToData(\n data,\n uploaded,\n buildThumbnailURL,\n isCollectionPrivate,\n )\n },\n ],\n beforeOperation: [\n ...(collection.hooks?.beforeOperation || []),\n ({ args, operation, req }) => {\n if (\n (operation !== 'create' && operation !== 'update') ||\n req.context?.skipCloudStorage ||\n !req.file ||\n req.file.clientUploadContext ||\n !('data' in args) ||\n !isRecord(args.data) ||\n typeof args.data.prefix !== 'string'\n ) {\n return args\n }\n\n // Payload checks filename uniqueness before cloud storage's beforeChange\n // normalizes the prefix. Use the persisted form during that lookup too.\n args.data.prefix = buildUploadStoragePathData({\n collectionPrefix: getCollectionFolder(rootFolder, collection.slug),\n docPrefix: args.data.prefix,\n filename: req.file.name,\n }).sanitizedDocPrefix\n return args\n },\n ],\n },\n upload: {\n // Focal points are opt-in for configured collections. Plugin-level\n // and collection-level options can override this default; a\n // collection's own `upload.focalPoint` still wins because it is\n // spread in afterwards.\n focalPoint: false,\n ...(typeof focalPoint === 'boolean' ? { focalPoint } : {}),\n ...(typeof collectionFocalPoint === 'boolean'\n ? { focalPoint: collectionFocalPoint }\n : {}),\n ...(typeof collection.upload === 'object' ? collection.upload : {}),\n adminThumbnail: ({ doc }) => buildThumbnailURL(doc),\n crop: false,\n },\n }\n }),\n }\n\n const uploadCollections = Object.fromEntries(\n Object.keys(collections).map((collectionSlug) => [\n collectionSlug,\n {\n isPrivate: privateSlugs.has(collectionSlug),\n prefix: getCollectionFolder(rootFolder, collectionSlug),\n },\n ]),\n ) as Partial<\n Record<UploadCollectionSlug, { isPrivate: boolean; prefix: string }>\n >\n\n initClientUploads({\n clientHandler: '@humaan/payload-storage-imagekit/client#ImageKitUploadHandler',\n collections: uploadCollections,\n config: configWithCropDisabled,\n enabled: enabled !== false && Boolean(clientUploads),\n serverHandler: createImageKitUploadAuthHandler({\n access: typeof clientUploads === 'object' ? clientUploads.access : undefined,\n collections: uploadCollections,\n privateKey,\n publicKey,\n verifyClientUpload,\n }),\n serverHandlerPath: imageKitUploadAuthPath,\n })\n\n const output = cloudStoragePlugin({\n alwaysInsertFields,\n collections: toCloudStorageCollections(\n collections,\n adapter,\n privateSlugs,\n rootFolder,\n disablePayloadAccessControl,\n ),\n enabled,\n })(configWithCropDisabled)\n\n // Make the provider identity unique at persistence time to reject concurrent receipt replays.\n // Keep the legacy object key index for existing uploads.\n output.collections = (output.collections || []).map((collection) => {\n if (!configuredSlugs.has(collection.slug)) {\n return collection\n }\n\n const originalPrefixField = (config.collections || [])\n .find((original) => original.slug === collection.slug)\n ?.fields.find((field) => 'name' in field && field.name === 'prefix')\n\n const withUniqueIdentity = (field: Field): Field => {\n if (\n field.type === 'text' &&\n 'name' in field &&\n field.name === 'prefix' &&\n (!originalPrefixField ||\n !('defaultValue' in originalPrefixField) ||\n originalPrefixField.defaultValue == null)\n ) {\n // Cloud storage's collection-prefix default is applied on reads of old\n // documents that have no stored prefix. Its URL hook then appends that\n // prefix, but Payload's file access check cannot find it in the DB.\n // Upload hooks still persist the collection prefix for new files.\n return {\n ...field,\n defaultValue: ({ req }) =>\n req.file && typeof field.defaultValue === 'string' ? field.defaultValue : '',\n }\n }\n if ('name' in field && field.name === '_objectKey') {\n return { ...field, unique: true } as Field\n }\n if (field.type === 'group' && 'name' in field && field.name === 'imagekit') {\n return {\n ...field,\n fields: field.fields.map((metadataField) =>\n 'name' in metadataField && metadataField.name === 'fileId'\n ? { ...metadataField, unique: true }\n : metadataField,\n ),\n }\n }\n // The imagekit group sits inside the adapter's presentational \"Storage: ImageKit\" panel.\n if (field.type === 'collapsible') {\n return { ...field, fields: field.fields.map(withUniqueIdentity) }\n }\n return field\n }\n\n return {\n ...collection,\n fields: collection.fields.map(withUniqueIdentity),\n }\n })\n\n return output\n }\n\nexport { migrateFolder } from './migrate.js'\nexport type { MigrateFolderOptions, MigrationJobResult } from './migrate.js'\n\n// Backward-compatible alias\nexport const payloadStorageImagekit = imagekitStorage\nexport const payloadStorageImageKit = imagekitStorage\n\nexport type PayloadStorageImagekitConfig = PayloadStorageImageKitConfig\n"],"names":["ImageKit","toFile","cloudStoragePlugin","buildStoragePathData","buildUploadStoragePathData","getStorageFilePath","initClientUploads","openAsBlob","path","APIError","getRangeRequestInfo","isXmlMimeType","UPLOAD_CONTENT_SECURITY_POLICY","validateMimeType","imageKitUploadAuthPath","isImageKitReceiptContext","createImageKitClientUploadVerifier","getClientUploadStorageFilePath","createImageKitUploadAuthHandler","getAdminThumbnailURL","getPrivateAdminThumbnailURL","isPdfMimeType","isVideoMimeType","getImageKitThumbnailSrc","getVideoPosterSrc","DEFAULT_SIGNED_URL_EXPIRES_IN","ORIGINAL_FILE_TRANSFORMATION","PRIVATE_COLLECTION_DESCRIPTION","isPrivateCollectionConfig","value","private","XML_FAMILY_MIME_TYPES","collectionCanStoreXml","collection","upload","Boolean","allowRestrictedFileTypes","allowedMimeTypes","mimeTypes","length","some","mimeType","includes","isDocumentPrivate","doc","collectionIsPrivate","stored","imagekit","isPrivate","normalizeFolder","folder","normalized","trim","replace","Error","getCollectionFolder","rootFolder","collectionSlug","posix","join","findImageKitFileId","filePath","parentPath","dirname","name","basename","limit","skip","assets","list","type","found","find","asset","fileId","undefined","isRecord","getStoredImageKitURL","storedURL","url","hasStoredImageKitURL","findUploadDocumentByFilename","filename","prefix","req","constraints","equals","push","payload","db","findOne","where","and","applyVerifiedClientUploadToData","data","uploaded","buildThumbnailURL","thumbnailUrl","thumbnailURL","imagekitAdapter","apiRoute","client","clientUploads","privateSlugs","signedUrlExpiresIn","urlEndpoint","verifyClientUpload","fetchUploadedFile","fileSize","fileURL","range","rangeResult","rangeHeader","Response","headers","Headers","status","normalizedRange","rangeStart","rangeEnd","response","fetch","cache","Range","signal","AbortController","ok","body","cancel","catch","upstreamLength","get","expectedLength","upstreamRange","expectedRange","set","slug","has","buildSignedURL","src","tr","helper","buildSrc","expiresIn","signed","transformation","raw","fields","admin","condition","initCollapsed","disableListColumn","disableListFilter","hidden","readOnly","label","_data","siblingData","description","hooks","afterRead","source","documentPrefix","_objectKey","storageFilePath","collectionPrefix","docPrefix","virtual","interfaceName","generateURL","handleDelete","storedFileId","files","delete","handleUpload","clientUploadContext","file","context","uploadSource","tempFilePath","buffer","uploadFile","uploadFolder","uploadFilename","fileName","useUniqueFileName","isPrivateFile","requiresClientUploadReceipt","staticHandler","args","requestURL","URL","searchParams","hasClientUploadContext","params","uploadedFile","expectedFilePath","verifiedFileURL","size","prefixQueryParam","imageKitPath","filesize","Number","isSafeInteger","signSrc","redirect","imageKitURL","toCloudStorageCollections","collections","adapter","disablePayloadAccessControl","mappedCollections","Object","keys","disableLocalStorage","imagekitStorage","pluginOptions","config","alwaysInsertFields","enabled","focalPoint","privateKey","publicKey","signedUrlExpiresInOption","Set","entries","filter","map","configuredSlugs","xmlCollectionSlugs","routes","api","imageKitClient","collectionSecurity","fromEntries","configWithCropDisabled","isCollectionPrivate","collectionOptions","collectionFocalPoint","existingDescription","baseAdmin","beforeChange","requestMimeType","mimetype","split","toLowerCase","expectedSize","expectedMimeType","existing","beforeOperation","operation","skipCloudStorage","sanitizedDocPrefix","adminThumbnail","crop","uploadCollections","clientHandler","serverHandler","access","serverHandlerPath","output","originalPrefixField","original","field","withUniqueIdentity","defaultValue","unique","metadataField","migrateFolder","payloadStorageImagekit","payloadStorageImageKit"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@humaan/payload-storage-imagekit",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.2",
|
|
4
4
|
"description": "ImageKit storage adapter plugin for Payload CMS uploads",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -37,17 +37,17 @@
|
|
|
37
37
|
],
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@eslint/eslintrc": "^3.3.7",
|
|
40
|
-
"@payloadcms/db-mongodb": "3.90.
|
|
41
|
-
"@payloadcms/db-postgres": "3.90.
|
|
42
|
-
"@payloadcms/db-sqlite": "3.90.
|
|
40
|
+
"@payloadcms/db-mongodb": "3.90.2",
|
|
41
|
+
"@payloadcms/db-postgres": "3.90.2",
|
|
42
|
+
"@payloadcms/db-sqlite": "3.90.2",
|
|
43
43
|
"@payloadcms/eslint-config": "3.28.0",
|
|
44
|
-
"@payloadcms/next": "3.90.
|
|
45
|
-
"@payloadcms/plugin-cloud-storage": "3.90.
|
|
46
|
-
"@payloadcms/richtext-lexical": "3.90.
|
|
47
|
-
"@payloadcms/ui": "3.90.
|
|
44
|
+
"@payloadcms/next": "3.90.2",
|
|
45
|
+
"@payloadcms/plugin-cloud-storage": "3.90.2",
|
|
46
|
+
"@payloadcms/richtext-lexical": "3.90.2",
|
|
47
|
+
"@payloadcms/ui": "3.90.2",
|
|
48
48
|
"@swc-node/register": "1.12.1",
|
|
49
49
|
"@swc/cli": "0.8.1",
|
|
50
|
-
"@types/node": "26.6.
|
|
50
|
+
"@types/node": "26.6.3",
|
|
51
51
|
"@types/react": "19.3.0",
|
|
52
52
|
"@types/react-dom": "19.3.0",
|
|
53
53
|
"conventional-changelog": "8.1.3",
|
|
@@ -55,21 +55,21 @@
|
|
|
55
55
|
"copyfiles": "2.4.1",
|
|
56
56
|
"cross-env": "10.1.0",
|
|
57
57
|
"eslint": "^9.39.5",
|
|
58
|
-
"eslint-config-next": "16.3.
|
|
58
|
+
"eslint-config-next": "16.3.8",
|
|
59
59
|
"graphql": "^16.14.2",
|
|
60
|
-
"mongodb-memory-server": "11.
|
|
61
|
-
"next": "16.3.
|
|
60
|
+
"mongodb-memory-server": "11.3.0",
|
|
61
|
+
"next": "16.3.8",
|
|
62
62
|
"open": "^11.0.4",
|
|
63
|
-
"payload": "3.90.
|
|
64
|
-
"prettier": "^3.9.
|
|
63
|
+
"payload": "3.90.2",
|
|
64
|
+
"prettier": "^3.9.9",
|
|
65
65
|
"qs-esm": "8.0.2",
|
|
66
66
|
"react": "19.3.0",
|
|
67
67
|
"react-dom": "19.3.0",
|
|
68
68
|
"rimraf": "6.1.3",
|
|
69
|
-
"sharp": "0.35.
|
|
69
|
+
"sharp": "0.35.5",
|
|
70
70
|
"sort-package-json": "^4.0.0",
|
|
71
71
|
"typescript": "6.0.3",
|
|
72
|
-
"vite": "8.3.
|
|
72
|
+
"vite": "8.3.1",
|
|
73
73
|
"vite-tsconfig-paths": "6.1.1",
|
|
74
74
|
"vitest": "4.1.11"
|
|
75
75
|
},
|