@payloadcms/plugin-cloud-storage 4.0.0-internal.77191e5 → 4.0.0-internal.7803e2c
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/dist/exports/utilities.d.ts +1 -0
- package/dist/exports/utilities.d.ts.map +1 -1
- package/dist/exports/utilities.js +1 -0
- package/dist/exports/utilities.js.map +1 -1
- package/dist/fields/getFields.d.ts.map +1 -1
- package/dist/fields/getFields.js +79 -8
- package/dist/fields/getFields.js.map +1 -1
- package/dist/hooks/afterChange.d.ts +3 -1
- package/dist/hooks/afterChange.d.ts.map +1 -1
- package/dist/hooks/afterChange.js +139 -17
- package/dist/hooks/afterChange.js.map +1 -1
- package/dist/hooks/afterDelete.d.ts +7 -1
- package/dist/hooks/afterDelete.d.ts.map +1 -1
- package/dist/hooks/afterDelete.js +108 -27
- package/dist/hooks/afterDelete.js.map +1 -1
- package/dist/hooks/afterRead.d.ts +6 -1
- package/dist/hooks/afterRead.d.ts.map +1 -1
- package/dist/hooks/afterRead.js +20 -6
- package/dist/hooks/afterRead.js.map +1 -1
- package/dist/hooks/beforeChange.d.ts +2 -1
- package/dist/hooks/beforeChange.d.ts.map +1 -1
- package/dist/hooks/beforeChange.js +7 -4
- package/dist/hooks/beforeChange.js.map +1 -1
- package/dist/hooks/publicOriginalURL.d.ts +8 -0
- package/dist/hooks/publicOriginalURL.d.ts.map +1 -0
- package/dist/hooks/publicOriginalURL.js +34 -0
- package/dist/hooks/publicOriginalURL.js.map +1 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +36 -5
- package/dist/plugin.js.map +1 -1
- package/dist/types.d.ts +30 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utilities/createFileOperations.d.ts +13 -0
- package/dist/utilities/createFileOperations.d.ts.map +1 -0
- package/dist/utilities/createFileOperations.js +258 -0
- package/dist/utilities/createFileOperations.js.map +1 -0
- package/dist/utilities/getFilePrefix.d.ts +2 -5
- package/dist/utilities/getFilePrefix.d.ts.map +1 -1
- package/dist/utilities/getFilePrefix.js +25 -10
- package/dist/utilities/getFilePrefix.js.map +1 -1
- package/dist/utilities/getIncomingFiles.js +2 -2
- package/dist/utilities/getIncomingFiles.js.map +1 -1
- package/dist/utilities/getStorageLocationSelect.d.ts +9 -0
- package/dist/utilities/getStorageLocationSelect.d.ts.map +1 -0
- package/dist/utilities/getStorageLocationSelect.js +54 -0
- package/dist/utilities/getStorageLocationSelect.js.map +1 -0
- package/dist/utilities/resolveSignedURLKey.d.ts +3 -1
- package/dist/utilities/resolveSignedURLKey.d.ts.map +1 -1
- package/dist/utilities/resolveSignedURLKey.js +30 -8
- package/dist/utilities/resolveSignedURLKey.js.map +1 -1
- package/package.json +24 -26
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { CollectionConfig, Field, FileData, ImageSize, PayloadHandler, PayloadRequest, TypeWithID, UploadCollectionSlug, UploadInstructionsAccess, UploadInstructionsCapability } from 'payload';
|
|
1
|
+
import type { CollectionConfig, Field, FileData, FileHandlerOperation, ImageSize, PayloadHandler, PayloadRequest, SanitizedCollectionConfig, TypeWithID, UploadCollectionSlug, UploadInstructionsAccess, UploadInstructionsCapability } from 'payload';
|
|
2
|
+
export type { FileHandlerOperation } from 'payload';
|
|
2
3
|
export interface File {
|
|
3
4
|
buffer: Buffer;
|
|
4
5
|
filename: string;
|
|
@@ -17,6 +18,10 @@ export type ClientUploadsConfig = {
|
|
|
17
18
|
*/
|
|
18
19
|
export type UploadReference = {
|
|
19
20
|
_objectKey?: string;
|
|
21
|
+
/** Data the adapter signed into the receipt with `resolveSignedURLKey`. Not stored on the document. */
|
|
22
|
+
adapterData?: Record<string, unknown>;
|
|
23
|
+
/** Fields the client added to `uploadReference`. Not verified; check them before use. */
|
|
24
|
+
clientData?: Record<string, unknown>;
|
|
20
25
|
prefix: string;
|
|
21
26
|
signedReceipt: `${string}.${string}`;
|
|
22
27
|
};
|
|
@@ -43,6 +48,23 @@ export type HandleDelete = (args: {
|
|
|
43
48
|
*/
|
|
44
49
|
storageFilePath: string;
|
|
45
50
|
}) => Promise<void> | void;
|
|
51
|
+
/** Deletes a stored object without requiring a saved document. */
|
|
52
|
+
export type DeleteFile = (args: {
|
|
53
|
+
collection: CollectionConfig;
|
|
54
|
+
req: PayloadRequest;
|
|
55
|
+
/** Complete storage path, with prefixes and `_objectKey` already resolved. */
|
|
56
|
+
storageFilePath: string;
|
|
57
|
+
}) => Promise<void> | void;
|
|
58
|
+
/** Complete storage keys are resolved before invoking a provider operation. */
|
|
59
|
+
export type FileOperationArgs = {
|
|
60
|
+
collection: SanitizedCollectionConfig;
|
|
61
|
+
from: string;
|
|
62
|
+
mimeType?: string;
|
|
63
|
+
req: PayloadRequest;
|
|
64
|
+
to: string;
|
|
65
|
+
};
|
|
66
|
+
export type CopyFile = (args: FileOperationArgs) => Promise<void>;
|
|
67
|
+
export type MoveFile = (args: FileOperationArgs) => Promise<void>;
|
|
46
68
|
export type GenerateURL = (args: {
|
|
47
69
|
collection: CollectionConfig;
|
|
48
70
|
data: any;
|
|
@@ -55,11 +77,15 @@ export type StaticHandler = (req: PayloadRequest, args: {
|
|
|
55
77
|
params: {
|
|
56
78
|
collection: string;
|
|
57
79
|
filename: string;
|
|
80
|
+
operation?: FileHandlerOperation;
|
|
58
81
|
prefix?: string;
|
|
59
82
|
uploadReference?: unknown;
|
|
60
83
|
};
|
|
61
84
|
}) => Promise<Response> | Response;
|
|
62
85
|
export interface GeneratedAdapter {
|
|
86
|
+
/** Preserve object metadata and fail if `to` already exists. Rename retains `from` for history. */
|
|
87
|
+
copyFile: CopyFile;
|
|
88
|
+
deleteFile: DeleteFile;
|
|
63
89
|
/**
|
|
64
90
|
* Additional fields to be injected into the base collection and image sizes
|
|
65
91
|
*/
|
|
@@ -70,9 +96,12 @@ export interface GeneratedAdapter {
|
|
|
70
96
|
generateURL?: GenerateURL;
|
|
71
97
|
handleDelete: HandleDelete;
|
|
72
98
|
handleUpload: HandleUpload;
|
|
99
|
+
moveFile?: MoveFile;
|
|
73
100
|
name: string;
|
|
74
101
|
onInit?: () => Promise<void> | void;
|
|
75
102
|
staticHandler: StaticHandler;
|
|
103
|
+
/** Reads source files by path when staging server-mediated uploads. */
|
|
104
|
+
supportsTempFiles?: boolean;
|
|
76
105
|
/** Generates upload instructions when supported. */
|
|
77
106
|
uploadInstructions?: {
|
|
78
107
|
adminHandler?: {
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,KAAK,EACL,QAAQ,EACR,SAAS,EACT,cAAc,EACd,cAAc,EACd,UAAU,EACV,oBAAoB,EACpB,wBAAwB,EACxB,4BAA4B,EAC7B,MAAM,SAAS,CAAA;AAEhB,MAAM,WAAW,IAAI;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED,MAAM,MAAM,mBAAmB,GAAG;IAAE,MAAM,CAAC,EAAE,wBAAwB,CAAA;CAAE,GAAG,OAAO,CAAA;AAEjF;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,GAAG,MAAM,IAAI,MAAM,EAAE,CAAA;CACrC,CAAA;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE;IAChC,UAAU,EAAE,gBAAgB,CAAA;IAC5B,IAAI,EAAE,GAAG,CAAA;IACT,IAAI,EAAE,IAAI,CAAA;IACV,GAAG,EAAE,cAAc,CAAA;IACnB;;OAEG;IACH,eAAe,EAAE,MAAM,CAAA;CACxB,KACG,OAAO,CAAC,QAAQ,GAAG,UAAU,CAAC,GAC9B,OAAO,CAAC,OAAO,CAAC,QAAQ,GAAG,UAAU,CAAC,CAAC,GACvC,OAAO,CAAC,IAAI,CAAC,GACb,IAAI,CAAA;AAER,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE;IAChC,UAAU,EAAE,gBAAgB,CAAA;IAC5B,GAAG,EAAE,QAAQ,GAAG,UAAU,GAAG,cAAc,CAAA;IAC3C,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,cAAc,CAAA;IACnB;;OAEG;IACH,eAAe,EAAE,MAAM,CAAA;CACxB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;AAE1B,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE;IAC/B,UAAU,EAAE,gBAAgB,CAAA;IAC5B,IAAI,EAAE,GAAG,CAAA;IACT,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAA;AAE9B,MAAM,MAAM,aAAa,GAAG,CAC1B,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE;IACJ,GAAG,CAAC,EAAE,UAAU,CAAA;IAChB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,MAAM,EAAE;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type {\n CollectionConfig,\n Field,\n FileData,\n ImageSize,\n PayloadHandler,\n PayloadRequest,\n TypeWithID,\n UploadCollectionSlug,\n UploadInstructionsAccess,\n UploadInstructionsCapability,\n} from 'payload'\n\nexport interface File {\n buffer: Buffer\n filename: string\n filesize: number\n mimeType: string\n tempFilePath?: string\n uploadReference?: unknown\n}\n\nexport type ClientUploadsConfig = { access?: UploadInstructionsAccess } | boolean\n\n/**\n * Reference to a client-uploaded object, returned by an upload handler and\n * submitted with the document. Always carries the signed receipt; `prefix`\n * locates the stored object.\n */\nexport type UploadReference = {\n _objectKey?: string\n prefix: string\n signedReceipt: `${string}.${string}`\n}\n\nexport type HandleUpload = (args: {\n collection: CollectionConfig\n data: any\n file: File\n req: PayloadRequest\n /**\n * Pre-resolved storage path (`_objectKey` folded in, contained beneath the collection prefix).\n */\n storageFilePath: string\n}) =>\n | Partial<FileData & TypeWithID>\n | Promise<Partial<FileData & TypeWithID>>\n | Promise<void>\n | void\n\nexport interface TypeWithPrefix {\n prefix?: string\n}\n\nexport type HandleDelete = (args: {\n collection: CollectionConfig\n doc: FileData & TypeWithID & TypeWithPrefix\n filename: string\n req: PayloadRequest\n /**\n * Pre-resolved storage path of the object to delete.\n */\n storageFilePath: string\n}) => Promise<void> | void\n\nexport type GenerateURL = (args: {\n collection: CollectionConfig\n data: any\n filename: string\n prefix?: string\n}) => Promise<string> | string\n\nexport type StaticHandler = (\n req: PayloadRequest,\n args: {\n doc?: TypeWithID\n headers?: Headers\n params: {
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type {\n CollectionConfig,\n Field,\n FileData,\n FileHandlerOperation,\n ImageSize,\n PayloadHandler,\n PayloadRequest,\n SanitizedCollectionConfig,\n TypeWithID,\n UploadCollectionSlug,\n UploadInstructionsAccess,\n UploadInstructionsCapability,\n} from 'payload'\n\nexport type { FileHandlerOperation } from 'payload'\n\nexport interface File {\n buffer: Buffer\n filename: string\n filesize: number\n mimeType: string\n tempFilePath?: string\n uploadReference?: unknown\n}\n\nexport type ClientUploadsConfig = { access?: UploadInstructionsAccess } | boolean\n\n/**\n * Reference to a client-uploaded object, returned by an upload handler and\n * submitted with the document. Always carries the signed receipt; `prefix`\n * locates the stored object.\n */\nexport type UploadReference = {\n _objectKey?: string\n /** Data the adapter signed into the receipt with `resolveSignedURLKey`. Not stored on the document. */\n adapterData?: Record<string, unknown>\n /** Fields the client added to `uploadReference`. Not verified; check them before use. */\n clientData?: Record<string, unknown>\n prefix: string\n signedReceipt: `${string}.${string}`\n}\n\nexport type HandleUpload = (args: {\n collection: CollectionConfig\n data: any\n file: File\n req: PayloadRequest\n /**\n * Pre-resolved storage path (`_objectKey` folded in, contained beneath the collection prefix).\n */\n storageFilePath: string\n}) =>\n | Partial<FileData & TypeWithID>\n | Promise<Partial<FileData & TypeWithID>>\n | Promise<void>\n | void\n\nexport interface TypeWithPrefix {\n prefix?: string\n}\n\nexport type HandleDelete = (args: {\n collection: CollectionConfig\n doc: FileData & TypeWithID & TypeWithPrefix\n filename: string\n req: PayloadRequest\n /**\n * Pre-resolved storage path of the object to delete.\n */\n storageFilePath: string\n}) => Promise<void> | void\n\n/** Deletes a stored object without requiring a saved document. */\nexport type DeleteFile = (args: {\n collection: CollectionConfig\n req: PayloadRequest\n /** Complete storage path, with prefixes and `_objectKey` already resolved. */\n storageFilePath: string\n}) => Promise<void> | void\n\n/** Complete storage keys are resolved before invoking a provider operation. */\nexport type FileOperationArgs = {\n collection: SanitizedCollectionConfig\n from: string\n mimeType?: string\n req: PayloadRequest\n to: string\n}\n\nexport type CopyFile = (args: FileOperationArgs) => Promise<void>\nexport type MoveFile = (args: FileOperationArgs) => Promise<void>\n\nexport type GenerateURL = (args: {\n collection: CollectionConfig\n data: any\n filename: string\n prefix?: string\n}) => Promise<string> | string\n\nexport type StaticHandler = (\n req: PayloadRequest,\n args: {\n doc?: TypeWithID\n headers?: Headers\n params: {\n collection: string\n filename: string\n operation?: FileHandlerOperation\n prefix?: string\n uploadReference?: unknown\n }\n },\n) => Promise<Response> | Response\n\nexport interface GeneratedAdapter {\n /** Preserve object metadata and fail if `to` already exists. Rename retains `from` for history. */\n copyFile: CopyFile\n deleteFile: DeleteFile\n /**\n * Additional fields to be injected into the base collection and image sizes\n */\n fields?: Field[]\n /**\n * Generates the public URL for a file\n */\n generateURL?: GenerateURL\n handleDelete: HandleDelete\n handleUpload: HandleUpload\n moveFile?: MoveFile\n name: string\n onInit?: () => Promise<void> | void\n staticHandler: StaticHandler\n /** Reads source files by path when staging server-mediated uploads. */\n supportsTempFiles?: boolean\n /** Generates upload instructions when supported. */\n uploadInstructions?: {\n adminHandler?: {\n path: string\n props?: Record<string, unknown>\n }\n enabled: boolean\n endpoint?: {\n handler: PayloadHandler\n path: `/${string}`\n }\n } & UploadInstructionsCapability\n}\n\nexport type Adapter = (args: { collection: CollectionConfig; prefix?: string }) => GeneratedAdapter\n\nexport type AllowList = Array<{\n hostname: string\n pathname?: string\n port?: string\n protocol?: 'http' | 'https'\n search?: string\n}>\n\nexport type GenerateFileURL = (args: {\n collection: CollectionConfig\n filename: string\n prefix?: string\n size?: ImageSize\n}) => Promise<string> | string\n\nexport interface CollectionOptions {\n adapter: Adapter | null\n disableLocalStorage?: boolean\n disablePayloadAccessControl?: true\n generateFileURL?: GenerateFileURL\n prefix?: string\n}\n\nexport interface PluginOptions {\n collections: Partial<Record<UploadCollectionSlug, CollectionOptions>>\n /**\n * Whether or not to enable the plugin\n *\n * Default: true\n */\n enabled?: boolean\n /**\n * When true (compositional prefixes), the stored `prefix` field is only the\n * document-level segment; the collection prefix comes from plugin options and\n * must not be pre-filled as the field default.\n *\n * Set by storage adapters that support compositional prefixes (e.g. S3, Azure, R2, Vercel Blob, GCS).\n *\n * @default false\n */\n useCompositePrefixes?: boolean\n}\n"],"names":[],"mappings":"AA8KA,WAkBC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CollectionConfig, UploadConfig } from 'payload';
|
|
2
|
+
import type { GeneratedAdapter, GenerateFileURL } from '../types.js';
|
|
3
|
+
type Args = {
|
|
4
|
+
adapter: GeneratedAdapter;
|
|
5
|
+
collection: CollectionConfig;
|
|
6
|
+
collectionPrefix?: string;
|
|
7
|
+
disablePayloadAccessControl?: boolean;
|
|
8
|
+
generateFileURL?: GenerateFileURL;
|
|
9
|
+
useCompositePrefixes?: boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare const createFileOperations: ({ adapter, collection, collectionPrefix, disablePayloadAccessControl, generateFileURL, useCompositePrefixes, }: Args) => NonNullable<UploadConfig['fileOperations']>;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=createFileOperations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createFileOperations.d.ts","sourceRoot":"","sources":["../../src/utilities/createFileOperations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAyB,YAAY,EAAE,MAAM,SAAS,CAAA;AAOpF,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AASpE,KAAK,IAAI,GAAG;IACV,OAAO,EAAE,gBAAgB,CAAA;IACzB,UAAU,EAAE,gBAAgB,CAAA;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,2BAA2B,CAAC,EAAE,OAAO,CAAA;IACrC,eAAe,CAAC,EAAE,eAAe,CAAA;IACjC,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B,CAAA;AAED,eAAO,MAAM,oBAAoB,mHAO9B,IAAI,KAAG,WAAW,CAAC,YAAY,CAAC,gBAAgB,CAAC,CA+OnD,CAAA"}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
4
|
+
import { deepCopyObjectSimple, generatePayloadFileURL } from 'payload';
|
|
5
|
+
import { buildPrefixWithObjectKey } from './buildPrefixWithObjectKey.js';
|
|
6
|
+
import { buildStoragePathData, buildUploadPrefix, buildUploadStoragePathData } from './buildStoragePathData.js';
|
|
7
|
+
export const createFileOperations = ({ adapter, collection, collectionPrefix, disablePayloadAccessControl, generateFileURL, useCompositePrefixes })=>{
|
|
8
|
+
if (typeof adapter.deleteFile !== 'function') {
|
|
9
|
+
throw new Error(`Storage adapter "${adapter.name}" must implement deleteFile for collection "${collection.slug}".`);
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
copy: async ({ from, req, to, trackStagedObject })=>{
|
|
13
|
+
await adapter.copyFile({
|
|
14
|
+
collection: req.payload.collections[collection.slug].config,
|
|
15
|
+
from,
|
|
16
|
+
req,
|
|
17
|
+
to
|
|
18
|
+
});
|
|
19
|
+
trackStagedObject({
|
|
20
|
+
key: to,
|
|
21
|
+
remove: async ()=>{
|
|
22
|
+
await adapter.deleteFile({
|
|
23
|
+
collection,
|
|
24
|
+
req,
|
|
25
|
+
storageFilePath: to
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
delete: async ({ key, req })=>{
|
|
31
|
+
await adapter.deleteFile({
|
|
32
|
+
collection,
|
|
33
|
+
req,
|
|
34
|
+
storageFilePath: key
|
|
35
|
+
});
|
|
36
|
+
},
|
|
37
|
+
hasLegacyFile: async ({ doc, req })=>{
|
|
38
|
+
if (typeof doc.filename !== 'string' || typeof doc.url !== 'string') {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
const docPrefix = typeof doc.prefix === 'string' ? doc.prefix : undefined;
|
|
42
|
+
const objectFolder = buildPrefixWithObjectKey({
|
|
43
|
+
objectKey: typeof doc._objectKey === 'string' ? doc._objectKey : undefined,
|
|
44
|
+
prefix: docPrefix
|
|
45
|
+
});
|
|
46
|
+
const expectedURL = async ({ filename, size })=>{
|
|
47
|
+
if (generateFileURL) {
|
|
48
|
+
return generateFileURL({
|
|
49
|
+
collection,
|
|
50
|
+
filename,
|
|
51
|
+
prefix: objectFolder,
|
|
52
|
+
size
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
if (disablePayloadAccessControl && adapter.generateURL) {
|
|
56
|
+
return adapter.generateURL({
|
|
57
|
+
collection,
|
|
58
|
+
data: doc,
|
|
59
|
+
filename,
|
|
60
|
+
prefix: objectFolder
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return generatePayloadFileURL({
|
|
64
|
+
collectionSlug: collection.slug,
|
|
65
|
+
config: req.payload.config,
|
|
66
|
+
filename,
|
|
67
|
+
relative: true
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
if (!isSameFileLocation({
|
|
71
|
+
actual: doc.url,
|
|
72
|
+
expected: await expectedURL({
|
|
73
|
+
filename: doc.filename
|
|
74
|
+
})
|
|
75
|
+
})) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
if (doc.variants && typeof doc.variants === 'object' && !Array.isArray(doc.variants)) {
|
|
79
|
+
for (const [sizeKey, size] of Object.entries(doc.variants)){
|
|
80
|
+
if (size && typeof size === 'object' && 'filename' in size && typeof size.filename === 'string') {
|
|
81
|
+
const imageSize = req.payload.collections[collection.slug]?.config.upload.variants?.find(({ name })=>name === sizeKey);
|
|
82
|
+
if (!('url' in size) || typeof size.url !== 'string' || !isSameFileLocation({
|
|
83
|
+
actual: size.url,
|
|
84
|
+
expected: await expectedURL({
|
|
85
|
+
filename: size.filename,
|
|
86
|
+
size: imageSize
|
|
87
|
+
})
|
|
88
|
+
})) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return true;
|
|
95
|
+
},
|
|
96
|
+
resolveStorageKey: ({ _objectKey, filename, prefix })=>buildStoragePathData({
|
|
97
|
+
collectionPrefix,
|
|
98
|
+
docPrefix: buildPrefixWithObjectKey({
|
|
99
|
+
objectKey: _objectKey,
|
|
100
|
+
prefix
|
|
101
|
+
}),
|
|
102
|
+
filename,
|
|
103
|
+
useCompositePrefixes
|
|
104
|
+
}).storageFilePath,
|
|
105
|
+
...adapter.moveFile && {
|
|
106
|
+
move: async ({ from, req, to, trackStagedObject })=>{
|
|
107
|
+
await adapter.moveFile({
|
|
108
|
+
collection: req.payload.collections[collection.slug].config,
|
|
109
|
+
from,
|
|
110
|
+
req,
|
|
111
|
+
to
|
|
112
|
+
});
|
|
113
|
+
trackStagedObject({
|
|
114
|
+
key: to,
|
|
115
|
+
remove: ()=>adapter.moveFile({
|
|
116
|
+
collection: req.payload.collections[collection.slug].config,
|
|
117
|
+
from: to,
|
|
118
|
+
req,
|
|
119
|
+
to: from
|
|
120
|
+
})
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
stage: async ({ data, files, req, trackStagedObject })=>{
|
|
125
|
+
const uploadedNames = new Set();
|
|
126
|
+
const metadata = {};
|
|
127
|
+
const verifiedOriginal = req.context?._payloadVerifiedProviderOriginal;
|
|
128
|
+
for (const file of files){
|
|
129
|
+
const filename = path.basename(file.path);
|
|
130
|
+
const representations = [
|
|
131
|
+
data
|
|
132
|
+
];
|
|
133
|
+
if (data.original && typeof data.original === 'object') {
|
|
134
|
+
representations.push(data.original);
|
|
135
|
+
}
|
|
136
|
+
if (data.variants && typeof data.variants === 'object') {
|
|
137
|
+
representations.push(...Object.values(data.variants).filter((value)=>Boolean(value) && typeof value === 'object'));
|
|
138
|
+
}
|
|
139
|
+
const destinations = representations.filter((value)=>value.filename === filename);
|
|
140
|
+
// Provider-direct uploads already placed the source object at its verified location.
|
|
141
|
+
// A bounded content probe can still appear in files, but it is not a representation
|
|
142
|
+
// Payload should upload again.
|
|
143
|
+
if (!destinations.length && verifiedOriginal) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (!destinations.length) {
|
|
147
|
+
throw new Error(`No stored representation describes ${filename}`);
|
|
148
|
+
}
|
|
149
|
+
const prefixLocation = destinations.find((destination)=>typeof destination.prefix === 'string');
|
|
150
|
+
const objectKeyLocation = destinations.find((destination)=>typeof destination._objectKey === 'string');
|
|
151
|
+
const prefix = typeof prefixLocation?.prefix === 'string' ? prefixLocation.prefix : undefined;
|
|
152
|
+
const objectKey = typeof objectKeyLocation?._objectKey === 'string' ? objectKeyLocation._objectKey : undefined;
|
|
153
|
+
const prefixData = buildUploadPrefix({
|
|
154
|
+
collectionPrefix,
|
|
155
|
+
docPrefix: typeof prefix === 'string' ? prefix : undefined,
|
|
156
|
+
useCompositePrefixes
|
|
157
|
+
});
|
|
158
|
+
const uploadPrefix = useCompositePrefixes ? prefixData.sanitizedDocPrefix : prefixData.uploadPrefix;
|
|
159
|
+
for (const destination of destinations){
|
|
160
|
+
destination.prefix = uploadPrefix;
|
|
161
|
+
destination._objectKey = objectKey;
|
|
162
|
+
}
|
|
163
|
+
const docPrefix = buildPrefixWithObjectKey({
|
|
164
|
+
objectKey,
|
|
165
|
+
prefix: uploadPrefix
|
|
166
|
+
});
|
|
167
|
+
const dataForUpload = {
|
|
168
|
+
...data,
|
|
169
|
+
prefix: docPrefix
|
|
170
|
+
};
|
|
171
|
+
const storageFilePath = buildUploadStoragePathData({
|
|
172
|
+
collectionPrefix,
|
|
173
|
+
docPrefix,
|
|
174
|
+
filename,
|
|
175
|
+
useCompositePrefixes
|
|
176
|
+
}).storageFilePath;
|
|
177
|
+
if (uploadedNames.has(filename)) {
|
|
178
|
+
throw new Error(`Duplicate managed cloud filename: ${filename}`);
|
|
179
|
+
}
|
|
180
|
+
uploadedNames.add(filename);
|
|
181
|
+
const buffer = 'buffer' in file ? file.buffer : adapter.supportsTempFiles ? Buffer.alloc(0) : await fs.readFile(file.sourcePath);
|
|
182
|
+
const uploadFile = {
|
|
183
|
+
buffer,
|
|
184
|
+
filename,
|
|
185
|
+
filesize: 'buffer' in file ? file.buffer.length : (await fs.stat(file.sourcePath)).size,
|
|
186
|
+
mimeType: getMimeType({
|
|
187
|
+
data,
|
|
188
|
+
filename
|
|
189
|
+
}),
|
|
190
|
+
...'sourcePath' in file ? {
|
|
191
|
+
tempFilePath: file.sourcePath
|
|
192
|
+
} : {}
|
|
193
|
+
};
|
|
194
|
+
// A new object folder is allocated for every server upload, so an interrupted
|
|
195
|
+
// provider write can be removed without touching an earlier revision.
|
|
196
|
+
trackStagedObject({
|
|
197
|
+
key: storageFilePath,
|
|
198
|
+
remove: async ()=>{
|
|
199
|
+
await adapter.deleteFile({
|
|
200
|
+
collection,
|
|
201
|
+
req,
|
|
202
|
+
storageFilePath
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
const dataBeforeUpload = deepCopyObjectSimple(dataForUpload);
|
|
207
|
+
const result = await adapter.handleUpload({
|
|
208
|
+
collection,
|
|
209
|
+
data: dataForUpload,
|
|
210
|
+
file: uploadFile,
|
|
211
|
+
req,
|
|
212
|
+
storageFilePath
|
|
213
|
+
});
|
|
214
|
+
if (result && typeof result === 'object') {
|
|
215
|
+
// Adapters may return the entire input document, including fields made stale by this upload.
|
|
216
|
+
for (const [key, value] of Object.entries(result)){
|
|
217
|
+
if (!isDeepStrictEqual(value, dataBeforeUpload[key])) {
|
|
218
|
+
metadata[key] = value;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
delete metadata.prefix;
|
|
224
|
+
delete metadata._objectKey;
|
|
225
|
+
return {
|
|
226
|
+
metadata
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
};
|
|
231
|
+
const getMimeType = ({ data, filename })=>{
|
|
232
|
+
if (data.original && typeof data.original === 'object') {
|
|
233
|
+
const original = data.original;
|
|
234
|
+
if (original.filename === filename && original.mimeType) {
|
|
235
|
+
return original.mimeType;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (data.variants && typeof data.variants === 'object') {
|
|
239
|
+
for (const size of Object.values(data.variants)){
|
|
240
|
+
const candidate = size;
|
|
241
|
+
if (candidate?.filename === filename && candidate.mimeType) {
|
|
242
|
+
return candidate.mimeType;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return typeof data.mimeType === 'string' ? data.mimeType : 'application/octet-stream';
|
|
247
|
+
};
|
|
248
|
+
/** Signed and time-limited URLs change their query on every generation, so only the location is compared. */ const isSameFileLocation = ({ actual, expected })=>typeof expected === 'string' && getFileLocation({
|
|
249
|
+
url: actual
|
|
250
|
+
}) === getFileLocation({
|
|
251
|
+
url: expected
|
|
252
|
+
});
|
|
253
|
+
const getFileLocation = ({ url })=>{
|
|
254
|
+
const parsed = new URL(url, 'http://payload.local');
|
|
255
|
+
return url.startsWith('/') ? parsed.pathname : `${parsed.origin}${parsed.pathname}`;
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
//# sourceMappingURL=createFileOperations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/createFileOperations.ts"],"sourcesContent":["import type { CollectionConfig, ImageSize, JsonObject, UploadConfig } from 'payload'\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { isDeepStrictEqual } from 'node:util'\nimport { deepCopyObjectSimple, generatePayloadFileURL } from 'payload'\n\nimport type { GeneratedAdapter, GenerateFileURL } from '../types.js'\n\nimport { buildPrefixWithObjectKey } from './buildPrefixWithObjectKey.js'\nimport {\n buildStoragePathData,\n buildUploadPrefix,\n buildUploadStoragePathData,\n} from './buildStoragePathData.js'\n\ntype Args = {\n adapter: GeneratedAdapter\n collection: CollectionConfig\n collectionPrefix?: string\n disablePayloadAccessControl?: boolean\n generateFileURL?: GenerateFileURL\n useCompositePrefixes?: boolean\n}\n\nexport const createFileOperations = ({\n adapter,\n collection,\n collectionPrefix,\n disablePayloadAccessControl,\n generateFileURL,\n useCompositePrefixes,\n}: Args): NonNullable<UploadConfig['fileOperations']> => {\n if (typeof adapter.deleteFile !== 'function') {\n throw new Error(\n `Storage adapter \"${adapter.name}\" must implement deleteFile for collection \"${collection.slug}\".`,\n )\n }\n\n return {\n copy: async ({ from, req, to, trackStagedObject }) => {\n await adapter.copyFile({\n collection: req.payload.collections[collection.slug]!.config,\n from,\n req,\n to,\n })\n trackStagedObject({\n key: to,\n remove: async () => {\n await adapter.deleteFile({\n collection,\n req,\n storageFilePath: to,\n })\n },\n })\n },\n delete: async ({ key, req }) => {\n await adapter.deleteFile({\n collection,\n req,\n storageFilePath: key,\n })\n },\n hasLegacyFile: async ({ doc, req }) => {\n if (typeof doc.filename !== 'string' || typeof doc.url !== 'string') {\n return false\n }\n\n const docPrefix = typeof doc.prefix === 'string' ? doc.prefix : undefined\n const objectFolder = buildPrefixWithObjectKey({\n objectKey: typeof doc._objectKey === 'string' ? doc._objectKey : undefined,\n prefix: docPrefix,\n })\n const expectedURL = async ({ filename, size }: { filename: string; size?: ImageSize }) => {\n if (generateFileURL) {\n return generateFileURL({ collection, filename, prefix: objectFolder, size })\n }\n if (disablePayloadAccessControl && adapter.generateURL) {\n return adapter.generateURL({ collection, data: doc, filename, prefix: objectFolder })\n }\n return generatePayloadFileURL({\n collectionSlug: collection.slug,\n config: req.payload.config as unknown as Parameters<\n typeof generatePayloadFileURL\n >[0]['config'],\n filename,\n relative: true,\n })\n }\n if (\n !isSameFileLocation({\n actual: doc.url,\n expected: await expectedURL({ filename: doc.filename }),\n })\n ) {\n return false\n }\n if (doc.variants && typeof doc.variants === 'object' && !Array.isArray(doc.variants)) {\n for (const [sizeKey, size] of Object.entries(doc.variants)) {\n if (\n size &&\n typeof size === 'object' &&\n 'filename' in size &&\n typeof size.filename === 'string'\n ) {\n const imageSize = req.payload.collections[\n collection.slug\n ]?.config.upload.variants?.find(({ name }) => name === sizeKey)\n if (\n !('url' in size) ||\n typeof size.url !== 'string' ||\n !isSameFileLocation({\n actual: size.url,\n expected: await expectedURL({ filename: size.filename, size: imageSize }),\n })\n ) {\n return false\n }\n }\n }\n }\n return true\n },\n resolveStorageKey: ({ _objectKey, filename, prefix }) =>\n buildStoragePathData({\n collectionPrefix,\n docPrefix: buildPrefixWithObjectKey({ objectKey: _objectKey, prefix }),\n filename,\n useCompositePrefixes,\n }).storageFilePath,\n ...(adapter.moveFile && {\n move: async ({ from, req, to, trackStagedObject }) => {\n await adapter.moveFile!({\n collection: req.payload.collections[collection.slug]!.config,\n from,\n req,\n to,\n })\n trackStagedObject({\n key: to,\n remove: () =>\n adapter.moveFile!({\n collection: req.payload.collections[collection.slug]!.config,\n from: to,\n req,\n to: from,\n }),\n })\n },\n }),\n stage: async ({ data, files, req, trackStagedObject }) => {\n const uploadedNames = new Set<string>()\n const metadata: Record<string, unknown> = {}\n const verifiedOriginal = req.context?._payloadVerifiedProviderOriginal as\n | { filename: string }\n | undefined\n for (const file of files) {\n const filename = path.basename(file.path)\n const representations: Record<string, unknown>[] = [data]\n if (data.original && typeof data.original === 'object') {\n representations.push(data.original as Record<string, unknown>)\n }\n if (data.variants && typeof data.variants === 'object') {\n representations.push(\n ...Object.values(data.variants).filter(\n (value): value is Record<string, unknown> =>\n Boolean(value) && typeof value === 'object',\n ),\n )\n }\n const destinations = representations.filter((value) => value.filename === filename)\n // Provider-direct uploads already placed the source object at its verified location.\n // A bounded content probe can still appear in files, but it is not a representation\n // Payload should upload again.\n if (!destinations.length && verifiedOriginal) {\n continue\n }\n if (!destinations.length) {\n throw new Error(`No stored representation describes ${filename}`)\n }\n const prefixLocation = destinations.find(\n (destination) => typeof destination.prefix === 'string',\n )\n const objectKeyLocation = destinations.find(\n (destination) => typeof destination._objectKey === 'string',\n )\n const prefix =\n typeof prefixLocation?.prefix === 'string' ? prefixLocation.prefix : undefined\n const objectKey =\n typeof objectKeyLocation?._objectKey === 'string'\n ? objectKeyLocation._objectKey\n : undefined\n const prefixData = buildUploadPrefix({\n collectionPrefix,\n docPrefix: typeof prefix === 'string' ? prefix : undefined,\n useCompositePrefixes,\n })\n const uploadPrefix = useCompositePrefixes\n ? prefixData.sanitizedDocPrefix\n : prefixData.uploadPrefix\n for (const destination of destinations) {\n destination.prefix = uploadPrefix\n destination._objectKey = objectKey\n }\n const docPrefix = buildPrefixWithObjectKey({ objectKey, prefix: uploadPrefix })\n const dataForUpload = { ...data, prefix: docPrefix }\n const storageFilePath = buildUploadStoragePathData({\n collectionPrefix,\n docPrefix,\n filename,\n useCompositePrefixes,\n }).storageFilePath\n\n if (uploadedNames.has(filename)) {\n throw new Error(`Duplicate managed cloud filename: ${filename}`)\n }\n\n uploadedNames.add(filename)\n const buffer =\n 'buffer' in file\n ? file.buffer\n : adapter.supportsTempFiles\n ? Buffer.alloc(0)\n : await fs.readFile(file.sourcePath)\n const uploadFile = {\n buffer,\n filename,\n filesize: 'buffer' in file ? file.buffer.length : (await fs.stat(file.sourcePath)).size,\n mimeType: getMimeType({ data, filename }),\n ...('sourcePath' in file ? { tempFilePath: file.sourcePath } : {}),\n }\n\n // A new object folder is allocated for every server upload, so an interrupted\n // provider write can be removed without touching an earlier revision.\n trackStagedObject({\n key: storageFilePath,\n remove: async () => {\n await adapter.deleteFile({\n collection,\n req,\n storageFilePath,\n })\n },\n })\n\n const dataBeforeUpload = deepCopyObjectSimple(dataForUpload as JsonObject)\n const result = await adapter.handleUpload({\n collection,\n data: dataForUpload,\n file: uploadFile,\n req,\n storageFilePath,\n })\n\n if (result && typeof result === 'object') {\n // Adapters may return the entire input document, including fields made stale by this upload.\n for (const [key, value] of Object.entries(result)) {\n if (!isDeepStrictEqual(value, dataBeforeUpload[key])) {\n metadata[key] = value\n }\n }\n }\n }\n\n delete metadata.prefix\n delete metadata._objectKey\n return { metadata }\n },\n }\n}\n\nconst getMimeType = ({\n data,\n filename,\n}: {\n data: Record<string, unknown>\n filename: string\n}): string => {\n if (data.original && typeof data.original === 'object') {\n const original = data.original as { filename?: string; mimeType?: string }\n if (original.filename === filename && original.mimeType) {\n return original.mimeType\n }\n }\n if (data.variants && typeof data.variants === 'object') {\n for (const size of Object.values(data.variants)) {\n const candidate = size as { filename?: string; mimeType?: string } | null\n if (candidate?.filename === filename && candidate.mimeType) {\n return candidate.mimeType\n }\n }\n }\n return typeof data.mimeType === 'string' ? data.mimeType : 'application/octet-stream'\n}\n\n/** Signed and time-limited URLs change their query on every generation, so only the location is compared. */\nconst isSameFileLocation = ({\n actual,\n expected,\n}: {\n actual: string\n expected: null | string | undefined\n}): boolean =>\n typeof expected === 'string' &&\n getFileLocation({ url: actual }) === getFileLocation({ url: expected })\n\nconst getFileLocation = ({ url }: { url: string }): string => {\n const parsed = new URL(url, 'http://payload.local')\n\n return url.startsWith('/') ? parsed.pathname : `${parsed.origin}${parsed.pathname}`\n}\n"],"names":["fs","path","isDeepStrictEqual","deepCopyObjectSimple","generatePayloadFileURL","buildPrefixWithObjectKey","buildStoragePathData","buildUploadPrefix","buildUploadStoragePathData","createFileOperations","adapter","collection","collectionPrefix","disablePayloadAccessControl","generateFileURL","useCompositePrefixes","deleteFile","Error","name","slug","copy","from","req","to","trackStagedObject","copyFile","payload","collections","config","key","remove","storageFilePath","delete","hasLegacyFile","doc","filename","url","docPrefix","prefix","undefined","objectFolder","objectKey","_objectKey","expectedURL","size","generateURL","data","collectionSlug","relative","isSameFileLocation","actual","expected","variants","Array","isArray","sizeKey","Object","entries","imageSize","upload","find","resolveStorageKey","moveFile","move","stage","files","uploadedNames","Set","metadata","verifiedOriginal","context","_payloadVerifiedProviderOriginal","file","basename","representations","original","push","values","filter","value","Boolean","destinations","length","prefixLocation","destination","objectKeyLocation","prefixData","uploadPrefix","sanitizedDocPrefix","dataForUpload","has","add","buffer","supportsTempFiles","Buffer","alloc","readFile","sourcePath","uploadFile","filesize","stat","mimeType","getMimeType","tempFilePath","dataBeforeUpload","result","handleUpload","candidate","getFileLocation","parsed","URL","startsWith","pathname","origin"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CollectionConfig, PayloadRequest, TypeWithID } from 'payload';
|
|
1
|
+
import type { CollectionConfig, FileData, PayloadRequest, TypeWithID } from 'payload';
|
|
2
2
|
/**
|
|
3
3
|
* Resolves the folder a stored object lives under (semantic `prefix` plus `_objectKey`).
|
|
4
4
|
*
|
|
@@ -11,10 +11,7 @@ import type { CollectionConfig, PayloadRequest, TypeWithID } from 'payload';
|
|
|
11
11
|
export declare function getFilePrefix({ collection, collectionPrefix, doc, filename, prefixQueryParam, req, uploadReference, useCompositePrefixes, }: {
|
|
12
12
|
collection: CollectionConfig;
|
|
13
13
|
collectionPrefix?: string;
|
|
14
|
-
doc?:
|
|
15
|
-
_objectKey?: string;
|
|
16
|
-
prefix?: string;
|
|
17
|
-
} & TypeWithID;
|
|
14
|
+
doc?: Partial<FileData> & TypeWithID;
|
|
18
15
|
filename: string;
|
|
19
16
|
/**
|
|
20
17
|
* Only narrows the access-controlled fallback lookup below; never trusted or
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getFilePrefix.d.ts","sourceRoot":"","sources":["../../src/utilities/getFilePrefix.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"getFilePrefix.d.ts","sourceRoot":"","sources":["../../src/utilities/getFilePrefix.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,QAAQ,EACR,cAAc,EAEd,UAAU,EACX,MAAM,SAAS,CAAA;AAMhB;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CAAC,EAClC,UAAU,EACV,gBAAgB,EAChB,GAAG,EACH,QAAQ,EACR,gBAAgB,EAChB,GAAG,EACH,eAAe,EACf,oBAA4B,GAC7B,EAAE;IACD,UAAU,EAAE,gBAAgB,CAAA;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,GAAG,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,UAAU,CAAA;IACpC,QAAQ,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,GAAG,EAAE,cAAc,CAAA;IACnB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B,GAAG,OAAO,CAAC,MAAM,CAAC,CA2ElB"}
|
|
@@ -13,9 +13,9 @@ import { sanitizePrefix } from './sanitizePrefix.js';
|
|
|
13
13
|
// The serve path already loaded and authorized this document — trust it over any
|
|
14
14
|
// client-supplied upload reference or query prefix.
|
|
15
15
|
if (doc) {
|
|
16
|
-
return
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
return getRepresentationPrefix({
|
|
17
|
+
doc,
|
|
18
|
+
filename
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
21
|
// Upload instructions call handlers without a document yet. Re-contain the claimed
|
|
@@ -34,7 +34,7 @@ import { sanitizePrefix } from './sanitizePrefix.js';
|
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
36
|
// Reads without a query prefix or read-access constraints skip the endpoint's document lookup.
|
|
37
|
-
const
|
|
37
|
+
const variants = collection?.upload?.variants || [];
|
|
38
38
|
const filenameClause = {
|
|
39
39
|
or: [
|
|
40
40
|
{
|
|
@@ -42,8 +42,13 @@ import { sanitizePrefix } from './sanitizePrefix.js';
|
|
|
42
42
|
equals: filename
|
|
43
43
|
}
|
|
44
44
|
},
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
{
|
|
46
|
+
'original.filename': {
|
|
47
|
+
equals: filename
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
...variants.map((imageSize)=>({
|
|
51
|
+
[`variants.${imageSize.name}.filename`]: {
|
|
47
52
|
equals: filename
|
|
48
53
|
}
|
|
49
54
|
}))
|
|
@@ -71,16 +76,26 @@ import { sanitizePrefix } from './sanitizePrefix.js';
|
|
|
71
76
|
req,
|
|
72
77
|
select: {
|
|
73
78
|
_objectKey: true,
|
|
74
|
-
|
|
79
|
+
filename: true,
|
|
80
|
+
original: true,
|
|
81
|
+
prefix: true,
|
|
82
|
+
variants: true
|
|
75
83
|
},
|
|
76
84
|
showHiddenFields: true,
|
|
77
85
|
where
|
|
78
86
|
});
|
|
79
87
|
const found = files?.docs?.[0];
|
|
88
|
+
return found ? getRepresentationPrefix({
|
|
89
|
+
doc: found,
|
|
90
|
+
filename
|
|
91
|
+
}) : '';
|
|
92
|
+
}
|
|
93
|
+
const getRepresentationPrefix = ({ doc, filename })=>{
|
|
94
|
+
const representation = doc.original?.filename === filename ? doc.original : Object.values(doc.variants ?? {}).find((variant)=>variant?.filename === filename) ?? doc;
|
|
80
95
|
return buildPrefixWithObjectKey({
|
|
81
|
-
objectKey:
|
|
82
|
-
prefix:
|
|
96
|
+
objectKey: representation._objectKey ?? (!doc.original?.filename ? doc._objectKey : undefined),
|
|
97
|
+
prefix: representation.prefix ?? (!doc.original?.filename ? doc.prefix : undefined)
|
|
83
98
|
});
|
|
84
|
-
}
|
|
99
|
+
};
|
|
85
100
|
|
|
86
101
|
//# sourceMappingURL=getFilePrefix.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utilities/getFilePrefix.ts"],"sourcesContent":["import type {
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/getFilePrefix.ts"],"sourcesContent":["import type {\n CollectionConfig,\n FileData,\n PayloadRequest,\n SanitizedUploadConfig,\n TypeWithID,\n} from 'payload'\n\nimport { buildPrefixWithObjectKey } from './buildPrefixWithObjectKey.js'\nimport { buildUploadStoragePathData } from './buildStoragePathData.js'\nimport { sanitizePrefix } from './sanitizePrefix.js'\n\n/**\n * Resolves the folder a stored object lives under (semantic `prefix` plus `_objectKey`).\n *\n * An already-authorized `doc` wins outright. Otherwise an `uploadReference` is used (its\n * `prefix` re-contained with {@link buildUploadStoragePathData}, since it hasn't been verified against\n * a signed receipt by every adapter). Otherwise the document is resolved with\n * `overrideAccess: false`, any query-param prefix only narrows that lookup, and `_objectKey`\n * is read from the resolved document via `showHiddenFields`.\n */\nexport async function getFilePrefix({\n collection,\n collectionPrefix,\n doc,\n filename,\n prefixQueryParam,\n req,\n uploadReference,\n useCompositePrefixes = false,\n}: {\n collection: CollectionConfig\n collectionPrefix?: string\n doc?: Partial<FileData> & TypeWithID\n filename: string\n /**\n * Only narrows the access-controlled fallback lookup below; never trusted or\n * returned directly. Prefer passing the access-checked document as `doc` instead.\n */\n prefixQueryParam?: string\n req: PayloadRequest\n uploadReference?: unknown\n useCompositePrefixes?: boolean\n}): Promise<string> {\n // The serve path already loaded and authorized this document — trust it over any\n // client-supplied upload reference or query prefix.\n if (doc) {\n return getRepresentationPrefix({ doc, filename })\n }\n\n // Upload instructions call handlers without a document yet. Re-contain the claimed\n // prefix — it hasn't been verified against a signed receipt by every adapter.\n if (\n uploadReference &&\n typeof uploadReference === 'object' &&\n 'prefix' in uploadReference &&\n typeof uploadReference.prefix === 'string'\n ) {\n const referenceObjectKey =\n '_objectKey' in uploadReference\n ? (uploadReference as { _objectKey?: string })._objectKey\n : undefined\n const containedPrefix = buildUploadStoragePathData({\n collectionPrefix,\n docPrefix: uploadReference.prefix,\n filename,\n useCompositePrefixes,\n }).sanitizedDocPrefix\n return buildPrefixWithObjectKey({ objectKey: referenceObjectKey, prefix: containedPrefix })\n }\n\n // Reads without a query prefix or read-access constraints skip the endpoint's document lookup.\n const variants = (collection?.upload as SanitizedUploadConfig)?.variants || []\n\n const filenameClause = {\n or: [\n {\n filename: { equals: filename },\n },\n { 'original.filename': { equals: filename } },\n ...variants.map((imageSize) => ({\n [`variants.${imageSize.name}.filename`]: { equals: filename },\n })),\n ],\n }\n\n // Only filter by prefix when the collection persists a `prefix` field (querying a missing field throws).\n const hasPrefixField = collection.fields?.some(\n (field) => 'name' in field && field.name === 'prefix',\n )\n const where =\n typeof prefixQueryParam === 'string' && hasPrefixField\n ? { and: [filenameClause, { prefix: { equals: sanitizePrefix(prefixQueryParam) } }] }\n : filenameClause\n\n const files = await req.payload.find({\n collection: collection.slug,\n depth: 0,\n draft: true,\n limit: 1,\n overrideAccess: false,\n pagination: false,\n req,\n select: {\n _objectKey: true,\n filename: true,\n original: true,\n prefix: true,\n variants: true,\n },\n showHiddenFields: true,\n where,\n })\n\n const found = files?.docs?.[0] as\n | ({ _objectKey?: string; prefix?: string } & Partial<FileData>)\n | undefined\n return found ? getRepresentationPrefix({ doc: found, filename }) : ''\n}\n\nconst getRepresentationPrefix = ({\n doc,\n filename,\n}: {\n doc: Partial<FileData>\n filename: string\n}): string => {\n const representation =\n doc.original?.filename === filename\n ? doc.original\n : (Object.values(doc.variants ?? {}).find((variant) => variant?.filename === filename) ?? doc)\n return buildPrefixWithObjectKey({\n objectKey: representation._objectKey ?? (!doc.original?.filename ? doc._objectKey : undefined),\n prefix: representation.prefix ?? (!doc.original?.filename ? doc.prefix : undefined),\n })\n}\n"],"names":["buildPrefixWithObjectKey","buildUploadStoragePathData","sanitizePrefix","getFilePrefix","collection","collectionPrefix","doc","filename","prefixQueryParam","req","uploadReference","useCompositePrefixes","getRepresentationPrefix","prefix","referenceObjectKey","_objectKey","undefined","containedPrefix","docPrefix","sanitizedDocPrefix","objectKey","variants","upload","filenameClause","or","equals","map","imageSize","name","hasPrefixField","fields","some","field","where","and","files","payload","find","slug","depth","draft","limit","overrideAccess","pagination","select","original","showHiddenFields","found","docs","representation","Object","values","variant"],"mappings":"AAQA,SAASA,wBAAwB,QAAQ,gCAA+B;AACxE,SAASC,0BAA0B,QAAQ,4BAA2B;AACtE,SAASC,cAAc,QAAQ,sBAAqB;AAEpD;;;;;;;;CAQC,GACD,OAAO,eAAeC,cAAc,EAClCC,UAAU,EACVC,gBAAgB,EAChBC,GAAG,EACHC,QAAQ,EACRC,gBAAgB,EAChBC,GAAG,EACHC,eAAe,EACfC,uBAAuB,KAAK,EAc7B;IACC,iFAAiF;IACjF,oDAAoD;IACpD,IAAIL,KAAK;QACP,OAAOM,wBAAwB;YAAEN;YAAKC;QAAS;IACjD;IAEA,mFAAmF;IACnF,8EAA8E;IAC9E,IACEG,mBACA,OAAOA,oBAAoB,YAC3B,YAAYA,mBACZ,OAAOA,gBAAgBG,MAAM,KAAK,UAClC;QACA,MAAMC,qBACJ,gBAAgBJ,kBACZ,AAACA,gBAA4CK,UAAU,GACvDC;QACN,MAAMC,kBAAkBhB,2BAA2B;YACjDI;YACAa,WAAWR,gBAAgBG,MAAM;YACjCN;YACAI;QACF,GAAGQ,kBAAkB;QACrB,OAAOnB,yBAAyB;YAAEoB,WAAWN;YAAoBD,QAAQI;QAAgB;IAC3F;IAEA,+FAA+F;IAC/F,MAAMI,WAAW,AAACjB,YAAYkB,QAAkCD,YAAY,EAAE;IAE9E,MAAME,iBAAiB;QACrBC,IAAI;YACF;gBACEjB,UAAU;oBAAEkB,QAAQlB;gBAAS;YAC/B;YACA;gBAAE,qBAAqB;oBAAEkB,QAAQlB;gBAAS;YAAE;eACzCc,SAASK,GAAG,CAAC,CAACC,YAAe,CAAA;oBAC9B,CAAC,CAAC,SAAS,EAAEA,UAAUC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE;wBAAEH,QAAQlB;oBAAS;gBAC9D,CAAA;SACD;IACH;IAEA,yGAAyG;IACzG,MAAMsB,iBAAiBzB,WAAW0B,MAAM,EAAEC,KACxC,CAACC,QAAU,UAAUA,SAASA,MAAMJ,IAAI,KAAK;IAE/C,MAAMK,QACJ,OAAOzB,qBAAqB,YAAYqB,iBACpC;QAAEK,KAAK;YAACX;YAAgB;gBAAEV,QAAQ;oBAAEY,QAAQvB,eAAeM;gBAAkB;YAAE;SAAE;IAAC,IAClFe;IAEN,MAAMY,QAAQ,MAAM1B,IAAI2B,OAAO,CAACC,IAAI,CAAC;QACnCjC,YAAYA,WAAWkC,IAAI;QAC3BC,OAAO;QACPC,OAAO;QACPC,OAAO;QACPC,gBAAgB;QAChBC,YAAY;QACZlC;QACAmC,QAAQ;YACN7B,YAAY;YACZR,UAAU;YACVsC,UAAU;YACVhC,QAAQ;YACRQ,UAAU;QACZ;QACAyB,kBAAkB;QAClBb;IACF;IAEA,MAAMc,QAAQZ,OAAOa,MAAM,CAAC,EAAE;IAG9B,OAAOD,QAAQnC,wBAAwB;QAAEN,KAAKyC;QAAOxC;IAAS,KAAK;AACrE;AAEA,MAAMK,0BAA0B,CAAC,EAC/BN,GAAG,EACHC,QAAQ,EAIT;IACC,MAAM0C,iBACJ3C,IAAIuC,QAAQ,EAAEtC,aAAaA,WACvBD,IAAIuC,QAAQ,GACXK,OAAOC,MAAM,CAAC7C,IAAIe,QAAQ,IAAI,CAAC,GAAGgB,IAAI,CAAC,CAACe,UAAYA,SAAS7C,aAAaA,aAAaD;IAC9F,OAAON,yBAAyB;QAC9BoB,WAAW6B,eAAelC,UAAU,IAAK,CAAA,CAACT,IAAIuC,QAAQ,EAAEtC,WAAWD,IAAIS,UAAU,GAAGC,SAAQ;QAC5FH,QAAQoC,eAAepC,MAAM,IAAK,CAAA,CAACP,IAAIuC,QAAQ,EAAEtC,WAAWD,IAAIO,MAAM,GAAGG,SAAQ;IACnF;AACF"}
|
|
@@ -16,8 +16,8 @@ export function getIncomingFiles({ data, req }) {
|
|
|
16
16
|
files = [
|
|
17
17
|
mainFile
|
|
18
18
|
];
|
|
19
|
-
if (data?.
|
|
20
|
-
Object.entries(data.
|
|
19
|
+
if (data?.variants) {
|
|
20
|
+
Object.entries(data.variants).forEach(([key, resizedFileData])=>{
|
|
21
21
|
if (payloadUploadSizes?.[key] && resizedFileData.mimeType) {
|
|
22
22
|
files = files.concat([
|
|
23
23
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utilities/getIncomingFiles.ts"],"sourcesContent":["import type { FileData, PayloadRequest } from 'payload'\n\nimport type { File } from '../types.js'\n\ninterface CloudStorageContext {\n file: PayloadRequest['file']\n uploadSizes: PayloadRequest['payloadUploadSizes']\n}\n\nexport function getIncomingFiles({\n data,\n req,\n}: {\n data: Partial<FileData>\n req: PayloadRequest\n}): File[] {\n // Fall back to context if req.file was cleared\n const ctx = req.context?._payloadCloudStorage as CloudStorageContext | undefined\n const file = req.file ?? ctx?.file\n const payloadUploadSizes = req.payloadUploadSizes ?? ctx?.uploadSizes\n\n let files: File[] = []\n\n if (file && data.filename && data.mimeType) {\n const mainFile: File = {\n buffer: file.data,\n filename: data.filename,\n filesize: file.size,\n mimeType: data.mimeType,\n tempFilePath: file.tempFilePath,\n uploadReference: file.uploadReference,\n }\n\n files = [mainFile]\n\n if (data?.
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/getIncomingFiles.ts"],"sourcesContent":["import type { FileData, PayloadRequest } from 'payload'\n\nimport type { File } from '../types.js'\n\ninterface CloudStorageContext {\n file: PayloadRequest['file']\n uploadSizes: PayloadRequest['payloadUploadSizes']\n}\n\nexport function getIncomingFiles({\n data,\n req,\n}: {\n data: Partial<FileData>\n req: PayloadRequest\n}): File[] {\n // Fall back to context if req.file was cleared\n const ctx = req.context?._payloadCloudStorage as CloudStorageContext | undefined\n const file = req.file ?? ctx?.file\n const payloadUploadSizes = req.payloadUploadSizes ?? ctx?.uploadSizes\n\n let files: File[] = []\n\n if (file && data.filename && data.mimeType) {\n const mainFile: File = {\n buffer: file.data,\n filename: data.filename,\n filesize: file.size,\n mimeType: data.mimeType,\n tempFilePath: file.tempFilePath,\n uploadReference: file.uploadReference,\n }\n\n files = [mainFile]\n\n if (data?.variants) {\n Object.entries(data.variants).forEach(([key, resizedFileData]) => {\n if (payloadUploadSizes?.[key] && resizedFileData.mimeType) {\n files = files.concat([\n {\n buffer: payloadUploadSizes[key],\n filename: `${resizedFileData.filename}`,\n filesize: payloadUploadSizes[key].length,\n mimeType: resizedFileData.mimeType,\n },\n ])\n }\n })\n }\n }\n\n return files\n}\n"],"names":["getIncomingFiles","data","req","ctx","context","_payloadCloudStorage","file","payloadUploadSizes","uploadSizes","files","filename","mimeType","mainFile","buffer","filesize","size","tempFilePath","uploadReference","variants","Object","entries","forEach","key","resizedFileData","concat","length"],"mappings":"AASA,OAAO,SAASA,iBAAiB,EAC/BC,IAAI,EACJC,GAAG,EAIJ;IACC,+CAA+C;IAC/C,MAAMC,MAAMD,IAAIE,OAAO,EAAEC;IACzB,MAAMC,OAAOJ,IAAII,IAAI,IAAIH,KAAKG;IAC9B,MAAMC,qBAAqBL,IAAIK,kBAAkB,IAAIJ,KAAKK;IAE1D,IAAIC,QAAgB,EAAE;IAEtB,IAAIH,QAAQL,KAAKS,QAAQ,IAAIT,KAAKU,QAAQ,EAAE;QAC1C,MAAMC,WAAiB;YACrBC,QAAQP,KAAKL,IAAI;YACjBS,UAAUT,KAAKS,QAAQ;YACvBI,UAAUR,KAAKS,IAAI;YACnBJ,UAAUV,KAAKU,QAAQ;YACvBK,cAAcV,KAAKU,YAAY;YAC/BC,iBAAiBX,KAAKW,eAAe;QACvC;QAEAR,QAAQ;YAACG;SAAS;QAElB,IAAIX,MAAMiB,UAAU;YAClBC,OAAOC,OAAO,CAACnB,KAAKiB,QAAQ,EAAEG,OAAO,CAAC,CAAC,CAACC,KAAKC,gBAAgB;gBAC3D,IAAIhB,oBAAoB,CAACe,IAAI,IAAIC,gBAAgBZ,QAAQ,EAAE;oBACzDF,QAAQA,MAAMe,MAAM,CAAC;wBACnB;4BACEX,QAAQN,kBAAkB,CAACe,IAAI;4BAC/BZ,UAAU,GAAGa,gBAAgBb,QAAQ,EAAE;4BACvCI,UAAUP,kBAAkB,CAACe,IAAI,CAACG,MAAM;4BACxCd,UAAUY,gBAAgBZ,QAAQ;wBACpC;qBACD;gBACH;YACF;QACF;IACF;IAEA,OAAOF;AACT"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CollectionConfig } from 'payload';
|
|
2
|
+
/**
|
|
3
|
+
* URL hooks rebuild direct URLs from each representation's stored location, so a positive
|
|
4
|
+
* selection of a URL must also read the fields that location is built from.
|
|
5
|
+
*/
|
|
6
|
+
export declare const getStorageLocationSelect: ({ select: existingSelect, }: {
|
|
7
|
+
select: CollectionConfig['select'];
|
|
8
|
+
}) => NonNullable<CollectionConfig['select']>;
|
|
9
|
+
//# sourceMappingURL=getStorageLocationSelect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getStorageLocationSelect.d.ts","sourceRoot":"","sources":["../../src/utilities/getStorageLocationSelect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAc,MAAM,SAAS,CAAA;AAQ3D;;;GAGG;AACH,eAAO,MAAM,wBAAwB,gCAGhC;IACD,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAA;CACnC,KAAG,WAAW,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CASzC,CAAA"}
|