@humaan/payload-storage-imagekit 2.9.1 → 3.0.0-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -7
- package/dist/clientUploads.d.ts +4 -41
- package/dist/clientUploads.js +1 -8
- package/dist/clientUploads.js.map +1 -1
- package/dist/components/BeforeDashboardServer.d.ts +1 -1
- package/dist/components/BeforeDashboardServer.js +6 -1
- package/dist/components/BeforeDashboardServer.js.map +1 -1
- package/dist/components/ImageKitUploadHandler.d.ts +3 -4
- package/dist/components/ImageKitUploadHandler.js +42 -80
- package/dist/components/ImageKitUploadHandler.js.map +1 -1
- package/dist/endpoints/createImageKitUploadInstructions.d.ts +14 -0
- package/dist/endpoints/createImageKitUploadInstructions.js +116 -0
- package/dist/endpoints/createImageKitUploadInstructions.js.map +1 -0
- package/dist/index.d.ts +6 -28
- package/dist/index.js +456 -638
- package/dist/index.js.map +1 -1
- package/dist/migrate.js +4 -1
- package/dist/migrate.js.map +1 -1
- package/package.json +13 -13
- package/dist/clientUploadVerification.d.ts +0 -30
- package/dist/clientUploadVerification.js +0 -106
- package/dist/clientUploadVerification.js.map +0 -1
- package/dist/endpoints/createImageKitUploadAuthHandler.d.ts +0 -26
- package/dist/endpoints/createImageKitUploadAuthHandler.js +0 -215
- package/dist/endpoints/createImageKitUploadAuthHandler.js.map +0 -1
package/README.md
CHANGED
|
@@ -21,8 +21,26 @@ This plugin integrates `@payloadcms/plugin-cloud-storage` with ImageKit and prov
|
|
|
21
21
|
pnpm add @humaan/payload-storage-imagekit
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
Payload `
|
|
25
|
-
`@payloadcms/plugin-cloud-storage`.
|
|
24
|
+
This branch targets Payload `4.0.0-beta.1`. Use that exact version of `payload` and
|
|
25
|
+
`@payloadcms/plugin-cloud-storage`. Node.js >=24.15.0 is required.
|
|
26
|
+
|
|
27
|
+
### Maintainer releases
|
|
28
|
+
|
|
29
|
+
In **Actions → Start release**, set `release_branch` to the branch to release from
|
|
30
|
+
(default: `main`). The version/changelog PR targets that branch. Stable `2.x`
|
|
31
|
+
releases continue from `main`; for Payload v4, select `feat/payload-v4-beta`,
|
|
32
|
+
`release_type: custom`, and `custom_version: 3.0.0-canary.0` (increment the canary
|
|
33
|
+
number for subsequent releases). The v4 feature PR does not need to merge.
|
|
34
|
+
|
|
35
|
+
Non-main releases must be prereleases. `canary` versions publish under the npm
|
|
36
|
+
`canary` tag without changing `latest`, after the release commit passes push CI.
|
|
37
|
+
For a publication dry run or receipt-based recovery, choose the same branch in
|
|
38
|
+
**Publish release (automatic)**. Recovery still requires the original receipt run
|
|
39
|
+
ID and the `publish` confirmation.
|
|
40
|
+
|
|
41
|
+
The shared `humaan/actions@v2` update must be deployed first, and these caller
|
|
42
|
+
workflow changes must be present on the selected branch. The `release` environment
|
|
43
|
+
must permit that branch under its deployment policy.
|
|
26
44
|
|
|
27
45
|
## Usage
|
|
28
46
|
|
|
@@ -38,7 +56,7 @@ export default buildConfig({
|
|
|
38
56
|
fields: [],
|
|
39
57
|
},
|
|
40
58
|
],
|
|
41
|
-
|
|
59
|
+
storage: [
|
|
42
60
|
imagekitStorage({
|
|
43
61
|
clientUploads: true,
|
|
44
62
|
folder: '/payload-uploads',
|
|
@@ -67,9 +85,40 @@ export default buildConfig({
|
|
|
67
85
|
- `signedUrlExpiresIn` (optional): expiry in seconds for the signed URLs generated for [private](#private-files) collections. Defaults to `900` (15 minutes). Signed URLs are generated fresh on every request, so a short window is safe.
|
|
68
86
|
- `enabled` (optional): enable / disable plugin
|
|
69
87
|
- `focalPoint` (optional): broad default for Payload's native focal-point picker (`upload.focalPoint`) on every configured collection. Focal points are disabled by default for configured collections, so prefer the per-collection `collections.<slug>.focalPoint` option for mixed upload collections, e.g. enable it for `media` and leave it off for `documents`. When `true`, editors can place a focal point and `focalX`/`focalY` (0–100) are stored on each doc for focal-aware cropping at render time. A collection's own `upload.focalPoint` still takes precedence.
|
|
70
|
-
- `alwaysInsertFields` (optional): pass-through to cloud storage plugin
|
|
71
88
|
- `disablePayloadAccessControl` (optional): pass-through to cloud storage plugin. It cannot be enabled for private collections or collections that can store SVG/XML, because those files require Payload's file route for signing or Content Security Policy headers. To use this option, every configured collection must have a MIME allow list that excludes XML-family types.
|
|
72
89
|
|
|
90
|
+
## Payload 4 migration / local development
|
|
91
|
+
|
|
92
|
+
These compatibility changes do not bump the package version or publish a release.
|
|
93
|
+
A custom canary can be released separately. Build a local snapshot with
|
|
94
|
+
`pnpm pack --out local-canary.tgz`, then reference that archive from the consuming
|
|
95
|
+
app with a `file:` dependency. Repack and update the dependency after edits.
|
|
96
|
+
|
|
97
|
+
Adapters are now registered in `config.storage`. Direct uploads use the shared
|
|
98
|
+
`POST /api/upload-instructions`, the `imagekit` dispatch handler, and Payload's
|
|
99
|
+
signed `uploadReference`. The old `/imagekit-upload-auth` endpoint and
|
|
100
|
+
`clientUploadContext` completion protocol are removed.
|
|
101
|
+
|
|
102
|
+
Each new source file is stored as `<prefix>/<objectKey>/<name>-original.ext`.
|
|
103
|
+
Payload manages originals, current representations, archived revisions, and
|
|
104
|
+
rollback cleanup through the adapter's copy/delete capabilities. XML-family files
|
|
105
|
+
remain server-mediated. Existing v3 records without `original`/`_objectKey`,
|
|
106
|
+
including records with missing or slash-prefixed prefixes, remain readable.
|
|
107
|
+
|
|
108
|
+
The browser still reauthorizes mislabeled raster images using content-detected
|
|
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.
|
|
112
|
+
|
|
113
|
+
Run `pnpm exec vitest run` for mock-provider REST/MongoDB, client transport, and
|
|
114
|
+
folder-migration coverage. Live provider tests require explicitly configured
|
|
115
|
+
`IMAGEKIT_INTEGRATION_*` credentials and are otherwise skipped. The integration
|
|
116
|
+
suite was migrated from the removed v3 endpoint/receipt protocol to the v4 REST
|
|
117
|
+
boundary; the obsolete context-only verification helper is no longer shipped.
|
|
118
|
+
|
|
119
|
+
The folder migration utility covers current/legacy records; migrating every
|
|
120
|
+
archived v4 representation between accounts needs a separate history-aware plan.
|
|
121
|
+
|
|
73
122
|
## Exports
|
|
74
123
|
|
|
75
124
|
- `imagekitStorage` (primary)
|
|
@@ -89,9 +138,7 @@ export default buildConfig({
|
|
|
89
138
|
Use the browser-safe `image` export in app image components to build ImageKit URLs from Payload upload data without importing the server plugin entry.
|
|
90
139
|
|
|
91
140
|
```ts
|
|
92
|
-
import {
|
|
93
|
-
withImageKitTransformation,
|
|
94
|
-
} from '@humaan/payload-storage-imagekit/image'
|
|
141
|
+
import { withImageKitTransformation } from '@humaan/payload-storage-imagekit/image'
|
|
95
142
|
|
|
96
143
|
const src = withImageKitTransformation(media.imagekit.url, {
|
|
97
144
|
focalX: media.focalX,
|
package/dist/clientUploads.d.ts
CHANGED
|
@@ -1,20 +1,7 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* proof. This backwards-compatible shape remains exported for existing type consumers.
|
|
6
|
-
*/
|
|
7
|
-
export type ImageKitClientUploadContext = {
|
|
8
|
-
fileId?: string;
|
|
9
|
-
filePath?: string;
|
|
10
|
-
fileType?: string;
|
|
11
|
-
mimeType?: string;
|
|
12
|
-
name?: string;
|
|
13
|
-
size?: number;
|
|
14
|
-
thumbnailUrl?: string;
|
|
15
|
-
url?: string;
|
|
16
|
-
} & Partial<ClientUploadContext>;
|
|
17
|
-
/** @deprecated The upload handler is configured internally by Payload 3.90.1. */
|
|
1
|
+
import type { UploadReference } from '@payloadcms/plugin-cloud-storage/types';
|
|
2
|
+
/** @deprecated Use Payload 4's UploadReference and UploadInstructions types. */
|
|
3
|
+
export type ImageKitClientUploadContext = Partial<UploadReference>;
|
|
4
|
+
/** @deprecated The upload handler is configured by the storage capability. */
|
|
18
5
|
export type ImageKitUploadHandlerProps = {
|
|
19
6
|
authPath: `/${string}`;
|
|
20
7
|
collectionSlug: string;
|
|
@@ -22,20 +9,6 @@ export type ImageKitUploadHandlerProps = {
|
|
|
22
9
|
isPrivate?: boolean;
|
|
23
10
|
publicKey: string;
|
|
24
11
|
};
|
|
25
|
-
export type ImageKitReceiptContext = {
|
|
26
|
-
/** Legacy uploads stored beneath a per-upload folder. */
|
|
27
|
-
_objectKey?: string;
|
|
28
|
-
collectionSlug: string;
|
|
29
|
-
expectedMimeType: string;
|
|
30
|
-
expectedSize: number;
|
|
31
|
-
/** Present only after the server verifies the completed upload. */
|
|
32
|
-
fileId?: string;
|
|
33
|
-
filename: string;
|
|
34
|
-
isPrivate: boolean;
|
|
35
|
-
prefix: string;
|
|
36
|
-
/** Binds a pending authorization to the newly uploaded provider object. */
|
|
37
|
-
uploadTag?: string;
|
|
38
|
-
};
|
|
39
12
|
export type ImageKitUploadPayload = {
|
|
40
13
|
checks: string;
|
|
41
14
|
fileName: string;
|
|
@@ -45,13 +18,3 @@ export type ImageKitUploadPayload = {
|
|
|
45
18
|
tags: string;
|
|
46
19
|
useUniqueFileName: 'false';
|
|
47
20
|
};
|
|
48
|
-
export type ImageKitUploadAuthorization = {
|
|
49
|
-
clientUploadContext: ClientUploadContext;
|
|
50
|
-
filename: string;
|
|
51
|
-
token: string;
|
|
52
|
-
uploadPayload: ImageKitUploadPayload;
|
|
53
|
-
};
|
|
54
|
-
export type ImageKitUploadCompletion = {
|
|
55
|
-
clientUploadContext: ClientUploadContext;
|
|
56
|
-
};
|
|
57
|
-
export declare const isImageKitReceiptContext: (value: unknown) => value is ImageKitReceiptContext;
|
package/dist/clientUploads.js
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
export
|
|
2
|
-
export const isImageKitReceiptContext = (value)=>{
|
|
3
|
-
if (!value || typeof value !== 'object') {
|
|
4
|
-
return false;
|
|
5
|
-
}
|
|
6
|
-
const context = value;
|
|
7
|
-
return (context._objectKey === undefined || typeof context._objectKey === 'string' && context._objectKey.length > 0) && (context.uploadTag === undefined || typeof context.uploadTag === 'string' && context.uploadTag.length > 0) && Boolean(context._objectKey || context.uploadTag || context.fileId) && typeof context.collectionSlug === 'string' && context.collectionSlug.length > 0 && typeof context.expectedMimeType === 'string' && context.expectedMimeType.length > 0 && typeof context.expectedSize === 'number' && Number.isSafeInteger(context.expectedSize) && context.expectedSize >= 0 && (context.fileId === undefined || typeof context.fileId === 'string' && context.fileId.length > 0) && typeof context.filename === 'string' && context.filename.length > 0 && typeof context.isPrivate === 'boolean' && typeof context.prefix === 'string';
|
|
8
|
-
};
|
|
1
|
+
export { };
|
|
9
2
|
|
|
10
3
|
//# sourceMappingURL=clientUploads.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/clientUploads.ts"],"sourcesContent":["import type {
|
|
1
|
+
{"version":3,"sources":["../src/clientUploads.ts"],"sourcesContent":["import type { UploadReference } from '@payloadcms/plugin-cloud-storage/types'\n\n/** @deprecated Use Payload 4's UploadReference and UploadInstructions types. */\nexport type ImageKitClientUploadContext = Partial<UploadReference>\n\n/** @deprecated The upload handler is configured by the storage capability. */\nexport type ImageKitUploadHandlerProps = {\n authPath: `/${string}`\n collectionSlug: string\n folder: string\n isPrivate?: boolean\n publicKey: string\n}\n\nexport type ImageKitUploadPayload = {\n checks: string\n fileName: string\n folder: string\n isPrivateFile: 'false' | 'true'\n overwriteFile: 'false'\n tags: string\n useUniqueFileName: 'false'\n}\n"],"names":[],"mappings":"AAcA,WAQC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { ServerComponentProps } from 'payload';
|
|
2
|
-
export declare const BeforeDashboardServer: (props: ServerComponentProps) => Promise<import("react").JSX.Element>;
|
|
2
|
+
export declare const BeforeDashboardServer: (props: ServerComponentProps) => Promise<import("react").JSX.Element | null>;
|
|
@@ -2,8 +2,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import styles from './BeforeDashboardServer.module.css';
|
|
3
3
|
export const BeforeDashboardServer = async (props)=>{
|
|
4
4
|
const { payload } = props;
|
|
5
|
+
const collection = payload.config.collections.find((collection)=>String(collection.slug) === 'plugin-collection');
|
|
6
|
+
if (!collection) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
5
9
|
const { docs } = await payload.find({
|
|
6
|
-
collection:
|
|
10
|
+
collection: collection.slug,
|
|
11
|
+
overrideAccess: true
|
|
7
12
|
});
|
|
8
13
|
return /*#__PURE__*/ _jsxs("div", {
|
|
9
14
|
className: styles.wrapper,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/BeforeDashboardServer.tsx"],"sourcesContent":["import type { ServerComponentProps } from 'payload'\n\nimport styles from './BeforeDashboardServer.module.css'\n\nexport const BeforeDashboardServer = async (props: ServerComponentProps) => {\n const { payload } = props\n\n const { docs } = await payload.find({ collection:
|
|
1
|
+
{"version":3,"sources":["../../src/components/BeforeDashboardServer.tsx"],"sourcesContent":["import type { ServerComponentProps } from 'payload'\n\nimport styles from './BeforeDashboardServer.module.css'\n\nexport const BeforeDashboardServer = async (props: ServerComponentProps) => {\n const { payload } = props\n\n const collection = payload.config.collections.find(\n (collection) => String(collection.slug) === 'plugin-collection',\n )\n if (!collection) {\n return null\n }\n const { docs } = await payload.find({ collection: collection.slug, overrideAccess: true })\n\n return (\n <div className={styles.wrapper}>\n <h1>Added by the plugin: Before Dashboard Server</h1>\n Docs from Local API:\n {docs.map((doc) => (\n <div key={doc.id}>{doc.id}</div>\n ))}\n </div>\n )\n}\n"],"names":["styles","BeforeDashboardServer","props","payload","collection","config","collections","find","String","slug","docs","overrideAccess","div","className","wrapper","h1","map","doc","id"],"mappings":";AAEA,OAAOA,YAAY,qCAAoC;AAEvD,OAAO,MAAMC,wBAAwB,OAAOC;IAC1C,MAAM,EAAEC,OAAO,EAAE,GAAGD;IAEpB,MAAME,aAAaD,QAAQE,MAAM,CAACC,WAAW,CAACC,IAAI,CAChD,CAACH,aAAeI,OAAOJ,WAAWK,IAAI,MAAM;IAE9C,IAAI,CAACL,YAAY;QACf,OAAO;IACT;IACA,MAAM,EAAEM,IAAI,EAAE,GAAG,MAAMP,QAAQI,IAAI,CAAC;QAAEH,YAAYA,WAAWK,IAAI;QAAEE,gBAAgB;IAAK;IAExF,qBACE,MAACC;QAAIC,WAAWb,OAAOc,OAAO;;0BAC5B,KAACC;0BAAG;;YAAiD;YAEpDL,KAAKM,GAAG,CAAC,CAACC,oBACT,KAACL;8BAAkBK,IAAIC,EAAE;mBAAfD,IAAIC,EAAE;;;AAIxB,EAAC"}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
export declare const ImageKitUploadHandler: ({ children, collectionSlug,
|
|
1
|
+
export declare const ImageKitUploadHandler: ({ children, collectionSlug, endpointPath, prefix, props, }: {
|
|
2
2
|
children: import("react").ReactNode;
|
|
3
3
|
collectionSlug: import("payload").UploadCollectionSlug;
|
|
4
|
-
|
|
5
|
-
extra: Record<string, unknown>;
|
|
4
|
+
endpointPath?: `/${string}`;
|
|
6
5
|
prefix?: string;
|
|
7
|
-
|
|
6
|
+
props: Record<string, unknown>;
|
|
8
7
|
}) => import("react").JSX.Element;
|
|
@@ -2,85 +2,26 @@
|
|
|
2
2
|
import { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client';
|
|
3
3
|
import { formatAdminURL } from 'payload/shared';
|
|
4
4
|
import { getClientImageIdentity } from '../clientImageIdentity.js';
|
|
5
|
-
const uploadAPIURL = 'https://upload.imagekit.io/api/v2/files/upload';
|
|
6
|
-
const readErrorMessage = async (response, fallback)=>{
|
|
7
|
-
try {
|
|
8
|
-
const result = await response.json();
|
|
9
|
-
if (typeof result.message === 'string' && result.message.trim().length > 0) {
|
|
10
|
-
return result.message;
|
|
11
|
-
}
|
|
12
|
-
const messages = result.errors?.map((error)=>error.message).filter((message)=>Boolean(message));
|
|
13
|
-
if (messages?.length) {
|
|
14
|
-
return messages.join(', ');
|
|
15
|
-
}
|
|
16
|
-
} catch {
|
|
17
|
-
// Ignore non-JSON responses and fall back to the default message.
|
|
18
|
-
}
|
|
19
|
-
return fallback;
|
|
20
|
-
};
|
|
21
5
|
export const ImageKitUploadHandler = createClientUploadHandler({
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
serverURL
|
|
27
|
-
});
|
|
6
|
+
name: 'imagekit',
|
|
7
|
+
handler: async ({ apiRoute, collectionSlug, data, docPrefix, file, serverURL, updateFilename })=>{
|
|
8
|
+
let authorization = data;
|
|
9
|
+
let uploadReference;
|
|
28
10
|
const identity = await getClientImageIdentity(file);
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
body: JSON.stringify(authBody),
|
|
38
|
-
credentials: 'include',
|
|
39
|
-
headers: {
|
|
40
|
-
'Content-Type': 'application/json'
|
|
41
|
-
},
|
|
42
|
-
method: 'POST'
|
|
43
|
-
});
|
|
44
|
-
if (!authResponse.ok) {
|
|
45
|
-
throw new Error(await readErrorMessage(authResponse, 'Failed to authorize direct ImageKit upload.'));
|
|
46
|
-
}
|
|
47
|
-
const authorization = await authResponse.json();
|
|
48
|
-
if (authorization.filename !== file.name) {
|
|
49
|
-
updateFilename(authorization.filename);
|
|
50
|
-
}
|
|
51
|
-
const formData = new FormData();
|
|
52
|
-
formData.set('file', file);
|
|
53
|
-
formData.set('token', authorization.token);
|
|
54
|
-
for (const [key, value] of Object.entries(authorization.uploadPayload)){
|
|
55
|
-
formData.set(key, value);
|
|
56
|
-
}
|
|
57
|
-
const uploadResponse = await fetch(uploadAPIURL, {
|
|
58
|
-
body: formData,
|
|
59
|
-
method: 'POST'
|
|
60
|
-
});
|
|
61
|
-
if (!uploadResponse.ok) {
|
|
62
|
-
const message = await readErrorMessage(uploadResponse, 'Failed to upload file to ImageKit.');
|
|
63
|
-
const isFilenameConflict = (uploadResponse.status === 400 || uploadResponse.status === 409) && message.includes('A file with the same name already exists at the exact location');
|
|
64
|
-
if (isFilenameConflict) {
|
|
65
|
-
authBody = {
|
|
66
|
-
action: 'retry',
|
|
67
|
-
collectionSlug,
|
|
68
|
-
signedReceipt: authorization.clientUploadContext.signedReceipt
|
|
69
|
-
};
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
throw new Error(message);
|
|
73
|
-
}
|
|
74
|
-
const uploaded = await uploadResponse.json();
|
|
75
|
-
if (typeof uploaded.fileId !== 'string' || !uploaded.fileId.trim()) {
|
|
76
|
-
throw new Error('ImageKit did not return an uploaded file ID.');
|
|
77
|
-
}
|
|
78
|
-
const completionResponse = await fetch(authURL, {
|
|
11
|
+
// Payload's initial instructions use the browser's MIME hint. Re-authorize
|
|
12
|
+
// mislabeled image bytes through the same shared endpoint, not a private API.
|
|
13
|
+
if (identity.filename !== file.name || identity.mimeType !== file.type) {
|
|
14
|
+
const response = await fetch(formatAdminURL({
|
|
15
|
+
apiRoute,
|
|
16
|
+
path: '/upload-instructions',
|
|
17
|
+
serverURL
|
|
18
|
+
}), {
|
|
79
19
|
body: JSON.stringify({
|
|
80
|
-
action: 'complete',
|
|
81
20
|
collectionSlug,
|
|
82
|
-
|
|
83
|
-
|
|
21
|
+
docPrefix,
|
|
22
|
+
filename: identity.filename,
|
|
23
|
+
filesize: file.size,
|
|
24
|
+
mimeType: identity.mimeType
|
|
84
25
|
}),
|
|
85
26
|
credentials: 'include',
|
|
86
27
|
headers: {
|
|
@@ -88,13 +29,34 @@ export const ImageKitUploadHandler = createClientUploadHandler({
|
|
|
88
29
|
},
|
|
89
30
|
method: 'POST'
|
|
90
31
|
});
|
|
91
|
-
if (!
|
|
92
|
-
throw new Error(
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
throw new Error('Failed to authorize detected image content.');
|
|
34
|
+
}
|
|
35
|
+
const instructions = await response.json();
|
|
36
|
+
if (instructions.type !== 'dispatch' || instructions.name !== 'imagekit') {
|
|
37
|
+
throw new Error('Expected ImageKit upload instructions.');
|
|
93
38
|
}
|
|
94
|
-
|
|
95
|
-
|
|
39
|
+
authorization = instructions.data;
|
|
40
|
+
uploadReference = instructions.file.uploadReference;
|
|
41
|
+
updateFilename(instructions.file.filename);
|
|
42
|
+
}
|
|
43
|
+
const formData = new FormData();
|
|
44
|
+
formData.set('file', file);
|
|
45
|
+
formData.set('token', authorization.token);
|
|
46
|
+
for (const [key, value] of Object.entries(authorization.uploadPayload)){
|
|
47
|
+
formData.set(key, value);
|
|
48
|
+
}
|
|
49
|
+
const response = await fetch('https://upload.imagekit.io/api/v2/files/upload', {
|
|
50
|
+
body: formData,
|
|
51
|
+
method: 'POST'
|
|
52
|
+
});
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
const error = await response.json().catch(()=>null);
|
|
55
|
+
throw new Error(error?.message || 'Failed to upload file to ImageKit.');
|
|
96
56
|
}
|
|
97
|
-
|
|
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.
|
|
59
|
+
return uploadReference;
|
|
98
60
|
}
|
|
99
61
|
});
|
|
100
62
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/ImageKitUploadHandler.tsx"],"sourcesContent":["'use client'\n\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\nimport { formatAdminURL } from 'payload/shared'\n\nimport
|
|
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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { GenerateUploadInstructions, UploadInstructionsAccess } from 'payload';
|
|
2
|
+
/** ImageKit enforces these constraints before accepting any provider-direct bytes. */
|
|
3
|
+
export declare const getUploadChecks: ({ allowedMimeTypes, allowRestrictedFileTypes, filesize, }: {
|
|
4
|
+
allowedMimeTypes: string[];
|
|
5
|
+
allowRestrictedFileTypes: boolean;
|
|
6
|
+
filesize: number;
|
|
7
|
+
}) => string;
|
|
8
|
+
export declare const createImageKitUploadInstructions: ({ access, isPrivate, prefix, privateKey, publicKey, }: {
|
|
9
|
+
access?: UploadInstructionsAccess;
|
|
10
|
+
isPrivate: boolean;
|
|
11
|
+
prefix: string;
|
|
12
|
+
privateKey: string;
|
|
13
|
+
publicKey: string;
|
|
14
|
+
}) => GenerateUploadInstructions;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { buildUploadStoragePathData } from '@payloadcms/plugin-cloud-storage/utilities';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { posix } from 'node:path';
|
|
4
|
+
import { Forbidden } from 'payload';
|
|
5
|
+
import { assertClientUploadAccess, assertClientUploadAllowed, createClientUploadReceipt, getOriginalFilename, getSafeFileName, incrementName } from 'payload/internal';
|
|
6
|
+
import { getSanitizedUploadFilename } from 'payload/shared';
|
|
7
|
+
import { createImageKitUploadToken } from '../imageKitUploadToken.js';
|
|
8
|
+
/** ImageKit enforces these constraints before accepting any provider-direct bytes. */ export const getUploadChecks = ({ allowedMimeTypes, allowRestrictedFileTypes, filesize })=>{
|
|
9
|
+
const checks = [
|
|
10
|
+
`"file.size" = ${filesize}`
|
|
11
|
+
];
|
|
12
|
+
const prefixes = allowedMimeTypes.map((mimeType)=>mimeType.replace('*', '')).filter((prefix)=>!/["\\]/.test(prefix));
|
|
13
|
+
if (prefixes.length && !prefixes.includes('')) {
|
|
14
|
+
checks.push(`(${prefixes.map((prefix)=>`"file.mime" : "${prefix}"`).join(' OR ')})`);
|
|
15
|
+
}
|
|
16
|
+
if (!allowRestrictedFileTypes) {
|
|
17
|
+
checks.push('"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"]');
|
|
18
|
+
}
|
|
19
|
+
return checks.join(' AND ');
|
|
20
|
+
};
|
|
21
|
+
export const createImageKitUploadInstructions = ({ access = ({ req })=>Boolean(req.user), isPrivate, prefix, privateKey, publicKey })=>async ({ collectionSlug, docPrefix, filename, filesize, mimeType, overrideAccess, req })=>{
|
|
22
|
+
if (!overrideAccess) {
|
|
23
|
+
await assertClientUploadAccess({
|
|
24
|
+
collectionSlug,
|
|
25
|
+
req
|
|
26
|
+
});
|
|
27
|
+
if (!await access({
|
|
28
|
+
collectionSlug,
|
|
29
|
+
req
|
|
30
|
+
})) {
|
|
31
|
+
throw new Forbidden(req.t);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const collection = req.payload.collections[collectionSlug].config;
|
|
35
|
+
assertClientUploadAllowed({
|
|
36
|
+
collection,
|
|
37
|
+
filename,
|
|
38
|
+
mimeType
|
|
39
|
+
});
|
|
40
|
+
let safeFilename = await getSafeFileName({
|
|
41
|
+
collectionSlug,
|
|
42
|
+
desiredFilename: getSanitizedUploadFilename(filename).replace(/[^\w.-]/g, '_'),
|
|
43
|
+
req
|
|
44
|
+
});
|
|
45
|
+
// Without a transformer, v4 exposes the retained original as the current file.
|
|
46
|
+
// Reserve both names so repeated uploads cannot violate filename uniqueness.
|
|
47
|
+
while(await getSafeFileName({
|
|
48
|
+
collectionSlug,
|
|
49
|
+
desiredFilename: getOriginalFilename({
|
|
50
|
+
filename: safeFilename
|
|
51
|
+
}),
|
|
52
|
+
req
|
|
53
|
+
}) !== getOriginalFilename({
|
|
54
|
+
filename: safeFilename
|
|
55
|
+
})){
|
|
56
|
+
safeFilename = incrementName(safeFilename);
|
|
57
|
+
}
|
|
58
|
+
const originalFilename = getOriginalFilename({
|
|
59
|
+
filename: safeFilename
|
|
60
|
+
});
|
|
61
|
+
const objectKey = randomUUID();
|
|
62
|
+
const resolved = buildUploadStoragePathData({
|
|
63
|
+
collectionPrefix: prefix,
|
|
64
|
+
docPrefix: docPrefix ?? prefix,
|
|
65
|
+
filename: originalFilename
|
|
66
|
+
});
|
|
67
|
+
// The admin sends an empty docPrefix for new files; the resolved destination retains the collection fallback.
|
|
68
|
+
const filePrefix = posix.dirname(resolved.storageFilePath);
|
|
69
|
+
const storageFilePath = posix.join(filePrefix, objectKey, originalFilename);
|
|
70
|
+
const uploadReference = {
|
|
71
|
+
_objectKey: objectKey,
|
|
72
|
+
prefix: filePrefix,
|
|
73
|
+
signedReceipt: createClientUploadReceipt({
|
|
74
|
+
_objectKey: objectKey,
|
|
75
|
+
collectionSlug,
|
|
76
|
+
filename: safeFilename,
|
|
77
|
+
filePrefix,
|
|
78
|
+
req,
|
|
79
|
+
storageFilePath
|
|
80
|
+
})
|
|
81
|
+
};
|
|
82
|
+
const upload = typeof collection.upload === 'object' ? collection.upload : undefined;
|
|
83
|
+
const uploadPayload = {
|
|
84
|
+
checks: getUploadChecks({
|
|
85
|
+
allowedMimeTypes: upload?.mimeTypes ?? [],
|
|
86
|
+
allowRestrictedFileTypes: upload?.allowRestrictedFileTypes ?? false,
|
|
87
|
+
filesize
|
|
88
|
+
}),
|
|
89
|
+
fileName: originalFilename,
|
|
90
|
+
folder: `/${posix.dirname(storageFilePath)}`,
|
|
91
|
+
isPrivateFile: isPrivate ? 'true' : 'false',
|
|
92
|
+
overwriteFile: 'false',
|
|
93
|
+
tags: `payload-upload-${objectKey}`,
|
|
94
|
+
useUniqueFileName: 'false'
|
|
95
|
+
};
|
|
96
|
+
return {
|
|
97
|
+
name: 'imagekit',
|
|
98
|
+
type: 'dispatch',
|
|
99
|
+
data: {
|
|
100
|
+
token: createImageKitUploadToken({
|
|
101
|
+
privateKey,
|
|
102
|
+
publicKey,
|
|
103
|
+
uploadPayload
|
|
104
|
+
}),
|
|
105
|
+
uploadPayload
|
|
106
|
+
},
|
|
107
|
+
file: {
|
|
108
|
+
filename: safeFilename,
|
|
109
|
+
mimeType,
|
|
110
|
+
size: filesize,
|
|
111
|
+
uploadReference
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
//# sourceMappingURL=createImageKitUploadInstructions.js.map
|
|
@@ -0,0 +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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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,47 +1,25 @@
|
|
|
1
1
|
import type { ClientUploadsConfig } from '@payloadcms/plugin-cloud-storage/types';
|
|
2
|
-
import type {
|
|
2
|
+
import type { StorageAdapter, UploadCollectionSlug } from 'payload';
|
|
3
3
|
export type ImageKitCollectionOptions = {
|
|
4
|
-
/**
|
|
5
|
-
* Override Payload's native focal-point picker (`upload.focalPoint`) for this
|
|
6
|
-
* collection.
|
|
7
|
-
*/
|
|
8
4
|
focalPoint?: boolean;
|
|
9
|
-
/**
|
|
10
|
-
* Upload files in this collection as ImageKit private files (`isPrivateFile: true`)
|
|
11
|
-
* and deliver them only through short-lived signed URLs, behind Payload access control.
|
|
12
|
-
*/
|
|
13
5
|
private?: boolean;
|
|
14
6
|
};
|
|
15
|
-
type ImageKitCollectionConfig = ImageKitCollectionOptions | true;
|
|
16
|
-
type ImageKitCollectionMap = Partial<Record<UploadCollectionSlug, ImageKitCollectionConfig>>;
|
|
17
7
|
export type PayloadStorageImageKitConfig = {
|
|
18
|
-
alwaysInsertFields?: boolean;
|
|
19
8
|
clientUploads?: ClientUploadsConfig;
|
|
20
|
-
collections:
|
|
9
|
+
collections: Partial<Record<UploadCollectionSlug, ImageKitCollectionOptions | true>>;
|
|
21
10
|
disablePayloadAccessControl?: true;
|
|
22
11
|
enabled?: boolean;
|
|
23
|
-
/**
|
|
24
|
-
* Broad default for Payload's native focal-point picker (`upload.focalPoint`)
|
|
25
|
-
* on every configured collection. Focal points are disabled by default for
|
|
26
|
-
* configured collections, so prefer collection-level `focalPoint: true` for
|
|
27
|
-
* image collections. A collection's own `upload.focalPoint` still takes
|
|
28
|
-
* precedence.
|
|
29
|
-
*/
|
|
30
12
|
focalPoint?: boolean;
|
|
31
13
|
folder: string;
|
|
32
14
|
privateKey: string;
|
|
33
15
|
publicKey: string;
|
|
34
|
-
/**
|
|
35
|
-
* Expiry (in seconds) applied to signed URLs generated for private collections.
|
|
36
|
-
* Defaults to 900 (15 minutes). URLs are signed fresh on every request, so a short
|
|
37
|
-
* window is safe.
|
|
38
|
-
*/
|
|
39
16
|
signedUrlExpiresIn?: number;
|
|
40
17
|
urlEndpoint: string;
|
|
41
18
|
};
|
|
42
|
-
|
|
19
|
+
/** Payload 4 storage capability. Register under config.storage, not config.plugins. */
|
|
20
|
+
export declare const imagekitStorage: (options: PayloadStorageImageKitConfig) => StorageAdapter;
|
|
43
21
|
export { migrateFolder } from './migrate.js';
|
|
44
22
|
export type { MigrateFolderOptions, MigrationJobResult, MigrationLocation } from './migrate.js';
|
|
45
|
-
export declare const payloadStorageImagekit: (
|
|
46
|
-
export declare const payloadStorageImageKit: (
|
|
23
|
+
export declare const payloadStorageImagekit: (options: PayloadStorageImageKitConfig) => StorageAdapter;
|
|
24
|
+
export declare const payloadStorageImageKit: (options: PayloadStorageImageKitConfig) => StorageAdapter;
|
|
47
25
|
export type PayloadStorageImagekitConfig = PayloadStorageImageKitConfig;
|