@humaan/payload-storage-imagekit 2.9.1 → 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 +73 -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 +69 -80
- package/dist/components/ImageKitUploadHandler.js.map +1 -1
- package/dist/endpoints/completeImageKitUpload.d.ts +10 -0
- package/dist/endpoints/completeImageKitUpload.js +84 -0
- package/dist/endpoints/completeImageKitUpload.js.map +1 -0
- package/dist/endpoints/createImageKitUploadInstructions.d.ts +14 -0
- package/dist/endpoints/createImageKitUploadInstructions.js +117 -0
- package/dist/endpoints/createImageKitUploadInstructions.js.map +1 -0
- package/dist/imageKitFileStore.d.ts +129 -0
- package/dist/imageKitFileStore.js +134 -0
- package/dist/imageKitFileStore.js.map +1 -0
- package/dist/index.d.ts +6 -28
- package/dist/index.js +465 -640
- 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,59 @@ 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
|
+
|
|
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.
|
|
131
|
+
|
|
132
|
+
Run `pnpm exec vitest run` for mock-provider REST/MongoDB, client transport, and
|
|
133
|
+
folder-migration coverage. Live provider tests require explicitly configured
|
|
134
|
+
`IMAGEKIT_INTEGRATION_*` credentials and are otherwise skipped. The integration
|
|
135
|
+
suite was migrated from the removed v3 endpoint/receipt protocol to the v4 REST
|
|
136
|
+
boundary; the obsolete context-only verification helper is no longer shipped.
|
|
137
|
+
|
|
138
|
+
The folder migration utility covers current/legacy records; migrating every
|
|
139
|
+
archived v4 representation between accounts needs a separate history-aware plan.
|
|
140
|
+
|
|
73
141
|
## Exports
|
|
74
142
|
|
|
75
143
|
- `imagekitStorage` (primary)
|
|
@@ -89,9 +157,7 @@ export default buildConfig({
|
|
|
89
157
|
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
158
|
|
|
91
159
|
```ts
|
|
92
|
-
import {
|
|
93
|
-
withImageKitTransformation,
|
|
94
|
-
} from '@humaan/payload-storage-imagekit/image'
|
|
160
|
+
import { withImageKitTransformation } from '@humaan/payload-storage-imagekit/image'
|
|
95
161
|
|
|
96
162
|
const src = withImageKitTransformation(media.imagekit.url, {
|
|
97
163
|
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, endpointPath, 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,61 @@ 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.');
|
|
93
34
|
}
|
|
94
|
-
const
|
|
95
|
-
|
|
35
|
+
const instructions = await response.json();
|
|
36
|
+
if (instructions.type !== 'dispatch' || instructions.name !== 'imagekit') {
|
|
37
|
+
throw new Error('Expected ImageKit upload instructions.');
|
|
38
|
+
}
|
|
39
|
+
authorization = instructions.data;
|
|
40
|
+
uploadReference = instructions.file.uploadReference;
|
|
41
|
+
updateFilename(instructions.file.filename);
|
|
42
|
+
}
|
|
43
|
+
if (!endpointPath || typeof authorization.uploadReference?.signedReceipt !== 'string') {
|
|
44
|
+
throw new Error('ImageKit upload configuration changed. Refresh the page and try again.');
|
|
45
|
+
}
|
|
46
|
+
const formData = new FormData();
|
|
47
|
+
formData.set('file', file);
|
|
48
|
+
formData.set('token', authorization.token);
|
|
49
|
+
for (const [key, value] of Object.entries(authorization.uploadPayload)){
|
|
50
|
+
formData.set(key, value);
|
|
51
|
+
}
|
|
52
|
+
const response = await fetch('https://upload.imagekit.io/api/v2/files/upload', {
|
|
53
|
+
body: formData,
|
|
54
|
+
method: 'POST'
|
|
55
|
+
});
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
const error = await response.json().catch(()=>null);
|
|
58
|
+
throw new Error(error?.message || 'Failed to upload file to ImageKit.');
|
|
59
|
+
}
|
|
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.');
|
|
96
83
|
}
|
|
97
|
-
|
|
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.
|
|
86
|
+
return uploadReference;
|
|
98
87
|
}
|
|
99
88
|
});
|
|
100
89
|
|
|
@@ -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 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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"}
|
|
@@ -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;
|