@humaan/payload-storage-imagekit 3.0.0-canary.0 → 3.0.0-canary.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -107,8 +107,27 @@ including records with missing or slash-prefixed prefixes, remain readable.
107
107
 
108
108
  The browser still reauthorizes mislabeled raster images using content-detected
109
109
  filename/MIME hints; the server independently validates provider metadata and bytes.
110
- Because v4 receipts carry storage paths rather than provider IDs, direct admission
111
- uses bounded retries when ImageKit's asset listing has not yet caught up.
110
+
111
+ After a provider-direct upload, the browser calls the adapter's completion endpoint
112
+ (normally `POST /api/imagekit-upload-complete`) with the collection slug, ImageKit's
113
+ returned `fileId`, and the original `signedReceipt`. The server checks access and
114
+ the user-bound receipt, then verifies the provider's exact path, name, upload tag,
115
+ privacy and MIME policy before recording the ID. Document admission retrieves the
116
+ file by that ID and rechecks provider state and file contents. It does not depend
117
+ on ImageKit's asset listing becoming visible immediately after upload.
118
+
119
+ Verified path-to-ID mappings use Payload's existing KV store (`payload-kv` with the
120
+ default database adapter), remain available while the provider object exists, and
121
+ are removed when the adapter deletes that object. Server uploads and managed copies
122
+ also record their returned IDs, so immediate rollback/deletion does not depend on
123
+ listing visibility. A failed registry write triggers cleanup by the returned ID.
124
+ If you configure a custom KV
125
+ adapter, it must persist and share entries across the instances handling completion
126
+ and document-save requests; a process-local cache is not sufficient for serverless
127
+ or multi-instance deployments. No new credentials, custom collections, storage-path
128
+ changes or content migration are required. Existing files and older/in-flight
129
+ clients without a mapping retain the path-listing fallback. Custom direct-upload
130
+ clients should call completion before saving their original Payload reference.
112
131
 
113
132
  Run `pnpm exec vitest run` for mock-provider REST/MongoDB, client transport, and
114
133
  folder-migration coverage. Live provider tests require explicitly configured
@@ -4,7 +4,7 @@ import { formatAdminURL } from 'payload/shared';
4
4
  import { getClientImageIdentity } from '../clientImageIdentity.js';
5
5
  export const ImageKitUploadHandler = createClientUploadHandler({
6
6
  name: 'imagekit',
7
- handler: async ({ apiRoute, collectionSlug, data, docPrefix, file, serverURL, updateFilename })=>{
7
+ handler: async ({ apiRoute, collectionSlug, data, docPrefix, endpointPath, file, serverURL, updateFilename })=>{
8
8
  let authorization = data;
9
9
  let uploadReference;
10
10
  const identity = await getClientImageIdentity(file);
@@ -40,6 +40,9 @@ export const ImageKitUploadHandler = createClientUploadHandler({
40
40
  uploadReference = instructions.file.uploadReference;
41
41
  updateFilename(instructions.file.filename);
42
42
  }
43
+ if (!endpointPath || typeof authorization.uploadReference?.signedReceipt !== 'string') {
44
+ throw new Error('ImageKit upload configuration changed. Refresh the page and try again.');
45
+ }
43
46
  const formData = new FormData();
44
47
  formData.set('file', file);
45
48
  formData.set('token', authorization.token);
@@ -54,8 +57,32 @@ export const ImageKitUploadHandler = createClientUploadHandler({
54
57
  const error = await response.json().catch(()=>null);
55
58
  throw new Error(error?.message || 'Failed to upload file to ImageKit.');
56
59
  }
57
- // Payload retains the signed uploadReference supplied by /upload-instructions.
58
- // Provider identity is resolved and verified on the server, never trusted from the browser.
60
+ const uploaded = await response.json().catch(()=>null);
61
+ if (typeof uploaded?.fileId !== 'string' || !uploaded.fileId) {
62
+ throw new Error('ImageKit returned incomplete upload metadata.');
63
+ }
64
+ const completed = await fetch(formatAdminURL({
65
+ apiRoute,
66
+ path: endpointPath,
67
+ serverURL
68
+ }), {
69
+ body: JSON.stringify({
70
+ collectionSlug,
71
+ fileId: uploaded.fileId,
72
+ signedReceipt: authorization.uploadReference.signedReceipt
73
+ }),
74
+ credentials: 'include',
75
+ headers: {
76
+ 'Content-Type': 'application/json'
77
+ },
78
+ method: 'POST'
79
+ });
80
+ if (!completed.ok) {
81
+ const error = await completed.json().catch(()=>null);
82
+ throw new Error(error?.errors?.[0]?.message || 'Failed to verify the ImageKit upload.');
83
+ }
84
+ // The completion callback remembers a server-verified ID, not a browser-supplied proof.
85
+ // Payload keeps its original signed reference and owns the original/history lifecycle.
59
86
  return uploadReference;
60
87
  }
61
88
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/ImageKitUploadHandler.tsx"],"sourcesContent":["'use client'\n\nimport type { UploadInstructions } from 'payload'\n\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\nimport { formatAdminURL } from 'payload/shared'\n\nimport { getClientImageIdentity } from '../clientImageIdentity.js'\n\nexport const ImageKitUploadHandler = createClientUploadHandler({\n name: 'imagekit',\n handler: async ({\n apiRoute,\n collectionSlug,\n data,\n docPrefix,\n file,\n serverURL,\n updateFilename,\n }) => {\n let authorization = data as {\n token: string\n uploadPayload: Record<string, string>\n }\n let uploadReference: Record<string, unknown> | undefined\n const identity = await getClientImageIdentity(file)\n // Payload's initial instructions use the browser's MIME hint. Re-authorize\n // mislabeled image bytes through the same shared endpoint, not a private API.\n if (identity.filename !== file.name || identity.mimeType !== file.type) {\n const response = await fetch(\n formatAdminURL({ apiRoute, path: '/upload-instructions', serverURL }),\n {\n body: JSON.stringify({\n collectionSlug,\n docPrefix,\n filename: identity.filename,\n filesize: file.size,\n mimeType: identity.mimeType,\n }),\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n method: 'POST',\n },\n )\n if (!response.ok) {\n throw new Error('Failed to authorize detected image content.')\n }\n const instructions = (await response.json()) as UploadInstructions\n if (instructions.type !== 'dispatch' || instructions.name !== 'imagekit') {\n throw new Error('Expected ImageKit upload instructions.')\n }\n authorization = instructions.data as typeof authorization\n uploadReference = instructions.file.uploadReference\n updateFilename(instructions.file.filename)\n }\n const formData = new FormData()\n formData.set('file', file)\n formData.set('token', authorization.token)\n for (const [key, value] of Object.entries(authorization.uploadPayload)) {\n formData.set(key, value)\n }\n const response = await fetch('https://upload.imagekit.io/api/v2/files/upload', {\n body: formData,\n method: 'POST',\n })\n if (!response.ok) {\n const error = (await response.json().catch(() => null)) as { message?: string } | null\n throw new Error(error?.message || 'Failed to upload file to ImageKit.')\n }\n // Payload retains the signed uploadReference supplied by /upload-instructions.\n // Provider identity is resolved and verified on the server, never trusted from the browser.\n return uploadReference\n },\n})\n"],"names":["createClientUploadHandler","formatAdminURL","getClientImageIdentity","ImageKitUploadHandler","name","handler","apiRoute","collectionSlug","data","docPrefix","file","serverURL","updateFilename","authorization","uploadReference","identity","filename","mimeType","type","response","fetch","path","body","JSON","stringify","filesize","size","credentials","headers","method","ok","Error","instructions","json","formData","FormData","set","token","key","value","Object","entries","uploadPayload","error","catch","message"],"mappings":"AAAA;AAIA,SAASA,yBAAyB,QAAQ,0CAAyC;AACnF,SAASC,cAAc,QAAQ,iBAAgB;AAE/C,SAASC,sBAAsB,QAAQ,4BAA2B;AAElE,OAAO,MAAMC,wBAAwBH,0BAA0B;IAC7DI,MAAM;IACNC,SAAS,OAAO,EACdC,QAAQ,EACRC,cAAc,EACdC,IAAI,EACJC,SAAS,EACTC,IAAI,EACJC,SAAS,EACTC,cAAc,EACf;QACC,IAAIC,gBAAgBL;QAIpB,IAAIM;QACJ,MAAMC,WAAW,MAAMb,uBAAuBQ;QAC9C,2EAA2E;QAC3E,8EAA8E;QAC9E,IAAIK,SAASC,QAAQ,KAAKN,KAAKN,IAAI,IAAIW,SAASE,QAAQ,KAAKP,KAAKQ,IAAI,EAAE;YACtE,MAAMC,WAAW,MAAMC,MACrBnB,eAAe;gBAAEK;gBAAUe,MAAM;gBAAwBV;YAAU,IACnE;gBACEW,MAAMC,KAAKC,SAAS,CAAC;oBACnBjB;oBACAE;oBACAO,UAAUD,SAASC,QAAQ;oBAC3BS,UAAUf,KAAKgB,IAAI;oBACnBT,UAAUF,SAASE,QAAQ;gBAC7B;gBACAU,aAAa;gBACbC,SAAS;oBAAE,gBAAgB;gBAAmB;gBAC9CC,QAAQ;YACV;YAEF,IAAI,CAACV,SAASW,EAAE,EAAE;gBAChB,MAAM,IAAIC,MAAM;YAClB;YACA,MAAMC,eAAgB,MAAMb,SAASc,IAAI;YACzC,IAAID,aAAad,IAAI,KAAK,cAAcc,aAAa5B,IAAI,KAAK,YAAY;gBACxE,MAAM,IAAI2B,MAAM;YAClB;YACAlB,gBAAgBmB,aAAaxB,IAAI;YACjCM,kBAAkBkB,aAAatB,IAAI,CAACI,eAAe;YACnDF,eAAeoB,aAAatB,IAAI,CAACM,QAAQ;QAC3C;QACA,MAAMkB,WAAW,IAAIC;QACrBD,SAASE,GAAG,CAAC,QAAQ1B;QACrBwB,SAASE,GAAG,CAAC,SAASvB,cAAcwB,KAAK;QACzC,KAAK,MAAM,CAACC,KAAKC,MAAM,IAAIC,OAAOC,OAAO,CAAC5B,cAAc6B,aAAa,EAAG;YACtER,SAASE,GAAG,CAACE,KAAKC;QACpB;QACA,MAAMpB,WAAW,MAAMC,MAAM,kDAAkD;YAC7EE,MAAMY;YACNL,QAAQ;QACV;QACA,IAAI,CAACV,SAASW,EAAE,EAAE;YAChB,MAAMa,QAAS,MAAMxB,SAASc,IAAI,GAAGW,KAAK,CAAC,IAAM;YACjD,MAAM,IAAIb,MAAMY,OAAOE,WAAW;QACpC;QACA,+EAA+E;QAC/E,4FAA4F;QAC5F,OAAO/B;IACT;AACF,GAAE"}
1
+ {"version":3,"sources":["../../src/components/ImageKitUploadHandler.tsx"],"sourcesContent":["'use client'\n\nimport type { UploadInstructions } from 'payload'\n\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\nimport { formatAdminURL } from 'payload/shared'\n\nimport { getClientImageIdentity } from '../clientImageIdentity.js'\n\nexport const ImageKitUploadHandler = createClientUploadHandler({\n name: 'imagekit',\n handler: async ({\n apiRoute,\n collectionSlug,\n data,\n docPrefix,\n endpointPath,\n file,\n serverURL,\n updateFilename,\n }) => {\n let authorization = data as {\n token: string\n uploadPayload: Record<string, string>\n uploadReference: Record<string, unknown>\n }\n let uploadReference: Record<string, unknown> | undefined\n const identity = await getClientImageIdentity(file)\n // Payload's initial instructions use the browser's MIME hint. Re-authorize\n // mislabeled image bytes through the same shared endpoint, not a private API.\n if (identity.filename !== file.name || identity.mimeType !== file.type) {\n const response = await fetch(\n formatAdminURL({ apiRoute, path: '/upload-instructions', serverURL }),\n {\n body: JSON.stringify({\n collectionSlug,\n docPrefix,\n filename: identity.filename,\n filesize: file.size,\n mimeType: identity.mimeType,\n }),\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n method: 'POST',\n },\n )\n if (!response.ok) {\n throw new Error('Failed to authorize detected image content.')\n }\n const instructions = (await response.json()) as UploadInstructions\n if (instructions.type !== 'dispatch' || instructions.name !== 'imagekit') {\n throw new Error('Expected ImageKit upload instructions.')\n }\n authorization = instructions.data as typeof authorization\n uploadReference = instructions.file.uploadReference\n updateFilename(instructions.file.filename)\n }\n if (!endpointPath || typeof authorization.uploadReference?.signedReceipt !== 'string') {\n throw new Error('ImageKit upload configuration changed. Refresh the page and try again.')\n }\n const formData = new FormData()\n formData.set('file', file)\n formData.set('token', authorization.token)\n for (const [key, value] of Object.entries(authorization.uploadPayload)) {\n formData.set(key, value)\n }\n const response = await fetch('https://upload.imagekit.io/api/v2/files/upload', {\n body: formData,\n method: 'POST',\n })\n if (!response.ok) {\n const error = (await response.json().catch(() => null)) as { message?: string } | null\n throw new Error(error?.message || 'Failed to upload file to ImageKit.')\n }\n const uploaded = (await response.json().catch(() => null)) as { fileId?: unknown } | null\n if (typeof uploaded?.fileId !== 'string' || !uploaded.fileId) {\n throw new Error('ImageKit returned incomplete upload metadata.')\n }\n const completed = await fetch(formatAdminURL({ apiRoute, path: endpointPath, serverURL }), {\n body: JSON.stringify({\n collectionSlug,\n fileId: uploaded.fileId,\n signedReceipt: authorization.uploadReference.signedReceipt,\n }),\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n method: 'POST',\n })\n if (!completed.ok) {\n const error = (await completed.json().catch(() => null)) as {\n errors?: { message?: string }[]\n } | null\n throw new Error(error?.errors?.[0]?.message || 'Failed to verify the ImageKit upload.')\n }\n // The completion callback remembers a server-verified ID, not a browser-supplied proof.\n // Payload keeps its original signed reference and owns the original/history lifecycle.\n return uploadReference\n },\n})\n"],"names":["createClientUploadHandler","formatAdminURL","getClientImageIdentity","ImageKitUploadHandler","name","handler","apiRoute","collectionSlug","data","docPrefix","endpointPath","file","serverURL","updateFilename","authorization","uploadReference","identity","filename","mimeType","type","response","fetch","path","body","JSON","stringify","filesize","size","credentials","headers","method","ok","Error","instructions","json","signedReceipt","formData","FormData","set","token","key","value","Object","entries","uploadPayload","error","catch","message","uploaded","fileId","completed","errors"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { PayloadHandler, UploadInstructionsAccess } from 'payload';
2
+ import type { createImageKitFileStore } from '../imageKitFileStore.js';
3
+ export declare const completeImageKitUpload: ({ access, collections, files, }: {
4
+ access?: UploadInstructionsAccess;
5
+ collections: Record<string, {
6
+ isPrivate: boolean;
7
+ prefix: string;
8
+ }>;
9
+ files: ReturnType<typeof createImageKitFileStore>;
10
+ }) => PayloadHandler;
@@ -0,0 +1,84 @@
1
+ import { NotFoundError } from '@imagekit/nodejs';
2
+ import { buildStoragePathData } from '@payloadcms/plugin-cloud-storage/utilities';
3
+ import path from 'node:path';
4
+ import { APIError, Forbidden } from 'payload';
5
+ import { assertClientUploadAccess, assertClientUploadAllowed, getOriginalFilename, verifyClientUploadReceipt } from 'payload/internal';
6
+ import { validateMimeType } from 'payload/shared';
7
+ export const completeImageKitUpload = ({ access = ({ req })=>Boolean(req.user), collections, files })=>async (req)=>{
8
+ if (!req.json) {
9
+ throw new APIError('Expected an ImageKit upload completion payload.', 400);
10
+ }
11
+ const body = await req.json().catch(()=>{
12
+ throw new APIError('Invalid ImageKit upload completion.', 400);
13
+ });
14
+ if (!body || typeof body.collectionSlug !== 'string' || !Object.hasOwn(collections, body.collectionSlug) || typeof body.fileId !== 'string' || !body.fileId.trim()) {
15
+ throw new APIError('Invalid ImageKit upload completion.', 400);
16
+ }
17
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -- Consumer-generated configs narrow UploadCollectionSlug.
18
+ const collectionSlug = body.collectionSlug;
19
+ await assertClientUploadAccess({
20
+ collectionSlug,
21
+ req
22
+ });
23
+ if (!await access({
24
+ collectionSlug,
25
+ req
26
+ })) {
27
+ throw new Forbidden(req.t);
28
+ }
29
+ const receipt = verifyClientUploadReceipt({
30
+ collectionSlug,
31
+ req,
32
+ signedReceipt: body.signedReceipt
33
+ });
34
+ const { isPrivate, prefix } = collections[collectionSlug];
35
+ const expectedPath = buildStoragePathData({
36
+ collectionPrefix: prefix,
37
+ docPrefix: path.posix.join(receipt.filePrefix, receipt._objectKey ?? ''),
38
+ filename: getOriginalFilename({
39
+ filename: receipt.filename
40
+ })
41
+ }).storageFilePath;
42
+ if (!receipt._objectKey || receipt.allowOverwrite || expectedPath !== receipt.storageFilePath || !expectedPath.startsWith(`${prefix}/`)) {
43
+ throw new APIError('Invalid ImageKit upload reference.', 400);
44
+ }
45
+ let file;
46
+ try {
47
+ file = await files.getByID({
48
+ fileId: body.fileId,
49
+ key: expectedPath
50
+ });
51
+ } catch (error) {
52
+ if (error instanceof NotFoundError) {
53
+ throw new APIError('ImageKit upload was not found.', 400);
54
+ }
55
+ throw error;
56
+ }
57
+ if (!file.mime || Boolean(file.isPrivateFile) !== isPrivate || !file.tags?.includes(`payload-upload-${receipt._objectKey}`)) {
58
+ throw new APIError('ImageKit upload did not match its authorization.', 400);
59
+ }
60
+ const collection = req.payload.collections[collectionSlug].config;
61
+ assertClientUploadAllowed({
62
+ collection,
63
+ filename: file.name,
64
+ mimeType: file.mime
65
+ });
66
+ if (typeof collection.upload === 'object' && !validateMimeType(file.mime, collection.upload.mimeTypes ?? [])) {
67
+ throw new APIError('ImageKit file type is not allowed for this collection.', 400);
68
+ }
69
+ // The browser's ID is only a lookup hint. Persist it only after checking authoritative state
70
+ // against the user-bound receipt. Save-time admission rechecks both the receipt and provider.
71
+ await files.remember({
72
+ fileId: file.fileId,
73
+ key: expectedPath,
74
+ req
75
+ });
76
+ return new Response(null, {
77
+ headers: {
78
+ 'Cache-Control': 'no-store'
79
+ },
80
+ status: 204
81
+ });
82
+ };
83
+
84
+ //# sourceMappingURL=completeImageKitUpload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/endpoints/completeImageKitUpload.ts"],"sourcesContent":["import type { PayloadHandler, UploadCollectionSlug, UploadInstructionsAccess } from 'payload'\n\nimport { NotFoundError } from '@imagekit/nodejs'\nimport { buildStoragePathData } from '@payloadcms/plugin-cloud-storage/utilities'\nimport path from 'node:path'\nimport { APIError, Forbidden } from 'payload'\nimport {\n assertClientUploadAccess,\n assertClientUploadAllowed,\n getOriginalFilename,\n verifyClientUploadReceipt,\n} from 'payload/internal'\nimport { validateMimeType } from 'payload/shared'\n\nimport type { createImageKitFileStore } from '../imageKitFileStore.js'\n\nexport const completeImageKitUpload =\n ({\n access = ({ req }) => Boolean(req.user),\n collections,\n files,\n }: {\n access?: UploadInstructionsAccess\n collections: Record<string, { isPrivate: boolean; prefix: string }>\n files: ReturnType<typeof createImageKitFileStore>\n }): PayloadHandler =>\n async (req) => {\n if (!req.json) {\n throw new APIError('Expected an ImageKit upload completion payload.', 400)\n }\n const body = (await req.json().catch(() => {\n throw new APIError('Invalid ImageKit upload completion.', 400)\n })) as {\n collectionSlug?: unknown\n fileId?: unknown\n signedReceipt?: unknown\n }\n if (\n !body ||\n typeof body.collectionSlug !== 'string' ||\n !Object.hasOwn(collections, body.collectionSlug) ||\n typeof body.fileId !== 'string' ||\n !body.fileId.trim()\n ) {\n throw new APIError('Invalid ImageKit upload completion.', 400)\n }\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -- Consumer-generated configs narrow UploadCollectionSlug.\n const collectionSlug = body.collectionSlug as UploadCollectionSlug\n await assertClientUploadAccess({ collectionSlug, req })\n if (!(await access({ collectionSlug, req }))) {\n throw new Forbidden(req.t)\n }\n const receipt = verifyClientUploadReceipt({\n collectionSlug,\n req,\n signedReceipt: body.signedReceipt,\n })\n const { isPrivate, prefix } = collections[collectionSlug]\n const expectedPath = buildStoragePathData({\n collectionPrefix: prefix,\n docPrefix: path.posix.join(receipt.filePrefix, receipt._objectKey ?? ''),\n filename: getOriginalFilename({ filename: receipt.filename }),\n }).storageFilePath\n if (\n !receipt._objectKey ||\n receipt.allowOverwrite ||\n expectedPath !== receipt.storageFilePath ||\n !expectedPath.startsWith(`${prefix}/`)\n ) {\n throw new APIError('Invalid ImageKit upload reference.', 400)\n }\n let file: Awaited<ReturnType<typeof files.getByID>>\n try {\n file = await files.getByID({ fileId: body.fileId, key: expectedPath })\n } catch (error) {\n if (error instanceof NotFoundError) {\n throw new APIError('ImageKit upload was not found.', 400)\n }\n throw error\n }\n if (\n !file.mime ||\n Boolean(file.isPrivateFile) !== isPrivate ||\n !file.tags?.includes(`payload-upload-${receipt._objectKey}`)\n ) {\n throw new APIError('ImageKit upload did not match its authorization.', 400)\n }\n const collection = req.payload.collections[collectionSlug].config\n assertClientUploadAllowed({\n collection,\n filename: file.name,\n mimeType: file.mime,\n })\n if (\n typeof collection.upload === 'object' &&\n !validateMimeType(file.mime, collection.upload.mimeTypes ?? [])\n ) {\n throw new APIError('ImageKit file type is not allowed for this collection.', 400)\n }\n // The browser's ID is only a lookup hint. Persist it only after checking authoritative state\n // against the user-bound receipt. Save-time admission rechecks both the receipt and provider.\n await files.remember({ fileId: file.fileId, key: expectedPath, req })\n return new Response(null, { headers: { 'Cache-Control': 'no-store' }, status: 204 })\n }\n"],"names":["NotFoundError","buildStoragePathData","path","APIError","Forbidden","assertClientUploadAccess","assertClientUploadAllowed","getOriginalFilename","verifyClientUploadReceipt","validateMimeType","completeImageKitUpload","access","req","Boolean","user","collections","files","json","body","catch","collectionSlug","Object","hasOwn","fileId","trim","t","receipt","signedReceipt","isPrivate","prefix","expectedPath","collectionPrefix","docPrefix","posix","join","filePrefix","_objectKey","filename","storageFilePath","allowOverwrite","startsWith","file","getByID","key","error","mime","isPrivateFile","tags","includes","collection","payload","config","name","mimeType","upload","mimeTypes","remember","Response","headers","status"],"mappings":"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"}
@@ -102,7 +102,8 @@ export const createImageKitUploadInstructions = ({ access = ({ req })=>Boolean(r
102
102
  publicKey,
103
103
  uploadPayload
104
104
  }),
105
- uploadPayload
105
+ uploadPayload,
106
+ uploadReference
106
107
  },
107
108
  file: {
108
109
  filename: safeFilename,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/endpoints/createImageKitUploadInstructions.ts"],"sourcesContent":["import type { GenerateUploadInstructions, UploadInstructionsAccess } from 'payload'\n\nimport { buildUploadStoragePathData } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { randomUUID } from 'node:crypto'\nimport { posix } from 'node:path'\nimport { Forbidden } from 'payload'\nimport {\n assertClientUploadAccess,\n assertClientUploadAllowed,\n createClientUploadReceipt,\n getOriginalFilename,\n getSafeFileName,\n incrementName,\n} from 'payload/internal'\nimport { getSanitizedUploadFilename } from 'payload/shared'\n\nimport type { ImageKitUploadPayload } from '../clientUploads.js'\n\nimport { createImageKitUploadToken } from '../imageKitUploadToken.js'\n\n/** ImageKit enforces these constraints before accepting any provider-direct bytes. */\nexport const getUploadChecks = ({\n allowedMimeTypes,\n allowRestrictedFileTypes,\n filesize,\n}: {\n allowedMimeTypes: string[]\n allowRestrictedFileTypes: boolean\n filesize: number\n}): string => {\n const checks = [`\"file.size\" = ${filesize}`]\n const prefixes = allowedMimeTypes\n .map((mimeType) => mimeType.replace('*', ''))\n .filter((prefix) => !/[\"\\\\]/.test(prefix))\n\n if (prefixes.length && !prefixes.includes('')) {\n checks.push(`(${prefixes.map((prefix) => `\"file.mime\" : \"${prefix}\"`).join(' OR ')})`)\n }\n if (!allowRestrictedFileTypes) {\n checks.push(\n '\"file.mime\" NOT IN [\"application/atom+xml\", \"application/mathml+xml\", \"application/rdf+xml\", \"application/rss+xml\", \"application/xhtml+xml\", \"application/xml\", \"application/xslt+xml\", \"image/svg+xml\", \"text/xml\"]',\n )\n }\n return checks.join(' AND ')\n}\n\nexport const createImageKitUploadInstructions =\n ({\n access = ({ req }) => Boolean(req.user),\n isPrivate,\n prefix,\n privateKey,\n publicKey,\n }: {\n access?: UploadInstructionsAccess\n isPrivate: boolean\n prefix: string\n privateKey: string\n publicKey: string\n }): GenerateUploadInstructions =>\n async ({ collectionSlug, docPrefix, filename, filesize, mimeType, overrideAccess, req }) => {\n if (!overrideAccess) {\n await assertClientUploadAccess({ collectionSlug, req })\n if (!(await access({ collectionSlug, req }))) {\n throw new Forbidden(req.t)\n }\n }\n const collection = req.payload.collections[collectionSlug].config\n assertClientUploadAllowed({ collection, filename, mimeType })\n let safeFilename = await getSafeFileName({\n collectionSlug,\n desiredFilename: getSanitizedUploadFilename(filename).replace(/[^\\w.-]/g, '_'),\n req,\n })\n // Without a transformer, v4 exposes the retained original as the current file.\n // Reserve both names so repeated uploads cannot violate filename uniqueness.\n while (\n (await getSafeFileName({\n collectionSlug,\n desiredFilename: getOriginalFilename({ filename: safeFilename }),\n req,\n })) !== getOriginalFilename({ filename: safeFilename })\n ) {\n safeFilename = incrementName(safeFilename)\n }\n const originalFilename = getOriginalFilename({ filename: safeFilename })\n const objectKey = randomUUID()\n const resolved = buildUploadStoragePathData({\n collectionPrefix: prefix,\n docPrefix: docPrefix ?? prefix,\n filename: originalFilename,\n })\n // The admin sends an empty docPrefix for new files; the resolved destination retains the collection fallback.\n const filePrefix = posix.dirname(resolved.storageFilePath)\n const storageFilePath = posix.join(filePrefix, objectKey, originalFilename)\n const uploadReference = {\n _objectKey: objectKey,\n prefix: filePrefix,\n signedReceipt: createClientUploadReceipt({\n _objectKey: objectKey,\n collectionSlug,\n filename: safeFilename,\n filePrefix,\n req,\n storageFilePath,\n }),\n }\n const upload = typeof collection.upload === 'object' ? collection.upload : undefined\n const uploadPayload: ImageKitUploadPayload = {\n checks: getUploadChecks({\n allowedMimeTypes: upload?.mimeTypes ?? [],\n allowRestrictedFileTypes: upload?.allowRestrictedFileTypes ?? false,\n filesize,\n }),\n fileName: originalFilename,\n folder: `/${posix.dirname(storageFilePath)}`,\n isPrivateFile: isPrivate ? 'true' : 'false',\n overwriteFile: 'false',\n tags: `payload-upload-${objectKey}`,\n useUniqueFileName: 'false',\n }\n return {\n name: 'imagekit',\n type: 'dispatch',\n data: {\n token: createImageKitUploadToken({ privateKey, publicKey, uploadPayload }),\n uploadPayload,\n },\n file: { filename: safeFilename, mimeType, size: filesize, uploadReference },\n }\n }\n"],"names":["buildUploadStoragePathData","randomUUID","posix","Forbidden","assertClientUploadAccess","assertClientUploadAllowed","createClientUploadReceipt","getOriginalFilename","getSafeFileName","incrementName","getSanitizedUploadFilename","createImageKitUploadToken","getUploadChecks","allowedMimeTypes","allowRestrictedFileTypes","filesize","checks","prefixes","map","mimeType","replace","filter","prefix","test","length","includes","push","join","createImageKitUploadInstructions","access","req","Boolean","user","isPrivate","privateKey","publicKey","collectionSlug","docPrefix","filename","overrideAccess","t","collection","payload","collections","config","safeFilename","desiredFilename","originalFilename","objectKey","resolved","collectionPrefix","filePrefix","dirname","storageFilePath","uploadReference","_objectKey","signedReceipt","upload","undefined","uploadPayload","mimeTypes","fileName","folder","isPrivateFile","overwriteFile","tags","useUniqueFileName","name","type","data","token","file","size"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/endpoints/createImageKitUploadInstructions.ts"],"sourcesContent":["import type { GenerateUploadInstructions, UploadInstructionsAccess } from 'payload'\n\nimport { buildUploadStoragePathData } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { randomUUID } from 'node:crypto'\nimport { posix } from 'node:path'\nimport { Forbidden } from 'payload'\nimport {\n assertClientUploadAccess,\n assertClientUploadAllowed,\n createClientUploadReceipt,\n getOriginalFilename,\n getSafeFileName,\n incrementName,\n} from 'payload/internal'\nimport { getSanitizedUploadFilename } from 'payload/shared'\n\nimport type { ImageKitUploadPayload } from '../clientUploads.js'\n\nimport { createImageKitUploadToken } from '../imageKitUploadToken.js'\n\n/** ImageKit enforces these constraints before accepting any provider-direct bytes. */\nexport const getUploadChecks = ({\n allowedMimeTypes,\n allowRestrictedFileTypes,\n filesize,\n}: {\n allowedMimeTypes: string[]\n allowRestrictedFileTypes: boolean\n filesize: number\n}): string => {\n const checks = [`\"file.size\" = ${filesize}`]\n const prefixes = allowedMimeTypes\n .map((mimeType) => mimeType.replace('*', ''))\n .filter((prefix) => !/[\"\\\\]/.test(prefix))\n\n if (prefixes.length && !prefixes.includes('')) {\n checks.push(`(${prefixes.map((prefix) => `\"file.mime\" : \"${prefix}\"`).join(' OR ')})`)\n }\n if (!allowRestrictedFileTypes) {\n checks.push(\n '\"file.mime\" NOT IN [\"application/atom+xml\", \"application/mathml+xml\", \"application/rdf+xml\", \"application/rss+xml\", \"application/xhtml+xml\", \"application/xml\", \"application/xslt+xml\", \"image/svg+xml\", \"text/xml\"]',\n )\n }\n return checks.join(' AND ')\n}\n\nexport const createImageKitUploadInstructions =\n ({\n access = ({ req }) => Boolean(req.user),\n isPrivate,\n prefix,\n privateKey,\n publicKey,\n }: {\n access?: UploadInstructionsAccess\n isPrivate: boolean\n prefix: string\n privateKey: string\n publicKey: string\n }): GenerateUploadInstructions =>\n async ({ collectionSlug, docPrefix, filename, filesize, mimeType, overrideAccess, req }) => {\n if (!overrideAccess) {\n await assertClientUploadAccess({ collectionSlug, req })\n if (!(await access({ collectionSlug, req }))) {\n throw new Forbidden(req.t)\n }\n }\n const collection = req.payload.collections[collectionSlug].config\n assertClientUploadAllowed({ collection, filename, mimeType })\n let safeFilename = await getSafeFileName({\n collectionSlug,\n desiredFilename: getSanitizedUploadFilename(filename).replace(/[^\\w.-]/g, '_'),\n req,\n })\n // Without a transformer, v4 exposes the retained original as the current file.\n // Reserve both names so repeated uploads cannot violate filename uniqueness.\n while (\n (await getSafeFileName({\n collectionSlug,\n desiredFilename: getOriginalFilename({ filename: safeFilename }),\n req,\n })) !== getOriginalFilename({ filename: safeFilename })\n ) {\n safeFilename = incrementName(safeFilename)\n }\n const originalFilename = getOriginalFilename({ filename: safeFilename })\n const objectKey = randomUUID()\n const resolved = buildUploadStoragePathData({\n collectionPrefix: prefix,\n docPrefix: docPrefix ?? prefix,\n filename: originalFilename,\n })\n // The admin sends an empty docPrefix for new files; the resolved destination retains the collection fallback.\n const filePrefix = posix.dirname(resolved.storageFilePath)\n const storageFilePath = posix.join(filePrefix, objectKey, originalFilename)\n const uploadReference = {\n _objectKey: objectKey,\n prefix: filePrefix,\n signedReceipt: createClientUploadReceipt({\n _objectKey: objectKey,\n collectionSlug,\n filename: safeFilename,\n filePrefix,\n req,\n storageFilePath,\n }),\n }\n const upload = typeof collection.upload === 'object' ? collection.upload : undefined\n const uploadPayload: ImageKitUploadPayload = {\n checks: getUploadChecks({\n allowedMimeTypes: upload?.mimeTypes ?? [],\n allowRestrictedFileTypes: upload?.allowRestrictedFileTypes ?? false,\n filesize,\n }),\n fileName: originalFilename,\n folder: `/${posix.dirname(storageFilePath)}`,\n isPrivateFile: isPrivate ? 'true' : 'false',\n overwriteFile: 'false',\n tags: `payload-upload-${objectKey}`,\n useUniqueFileName: 'false',\n }\n return {\n name: 'imagekit',\n type: 'dispatch',\n data: {\n token: createImageKitUploadToken({ privateKey, publicKey, uploadPayload }),\n uploadPayload,\n uploadReference,\n },\n file: { filename: safeFilename, mimeType, size: filesize, uploadReference },\n }\n }\n"],"names":["buildUploadStoragePathData","randomUUID","posix","Forbidden","assertClientUploadAccess","assertClientUploadAllowed","createClientUploadReceipt","getOriginalFilename","getSafeFileName","incrementName","getSanitizedUploadFilename","createImageKitUploadToken","getUploadChecks","allowedMimeTypes","allowRestrictedFileTypes","filesize","checks","prefixes","map","mimeType","replace","filter","prefix","test","length","includes","push","join","createImageKitUploadInstructions","access","req","Boolean","user","isPrivate","privateKey","publicKey","collectionSlug","docPrefix","filename","overrideAccess","t","collection","payload","collections","config","safeFilename","desiredFilename","originalFilename","objectKey","resolved","collectionPrefix","filePrefix","dirname","storageFilePath","uploadReference","_objectKey","signedReceipt","upload","undefined","uploadPayload","mimeTypes","fileName","folder","isPrivateFile","overwriteFile","tags","useUniqueFileName","name","type","data","token","file","size"],"mappings":"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"}
@@ -0,0 +1,129 @@
1
+ import type ImageKit from '@imagekit/nodejs';
2
+ import type { PayloadRequest } from 'payload';
3
+ /** Resolve new uploads by provider ID, without depending on ImageKit's eventual-consistency listing. */
4
+ export declare const createImageKitFileStore: (client: ImageKit, urlEndpoint: string) => {
5
+ find: ({ key, req }: {
6
+ key: string;
7
+ req: PayloadRequest;
8
+ }) => Promise<{
9
+ name: string;
10
+ fileId: string;
11
+ size: number;
12
+ url: string;
13
+ AITags?: Array<ImageKit.Files.File.AITag> | null;
14
+ audioCodec?: string;
15
+ bitRate?: number;
16
+ createdAt?: string;
17
+ customCoordinates?: string | null;
18
+ customMetadata?: {
19
+ [key: string]: unknown;
20
+ };
21
+ description?: string;
22
+ duration?: number;
23
+ embeddedMetadata?: {
24
+ [key: string]: unknown;
25
+ };
26
+ filePath?: string;
27
+ fileType?: string;
28
+ hasAlpha?: boolean;
29
+ height?: number;
30
+ isPrivateFile?: boolean;
31
+ isPublished?: boolean;
32
+ mime?: string;
33
+ selectedFieldsSchema?: {
34
+ [key: string]: ImageKit.Files.File.SelectedFieldsSchema;
35
+ };
36
+ tags?: Array<string> | null;
37
+ thumbnail?: string;
38
+ type?: "file" | "file-version";
39
+ updatedAt?: string;
40
+ versionInfo?: ImageKit.Files.File.VersionInfo;
41
+ videoCodec?: string;
42
+ width?: number;
43
+ } | undefined>;
44
+ getByID: ({ fileId, key }: {
45
+ fileId: string;
46
+ key: string;
47
+ }) => Promise<{
48
+ name: string;
49
+ fileId: string;
50
+ size: number;
51
+ url: string;
52
+ AITags?: Array<ImageKit.Files.File.AITag> | null;
53
+ audioCodec?: string;
54
+ bitRate?: number;
55
+ createdAt?: string;
56
+ customCoordinates?: string | null;
57
+ customMetadata?: {
58
+ [key: string]: unknown;
59
+ };
60
+ description?: string;
61
+ duration?: number;
62
+ embeddedMetadata?: {
63
+ [key: string]: unknown;
64
+ };
65
+ filePath?: string;
66
+ fileType?: string;
67
+ hasAlpha?: boolean;
68
+ height?: number;
69
+ isPrivateFile?: boolean;
70
+ isPublished?: boolean;
71
+ mime?: string;
72
+ selectedFieldsSchema?: {
73
+ [key: string]: ImageKit.Files.File.SelectedFieldsSchema;
74
+ };
75
+ tags?: Array<string> | null;
76
+ thumbnail?: string;
77
+ type?: "file" | "file-version";
78
+ updatedAt?: string;
79
+ versionInfo?: ImageKit.Files.File.VersionInfo;
80
+ videoCodec?: string;
81
+ width?: number;
82
+ }>;
83
+ remember: ({ fileId, key, req, }: {
84
+ fileId: string;
85
+ key: string;
86
+ req: PayloadRequest;
87
+ }) => Promise<void>;
88
+ remove: ({ key, req }: {
89
+ key: string;
90
+ req: PayloadRequest;
91
+ }) => Promise<void>;
92
+ upload: ({ body, key, req, }: {
93
+ body: Parameters<ImageKit["files"]["upload"]>[0];
94
+ key: string;
95
+ req: PayloadRequest;
96
+ }) => Promise<{
97
+ fileId: string;
98
+ url: string;
99
+ AITags?: Array<ImageKit.Files.FileUploadResponse.AITag> | null;
100
+ audioCodec?: string;
101
+ bitRate?: number;
102
+ customCoordinates?: string | null;
103
+ customMetadata?: {
104
+ [key: string]: unknown;
105
+ };
106
+ description?: string;
107
+ duration?: number;
108
+ embeddedMetadata?: {
109
+ [key: string]: unknown;
110
+ };
111
+ extensionStatus?: ImageKit.Files.FileUploadResponse.ExtensionStatus;
112
+ filePath?: string;
113
+ fileType?: string;
114
+ height?: number;
115
+ isPrivateFile?: boolean;
116
+ isPublished?: boolean;
117
+ metadata?: ImageKit.Files.Metadata;
118
+ name?: string;
119
+ selectedFieldsSchema?: {
120
+ [key: string]: ImageKit.Files.FileUploadResponse.SelectedFieldsSchema;
121
+ };
122
+ size?: number;
123
+ tags?: Array<string> | null;
124
+ thumbnailUrl?: string;
125
+ versionInfo?: ImageKit.Files.FileUploadResponse.VersionInfo;
126
+ videoCodec?: string;
127
+ width?: number;
128
+ }>;
129
+ };
@@ -0,0 +1,134 @@
1
+ import { NotFoundError } from '@imagekit/nodejs';
2
+ import { createHash } from 'node:crypto';
3
+ import path from 'node:path';
4
+ import { APIError } from 'payload';
5
+ class FileLocationMismatch extends APIError {
6
+ constructor(){
7
+ super('ImageKit file does not match the authorized destination.', 400);
8
+ }
9
+ }
10
+ /** Resolve new uploads by provider ID, without depending on ImageKit's eventual-consistency listing. */ export const createImageKitFileStore = (client, urlEndpoint)=>{
11
+ const registryKey = (key)=>`imagekit:file:${createHash('sha256').update(JSON.stringify([
12
+ urlEndpoint,
13
+ key
14
+ ])).digest('hex')}`;
15
+ const getByID = async ({ fileId, key })=>{
16
+ const filePath = `/${key.replace(/^\/+/, '')}`;
17
+ const file = await client.files.get(fileId);
18
+ if (file.fileId !== fileId || typeof file.name !== 'string' || typeof file.filePath !== 'string' || typeof file.size !== 'number' || !Number.isSafeInteger(file.size) || file.size < 0 || !file.url) {
19
+ throw new APIError('ImageKit returned incomplete file metadata.', 502);
20
+ }
21
+ if (file.name !== path.posix.basename(filePath) || file.filePath !== filePath) {
22
+ throw new FileLocationMismatch();
23
+ }
24
+ return {
25
+ ...file,
26
+ name: file.name,
27
+ fileId,
28
+ size: file.size,
29
+ url: file.url
30
+ };
31
+ };
32
+ const remember = async ({ fileId, key, req })=>{
33
+ // This is a provider-object index, not an authorization or an upload session.
34
+ // Keep it for the object's lifetime (including retained versions), then remove it on delete.
35
+ await req.payload.kv.set(registryKey(key), fileId);
36
+ };
37
+ const upload = async ({ body, key, req })=>{
38
+ const uploaded = await client.files.upload(body);
39
+ try {
40
+ if (!uploaded.fileId || !uploaded.url) {
41
+ throw new APIError('ImageKit returned incomplete upload metadata.', 502);
42
+ }
43
+ await remember({
44
+ fileId: uploaded.fileId,
45
+ key,
46
+ req
47
+ });
48
+ return {
49
+ ...uploaded,
50
+ fileId: uploaded.fileId,
51
+ url: uploaded.url
52
+ };
53
+ } catch (error) {
54
+ // The provider write already succeeded, but the framework may not yet have registered
55
+ // its rollback action (notably for copies). Compensate by the returned ID, never a listing.
56
+ if (uploaded.fileId) {
57
+ try {
58
+ await client.files.delete(uploaded.fileId);
59
+ await req.payload.kv.delete(registryKey(key));
60
+ } catch (cleanupError) {
61
+ req.payload.logger.error({
62
+ err: cleanupError,
63
+ msg: 'Failed to roll back an unregistered ImageKit upload.'
64
+ });
65
+ }
66
+ }
67
+ throw error;
68
+ }
69
+ };
70
+ const find = async ({ key, req })=>{
71
+ const rememberedID = await req.payload.kv.get(registryKey(key));
72
+ if (rememberedID) {
73
+ try {
74
+ return await getByID({
75
+ fileId: rememberedID,
76
+ key
77
+ });
78
+ } catch (error) {
79
+ if (!(error instanceof NotFoundError) && !(error instanceof FileLocationMismatch)) {
80
+ throw error;
81
+ }
82
+ // External deletes or moves can stale the index. Never delete the mismatched object;
83
+ // forget only its mapping and resolve the requested path normally.
84
+ await req.payload.kv.delete(registryKey(key));
85
+ }
86
+ }
87
+ // Existing installations and uploads started by older admin bundles have no registry entry.
88
+ const filePath = `/${key.replace(/^\/+/, '')}`;
89
+ const name = path.posix.basename(filePath);
90
+ for(let skip = 0;; skip += 100){
91
+ const assets = await client.assets.list({
92
+ type: 'file',
93
+ limit: 100,
94
+ path: `${path.posix.dirname(filePath)}/`,
95
+ skip
96
+ });
97
+ const found = assets.find((asset)=>asset.filePath === filePath && asset.name === name);
98
+ if (found?.fileId) {
99
+ return getByID({
100
+ fileId: found.fileId,
101
+ key
102
+ });
103
+ }
104
+ if (assets.length < 100) {
105
+ return undefined;
106
+ }
107
+ }
108
+ };
109
+ const remove = async ({ key, req })=>{
110
+ const file = await find({
111
+ key,
112
+ req
113
+ });
114
+ if (file) {
115
+ try {
116
+ await client.files.delete(file.fileId);
117
+ } catch (error) {
118
+ if (!(error instanceof NotFoundError)) {
119
+ throw error;
120
+ }
121
+ }
122
+ }
123
+ await req.payload.kv.delete(registryKey(key));
124
+ };
125
+ return {
126
+ find,
127
+ getByID,
128
+ remember,
129
+ remove,
130
+ upload
131
+ };
132
+ };
133
+
134
+ //# sourceMappingURL=imageKitFileStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/imageKitFileStore.ts"],"sourcesContent":["import type ImageKit from '@imagekit/nodejs'\nimport type { PayloadRequest } from 'payload'\n\nimport { NotFoundError } from '@imagekit/nodejs'\nimport { createHash } from 'node:crypto'\nimport path from 'node:path'\nimport { APIError } from 'payload'\n\nclass FileLocationMismatch extends APIError {\n constructor() {\n super('ImageKit file does not match the authorized destination.', 400)\n }\n}\n\n/** Resolve new uploads by provider ID, without depending on ImageKit's eventual-consistency listing. */\nexport const createImageKitFileStore = (client: ImageKit, urlEndpoint: string) => {\n const registryKey = (key: string) =>\n `imagekit:file:${createHash('sha256')\n .update(JSON.stringify([urlEndpoint, key]))\n .digest('hex')}`\n\n const getByID = async ({ fileId, key }: { fileId: string; key: string }) => {\n const filePath = `/${key.replace(/^\\/+/, '')}`\n const file = await client.files.get(fileId)\n if (\n file.fileId !== fileId ||\n typeof file.name !== 'string' ||\n typeof file.filePath !== 'string' ||\n typeof file.size !== 'number' ||\n !Number.isSafeInteger(file.size) ||\n file.size < 0 ||\n !file.url\n ) {\n throw new APIError('ImageKit returned incomplete file metadata.', 502)\n }\n if (file.name !== path.posix.basename(filePath) || file.filePath !== filePath) {\n throw new FileLocationMismatch()\n }\n return { ...file, name: file.name, fileId, size: file.size, url: file.url }\n }\n\n const remember = async ({\n fileId,\n key,\n req,\n }: {\n fileId: string\n key: string\n req: PayloadRequest\n }) => {\n // This is a provider-object index, not an authorization or an upload session.\n // Keep it for the object's lifetime (including retained versions), then remove it on delete.\n await req.payload.kv.set(registryKey(key), fileId)\n }\n\n const upload = async ({\n body,\n key,\n req,\n }: {\n body: Parameters<ImageKit['files']['upload']>[0]\n key: string\n req: PayloadRequest\n }) => {\n const uploaded = await client.files.upload(body)\n try {\n if (!uploaded.fileId || !uploaded.url) {\n throw new APIError('ImageKit returned incomplete upload metadata.', 502)\n }\n await remember({ fileId: uploaded.fileId, key, req })\n return { ...uploaded, fileId: uploaded.fileId, url: uploaded.url }\n } catch (error) {\n // The provider write already succeeded, but the framework may not yet have registered\n // its rollback action (notably for copies). Compensate by the returned ID, never a listing.\n if (uploaded.fileId) {\n try {\n await client.files.delete(uploaded.fileId)\n await req.payload.kv.delete(registryKey(key))\n } catch (cleanupError) {\n req.payload.logger.error({\n err: cleanupError,\n msg: 'Failed to roll back an unregistered ImageKit upload.',\n })\n }\n }\n throw error\n }\n }\n\n const find = async ({ key, req }: { key: string; req: PayloadRequest }) => {\n const rememberedID = await req.payload.kv.get<string>(registryKey(key))\n if (rememberedID) {\n try {\n return await getByID({ fileId: rememberedID, key })\n } catch (error) {\n if (!(error instanceof NotFoundError) && !(error instanceof FileLocationMismatch)) {\n throw error\n }\n // External deletes or moves can stale the index. Never delete the mismatched object;\n // forget only its mapping and resolve the requested path normally.\n await req.payload.kv.delete(registryKey(key))\n }\n }\n // Existing installations and uploads started by older admin bundles have no registry entry.\n const filePath = `/${key.replace(/^\\/+/, '')}`\n const name = path.posix.basename(filePath)\n for (let skip = 0; ; skip += 100) {\n const assets = await client.assets.list({\n type: 'file',\n limit: 100,\n path: `${path.posix.dirname(filePath)}/`,\n skip,\n })\n const found = assets.find((asset) => asset.filePath === filePath && asset.name === name)\n if (found?.fileId) {\n return getByID({ fileId: found.fileId, key })\n }\n if (assets.length < 100) {\n return undefined\n }\n }\n }\n\n const remove = async ({ key, req }: { key: string; req: PayloadRequest }) => {\n const file = await find({ key, req })\n if (file) {\n try {\n await client.files.delete(file.fileId)\n } catch (error) {\n if (!(error instanceof NotFoundError)) {\n throw error\n }\n }\n }\n await req.payload.kv.delete(registryKey(key))\n }\n\n return { find, getByID, remember, remove, upload }\n}\n"],"names":["NotFoundError","createHash","path","APIError","FileLocationMismatch","createImageKitFileStore","client","urlEndpoint","registryKey","key","update","JSON","stringify","digest","getByID","fileId","filePath","replace","file","files","get","name","size","Number","isSafeInteger","url","posix","basename","remember","req","payload","kv","set","upload","body","uploaded","error","delete","cleanupError","logger","err","msg","find","rememberedID","skip","assets","list","type","limit","dirname","found","asset","length","undefined","remove"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -5,8 +5,10 @@ import { openAsBlob } from 'node:fs';
5
5
  import path from 'node:path';
6
6
  import { APIError } from 'payload';
7
7
  import { assertClientUploadAllowed, getRangeRequestInfo, isXmlMimeType, uploadContentSecurityPolicy, verifyClientUploadReceipt } from 'payload/internal';
8
+ import { completeImageKitUpload } from './endpoints/completeImageKitUpload.js';
8
9
  import { createImageKitUploadInstructions } from './endpoints/createImageKitUploadInstructions.js';
9
10
  import { getAdminThumbnailURL, getPrivateAdminThumbnailURL, isPdfMimeType, isVideoMimeType } from './getAdminThumbnailURL.js';
11
+ import { createImageKitFileStore } from './imageKitFileStore.js';
10
12
  import { getImageKitThumbnailSrc, getVideoPosterSrc } from './imageTransforms.js';
11
13
  /** Payload 4 storage capability. Register under config.storage, not config.plugins. */ export const imagekitStorage = (options)=>({
12
14
  name: 'imagekit',
@@ -46,6 +48,18 @@ function initializeStorage(config, options) {
46
48
  const value = collections[slug];
47
49
  return value !== true && value?.private === true;
48
50
  };
51
+ const files = createImageKitFileStore(client, options.urlEndpoint);
52
+ const completeUpload = completeImageKitUpload({
53
+ access: typeof options.clientUploads === 'object' ? options.clientUploads.access : undefined,
54
+ collections: Object.fromEntries(Object.keys(collections).map((slug)=>[
55
+ slug,
56
+ {
57
+ isPrivate: collectionIsPrivate(slug),
58
+ prefix: prefixFor(slug)
59
+ }
60
+ ])),
61
+ files
62
+ });
49
63
  const sourceConfig = {
50
64
  ...config,
51
65
  collections: (config.collections ?? []).map((collection)=>{
@@ -116,33 +130,41 @@ function initializeStorage(config, options) {
116
130
  ] : undefined,
117
131
  urlEndpoint: options.urlEndpoint
118
132
  });
119
- const deleteFile = async ({ storageFilePath })=>{
120
- const file = await findFile(client, storageFilePath);
121
- if (file) {
122
- await client.files.delete(file.fileId);
123
- }
124
- };
133
+ const deleteFile = ({ req, storageFilePath })=>files.remove({
134
+ key: storageFilePath,
135
+ req
136
+ });
125
137
  return {
126
138
  name: 'imagekit',
127
- copyFile: async ({ from, to })=>{
128
- if (await findFile(client, to)) {
139
+ copyFile: async ({ from, req, to })=>{
140
+ if (await files.find({
141
+ key: to,
142
+ req
143
+ })) {
129
144
  throw new Error(`ImageKit destination already exists: ${to}`);
130
145
  }
131
- const source = await findFile(client, from);
146
+ const source = await files.find({
147
+ key: from,
148
+ req
149
+ });
132
150
  if (!source) {
133
151
  throw new Error(`ImageKit source not found: ${from}`);
134
152
  }
135
153
  // Upload by URL copies the exact original without buffering it in the app process.
136
154
  // overwriteFile:false protects a destination created concurrently with the lookup.
137
- await client.files.upload({
138
- customMetadata: source.customMetadata ?? undefined,
139
- file: buildURL(from, Boolean(source.isPrivateFile), 'orig-true'),
140
- fileName: path.posix.basename(to),
141
- folder: `/${path.posix.dirname(to)}`,
142
- isPrivateFile: source.isPrivateFile ?? false,
143
- overwriteFile: false,
144
- tags: source.tags ?? undefined,
145
- useUniqueFileName: false
155
+ await files.upload({
156
+ body: {
157
+ customMetadata: source.customMetadata ?? undefined,
158
+ file: buildURL(from, Boolean(source.isPrivateFile), 'orig-true'),
159
+ fileName: path.posix.basename(to),
160
+ folder: `/${path.posix.dirname(to)}`,
161
+ isPrivateFile: source.isPrivateFile ?? false,
162
+ overwriteFile: false,
163
+ tags: source.tags ?? undefined,
164
+ useUniqueFileName: false
165
+ },
166
+ key: to,
167
+ req
146
168
  });
147
169
  },
148
170
  deleteFile,
@@ -158,23 +180,24 @@ function initializeStorage(config, options) {
158
180
  filename
159
181
  }).storageFilePath, false),
160
182
  handleDelete: deleteFile,
161
- handleUpload: async ({ data, file, storageFilePath })=>{
183
+ handleUpload: async ({ data, file, req, storageFilePath })=>{
162
184
  const uploadSource = file.tempFilePath ? await openAsBlob(file.tempFilePath, {
163
185
  type: file.mimeType
164
186
  }) : file.buffer;
165
- const uploaded = await client.files.upload({
166
- file: await toFile(uploadSource, file.filename, {
167
- type: file.mimeType
168
- }),
169
- fileName: path.posix.basename(storageFilePath),
170
- folder: `/${path.posix.dirname(storageFilePath)}`,
171
- isPrivateFile: isPrivate,
172
- overwriteFile: false,
173
- useUniqueFileName: false
187
+ const uploaded = await files.upload({
188
+ body: {
189
+ file: await toFile(uploadSource, file.filename, {
190
+ type: file.mimeType
191
+ }),
192
+ fileName: path.posix.basename(storageFilePath),
193
+ folder: `/${path.posix.dirname(storageFilePath)}`,
194
+ isPrivateFile: isPrivate,
195
+ overwriteFile: false,
196
+ useUniqueFileName: false
197
+ },
198
+ key: storageFilePath,
199
+ req
174
200
  });
175
- if (!uploaded.fileId || !uploaded.url) {
176
- throw new Error('ImageKit returned incomplete upload metadata.');
177
- }
178
201
  return applyMetadata(data, {
179
202
  name: path.posix.basename(storageFilePath),
180
203
  fileId: uploaded.fileId,
@@ -202,7 +225,7 @@ function initializeStorage(config, options) {
202
225
  if (!receipt._objectKey || expectedPath !== receipt.storageFilePath || !expectedPath.startsWith(`${prefix.replace(/^\/+/, '')}/`)) {
203
226
  throw new APIError('Invalid ImageKit upload reference.', 400);
204
227
  }
205
- const uploaded = await findUploadedFile(client, expectedPath);
228
+ const uploaded = await findUploadedFile(files, expectedPath, req);
206
229
  if (!uploaded || !uploaded.mime || Boolean(uploaded.isPrivateFile) !== isPrivate) {
207
230
  throw new APIError('ImageKit upload did not match its authorization.', 400);
208
231
  }
@@ -246,7 +269,10 @@ function initializeStorage(config, options) {
246
269
  }).storageFilePath;
247
270
  const privateFile = isPrivateDocument(document, isPrivate);
248
271
  if (params.operation === 'transform' || isXmlMimeType(source.mimeType ?? '')) {
249
- const file = await findFile(client, key);
272
+ const file = await files.find({
273
+ key,
274
+ req
275
+ });
250
276
  if (!file?.mime) {
251
277
  throw new APIError('ImageKit source not found.', 404);
252
278
  }
@@ -276,6 +302,10 @@ function initializeStorage(config, options) {
276
302
  path: '@humaan/payload-storage-imagekit/client#ImageKitUploadHandler'
277
303
  },
278
304
  enabled: options.enabled !== false && Boolean(options.clientUploads),
305
+ endpoint: {
306
+ handler: completeUpload,
307
+ path: '/imagekit-upload-complete'
308
+ },
279
309
  generate: createImageKitUploadInstructions({
280
310
  access: typeof options.clientUploads === 'object' ? options.clientUploads.access : undefined,
281
311
  isPrivate,
@@ -311,39 +341,13 @@ function initializeStorage(config, options) {
311
341
  });
312
342
  return output;
313
343
  }
314
- async function findFile(client, storageFilePath) {
315
- const filePath = `/${storageFilePath.replace(/^\/+/, '')}`;
316
- const name = path.posix.basename(filePath);
317
- for(let skip = 0;; skip += 100){
318
- const assets = await client.assets.list({
319
- type: 'file',
320
- limit: 100,
321
- path: `${path.posix.dirname(filePath)}/`,
322
- skip
323
- });
324
- const found = assets.find((asset)=>'fileId' in asset && asset.filePath === filePath && asset.name === name);
325
- if (found && 'fileId' in found && found.fileId) {
326
- const file = await client.files.get(found.fileId);
327
- if (file.fileId !== found.fileId || file.name !== name || file.filePath !== filePath || typeof file.size !== 'number' || !Number.isSafeInteger(file.size) || file.size < 0 || !file.url) {
328
- throw new Error('ImageKit returned incomplete file metadata.');
329
- }
330
- return {
331
- ...file,
332
- name: file.name,
333
- fileId: file.fileId,
334
- size: file.size,
335
- url: file.url
336
- };
337
- }
338
- if (assets.length < 100) {
339
- return undefined;
340
- }
341
- }
342
- }
343
- async function findUploadedFile(client, key) {
344
+ async function findUploadedFile(files, key, req) {
344
345
  // ImageKit asset listings can lag a successful upload. Only admission retries;
345
346
  // ordinary missing reads/deletes and collision lookups must stay immediate.
346
- let file = await findFile(client, key);
347
+ let file = await files.find({
348
+ key,
349
+ req
350
+ });
347
351
  for (const delay of [
348
352
  250,
349
353
  500,
@@ -354,7 +358,10 @@ async function findUploadedFile(client, key) {
354
358
  break;
355
359
  }
356
360
  await new Promise((resolve)=>setTimeout(resolve, delay));
357
- file = await findFile(client, key);
361
+ file = await files.find({
362
+ key,
363
+ req
364
+ });
358
365
  }
359
366
  return file;
360
367
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Adapter, ClientUploadsConfig } from '@payloadcms/plugin-cloud-storage/types'\nimport type {\n CollectionConfig,\n CollectionSlug,\n Config,\n Field,\n PayloadRequest,\n StorageAdapter,\n UploadCollectionSlug,\n} from 'payload'\n\nimport ImageKit, { toFile } from '@imagekit/nodejs'\nimport { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage'\nimport { buildStoragePathData } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { openAsBlob } from 'node:fs'\nimport path from 'node:path'\nimport { APIError } from 'payload'\nimport {\n assertClientUploadAllowed,\n getRangeRequestInfo,\n isXmlMimeType,\n uploadContentSecurityPolicy,\n verifyClientUploadReceipt,\n} from 'payload/internal'\n\nimport { createImageKitUploadInstructions } from './endpoints/createImageKitUploadInstructions.js'\nimport {\n getAdminThumbnailURL,\n getPrivateAdminThumbnailURL,\n isPdfMimeType,\n isVideoMimeType,\n} from './getAdminThumbnailURL.js'\nimport { getImageKitThumbnailSrc, getVideoPosterSrc } from './imageTransforms.js'\n\nexport type ImageKitCollectionOptions = { focalPoint?: boolean; private?: boolean }\nexport type PayloadStorageImageKitConfig = {\n clientUploads?: ClientUploadsConfig\n collections: Partial<Record<UploadCollectionSlug, ImageKitCollectionOptions | true>>\n disablePayloadAccessControl?: true\n enabled?: boolean\n focalPoint?: boolean\n folder: string\n privateKey: string\n publicKey: string\n signedUrlExpiresIn?: number\n urlEndpoint: string\n}\n\n/** Payload 4 storage capability. Register under config.storage, not config.plugins. */\nexport const imagekitStorage = (options: PayloadStorageImageKitConfig): StorageAdapter => ({\n name: 'imagekit',\n collections: Object.keys(options.collections),\n init: (config) => initializeStorage(config, options),\n})\n\nexport { migrateFolder } from './migrate.js'\nexport type { MigrateFolderOptions, MigrationJobResult, MigrationLocation } from './migrate.js'\nexport const payloadStorageImagekit = imagekitStorage\nexport const payloadStorageImageKit = imagekitStorage\nexport type PayloadStorageImagekitConfig = PayloadStorageImageKitConfig\n\ninterface ProviderFile {\n fileId: string\n isPrivateFile?: boolean\n mime?: string\n name: string\n size: number\n thumbnail?: string\n url: string\n}\n\ninterface FileLocation {\n _objectKey?: string\n filename?: string\n filesize?: number\n mimeType?: string\n prefix?: string\n}\n\nfunction initializeStorage(config: Config, options: PayloadStorageImageKitConfig): Config {\n for (const key of ['privateKey', 'publicKey', 'urlEndpoint', 'folder'] as const) {\n if (!options[key]?.trim()) {\n throw new Error(`imagekitStorage: \\`${key}\\` is required.`)\n }\n }\n const folder = options.folder.trim().replace(/^\\/+|\\/+$/g, '')\n if (folder.split('/').some((segment) => !segment || segment === '.' || segment === '..')) {\n throw new Error('imagekitStorage: `folder` must not contain empty or dot segments.')\n }\n const client = new ImageKit({ privateKey: options.privateKey })\n const collections: Partial<Record<string, ImageKitCollectionOptions | true>> = options.collections\n if (!Object.keys(collections).length) {\n throw new Error('imagekitStorage: `collections` must not be empty.')\n }\n const configured = new Set(Object.keys(collections))\n const apiRoute = config.routes?.api ?? '/api'\n const expiresIn = options.signedUrlExpiresIn ?? 900\n const prefixFor = (slug: string) => path.posix.join(folder, slug)\n const collectionIsPrivate = (slug: string) => {\n const value = collections[slug]\n return value !== true && value?.private === true\n }\n const sourceConfig: Config = {\n ...config,\n collections: (config.collections ?? []).map((collection) => {\n if (!configured.has(collection.slug)) {\n return collection\n }\n const isPrivate = collectionIsPrivate(collection.slug)\n if (options.disablePayloadAccessControl && (isPrivate || canStoreXml(collection))) {\n throw new Error(\n 'imagekitStorage: private and XML-capable collections require Payload access control.',\n )\n }\n const setting = collections[collection.slug]\n const focalPoint = setting !== true ? setting?.focalPoint : undefined\n const description = collection.admin?.description\n return {\n ...collection,\n admin: isPrivate\n ? {\n ...collection.admin,\n description:\n typeof description === 'string'\n ? `${description} Files in this collection are uploaded to ImageKit as private files and delivered through short-lived signed URLs.`\n : (description ??\n 'Files in this collection are uploaded to ImageKit as private files and delivered through short-lived signed URLs.'),\n }\n : collection.admin,\n hooks: {\n ...collection.hooks,\n beforeChange: [\n ...(collection.hooks?.beforeChange ?? []),\n async ({ data, operation, originalDoc, req }) => {\n if (operation === 'validate') {\n return data\n }\n const verified = req.context.imagekitVerifiedUpload as ProviderFile | undefined\n if (!verified) {\n return data\n }\n const existing = await req.payload.db.findOne({\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -- Generated consumer configs narrow CollectionSlug, unlike the standalone package build.\n collection: collection.slug as CollectionSlug,\n req,\n where: { 'imagekit.fileId': { equals: verified.fileId } },\n })\n if (existing && existing.id !== originalDoc?.id) {\n throw new APIError('This ImageKit upload reference has already been used.', 400)\n }\n return applyMetadata(data, verified, isPrivate, collection.slug, apiRoute)\n },\n ],\n },\n upload: {\n focalPoint: focalPoint ?? options.focalPoint ?? false,\n ...(typeof collection.upload === 'object' ? collection.upload : {}),\n adminThumbnail: ({ doc }) =>\n isPrivateDocument(doc, isPrivate)\n ? getPrivateAdminThumbnailURL(doc, collection.slug, apiRoute)\n : getAdminThumbnailURL(doc),\n crop: false,\n },\n }\n }),\n }\n const adapter: Adapter = ({ collection, prefix = prefixFor(collection.slug) }) => {\n const isPrivate = collectionIsPrivate(collection.slug)\n const buildURL = (key: string, signed: boolean, transformation?: null | string) =>\n client.helper.buildSrc({\n expiresIn: signed ? expiresIn : undefined,\n signed,\n src: `/${key.replace(/^\\/+/, '')}`,\n transformation: transformation ? [{ raw: transformation }] : undefined,\n urlEndpoint: options.urlEndpoint,\n })\n const deleteFile = async ({ storageFilePath }: { storageFilePath: string }) => {\n const file = await findFile(client, storageFilePath)\n if (file) {\n await client.files.delete(file.fileId)\n }\n }\n return {\n name: 'imagekit',\n copyFile: async ({ from, to }) => {\n if (await findFile(client, to)) {\n throw new Error(`ImageKit destination already exists: ${to}`)\n }\n const source = await findFile(client, from)\n if (!source) {\n throw new Error(`ImageKit source not found: ${from}`)\n }\n // Upload by URL copies the exact original without buffering it in the app process.\n // overwriteFile:false protects a destination created concurrently with the lookup.\n await client.files.upload({\n customMetadata: source.customMetadata ?? undefined,\n file: buildURL(from, Boolean(source.isPrivateFile), 'orig-true'),\n fileName: path.posix.basename(to),\n folder: `/${path.posix.dirname(to)}`,\n isPrivateFile: source.isPrivateFile ?? false,\n overwriteFile: false,\n tags: source.tags ?? undefined,\n useUniqueFileName: false,\n })\n },\n deleteFile,\n fields: storageFields({ buildURL, expiresIn, isPrivate, prefix }),\n generateURL: ({ filename, prefix: docPrefix }) =>\n buildURL(\n buildStoragePathData({\n collectionPrefix: prefix,\n docPrefix,\n filename,\n }).storageFilePath,\n false,\n ),\n handleDelete: deleteFile,\n handleUpload: async ({ data, file, storageFilePath }) => {\n const uploadSource = file.tempFilePath\n ? await openAsBlob(file.tempFilePath, { type: file.mimeType })\n : file.buffer\n const uploaded = await client.files.upload({\n file: await toFile(uploadSource, file.filename, { type: file.mimeType }),\n fileName: path.posix.basename(storageFilePath),\n folder: `/${path.posix.dirname(storageFilePath)}`,\n isPrivateFile: isPrivate,\n overwriteFile: false,\n useUniqueFileName: false,\n })\n if (!uploaded.fileId || !uploaded.url) {\n throw new Error('ImageKit returned incomplete upload metadata.')\n }\n return applyMetadata(\n data,\n {\n name: path.posix.basename(storageFilePath),\n fileId: uploaded.fileId,\n isPrivateFile: isPrivate,\n mime: file.mimeType,\n size: file.filesize,\n thumbnail: uploaded.thumbnailUrl,\n url: uploaded.url,\n },\n isPrivate,\n collection.slug,\n apiRoute,\n )\n },\n staticHandler: async (req, { doc, params }) => {\n if (params.uploadReference) {\n try {\n const reference = params.uploadReference as { signedReceipt?: unknown }\n const receipt = verifyClientUploadReceipt({\n collectionSlug: collection.slug,\n req,\n signedReceipt: reference.signedReceipt,\n })\n const expectedPath = buildStoragePathData({\n collectionPrefix: prefix,\n docPrefix: path.posix.join(receipt.filePrefix, receipt._objectKey ?? ''),\n filename: params.filename,\n }).storageFilePath\n if (\n !receipt._objectKey ||\n expectedPath !== receipt.storageFilePath ||\n !expectedPath.startsWith(`${prefix.replace(/^\\/+/, '')}/`)\n ) {\n throw new APIError('Invalid ImageKit upload reference.', 400)\n }\n const uploaded = await findUploadedFile(client, expectedPath)\n if (!uploaded || !uploaded.mime || Boolean(uploaded.isPrivateFile) !== isPrivate) {\n throw new APIError('ImageKit upload did not match its authorization.', 400)\n }\n assertClientUploadAllowed({\n collection,\n filename: uploaded.name,\n mimeType: uploaded.mime,\n })\n req.context.imagekitVerifiedUpload = uploaded\n return await fetchOriginal({\n file: uploaded,\n req,\n url: buildURL(expectedPath, isPrivate, 'orig-true'),\n })\n } catch (error) {\n if (error instanceof APIError) {\n return Response.json({ message: error.message }, { status: error.status })\n }\n throw error\n }\n }\n const document =\n doc ??\n (await req.payload.db.findOne({\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -- Generated consumer configs narrow CollectionSlug, unlike the standalone package build.\n collection: collection.slug as CollectionSlug,\n req,\n where: { filename: { equals: params.filename } },\n }))\n const source = getFileLocation(document, params.filename)\n const key = buildStoragePathData({\n collectionPrefix: prefix,\n docPrefix: path.posix.join(\n params.prefix ?? source.prefix ?? prefix,\n source._objectKey ?? '',\n ),\n filename: params.filename,\n }).storageFilePath\n const privateFile = isPrivateDocument(document, isPrivate)\n if (params.operation === 'transform' || isXmlMimeType(source.mimeType ?? '')) {\n const file = await findFile(client, key)\n if (!file?.mime) {\n throw new APIError('ImageKit source not found.', 404)\n }\n return fetchOriginal({ file, req, url: buildURL(key, privateFile, 'orig-true') })\n }\n const tr = new URL(req.url ?? 'http://localhost').searchParams.get('tr')\n let src = key\n if (tr && isVideoMimeType(source.mimeType)) {\n src = getVideoPosterSrc(src)\n } else if (tr && isPdfMimeType(source.mimeType)) {\n src = getImageKitThumbnailSrc(src)\n }\n // Existing v3 files may live under an earlier folder or delivery endpoint.\n const storedURL = (document as { imagekit?: { url?: string }; original?: unknown } | null)\n ?.imagekit?.url\n if (\n !privateFile &&\n !tr &&\n !(document && 'original' in document && document.original) &&\n storedURL\n ) {\n return Response.redirect(storedURL, 302)\n }\n return Response.redirect(buildURL(src, privateFile, tr), 302)\n },\n supportsTempFiles: true,\n uploadInstructions: {\n adminHandler: { path: '@humaan/payload-storage-imagekit/client#ImageKitUploadHandler' },\n enabled: options.enabled !== false && Boolean(options.clientUploads),\n generate: createImageKitUploadInstructions({\n access:\n typeof options.clientUploads === 'object' ? options.clientUploads.access : undefined,\n isPrivate,\n prefix,\n privateKey: options.privateKey,\n publicKey: options.publicKey,\n }),\n requiresUploadReceipt: true,\n useInAdmin: true,\n },\n }\n }\n const output = cloudStoragePlugin({\n collections: Object.fromEntries(\n Object.keys(collections).map((slug) => [\n slug,\n {\n adapter,\n disableLocalStorage: true,\n disablePayloadAccessControl: options.disablePayloadAccessControl,\n prefix: prefixFor(slug),\n },\n ]),\n ),\n enabled: options.enabled,\n })(sourceConfig)\n // Do not invent a prefix on reads of legacy documents. A stored prefix is retained;\n // new uploads get their normalized destination from the file-operation pipeline.\n output.collections = output.collections?.map((collection) =>\n !configured.has(collection.slug)\n ? collection\n : {\n ...collection,\n fields: collection.fields.map((field) =>\n field.type === 'text' && field.name === 'prefix'\n ? {\n ...field,\n defaultValue: ({ req }) =>\n req.file && typeof field.defaultValue === 'string' ? field.defaultValue : '',\n }\n : field,\n ),\n },\n )\n return output\n}\n\nasync function findFile(client: ImageKit, storageFilePath: string) {\n const filePath = `/${storageFilePath.replace(/^\\/+/, '')}`\n const name = path.posix.basename(filePath)\n for (let skip = 0; ; skip += 100) {\n const assets = await client.assets.list({\n type: 'file',\n limit: 100,\n path: `${path.posix.dirname(filePath)}/`,\n skip,\n })\n const found = assets.find(\n (asset) => 'fileId' in asset && asset.filePath === filePath && asset.name === name,\n )\n if (found && 'fileId' in found && found.fileId) {\n const file = await client.files.get(found.fileId)\n if (\n file.fileId !== found.fileId ||\n file.name !== name ||\n file.filePath !== filePath ||\n typeof file.size !== 'number' ||\n !Number.isSafeInteger(file.size) ||\n file.size < 0 ||\n !file.url\n ) {\n throw new Error('ImageKit returned incomplete file metadata.')\n }\n return { ...file, name: file.name, fileId: file.fileId, size: file.size, url: file.url }\n }\n if (assets.length < 100) {\n return undefined\n }\n }\n}\n\nasync function findUploadedFile(client: ImageKit, key: string) {\n // ImageKit asset listings can lag a successful upload. Only admission retries;\n // ordinary missing reads/deletes and collision lookups must stay immediate.\n let file = await findFile(client, key)\n for (const delay of [250, 500, 1000, 2000]) {\n if (file) {\n break\n }\n await new Promise((resolve) => setTimeout(resolve, delay))\n file = await findFile(client, key)\n }\n return file\n}\n\nfunction isPrivateDocument(doc: unknown, fallback: boolean): boolean {\n const value = (doc as { imagekit?: { isPrivate?: boolean } } | null)?.imagekit?.isPrivate\n return typeof value === 'boolean' ? value : fallback\n}\n\nfunction getFileLocation(doc: unknown, filename: string): FileLocation {\n const value = (doc ?? {}) as {\n original?: FileLocation\n variants?: Record<string, FileLocation>\n } & FileLocation\n return (\n [value, value.original, ...Object.values(value.variants ?? {})].find(\n (file) => file?.filename === filename,\n ) ?? value\n )\n}\n\nfunction applyMetadata(\n data: Record<string, unknown>,\n file: ProviderFile,\n isPrivate: boolean,\n slug: string,\n apiRoute: string,\n) {\n data.imagekit = {\n fileId: file.fileId,\n isPrivate,\n thumbnailUrl: file.thumbnail,\n url: file.url,\n }\n data.thumbnailURL = isPrivate\n ? getPrivateAdminThumbnailURL(data, slug, apiRoute)\n : getAdminThumbnailURL(data)\n return data\n}\n\nasync function fetchOriginal({\n file,\n req,\n url,\n}: {\n file: ProviderFile\n req: PayloadRequest\n url: string\n}): Promise<Response> {\n const range = getRangeRequestInfo({ fileSize: file.size, rangeHeader: req.headers.get('Range') })\n if (range.type === 'invalid') {\n return new Response(null, { headers: range.headers, status: range.status })\n }\n const response = await fetch(url, {\n cache: 'no-store',\n headers:\n range.type === 'partial'\n ? { Range: `bytes=${range.rangeStart}-${range.rangeEnd}` }\n : undefined,\n signal: new AbortController().signal,\n })\n if (\n response.status !== range.status ||\n response.headers.get('Content-Length') !== range.headers['Content-Length'] ||\n (range.type === 'partial' &&\n response.headers.get('Content-Range') !== range.headers['Content-Range'])\n ) {\n void response.body?.cancel().catch(() => undefined)\n throw new APIError('ImageKit returned mismatched file content metadata.', 502)\n }\n const headers = new Headers(range.headers)\n headers.set('Content-Type', file.mime ?? 'application/octet-stream')\n if (isXmlMimeType(file.mime ?? '')) {\n headers.set('Content-Security-Policy', uploadContentSecurityPolicy)\n }\n return new Response(response.body, { headers, status: range.status })\n}\n\nfunction canStoreXml(collection: CollectionConfig): boolean {\n const upload = typeof collection.upload === 'object' ? collection.upload : undefined\n return (\n Boolean(upload?.allowRestrictedFileTypes) ||\n !upload?.mimeTypes?.length ||\n upload.mimeTypes.some((mime) => mime.includes('*') || isXmlMimeType(mime))\n )\n}\n\nfunction storageFields({\n buildURL,\n expiresIn,\n isPrivate,\n prefix,\n}: {\n buildURL: (key: string, signed: boolean, transformation?: null | string) => string\n expiresIn: number\n isPrivate: boolean\n prefix: string\n}): Field[] {\n return [\n {\n type: 'collapsible',\n admin: {\n condition: (data) => Boolean(data?.imagekit?.url),\n initCollapsed: true,\n },\n fields: [\n {\n name: 'imagekit',\n type: 'group',\n admin: { disabled: { column: true, filter: true } },\n fields: [\n { name: 'fileId', type: 'text', admin: { hidden: true }, unique: true },\n { name: 'url', type: 'text', admin: { readOnly: true }, label: 'URL' },\n {\n name: 'isPrivate',\n type: 'checkbox',\n admin: { readOnly: true },\n label: 'Private file',\n },\n {\n name: 'signedUrl',\n type: 'text',\n admin: {\n condition: (_data, siblingData) =>\n isPrivateDocument({ imagekit: siblingData }, isPrivate),\n description: `Temporary signed URL. Expires ${expiresIn} seconds after load. Anyone with this URL can access the file until it expires.`,\n readOnly: true,\n },\n hooks: {\n afterRead: [\n ({ data, siblingData }) => {\n if (\n !data?.filename ||\n !isPrivateDocument({ imagekit: siblingData }, isPrivate)\n ) {\n return undefined\n }\n const key = buildStoragePathData({\n collectionPrefix: prefix,\n docPrefix: path.posix.join(data.prefix || prefix, data._objectKey || ''),\n filename: data.filename,\n }).storageFilePath\n return buildURL(key, true)\n },\n ],\n },\n label: 'Signed URL',\n virtual: true,\n },\n { name: 'thumbnailUrl', type: 'text', admin: { hidden: true } },\n ],\n interfaceName: 'imagekitFields',\n label: false,\n },\n ],\n label: 'Storage: ImageKit',\n },\n ]\n}\n"],"names":["ImageKit","toFile","cloudStoragePlugin","buildStoragePathData","openAsBlob","path","APIError","assertClientUploadAllowed","getRangeRequestInfo","isXmlMimeType","uploadContentSecurityPolicy","verifyClientUploadReceipt","createImageKitUploadInstructions","getAdminThumbnailURL","getPrivateAdminThumbnailURL","isPdfMimeType","isVideoMimeType","getImageKitThumbnailSrc","getVideoPosterSrc","imagekitStorage","options","name","collections","Object","keys","init","config","initializeStorage","migrateFolder","payloadStorageImagekit","payloadStorageImageKit","key","trim","Error","folder","replace","split","some","segment","client","privateKey","length","configured","Set","apiRoute","routes","api","expiresIn","signedUrlExpiresIn","prefixFor","slug","posix","join","collectionIsPrivate","value","private","sourceConfig","map","collection","has","isPrivate","disablePayloadAccessControl","canStoreXml","setting","focalPoint","undefined","description","admin","hooks","beforeChange","data","operation","originalDoc","req","verified","context","imagekitVerifiedUpload","existing","payload","db","findOne","where","equals","fileId","id","applyMetadata","upload","adminThumbnail","doc","isPrivateDocument","crop","adapter","prefix","buildURL","signed","transformation","helper","buildSrc","src","raw","urlEndpoint","deleteFile","storageFilePath","file","findFile","files","delete","copyFile","from","to","source","customMetadata","Boolean","isPrivateFile","fileName","basename","dirname","overwriteFile","tags","useUniqueFileName","fields","storageFields","generateURL","filename","docPrefix","collectionPrefix","handleDelete","handleUpload","uploadSource","tempFilePath","type","mimeType","buffer","uploaded","url","mime","size","filesize","thumbnail","thumbnailUrl","staticHandler","params","uploadReference","reference","receipt","collectionSlug","signedReceipt","expectedPath","filePrefix","_objectKey","startsWith","findUploadedFile","fetchOriginal","error","Response","json","message","status","document","getFileLocation","privateFile","tr","URL","searchParams","get","storedURL","imagekit","original","redirect","supportsTempFiles","uploadInstructions","adminHandler","enabled","clientUploads","generate","access","publicKey","requiresUploadReceipt","useInAdmin","output","fromEntries","disableLocalStorage","field","defaultValue","filePath","skip","assets","list","limit","found","find","asset","Number","isSafeInteger","delay","Promise","resolve","setTimeout","fallback","values","variants","thumbnailURL","range","fileSize","rangeHeader","headers","response","fetch","cache","Range","rangeStart","rangeEnd","signal","AbortController","body","cancel","catch","Headers","set","allowRestrictedFileTypes","mimeTypes","includes","condition","initCollapsed","disabled","column","filter","hidden","unique","readOnly","label","_data","siblingData","afterRead","virtual","interfaceName"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Adapter, ClientUploadsConfig } from '@payloadcms/plugin-cloud-storage/types'\nimport type {\n CollectionConfig,\n CollectionSlug,\n Config,\n Field,\n PayloadRequest,\n StorageAdapter,\n UploadCollectionSlug,\n} from 'payload'\n\nimport ImageKit, { toFile } from '@imagekit/nodejs'\nimport { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage'\nimport { buildStoragePathData } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { openAsBlob } from 'node:fs'\nimport path from 'node:path'\nimport { APIError } from 'payload'\nimport {\n assertClientUploadAllowed,\n getRangeRequestInfo,\n isXmlMimeType,\n uploadContentSecurityPolicy,\n verifyClientUploadReceipt,\n} from 'payload/internal'\n\nimport { completeImageKitUpload } from './endpoints/completeImageKitUpload.js'\nimport { createImageKitUploadInstructions } from './endpoints/createImageKitUploadInstructions.js'\nimport {\n getAdminThumbnailURL,\n getPrivateAdminThumbnailURL,\n isPdfMimeType,\n isVideoMimeType,\n} from './getAdminThumbnailURL.js'\nimport { createImageKitFileStore } from './imageKitFileStore.js'\nimport { getImageKitThumbnailSrc, getVideoPosterSrc } from './imageTransforms.js'\n\nexport type ImageKitCollectionOptions = { focalPoint?: boolean; private?: boolean }\nexport type PayloadStorageImageKitConfig = {\n clientUploads?: ClientUploadsConfig\n collections: Partial<Record<UploadCollectionSlug, ImageKitCollectionOptions | true>>\n disablePayloadAccessControl?: true\n enabled?: boolean\n focalPoint?: boolean\n folder: string\n privateKey: string\n publicKey: string\n signedUrlExpiresIn?: number\n urlEndpoint: string\n}\n\n/** Payload 4 storage capability. Register under config.storage, not config.plugins. */\nexport const imagekitStorage = (options: PayloadStorageImageKitConfig): StorageAdapter => ({\n name: 'imagekit',\n collections: Object.keys(options.collections),\n init: (config) => initializeStorage(config, options),\n})\n\nexport { migrateFolder } from './migrate.js'\nexport type { MigrateFolderOptions, MigrationJobResult, MigrationLocation } from './migrate.js'\nexport const payloadStorageImagekit = imagekitStorage\nexport const payloadStorageImageKit = imagekitStorage\nexport type PayloadStorageImagekitConfig = PayloadStorageImageKitConfig\n\ninterface ProviderFile {\n fileId: string\n isPrivateFile?: boolean\n mime?: string\n name: string\n size: number\n thumbnail?: string\n url: string\n}\n\ninterface FileLocation {\n _objectKey?: string\n filename?: string\n filesize?: number\n mimeType?: string\n prefix?: string\n}\n\nfunction initializeStorage(config: Config, options: PayloadStorageImageKitConfig): Config {\n for (const key of ['privateKey', 'publicKey', 'urlEndpoint', 'folder'] as const) {\n if (!options[key]?.trim()) {\n throw new Error(`imagekitStorage: \\`${key}\\` is required.`)\n }\n }\n const folder = options.folder.trim().replace(/^\\/+|\\/+$/g, '')\n if (folder.split('/').some((segment) => !segment || segment === '.' || segment === '..')) {\n throw new Error('imagekitStorage: `folder` must not contain empty or dot segments.')\n }\n const client = new ImageKit({ privateKey: options.privateKey })\n const collections: Partial<Record<string, ImageKitCollectionOptions | true>> = options.collections\n if (!Object.keys(collections).length) {\n throw new Error('imagekitStorage: `collections` must not be empty.')\n }\n const configured = new Set(Object.keys(collections))\n const apiRoute = config.routes?.api ?? '/api'\n const expiresIn = options.signedUrlExpiresIn ?? 900\n const prefixFor = (slug: string) => path.posix.join(folder, slug)\n const collectionIsPrivate = (slug: string) => {\n const value = collections[slug]\n return value !== true && value?.private === true\n }\n const files = createImageKitFileStore(client, options.urlEndpoint)\n const completeUpload = completeImageKitUpload({\n access: typeof options.clientUploads === 'object' ? options.clientUploads.access : undefined,\n collections: Object.fromEntries(\n Object.keys(collections).map((slug) => [\n slug,\n { isPrivate: collectionIsPrivate(slug), prefix: prefixFor(slug) },\n ]),\n ),\n files,\n })\n const sourceConfig: Config = {\n ...config,\n collections: (config.collections ?? []).map((collection) => {\n if (!configured.has(collection.slug)) {\n return collection\n }\n const isPrivate = collectionIsPrivate(collection.slug)\n if (options.disablePayloadAccessControl && (isPrivate || canStoreXml(collection))) {\n throw new Error(\n 'imagekitStorage: private and XML-capable collections require Payload access control.',\n )\n }\n const setting = collections[collection.slug]\n const focalPoint = setting !== true ? setting?.focalPoint : undefined\n const description = collection.admin?.description\n return {\n ...collection,\n admin: isPrivate\n ? {\n ...collection.admin,\n description:\n typeof description === 'string'\n ? `${description} Files in this collection are uploaded to ImageKit as private files and delivered through short-lived signed URLs.`\n : (description ??\n 'Files in this collection are uploaded to ImageKit as private files and delivered through short-lived signed URLs.'),\n }\n : collection.admin,\n hooks: {\n ...collection.hooks,\n beforeChange: [\n ...(collection.hooks?.beforeChange ?? []),\n async ({ data, operation, originalDoc, req }) => {\n if (operation === 'validate') {\n return data\n }\n const verified = req.context.imagekitVerifiedUpload as ProviderFile | undefined\n if (!verified) {\n return data\n }\n const existing = await req.payload.db.findOne({\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -- Generated consumer configs narrow CollectionSlug, unlike the standalone package build.\n collection: collection.slug as CollectionSlug,\n req,\n where: { 'imagekit.fileId': { equals: verified.fileId } },\n })\n if (existing && existing.id !== originalDoc?.id) {\n throw new APIError('This ImageKit upload reference has already been used.', 400)\n }\n return applyMetadata(data, verified, isPrivate, collection.slug, apiRoute)\n },\n ],\n },\n upload: {\n focalPoint: focalPoint ?? options.focalPoint ?? false,\n ...(typeof collection.upload === 'object' ? collection.upload : {}),\n adminThumbnail: ({ doc }) =>\n isPrivateDocument(doc, isPrivate)\n ? getPrivateAdminThumbnailURL(doc, collection.slug, apiRoute)\n : getAdminThumbnailURL(doc),\n crop: false,\n },\n }\n }),\n }\n const adapter: Adapter = ({ collection, prefix = prefixFor(collection.slug) }) => {\n const isPrivate = collectionIsPrivate(collection.slug)\n const buildURL = (key: string, signed: boolean, transformation?: null | string) =>\n client.helper.buildSrc({\n expiresIn: signed ? expiresIn : undefined,\n signed,\n src: `/${key.replace(/^\\/+/, '')}`,\n transformation: transformation ? [{ raw: transformation }] : undefined,\n urlEndpoint: options.urlEndpoint,\n })\n const deleteFile = ({\n req,\n storageFilePath,\n }: {\n req: PayloadRequest\n storageFilePath: string\n }) => files.remove({ key: storageFilePath, req })\n return {\n name: 'imagekit',\n copyFile: async ({ from, req, to }) => {\n if (await files.find({ key: to, req })) {\n throw new Error(`ImageKit destination already exists: ${to}`)\n }\n const source = await files.find({ key: from, req })\n if (!source) {\n throw new Error(`ImageKit source not found: ${from}`)\n }\n // Upload by URL copies the exact original without buffering it in the app process.\n // overwriteFile:false protects a destination created concurrently with the lookup.\n await files.upload({\n body: {\n customMetadata: source.customMetadata ?? undefined,\n file: buildURL(from, Boolean(source.isPrivateFile), 'orig-true'),\n fileName: path.posix.basename(to),\n folder: `/${path.posix.dirname(to)}`,\n isPrivateFile: source.isPrivateFile ?? false,\n overwriteFile: false,\n tags: source.tags ?? undefined,\n useUniqueFileName: false,\n },\n key: to,\n req,\n })\n },\n deleteFile,\n fields: storageFields({ buildURL, expiresIn, isPrivate, prefix }),\n generateURL: ({ filename, prefix: docPrefix }) =>\n buildURL(\n buildStoragePathData({\n collectionPrefix: prefix,\n docPrefix,\n filename,\n }).storageFilePath,\n false,\n ),\n handleDelete: deleteFile,\n handleUpload: async ({ data, file, req, storageFilePath }) => {\n const uploadSource = file.tempFilePath\n ? await openAsBlob(file.tempFilePath, { type: file.mimeType })\n : file.buffer\n const uploaded = await files.upload({\n body: {\n file: await toFile(uploadSource, file.filename, { type: file.mimeType }),\n fileName: path.posix.basename(storageFilePath),\n folder: `/${path.posix.dirname(storageFilePath)}`,\n isPrivateFile: isPrivate,\n overwriteFile: false,\n useUniqueFileName: false,\n },\n key: storageFilePath,\n req,\n })\n return applyMetadata(\n data,\n {\n name: path.posix.basename(storageFilePath),\n fileId: uploaded.fileId,\n isPrivateFile: isPrivate,\n mime: file.mimeType,\n size: file.filesize,\n thumbnail: uploaded.thumbnailUrl,\n url: uploaded.url,\n },\n isPrivate,\n collection.slug,\n apiRoute,\n )\n },\n staticHandler: async (req, { doc, params }) => {\n if (params.uploadReference) {\n try {\n const reference = params.uploadReference as { signedReceipt?: unknown }\n const receipt = verifyClientUploadReceipt({\n collectionSlug: collection.slug,\n req,\n signedReceipt: reference.signedReceipt,\n })\n const expectedPath = buildStoragePathData({\n collectionPrefix: prefix,\n docPrefix: path.posix.join(receipt.filePrefix, receipt._objectKey ?? ''),\n filename: params.filename,\n }).storageFilePath\n if (\n !receipt._objectKey ||\n expectedPath !== receipt.storageFilePath ||\n !expectedPath.startsWith(`${prefix.replace(/^\\/+/, '')}/`)\n ) {\n throw new APIError('Invalid ImageKit upload reference.', 400)\n }\n const uploaded = await findUploadedFile(files, expectedPath, req)\n if (!uploaded || !uploaded.mime || Boolean(uploaded.isPrivateFile) !== isPrivate) {\n throw new APIError('ImageKit upload did not match its authorization.', 400)\n }\n assertClientUploadAllowed({\n collection,\n filename: uploaded.name,\n mimeType: uploaded.mime,\n })\n req.context.imagekitVerifiedUpload = uploaded\n return await fetchOriginal({\n file: uploaded,\n req,\n url: buildURL(expectedPath, isPrivate, 'orig-true'),\n })\n } catch (error) {\n if (error instanceof APIError) {\n return Response.json({ message: error.message }, { status: error.status })\n }\n throw error\n }\n }\n const document =\n doc ??\n (await req.payload.db.findOne({\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -- Generated consumer configs narrow CollectionSlug, unlike the standalone package build.\n collection: collection.slug as CollectionSlug,\n req,\n where: { filename: { equals: params.filename } },\n }))\n const source = getFileLocation(document, params.filename)\n const key = buildStoragePathData({\n collectionPrefix: prefix,\n docPrefix: path.posix.join(\n params.prefix ?? source.prefix ?? prefix,\n source._objectKey ?? '',\n ),\n filename: params.filename,\n }).storageFilePath\n const privateFile = isPrivateDocument(document, isPrivate)\n if (params.operation === 'transform' || isXmlMimeType(source.mimeType ?? '')) {\n const file = await files.find({ key, req })\n if (!file?.mime) {\n throw new APIError('ImageKit source not found.', 404)\n }\n return fetchOriginal({ file, req, url: buildURL(key, privateFile, 'orig-true') })\n }\n const tr = new URL(req.url ?? 'http://localhost').searchParams.get('tr')\n let src = key\n if (tr && isVideoMimeType(source.mimeType)) {\n src = getVideoPosterSrc(src)\n } else if (tr && isPdfMimeType(source.mimeType)) {\n src = getImageKitThumbnailSrc(src)\n }\n // Existing v3 files may live under an earlier folder or delivery endpoint.\n const storedURL = (document as { imagekit?: { url?: string }; original?: unknown } | null)\n ?.imagekit?.url\n if (\n !privateFile &&\n !tr &&\n !(document && 'original' in document && document.original) &&\n storedURL\n ) {\n return Response.redirect(storedURL, 302)\n }\n return Response.redirect(buildURL(src, privateFile, tr), 302)\n },\n supportsTempFiles: true,\n uploadInstructions: {\n adminHandler: { path: '@humaan/payload-storage-imagekit/client#ImageKitUploadHandler' },\n enabled: options.enabled !== false && Boolean(options.clientUploads),\n endpoint: { handler: completeUpload, path: '/imagekit-upload-complete' },\n generate: createImageKitUploadInstructions({\n access:\n typeof options.clientUploads === 'object' ? options.clientUploads.access : undefined,\n isPrivate,\n prefix,\n privateKey: options.privateKey,\n publicKey: options.publicKey,\n }),\n requiresUploadReceipt: true,\n useInAdmin: true,\n },\n }\n }\n const output = cloudStoragePlugin({\n collections: Object.fromEntries(\n Object.keys(collections).map((slug) => [\n slug,\n {\n adapter,\n disableLocalStorage: true,\n disablePayloadAccessControl: options.disablePayloadAccessControl,\n prefix: prefixFor(slug),\n },\n ]),\n ),\n enabled: options.enabled,\n })(sourceConfig)\n // Do not invent a prefix on reads of legacy documents. A stored prefix is retained;\n // new uploads get their normalized destination from the file-operation pipeline.\n output.collections = output.collections?.map((collection) =>\n !configured.has(collection.slug)\n ? collection\n : {\n ...collection,\n fields: collection.fields.map((field) =>\n field.type === 'text' && field.name === 'prefix'\n ? {\n ...field,\n defaultValue: ({ req }) =>\n req.file && typeof field.defaultValue === 'string' ? field.defaultValue : '',\n }\n : field,\n ),\n },\n )\n return output\n}\n\nasync function findUploadedFile(\n files: ReturnType<typeof createImageKitFileStore>,\n key: string,\n req: PayloadRequest,\n) {\n // ImageKit asset listings can lag a successful upload. Only admission retries;\n // ordinary missing reads/deletes and collision lookups must stay immediate.\n let file = await files.find({ key, req })\n for (const delay of [250, 500, 1000, 2000]) {\n if (file) {\n break\n }\n await new Promise((resolve) => setTimeout(resolve, delay))\n file = await files.find({ key, req })\n }\n return file\n}\n\nfunction isPrivateDocument(doc: unknown, fallback: boolean): boolean {\n const value = (doc as { imagekit?: { isPrivate?: boolean } } | null)?.imagekit?.isPrivate\n return typeof value === 'boolean' ? value : fallback\n}\n\nfunction getFileLocation(doc: unknown, filename: string): FileLocation {\n const value = (doc ?? {}) as {\n original?: FileLocation\n variants?: Record<string, FileLocation>\n } & FileLocation\n return (\n [value, value.original, ...Object.values(value.variants ?? {})].find(\n (file) => file?.filename === filename,\n ) ?? value\n )\n}\n\nfunction applyMetadata(\n data: Record<string, unknown>,\n file: ProviderFile,\n isPrivate: boolean,\n slug: string,\n apiRoute: string,\n) {\n data.imagekit = {\n fileId: file.fileId,\n isPrivate,\n thumbnailUrl: file.thumbnail,\n url: file.url,\n }\n data.thumbnailURL = isPrivate\n ? getPrivateAdminThumbnailURL(data, slug, apiRoute)\n : getAdminThumbnailURL(data)\n return data\n}\n\nasync function fetchOriginal({\n file,\n req,\n url,\n}: {\n file: ProviderFile\n req: PayloadRequest\n url: string\n}): Promise<Response> {\n const range = getRangeRequestInfo({ fileSize: file.size, rangeHeader: req.headers.get('Range') })\n if (range.type === 'invalid') {\n return new Response(null, { headers: range.headers, status: range.status })\n }\n const response = await fetch(url, {\n cache: 'no-store',\n headers:\n range.type === 'partial'\n ? { Range: `bytes=${range.rangeStart}-${range.rangeEnd}` }\n : undefined,\n signal: new AbortController().signal,\n })\n if (\n response.status !== range.status ||\n response.headers.get('Content-Length') !== range.headers['Content-Length'] ||\n (range.type === 'partial' &&\n response.headers.get('Content-Range') !== range.headers['Content-Range'])\n ) {\n void response.body?.cancel().catch(() => undefined)\n throw new APIError('ImageKit returned mismatched file content metadata.', 502)\n }\n const headers = new Headers(range.headers)\n headers.set('Content-Type', file.mime ?? 'application/octet-stream')\n if (isXmlMimeType(file.mime ?? '')) {\n headers.set('Content-Security-Policy', uploadContentSecurityPolicy)\n }\n return new Response(response.body, { headers, status: range.status })\n}\n\nfunction canStoreXml(collection: CollectionConfig): boolean {\n const upload = typeof collection.upload === 'object' ? collection.upload : undefined\n return (\n Boolean(upload?.allowRestrictedFileTypes) ||\n !upload?.mimeTypes?.length ||\n upload.mimeTypes.some((mime) => mime.includes('*') || isXmlMimeType(mime))\n )\n}\n\nfunction storageFields({\n buildURL,\n expiresIn,\n isPrivate,\n prefix,\n}: {\n buildURL: (key: string, signed: boolean, transformation?: null | string) => string\n expiresIn: number\n isPrivate: boolean\n prefix: string\n}): Field[] {\n return [\n {\n type: 'collapsible',\n admin: {\n condition: (data) => Boolean(data?.imagekit?.url),\n initCollapsed: true,\n },\n fields: [\n {\n name: 'imagekit',\n type: 'group',\n admin: { disabled: { column: true, filter: true } },\n fields: [\n { name: 'fileId', type: 'text', admin: { hidden: true }, unique: true },\n { name: 'url', type: 'text', admin: { readOnly: true }, label: 'URL' },\n {\n name: 'isPrivate',\n type: 'checkbox',\n admin: { readOnly: true },\n label: 'Private file',\n },\n {\n name: 'signedUrl',\n type: 'text',\n admin: {\n condition: (_data, siblingData) =>\n isPrivateDocument({ imagekit: siblingData }, isPrivate),\n description: `Temporary signed URL. Expires ${expiresIn} seconds after load. Anyone with this URL can access the file until it expires.`,\n readOnly: true,\n },\n hooks: {\n afterRead: [\n ({ data, siblingData }) => {\n if (\n !data?.filename ||\n !isPrivateDocument({ imagekit: siblingData }, isPrivate)\n ) {\n return undefined\n }\n const key = buildStoragePathData({\n collectionPrefix: prefix,\n docPrefix: path.posix.join(data.prefix || prefix, data._objectKey || ''),\n filename: data.filename,\n }).storageFilePath\n return buildURL(key, true)\n },\n ],\n },\n label: 'Signed URL',\n virtual: true,\n },\n { name: 'thumbnailUrl', type: 'text', admin: { hidden: true } },\n ],\n interfaceName: 'imagekitFields',\n label: false,\n },\n ],\n label: 'Storage: ImageKit',\n },\n ]\n}\n"],"names":["ImageKit","toFile","cloudStoragePlugin","buildStoragePathData","openAsBlob","path","APIError","assertClientUploadAllowed","getRangeRequestInfo","isXmlMimeType","uploadContentSecurityPolicy","verifyClientUploadReceipt","completeImageKitUpload","createImageKitUploadInstructions","getAdminThumbnailURL","getPrivateAdminThumbnailURL","isPdfMimeType","isVideoMimeType","createImageKitFileStore","getImageKitThumbnailSrc","getVideoPosterSrc","imagekitStorage","options","name","collections","Object","keys","init","config","initializeStorage","migrateFolder","payloadStorageImagekit","payloadStorageImageKit","key","trim","Error","folder","replace","split","some","segment","client","privateKey","length","configured","Set","apiRoute","routes","api","expiresIn","signedUrlExpiresIn","prefixFor","slug","posix","join","collectionIsPrivate","value","private","files","urlEndpoint","completeUpload","access","clientUploads","undefined","fromEntries","map","isPrivate","prefix","sourceConfig","collection","has","disablePayloadAccessControl","canStoreXml","setting","focalPoint","description","admin","hooks","beforeChange","data","operation","originalDoc","req","verified","context","imagekitVerifiedUpload","existing","payload","db","findOne","where","equals","fileId","id","applyMetadata","upload","adminThumbnail","doc","isPrivateDocument","crop","adapter","buildURL","signed","transformation","helper","buildSrc","src","raw","deleteFile","storageFilePath","remove","copyFile","from","to","find","source","body","customMetadata","file","Boolean","isPrivateFile","fileName","basename","dirname","overwriteFile","tags","useUniqueFileName","fields","storageFields","generateURL","filename","docPrefix","collectionPrefix","handleDelete","handleUpload","uploadSource","tempFilePath","type","mimeType","buffer","uploaded","mime","size","filesize","thumbnail","thumbnailUrl","url","staticHandler","params","uploadReference","reference","receipt","collectionSlug","signedReceipt","expectedPath","filePrefix","_objectKey","startsWith","findUploadedFile","fetchOriginal","error","Response","json","message","status","document","getFileLocation","privateFile","tr","URL","searchParams","get","storedURL","imagekit","original","redirect","supportsTempFiles","uploadInstructions","adminHandler","enabled","endpoint","handler","generate","publicKey","requiresUploadReceipt","useInAdmin","output","disableLocalStorage","field","defaultValue","delay","Promise","resolve","setTimeout","fallback","values","variants","thumbnailURL","range","fileSize","rangeHeader","headers","response","fetch","cache","Range","rangeStart","rangeEnd","signal","AbortController","cancel","catch","Headers","set","allowRestrictedFileTypes","mimeTypes","includes","condition","initCollapsed","disabled","column","filter","hidden","unique","readOnly","label","_data","siblingData","afterRead","virtual","interfaceName"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@humaan/payload-storage-imagekit",
3
- "version": "3.0.0-canary.0",
3
+ "version": "3.0.0-canary.1",
4
4
  "description": "ImageKit storage adapter plugin for Payload CMS uploads",
5
5
  "license": "MIT",
6
6
  "type": "module",