@humaan/payload-storage-imagekit 2.9.1 → 3.0.0-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -7
- package/dist/clientUploads.d.ts +4 -41
- package/dist/clientUploads.js +1 -8
- package/dist/clientUploads.js.map +1 -1
- package/dist/components/BeforeDashboardServer.d.ts +1 -1
- package/dist/components/BeforeDashboardServer.js +6 -1
- package/dist/components/BeforeDashboardServer.js.map +1 -1
- package/dist/components/ImageKitUploadHandler.d.ts +3 -4
- package/dist/components/ImageKitUploadHandler.js +42 -80
- package/dist/components/ImageKitUploadHandler.js.map +1 -1
- package/dist/endpoints/createImageKitUploadInstructions.d.ts +14 -0
- package/dist/endpoints/createImageKitUploadInstructions.js +116 -0
- package/dist/endpoints/createImageKitUploadInstructions.js.map +1 -0
- package/dist/index.d.ts +6 -28
- package/dist/index.js +456 -638
- package/dist/index.js.map +1 -1
- package/dist/migrate.js +4 -1
- package/dist/migrate.js.map +1 -1
- package/package.json +13 -13
- package/dist/clientUploadVerification.d.ts +0 -30
- package/dist/clientUploadVerification.js +0 -106
- package/dist/clientUploadVerification.js.map +0 -1
- package/dist/endpoints/createImageKitUploadAuthHandler.d.ts +0 -26
- package/dist/endpoints/createImageKitUploadAuthHandler.js +0 -215
- package/dist/endpoints/createImageKitUploadAuthHandler.js.map +0 -1
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { ClientUploadsAccess } from '@payloadcms/plugin-cloud-storage/types';
|
|
2
|
-
import type { PayloadHandler, UploadCollectionSlug } from 'payload';
|
|
3
|
-
import type { createImageKitClientUploadVerifier } from '../clientUploadVerification.js';
|
|
4
|
-
type ImageKitUploadCollection = {
|
|
5
|
-
isPrivate: boolean;
|
|
6
|
-
prefix: string;
|
|
7
|
-
};
|
|
8
|
-
type CreateImageKitUploadAuthHandlerArgs = {
|
|
9
|
-
access?: ClientUploadsAccess;
|
|
10
|
-
collections: Partial<Record<UploadCollectionSlug, ImageKitUploadCollection>>;
|
|
11
|
-
privateKey: string;
|
|
12
|
-
publicKey: string;
|
|
13
|
-
verifyClientUpload: ReturnType<typeof createImageKitClientUploadVerifier>;
|
|
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;
|
|
25
|
-
export declare const createImageKitUploadAuthHandler: ({ access, collections, privateKey, publicKey, verifyClientUpload, }: CreateImageKitUploadAuthHandlerArgs) => PayloadHandler;
|
|
26
|
-
export {};
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
import { buildUploadStoragePathData } from '@payloadcms/plugin-cloud-storage/utilities';
|
|
2
|
-
import { randomUUID } from 'node:crypto';
|
|
3
|
-
import { posix } from 'node:path';
|
|
4
|
-
import { APIError, Forbidden } from 'payload';
|
|
5
|
-
import { assertClientUploadAccess, assertClientUploadAllowed, assertClientUploadFileSize, createClientUploadReceipt, getSafeFileName, verifyClientUploadReceipt } from 'payload/internal';
|
|
6
|
-
import { getSanitizedUploadFilename, validateMimeType } from 'payload/shared';
|
|
7
|
-
import { isImageKitReceiptContext } from '../clientUploads.js';
|
|
8
|
-
import { createImageKitUploadToken } from '../imageKitUploadToken.js';
|
|
9
|
-
const defaultAccess = ({ req })=>Boolean(req.user);
|
|
10
|
-
const bytesToMB = (bytes)=>(bytes / 1024 / 1024).toFixed(2);
|
|
11
|
-
const getMimeTypeEssence = (mimeType)=>(mimeType.split(';', 1)[0] || '').trim().toLowerCase();
|
|
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
|
-
};
|
|
44
|
-
// Continue Payload's numeric suffix convention when the conflict exists only in ImageKit.
|
|
45
|
-
const nextFilename = (filename)=>{
|
|
46
|
-
const { name, ext } = posix.parse(filename);
|
|
47
|
-
const suffix = /^(.*)-(\d+)$/.exec(name);
|
|
48
|
-
return `${suffix ? `${suffix[1]}-${Number(suffix[2]) + 1}` : `${name}-1`}${ext}`;
|
|
49
|
-
};
|
|
50
|
-
export const createImageKitUploadAuthHandler = ({ access = defaultAccess, collections, privateKey, publicKey, verifyClientUpload })=>{
|
|
51
|
-
return async (req)=>{
|
|
52
|
-
if (!req.json) {
|
|
53
|
-
throw new APIError('Content-Type expected to be application/json', 400);
|
|
54
|
-
}
|
|
55
|
-
const body = await req.json();
|
|
56
|
-
const { action, collectionSlug, fileId, signedReceipt } = body;
|
|
57
|
-
let { filename, filesize, mimeType } = body;
|
|
58
|
-
if (typeof collectionSlug !== 'string' || !Object.hasOwn(collections, collectionSlug)) {
|
|
59
|
-
throw new APIError('Invalid upload payload.', 400);
|
|
60
|
-
}
|
|
61
|
-
await assertClientUploadAccess({
|
|
62
|
-
collectionSlug,
|
|
63
|
-
req
|
|
64
|
-
});
|
|
65
|
-
const imageKitCollection = collections[collectionSlug];
|
|
66
|
-
if (!imageKitCollection) {
|
|
67
|
-
throw new APIError(`Collection ${collectionSlug} was not found in ImageKit options.`, 400);
|
|
68
|
-
}
|
|
69
|
-
if (!await access({
|
|
70
|
-
collectionSlug,
|
|
71
|
-
req
|
|
72
|
-
})) {
|
|
73
|
-
throw new Forbidden(req.t);
|
|
74
|
-
}
|
|
75
|
-
if (action === 'complete') {
|
|
76
|
-
if (typeof fileId !== 'string' || !fileId.trim() || typeof signedReceipt !== 'string') {
|
|
77
|
-
throw new APIError('Invalid ImageKit upload completion payload.', 400);
|
|
78
|
-
}
|
|
79
|
-
const receipt = verifyClientUploadReceipt({
|
|
80
|
-
collectionSlug,
|
|
81
|
-
req,
|
|
82
|
-
signedReceipt
|
|
83
|
-
});
|
|
84
|
-
if (!isImageKitReceiptContext(receipt.context) || receipt.filename !== receipt.context.filename || receipt.context.fileId !== undefined && receipt.context.fileId !== fileId) {
|
|
85
|
-
throw new APIError('Invalid ImageKit client upload context.', 400);
|
|
86
|
-
}
|
|
87
|
-
// The browser supplies a lookup ID, not proof. Check provider state against the
|
|
88
|
-
// original signed authorization before including that ID in a new signed receipt.
|
|
89
|
-
const context = {
|
|
90
|
-
...receipt.context,
|
|
91
|
-
fileId
|
|
92
|
-
};
|
|
93
|
-
await verifyClientUpload({
|
|
94
|
-
collectionSlug,
|
|
95
|
-
context
|
|
96
|
-
});
|
|
97
|
-
delete context.uploadTag;
|
|
98
|
-
const response = {
|
|
99
|
-
clientUploadContext: {
|
|
100
|
-
_objectKey: context._objectKey,
|
|
101
|
-
prefix: context.prefix,
|
|
102
|
-
signedReceipt: createClientUploadReceipt({
|
|
103
|
-
collectionSlug,
|
|
104
|
-
context,
|
|
105
|
-
filename: context.filename,
|
|
106
|
-
req
|
|
107
|
-
})
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
return Response.json(response, {
|
|
111
|
-
headers: {
|
|
112
|
-
'Cache-Control': 'no-store'
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
if (action === 'retry') {
|
|
117
|
-
if (typeof signedReceipt !== 'string') {
|
|
118
|
-
throw new APIError('Invalid ImageKit upload retry payload.', 400);
|
|
119
|
-
}
|
|
120
|
-
const receipt = verifyClientUploadReceipt({
|
|
121
|
-
collectionSlug,
|
|
122
|
-
req,
|
|
123
|
-
signedReceipt
|
|
124
|
-
});
|
|
125
|
-
if (!isImageKitReceiptContext(receipt.context) || receipt.filename !== receipt.context.filename || receipt.context.fileId !== undefined) {
|
|
126
|
-
throw new APIError('Invalid ImageKit upload retry payload.', 400);
|
|
127
|
-
}
|
|
128
|
-
filename = nextFilename(receipt.context.filename);
|
|
129
|
-
filesize = receipt.context.expectedSize;
|
|
130
|
-
mimeType = receipt.context.expectedMimeType;
|
|
131
|
-
}
|
|
132
|
-
if (action !== undefined && action !== 'retry' || typeof filename !== 'string' || typeof mimeType !== 'string') {
|
|
133
|
-
throw new APIError('Invalid upload payload.', 400);
|
|
134
|
-
}
|
|
135
|
-
assertClientUploadAllowed({
|
|
136
|
-
collection: req.payload.collections[collectionSlug]?.config,
|
|
137
|
-
filename,
|
|
138
|
-
mimeType
|
|
139
|
-
});
|
|
140
|
-
assertClientUploadFileSize(filesize);
|
|
141
|
-
const requestedSize = filesize;
|
|
142
|
-
const expectedMimeType = getMimeTypeEssence(mimeType);
|
|
143
|
-
if (!expectedMimeType) {
|
|
144
|
-
throw new APIError('A valid MIME type is required for ImageKit client uploads.', 400);
|
|
145
|
-
}
|
|
146
|
-
const collectionConfig = req.payload.collections[collectionSlug]?.config;
|
|
147
|
-
const allowedMimeTypes = collectionConfig && typeof collectionConfig.upload === 'object' && Array.isArray(collectionConfig.upload.mimeTypes) ? collectionConfig.upload.mimeTypes : [];
|
|
148
|
-
if (allowedMimeTypes.length > 0 && !validateMimeType(expectedMimeType, allowedMimeTypes)) {
|
|
149
|
-
throw new APIError(`File type ${expectedMimeType} is not allowed.`, 400);
|
|
150
|
-
}
|
|
151
|
-
const filesizeLimit = req.payload.config.upload.limits?.fileSize;
|
|
152
|
-
if (typeof filesizeLimit === 'number' && Number.isFinite(filesizeLimit) && requestedSize > filesizeLimit) {
|
|
153
|
-
throw new APIError(`Exceeded file size limit. Limit: ${bytesToMB(filesizeLimit)}MB, got: ${bytesToMB(requestedSize)}MB`, 400);
|
|
154
|
-
}
|
|
155
|
-
const sanitizedFilename = await getSafeFileName({
|
|
156
|
-
collectionSlug,
|
|
157
|
-
desiredFilename: getImageKitSafeFilename(filename),
|
|
158
|
-
req
|
|
159
|
-
});
|
|
160
|
-
const resolved = buildUploadStoragePathData({
|
|
161
|
-
collectionPrefix: imageKitCollection.prefix,
|
|
162
|
-
docPrefix: imageKitCollection.prefix,
|
|
163
|
-
filename: sanitizedFilename
|
|
164
|
-
});
|
|
165
|
-
const folder = `/${resolved.storageFilePath.slice(0, -(sanitizedFilename.length + 1))}`;
|
|
166
|
-
const uploadTag = `payload-upload-${randomUUID()}`;
|
|
167
|
-
const uploadPayload = {
|
|
168
|
-
checks: getUploadChecks({
|
|
169
|
-
allowedMimeTypes,
|
|
170
|
-
allowRestrictedFileTypes: typeof collectionConfig?.upload === 'object' && Boolean(collectionConfig.upload.allowRestrictedFileTypes),
|
|
171
|
-
filesize: requestedSize
|
|
172
|
-
}),
|
|
173
|
-
fileName: sanitizedFilename,
|
|
174
|
-
folder,
|
|
175
|
-
isPrivateFile: imageKitCollection.isPrivate ? 'true' : 'false',
|
|
176
|
-
overwriteFile: 'false',
|
|
177
|
-
tags: uploadTag,
|
|
178
|
-
useUniqueFileName: 'false'
|
|
179
|
-
};
|
|
180
|
-
const receiptContext = {
|
|
181
|
-
collectionSlug,
|
|
182
|
-
expectedMimeType,
|
|
183
|
-
expectedSize: requestedSize,
|
|
184
|
-
filename: sanitizedFilename,
|
|
185
|
-
isPrivate: imageKitCollection.isPrivate,
|
|
186
|
-
prefix: resolved.sanitizedDocPrefix,
|
|
187
|
-
uploadTag
|
|
188
|
-
};
|
|
189
|
-
const response = {
|
|
190
|
-
clientUploadContext: {
|
|
191
|
-
prefix: resolved.sanitizedDocPrefix,
|
|
192
|
-
signedReceipt: createClientUploadReceipt({
|
|
193
|
-
collectionSlug,
|
|
194
|
-
context: receiptContext,
|
|
195
|
-
filename: sanitizedFilename,
|
|
196
|
-
req
|
|
197
|
-
})
|
|
198
|
-
},
|
|
199
|
-
filename: sanitizedFilename,
|
|
200
|
-
token: createImageKitUploadToken({
|
|
201
|
-
privateKey,
|
|
202
|
-
publicKey,
|
|
203
|
-
uploadPayload
|
|
204
|
-
}),
|
|
205
|
-
uploadPayload
|
|
206
|
-
};
|
|
207
|
-
return Response.json(response, {
|
|
208
|
-
headers: {
|
|
209
|
-
'Cache-Control': 'no-store'
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
};
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
//# sourceMappingURL=createImageKitUploadAuthHandler.js.map
|
|
@@ -1 +0,0 @@
|
|
|
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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"}
|