@payloadcms/storage-vercel-blob 4.0.0-internal.5b1e7cd → 4.0.0-internal.5e065b3
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/LICENSE.md +1 -1
- package/README.md +2 -2
- package/dist/adapter.d.ts +3 -1
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +100 -17
- package/dist/adapter.js.map +1 -1
- package/dist/authorizeFileOverwrite.d.ts +16 -0
- package/dist/authorizeFileOverwrite.d.ts.map +1 -0
- package/dist/authorizeFileOverwrite.js +117 -0
- package/dist/authorizeFileOverwrite.js.map +1 -0
- package/dist/client/VercelBlobClientUploadHandler.d.ts +3 -8
- package/dist/client/VercelBlobClientUploadHandler.d.ts.map +1 -1
- package/dist/client/VercelBlobClientUploadHandler.js +9 -26
- package/dist/client/VercelBlobClientUploadHandler.js.map +1 -1
- package/dist/deleteFile.d.ts +2 -5
- package/dist/deleteFile.d.ts.map +1 -1
- package/dist/deleteFile.js +3 -10
- package/dist/deleteFile.js.map +1 -1
- package/dist/generateURL.d.ts +1 -0
- package/dist/generateURL.d.ts.map +1 -1
- package/dist/generateURL.js +13 -9
- package/dist/generateURL.js.map +1 -1
- package/dist/getCollectionSources.d.ts +20 -0
- package/dist/getCollectionSources.d.ts.map +1 -0
- package/dist/getCollectionSources.js +27 -0
- package/dist/getCollectionSources.js.map +1 -0
- package/dist/getFile.d.ts +5 -4
- package/dist/getFile.d.ts.map +1 -1
- package/dist/getFile.js +21 -13
- package/dist/getFile.js.map +1 -1
- package/dist/index.d.ts +12 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +69 -76
- package/dist/index.js.map +1 -1
- package/dist/uploadFile.d.ts +2 -5
- package/dist/uploadFile.d.ts.map +1 -1
- package/dist/uploadFile.js +3 -9
- package/dist/uploadFile.js.map +1 -1
- package/package.json +5 -5
- package/dist/getClientUploadRoute.d.ts +0 -13
- package/dist/getClientUploadRoute.d.ts.map +0 -1
- package/dist/getClientUploadRoute.js +0 -35
- package/dist/getClientUploadRoute.js.map +0 -1
package/LICENSE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2018-
|
|
3
|
+
Copyright (c) 2018-2026 Payload CMS, LLC <info@payloadcms.com>
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
6
|
a copy of this software and associated documentation files (the
|
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ pnpm add @payloadcms/storage-vercel-blob
|
|
|
15
15
|
- Configure the `collections` object to specify which collections should use the Vercel Blob adapter. The slug _must_ match one of your existing collection slugs.
|
|
16
16
|
- Ensure you have `BLOB_READ_WRITE_TOKEN` set in your Vercel environment variables. This is usually set by Vercel automatically after adding blob storage to your project.
|
|
17
17
|
- When enabled, this package will automatically set `disableLocalStorage` to `true` for each collection.
|
|
18
|
-
- When deploying to Vercel, server uploads are limited
|
|
18
|
+
- When deploying to Vercel, server uploads are limited to 4.5MB. Set `clientUploads` to `true` to use upload instructions and send files directly to Vercel Blob.
|
|
19
19
|
|
|
20
20
|
```ts
|
|
21
21
|
import { vercelBlobStorage } from '@payloadcms/storage-vercel-blob'
|
|
@@ -48,4 +48,4 @@ export default buildConfig({
|
|
|
48
48
|
| `addRandomSuffix` | Add a random suffix to the uploaded file name in Vercel Blob storage | `false` |
|
|
49
49
|
| `cacheControlMaxAge` | Cache-Control max-age in seconds | `365 * 24 * 60 * 60` (1 Year) |
|
|
50
50
|
| `token` | Vercel Blob storage read/write token | `''` |
|
|
51
|
-
| `clientUploads` |
|
|
51
|
+
| `clientUploads` | Upload directly to Vercel Blob instead of through Payload. | |
|
package/dist/adapter.d.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import type { Adapter, ClientUploadsConfig } from '@payloadcms/plugin-cloud-storage/types';
|
|
2
|
+
import type { VercelBlobCollectionSource } from './authorizeFileOverwrite.js';
|
|
2
3
|
interface CreateVercelBlobAdapterArgs {
|
|
3
4
|
access: 'public';
|
|
4
5
|
addRandomSuffix?: boolean;
|
|
5
6
|
baseUrl: string;
|
|
6
7
|
cacheControlMaxAge: number;
|
|
7
8
|
clientUploads?: ClientUploadsConfig;
|
|
9
|
+
collectionSources: VercelBlobCollectionSource[];
|
|
8
10
|
token: string;
|
|
9
11
|
useCompositePrefixes?: boolean;
|
|
10
12
|
}
|
|
11
|
-
export declare function createVercelBlobAdapter({ access, addRandomSuffix, baseUrl, cacheControlMaxAge, clientUploads, token, useCompositePrefixes, }: CreateVercelBlobAdapterArgs): Adapter;
|
|
13
|
+
export declare function createVercelBlobAdapter({ access, addRandomSuffix, baseUrl, cacheControlMaxAge, clientUploads, collectionSources, token, useCompositePrefixes, }: CreateVercelBlobAdapterArgs): Adapter;
|
|
12
14
|
export {};
|
|
13
15
|
//# sourceMappingURL=adapter.d.ts.map
|
package/dist/adapter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,mBAAmB,EAEpB,MAAM,wCAAwC,CAAA;
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,mBAAmB,EAEpB,MAAM,wCAAwC,CAAA;AAU/C,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAA;AAQ7E,UAAU,2BAA2B;IACnC,MAAM,EAAE,QAAQ,CAAA;IAChB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,kBAAkB,EAAE,MAAM,CAAA;IAC1B,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC,iBAAiB,EAAE,0BAA0B,EAAE,CAAA;IAC/C,KAAK,EAAE,MAAM,CAAA;IACb,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAgB,uBAAuB,CAAC,EACtC,MAAM,EACN,eAAe,EACf,OAAO,EACP,kBAAkB,EAClB,aAAa,EACb,iBAAiB,EACjB,KAAK,EACL,oBAA4B,GAC7B,EAAE,2BAA2B,GAAG,OAAO,CAiJvC"}
|
package/dist/adapter.js
CHANGED
|
@@ -1,11 +1,99 @@
|
|
|
1
|
+
import { buildUploadStoragePathData, resolveSignedURLKey } from '@payloadcms/plugin-cloud-storage/utilities';
|
|
2
|
+
import { generateClientTokenFromReadWriteToken } from '@vercel/blob/client';
|
|
3
|
+
import { Forbidden } from 'payload';
|
|
4
|
+
import { assertClientUploadAllowed, createClientUploadReceipt } from 'payload/internal';
|
|
5
|
+
import { authorizeClientOverwrite } from './authorizeFileOverwrite.js';
|
|
1
6
|
import { deleteFile } from './deleteFile.js';
|
|
2
7
|
import { generateURL } from './generateURL.js';
|
|
3
8
|
import { getFile } from './getFile.js';
|
|
4
9
|
import { uploadFile } from './uploadFile.js';
|
|
5
|
-
export function createVercelBlobAdapter({ access, addRandomSuffix, baseUrl, cacheControlMaxAge, clientUploads, token, useCompositePrefixes = false }) {
|
|
10
|
+
export function createVercelBlobAdapter({ access, addRandomSuffix, baseUrl, cacheControlMaxAge, clientUploads, collectionSources, token, useCompositePrefixes = false }) {
|
|
11
|
+
const clientUploadsAccess = typeof clientUploads === 'object' ? clientUploads.access : undefined;
|
|
6
12
|
return ({ collection, prefix = '' })=>({
|
|
7
13
|
name: 'vercel-blob',
|
|
8
|
-
|
|
14
|
+
uploadInstructions: {
|
|
15
|
+
adminHandler: {
|
|
16
|
+
path: '@payloadcms/storage-vercel-blob/client#VercelBlobClientUploadHandler'
|
|
17
|
+
},
|
|
18
|
+
enabled: Boolean(clientUploads),
|
|
19
|
+
generate: async ({ collectionSlug, docPrefix, filename, filesize, mimeType, overrideAccess, req })=>{
|
|
20
|
+
if (!overrideAccess && (clientUploadsAccess ? !await clientUploadsAccess({
|
|
21
|
+
collectionSlug,
|
|
22
|
+
req
|
|
23
|
+
}) : !req.user)) {
|
|
24
|
+
throw new Forbidden(req.t);
|
|
25
|
+
}
|
|
26
|
+
assertClientUploadAllowed({
|
|
27
|
+
collection,
|
|
28
|
+
filename,
|
|
29
|
+
mimeType
|
|
30
|
+
});
|
|
31
|
+
const requested = buildUploadStoragePathData({
|
|
32
|
+
collectionPrefix: prefix,
|
|
33
|
+
docPrefix,
|
|
34
|
+
filename,
|
|
35
|
+
useCompositePrefixes
|
|
36
|
+
});
|
|
37
|
+
const allowOverwrite = await authorizeClientOverwrite({
|
|
38
|
+
collectionPrefix: prefix,
|
|
39
|
+
collectionSources,
|
|
40
|
+
overrideAccess,
|
|
41
|
+
req,
|
|
42
|
+
requestedCollectionSlug: collectionSlug,
|
|
43
|
+
requestedFilename: requested.sanitizedFilename,
|
|
44
|
+
requestedStorageFilePath: requested.storageFilePath
|
|
45
|
+
});
|
|
46
|
+
const resolved = allowOverwrite ? {
|
|
47
|
+
...requested,
|
|
48
|
+
uploadReference: {
|
|
49
|
+
prefix: requested.sanitizedDocPrefix,
|
|
50
|
+
signedReceipt: createClientUploadReceipt({
|
|
51
|
+
allowOverwrite: true,
|
|
52
|
+
collectionSlug,
|
|
53
|
+
filename: requested.sanitizedFilename,
|
|
54
|
+
filePrefix: requested.sanitizedDocPrefix,
|
|
55
|
+
req,
|
|
56
|
+
storageFilePath: requested.storageFilePath
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
} : await resolveSignedURLKey({
|
|
60
|
+
collectionPrefix: prefix,
|
|
61
|
+
collectionSlug,
|
|
62
|
+
docPrefix,
|
|
63
|
+
filename,
|
|
64
|
+
req,
|
|
65
|
+
useCompositePrefixes
|
|
66
|
+
});
|
|
67
|
+
return {
|
|
68
|
+
name: 'uploadToVercelBlob',
|
|
69
|
+
type: 'dispatch',
|
|
70
|
+
data: {
|
|
71
|
+
pathname: resolved.storageFilePath,
|
|
72
|
+
token: await generateClientTokenFromReadWriteToken({
|
|
73
|
+
addRandomSuffix: false,
|
|
74
|
+
allowedContentTypes: mimeType ? [
|
|
75
|
+
mimeType
|
|
76
|
+
] : undefined,
|
|
77
|
+
...allowOverwrite && {
|
|
78
|
+
allowOverwrite: true
|
|
79
|
+
},
|
|
80
|
+
cacheControlMaxAge,
|
|
81
|
+
maximumSizeInBytes: filesize,
|
|
82
|
+
pathname: resolved.storageFilePath,
|
|
83
|
+
token
|
|
84
|
+
})
|
|
85
|
+
},
|
|
86
|
+
file: {
|
|
87
|
+
filename: resolved.sanitizedFilename,
|
|
88
|
+
mimeType,
|
|
89
|
+
size: filesize,
|
|
90
|
+
uploadReference: resolved.uploadReference
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
},
|
|
94
|
+
requiresUploadReceipt: true,
|
|
95
|
+
useInAdmin: true
|
|
96
|
+
},
|
|
9
97
|
generateURL: ({ filename, prefix: urlPrefix = '' })=>generateURL({
|
|
10
98
|
baseUrl,
|
|
11
99
|
collectionPrefix: prefix,
|
|
@@ -13,43 +101,38 @@ export function createVercelBlobAdapter({ access, addRandomSuffix, baseUrl, cach
|
|
|
13
101
|
prefix: urlPrefix,
|
|
14
102
|
useCompositePrefixes
|
|
15
103
|
}),
|
|
16
|
-
handleDelete: ({
|
|
104
|
+
handleDelete: ({ storageFilePath })=>deleteFile({
|
|
17
105
|
baseUrl,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
filename,
|
|
21
|
-
token,
|
|
22
|
-
useCompositePrefixes
|
|
106
|
+
storageFilePath,
|
|
107
|
+
token
|
|
23
108
|
}),
|
|
24
|
-
handleUpload: async ({ data, file: { buffer,
|
|
109
|
+
handleUpload: async ({ data, file: { buffer, mimeType }, storageFilePath })=>{
|
|
25
110
|
const result = await uploadFile({
|
|
26
111
|
access,
|
|
27
112
|
addRandomSuffix,
|
|
28
113
|
buffer,
|
|
29
114
|
cacheControlMaxAge,
|
|
30
|
-
collectionPrefix: prefix,
|
|
31
|
-
docPrefix: data.prefix,
|
|
32
|
-
filename,
|
|
33
115
|
mimeType,
|
|
34
|
-
|
|
35
|
-
|
|
116
|
+
storageFilePath,
|
|
117
|
+
token
|
|
36
118
|
});
|
|
37
119
|
if (result.filename) {
|
|
38
120
|
data.filename = result.filename;
|
|
39
121
|
}
|
|
40
122
|
return data;
|
|
41
123
|
},
|
|
42
|
-
staticHandler: (req, { headers, params: {
|
|
124
|
+
staticHandler: (req, { doc, headers, params: { filename, operation, uploadReference } })=>getFile({
|
|
43
125
|
baseUrl,
|
|
44
126
|
cacheControlMaxAge,
|
|
45
|
-
clientUploadContext,
|
|
46
127
|
collection,
|
|
47
128
|
collectionPrefix: prefix,
|
|
129
|
+
doc,
|
|
48
130
|
filename,
|
|
49
131
|
incomingHeaders: headers,
|
|
50
|
-
|
|
132
|
+
operation,
|
|
51
133
|
req,
|
|
52
134
|
token,
|
|
135
|
+
uploadReference,
|
|
53
136
|
useCompositePrefixes
|
|
54
137
|
})
|
|
55
138
|
});
|
package/dist/adapter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapter.ts"],"sourcesContent":["import type {\n Adapter,\n ClientUploadsConfig,\n GeneratedAdapter,\n} from '@payloadcms/plugin-cloud-storage/types'\n\nimport { deleteFile } from './deleteFile.js'\nimport { generateURL } from './generateURL.js'\nimport { getFile } from './getFile.js'\nimport { uploadFile } from './uploadFile.js'\n\ninterface CreateVercelBlobAdapterArgs {\n access: 'public'\n addRandomSuffix?: boolean\n baseUrl: string\n cacheControlMaxAge: number\n clientUploads?: ClientUploadsConfig\n token: string\n useCompositePrefixes?: boolean\n}\n\nexport function createVercelBlobAdapter({\n access,\n addRandomSuffix,\n baseUrl,\n cacheControlMaxAge,\n clientUploads,\n token,\n useCompositePrefixes = false,\n}: CreateVercelBlobAdapterArgs): Adapter {\n return ({ collection, prefix = '' }): GeneratedAdapter => ({\n name: 'vercel-blob',\n clientUploads,\n\n
|
|
1
|
+
{"version":3,"sources":["../src/adapter.ts"],"sourcesContent":["import type {\n Adapter,\n ClientUploadsConfig,\n GeneratedAdapter,\n} from '@payloadcms/plugin-cloud-storage/types'\n\nimport {\n buildUploadStoragePathData,\n resolveSignedURLKey,\n} from '@payloadcms/plugin-cloud-storage/utilities'\nimport { generateClientTokenFromReadWriteToken } from '@vercel/blob/client'\nimport { Forbidden } from 'payload'\nimport { assertClientUploadAllowed, createClientUploadReceipt } from 'payload/internal'\n\nimport type { VercelBlobCollectionSource } from './authorizeFileOverwrite.js'\n\nimport { authorizeClientOverwrite } from './authorizeFileOverwrite.js'\nimport { deleteFile } from './deleteFile.js'\nimport { generateURL } from './generateURL.js'\nimport { getFile } from './getFile.js'\nimport { uploadFile } from './uploadFile.js'\n\ninterface CreateVercelBlobAdapterArgs {\n access: 'public'\n addRandomSuffix?: boolean\n baseUrl: string\n cacheControlMaxAge: number\n clientUploads?: ClientUploadsConfig\n collectionSources: VercelBlobCollectionSource[]\n token: string\n useCompositePrefixes?: boolean\n}\n\nexport function createVercelBlobAdapter({\n access,\n addRandomSuffix,\n baseUrl,\n cacheControlMaxAge,\n clientUploads,\n collectionSources,\n token,\n useCompositePrefixes = false,\n}: CreateVercelBlobAdapterArgs): Adapter {\n const clientUploadsAccess = typeof clientUploads === 'object' ? clientUploads.access : undefined\n\n return ({ collection, prefix = '' }): GeneratedAdapter => ({\n name: 'vercel-blob',\n\n uploadInstructions: {\n adminHandler: {\n path: '@payloadcms/storage-vercel-blob/client#VercelBlobClientUploadHandler',\n },\n enabled: Boolean(clientUploads),\n generate: async ({\n collectionSlug,\n docPrefix,\n filename,\n filesize,\n mimeType,\n overrideAccess,\n req,\n }) => {\n if (\n !overrideAccess &&\n (clientUploadsAccess ? !(await clientUploadsAccess({ collectionSlug, req })) : !req.user)\n ) {\n throw new Forbidden(req.t)\n }\n\n assertClientUploadAllowed({ collection, filename, mimeType })\n\n const requested = buildUploadStoragePathData({\n collectionPrefix: prefix,\n docPrefix,\n filename,\n useCompositePrefixes,\n })\n const allowOverwrite = await authorizeClientOverwrite({\n collectionPrefix: prefix,\n collectionSources,\n overrideAccess,\n req,\n requestedCollectionSlug: collectionSlug,\n requestedFilename: requested.sanitizedFilename,\n requestedStorageFilePath: requested.storageFilePath,\n })\n const resolved = allowOverwrite\n ? {\n ...requested,\n uploadReference: {\n prefix: requested.sanitizedDocPrefix,\n signedReceipt: createClientUploadReceipt({\n allowOverwrite: true,\n collectionSlug,\n filename: requested.sanitizedFilename,\n filePrefix: requested.sanitizedDocPrefix,\n req,\n storageFilePath: requested.storageFilePath,\n }),\n },\n }\n : await resolveSignedURLKey({\n collectionPrefix: prefix,\n collectionSlug,\n docPrefix,\n filename,\n req,\n useCompositePrefixes,\n })\n\n return {\n name: 'uploadToVercelBlob',\n type: 'dispatch',\n data: {\n pathname: resolved.storageFilePath,\n token: await generateClientTokenFromReadWriteToken({\n addRandomSuffix: false,\n allowedContentTypes: mimeType ? [mimeType] : undefined,\n ...(allowOverwrite && { allowOverwrite: true }),\n cacheControlMaxAge,\n maximumSizeInBytes: filesize,\n pathname: resolved.storageFilePath,\n token,\n }),\n },\n file: {\n filename: resolved.sanitizedFilename,\n mimeType,\n size: filesize,\n uploadReference: resolved.uploadReference,\n },\n }\n },\n requiresUploadReceipt: true,\n useInAdmin: true,\n },\n\n generateURL: ({ filename, prefix: urlPrefix = '' }) =>\n generateURL({\n baseUrl,\n collectionPrefix: prefix,\n filename,\n prefix: urlPrefix,\n useCompositePrefixes,\n }),\n\n handleDelete: ({ storageFilePath }) =>\n deleteFile({\n baseUrl,\n storageFilePath,\n token,\n }),\n\n handleUpload: async ({ data, file: { buffer, mimeType }, storageFilePath }) => {\n const result = await uploadFile({\n access,\n addRandomSuffix,\n buffer,\n cacheControlMaxAge,\n mimeType,\n storageFilePath,\n token,\n })\n\n if (result.filename) {\n data.filename = result.filename\n }\n\n return data\n },\n\n staticHandler: (req, { doc, headers, params: { filename, operation, uploadReference } }) =>\n getFile({\n baseUrl,\n cacheControlMaxAge,\n collection,\n collectionPrefix: prefix,\n doc,\n filename,\n incomingHeaders: headers,\n operation,\n req,\n token,\n uploadReference,\n useCompositePrefixes,\n }),\n })\n}\n"],"names":["buildUploadStoragePathData","resolveSignedURLKey","generateClientTokenFromReadWriteToken","Forbidden","assertClientUploadAllowed","createClientUploadReceipt","authorizeClientOverwrite","deleteFile","generateURL","getFile","uploadFile","createVercelBlobAdapter","access","addRandomSuffix","baseUrl","cacheControlMaxAge","clientUploads","collectionSources","token","useCompositePrefixes","clientUploadsAccess","undefined","collection","prefix","name","uploadInstructions","adminHandler","path","enabled","Boolean","generate","collectionSlug","docPrefix","filename","filesize","mimeType","overrideAccess","req","user","t","requested","collectionPrefix","allowOverwrite","requestedCollectionSlug","requestedFilename","sanitizedFilename","requestedStorageFilePath","storageFilePath","resolved","uploadReference","sanitizedDocPrefix","signedReceipt","filePrefix","type","data","pathname","allowedContentTypes","maximumSizeInBytes","file","size","requiresUploadReceipt","useInAdmin","urlPrefix","handleDelete","handleUpload","buffer","result","staticHandler","doc","headers","params","operation","incomingHeaders"],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { PayloadRequest } from 'payload';
|
|
2
|
+
export type VercelBlobCollectionSource = {
|
|
3
|
+
collectionPrefix: string;
|
|
4
|
+
collectionSlug: string;
|
|
5
|
+
useCompositePrefixes: boolean;
|
|
6
|
+
};
|
|
7
|
+
export declare function authorizeClientOverwrite({ collectionPrefix: collectionPrefixArg, collectionSources, overrideAccess, req, requestedCollectionSlug, requestedFilename, requestedStorageFilePath, }: {
|
|
8
|
+
collectionPrefix: string;
|
|
9
|
+
collectionSources: VercelBlobCollectionSource[];
|
|
10
|
+
overrideAccess?: boolean;
|
|
11
|
+
req: PayloadRequest;
|
|
12
|
+
requestedCollectionSlug: string;
|
|
13
|
+
requestedFilename: string;
|
|
14
|
+
requestedStorageFilePath: string;
|
|
15
|
+
}): Promise<boolean>;
|
|
16
|
+
//# sourceMappingURL=authorizeFileOverwrite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorizeFileOverwrite.d.ts","sourceRoot":"","sources":["../src/authorizeFileOverwrite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAY,cAAc,EAAqB,MAAM,SAAS,CAAA;AAU1E,MAAM,MAAM,0BAA0B,GAAG;IACvC,gBAAgB,EAAE,MAAM,CAAA;IACxB,cAAc,EAAE,MAAM,CAAA;IACtB,oBAAoB,EAAE,OAAO,CAAA;CAC9B,CAAA;AAmHD,wBAAsB,wBAAwB,CAAC,EAC7C,gBAAgB,EAAE,mBAAmB,EACrC,iBAAiB,EACjB,cAAsB,EACtB,GAAG,EACH,uBAAuB,EACvB,iBAAiB,EACjB,wBAAwB,GACzB,EAAE;IACD,gBAAgB,EAAE,MAAM,CAAA;IACxB,iBAAiB,EAAE,0BAA0B,EAAE,CAAA;IAC/C,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,GAAG,EAAE,cAAc,CAAA;IACnB,uBAAuB,EAAE,MAAM,CAAA;IAC/B,iBAAiB,EAAE,MAAM,CAAA;IACzB,wBAAwB,EAAE,MAAM,CAAA;CACjC,GAAG,OAAO,CAAC,OAAO,CAAC,CA8CnB"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { buildStoragePathData, isStoragePathWithinCollectionPrefix } from '@payloadcms/plugin-cloud-storage/utilities';
|
|
2
|
+
import { combineQueries, executeAccess, Forbidden } from 'payload';
|
|
3
|
+
function getDocumentFileKeys({ collectionPrefix, doc, useCompositePrefixes }) {
|
|
4
|
+
return [
|
|
5
|
+
doc.filename,
|
|
6
|
+
...Object.values(doc.variants || {}).map((size)=>size?.filename)
|
|
7
|
+
].filter((filename)=>typeof filename === 'string').map((filename)=>buildStoragePathData({
|
|
8
|
+
collectionPrefix,
|
|
9
|
+
docPrefix: doc.prefix,
|
|
10
|
+
filename,
|
|
11
|
+
useCompositePrefixes
|
|
12
|
+
}).storageFilePath);
|
|
13
|
+
}
|
|
14
|
+
async function findMatchingDocs({ collectionPrefix, collectionSlug, req, requestedFilename, requestedStorageFilePath, useCompositePrefixes }) {
|
|
15
|
+
const collection = req.payload.collections[collectionSlug];
|
|
16
|
+
if (!collection) {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
const variants = collection.config.upload && typeof collection.config.upload === 'object' ? collection.config.upload.variants || [] : [];
|
|
20
|
+
const filenameQueries = [
|
|
21
|
+
{
|
|
22
|
+
filename: {
|
|
23
|
+
equals: requestedFilename
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
...variants.map(({ name })=>({
|
|
27
|
+
[`variants.${name}.filename`]: {
|
|
28
|
+
equals: requestedFilename
|
|
29
|
+
}
|
|
30
|
+
}))
|
|
31
|
+
];
|
|
32
|
+
const result = await req.payload.find({
|
|
33
|
+
collection: collectionSlug,
|
|
34
|
+
depth: 0,
|
|
35
|
+
draft: true,
|
|
36
|
+
overrideAccess: true,
|
|
37
|
+
pagination: false,
|
|
38
|
+
req,
|
|
39
|
+
where: {
|
|
40
|
+
or: filenameQueries
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
return result.docs.filter((doc)=>getDocumentFileKeys({
|
|
44
|
+
collectionPrefix,
|
|
45
|
+
doc,
|
|
46
|
+
useCompositePrefixes
|
|
47
|
+
}).includes(requestedStorageFilePath));
|
|
48
|
+
}
|
|
49
|
+
async function canUpdateDoc({ collectionSlug, doc, req }) {
|
|
50
|
+
const collection = req.payload.collections[collectionSlug];
|
|
51
|
+
if (!collection) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const accessResult = await executeAccess({
|
|
55
|
+
id: doc.id,
|
|
56
|
+
slug: collectionSlug,
|
|
57
|
+
disableErrors: true,
|
|
58
|
+
req
|
|
59
|
+
}, collection.config.access.update);
|
|
60
|
+
if (typeof accessResult === 'boolean') {
|
|
61
|
+
return accessResult;
|
|
62
|
+
}
|
|
63
|
+
const result = await req.payload.find({
|
|
64
|
+
collection: collectionSlug,
|
|
65
|
+
depth: 0,
|
|
66
|
+
draft: true,
|
|
67
|
+
limit: 1,
|
|
68
|
+
overrideAccess: true,
|
|
69
|
+
pagination: false,
|
|
70
|
+
req,
|
|
71
|
+
where: combineQueries({
|
|
72
|
+
id: {
|
|
73
|
+
equals: doc.id
|
|
74
|
+
}
|
|
75
|
+
}, accessResult)
|
|
76
|
+
});
|
|
77
|
+
return result.docs.length === 1;
|
|
78
|
+
}
|
|
79
|
+
export async function authorizeClientOverwrite({ collectionPrefix: collectionPrefixArg, collectionSources, overrideAccess = false, req, requestedCollectionSlug, requestedFilename, requestedStorageFilePath }) {
|
|
80
|
+
if (!isStoragePathWithinCollectionPrefix({
|
|
81
|
+
collectionPrefix: collectionPrefixArg,
|
|
82
|
+
docPrefix: requestedStorageFilePath
|
|
83
|
+
})) {
|
|
84
|
+
throw new Forbidden(req.t);
|
|
85
|
+
}
|
|
86
|
+
const matchedDocs = (await Promise.all(collectionSources.map(async ({ collectionPrefix, collectionSlug, useCompositePrefixes })=>{
|
|
87
|
+
const docs = await findMatchingDocs({
|
|
88
|
+
collectionPrefix,
|
|
89
|
+
collectionSlug,
|
|
90
|
+
req,
|
|
91
|
+
requestedFilename,
|
|
92
|
+
requestedStorageFilePath,
|
|
93
|
+
useCompositePrefixes
|
|
94
|
+
});
|
|
95
|
+
return docs.map((doc)=>({
|
|
96
|
+
collectionSlug,
|
|
97
|
+
doc
|
|
98
|
+
}));
|
|
99
|
+
}))).flat();
|
|
100
|
+
if (matchedDocs.length === 0) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
const matchedDoc = matchedDocs[0];
|
|
104
|
+
if (matchedDocs.length !== 1 || matchedDoc.collectionSlug !== requestedCollectionSlug) {
|
|
105
|
+
throw new Forbidden(req.t);
|
|
106
|
+
}
|
|
107
|
+
if (!overrideAccess && !await canUpdateDoc({
|
|
108
|
+
collectionSlug: matchedDoc.collectionSlug,
|
|
109
|
+
doc: matchedDoc.doc,
|
|
110
|
+
req
|
|
111
|
+
})) {
|
|
112
|
+
throw new Forbidden(req.t);
|
|
113
|
+
}
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
//# sourceMappingURL=authorizeFileOverwrite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/authorizeFileOverwrite.ts"],"sourcesContent":["import type { FileData, PayloadRequest, TypeWithID, Where } from 'payload'\n\nimport {\n buildStoragePathData,\n isStoragePathWithinCollectionPrefix,\n} from '@payloadcms/plugin-cloud-storage/utilities'\nimport { combineQueries, executeAccess, Forbidden } from 'payload'\n\ntype OwnerDocument = { prefix?: string } & FileData & TypeWithID\n\nexport type VercelBlobCollectionSource = {\n collectionPrefix: string\n collectionSlug: string\n useCompositePrefixes: boolean\n}\n\ntype Owner = {\n collectionSlug: string\n doc: OwnerDocument\n}\n\nfunction getDocumentFileKeys({\n collectionPrefix,\n doc,\n useCompositePrefixes,\n}: {\n collectionPrefix: string\n doc: OwnerDocument\n useCompositePrefixes: boolean\n}): string[] {\n return [doc.filename, ...Object.values(doc.variants || {}).map((size) => size?.filename)]\n .filter((filename): filename is string => typeof filename === 'string')\n .map(\n (filename) =>\n buildStoragePathData({\n collectionPrefix,\n docPrefix: doc.prefix,\n filename,\n useCompositePrefixes,\n }).storageFilePath,\n )\n}\n\nasync function findMatchingDocs({\n collectionPrefix,\n collectionSlug,\n req,\n requestedFilename,\n requestedStorageFilePath,\n useCompositePrefixes,\n}: {\n collectionPrefix: string\n collectionSlug: string\n req: PayloadRequest\n requestedFilename: string\n requestedStorageFilePath: string\n useCompositePrefixes: boolean\n}): Promise<OwnerDocument[]> {\n const collection = req.payload.collections[collectionSlug]\n\n if (!collection) {\n return []\n }\n\n const variants =\n collection.config.upload && typeof collection.config.upload === 'object'\n ? collection.config.upload.variants || []\n : []\n const filenameQueries: Where[] = [\n { filename: { equals: requestedFilename } },\n ...variants.map(({ name }) => ({\n [`variants.${name}.filename`]: { equals: requestedFilename },\n })),\n ]\n const result = await req.payload.find({\n collection: collectionSlug,\n depth: 0,\n draft: true,\n overrideAccess: true,\n pagination: false,\n req,\n where: { or: filenameQueries },\n })\n\n return (result.docs as OwnerDocument[]).filter((doc) =>\n getDocumentFileKeys({ collectionPrefix, doc, useCompositePrefixes }).includes(\n requestedStorageFilePath,\n ),\n )\n}\n\nasync function canUpdateDoc({\n collectionSlug,\n doc,\n req,\n}: {\n collectionSlug: string\n doc: OwnerDocument\n req: PayloadRequest\n}): Promise<boolean> {\n const collection = req.payload.collections[collectionSlug]\n\n if (!collection) {\n return false\n }\n\n const accessResult = await executeAccess(\n { id: doc.id, slug: collectionSlug, disableErrors: true, req },\n collection.config.access.update,\n )\n\n if (typeof accessResult === 'boolean') {\n return accessResult\n }\n\n const result = await req.payload.find({\n collection: collectionSlug,\n depth: 0,\n draft: true,\n limit: 1,\n overrideAccess: true,\n pagination: false,\n req,\n where: combineQueries({ id: { equals: doc.id } }, accessResult),\n })\n\n return result.docs.length === 1\n}\n\nexport async function authorizeClientOverwrite({\n collectionPrefix: collectionPrefixArg,\n collectionSources,\n overrideAccess = false,\n req,\n requestedCollectionSlug,\n requestedFilename,\n requestedStorageFilePath,\n}: {\n collectionPrefix: string\n collectionSources: VercelBlobCollectionSource[]\n overrideAccess?: boolean\n req: PayloadRequest\n requestedCollectionSlug: string\n requestedFilename: string\n requestedStorageFilePath: string\n}): Promise<boolean> {\n if (\n !isStoragePathWithinCollectionPrefix({\n collectionPrefix: collectionPrefixArg,\n docPrefix: requestedStorageFilePath,\n })\n ) {\n throw new Forbidden(req.t)\n }\n\n const matchedDocs = (\n await Promise.all(\n collectionSources.map(\n async ({ collectionPrefix, collectionSlug, useCompositePrefixes }): Promise<Owner[]> => {\n const docs = await findMatchingDocs({\n collectionPrefix,\n collectionSlug,\n req,\n requestedFilename,\n requestedStorageFilePath,\n useCompositePrefixes,\n })\n\n return docs.map((doc) => ({ collectionSlug, doc }))\n },\n ),\n )\n ).flat()\n\n if (matchedDocs.length === 0) {\n return false\n }\n\n const matchedDoc = matchedDocs[0]!\n if (matchedDocs.length !== 1 || matchedDoc.collectionSlug !== requestedCollectionSlug) {\n throw new Forbidden(req.t)\n }\n\n if (\n !overrideAccess &&\n !(await canUpdateDoc({ collectionSlug: matchedDoc.collectionSlug, doc: matchedDoc.doc, req }))\n ) {\n throw new Forbidden(req.t)\n }\n\n return true\n}\n"],"names":["buildStoragePathData","isStoragePathWithinCollectionPrefix","combineQueries","executeAccess","Forbidden","getDocumentFileKeys","collectionPrefix","doc","useCompositePrefixes","filename","Object","values","variants","map","size","filter","docPrefix","prefix","storageFilePath","findMatchingDocs","collectionSlug","req","requestedFilename","requestedStorageFilePath","collection","payload","collections","config","upload","filenameQueries","equals","name","result","find","depth","draft","overrideAccess","pagination","where","or","docs","includes","canUpdateDoc","accessResult","id","slug","disableErrors","access","update","limit","length","authorizeClientOverwrite","collectionPrefixArg","collectionSources","requestedCollectionSlug","t","matchedDocs","Promise","all","flat","matchedDoc"],"mappings":"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"}
|
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
export
|
|
2
|
-
addRandomSuffix: boolean;
|
|
3
|
-
useCompositePrefixes: boolean;
|
|
4
|
-
};
|
|
5
|
-
export declare const VercelBlobClientUploadHandler: ({ children, collectionSlug, enabled, extra, prefix, serverHandlerPath, }: {
|
|
1
|
+
export declare const VercelBlobClientUploadHandler: ({ children, collectionSlug, endpointPath, prefix, props, }: {
|
|
6
2
|
children: import("react").ReactNode;
|
|
7
3
|
collectionSlug: import("payload").UploadCollectionSlug;
|
|
8
|
-
|
|
9
|
-
extra: VercelBlobClientUploadHandlerExtra;
|
|
4
|
+
endpointPath?: `/${string}`;
|
|
10
5
|
prefix?: string;
|
|
11
|
-
|
|
6
|
+
props: Record<string, unknown>;
|
|
12
7
|
}) => import("react").JSX.Element;
|
|
13
8
|
//# sourceMappingURL=VercelBlobClientUploadHandler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VercelBlobClientUploadHandler.d.ts","sourceRoot":"","sources":["../../src/client/VercelBlobClientUploadHandler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"VercelBlobClientUploadHandler.d.ts","sourceRoot":"","sources":["../../src/client/VercelBlobClientUploadHandler.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,6BAA6B;;;;;;iCAgBxC,CAAA"}
|
|
@@ -1,41 +1,24 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client';
|
|
3
|
-
import {
|
|
4
|
-
import { upload } from '@vercel/blob/client';
|
|
5
|
-
import { formatAdminURL } from 'payload/shared';
|
|
3
|
+
import { put } from '@vercel/blob/client';
|
|
6
4
|
/** Last path segment only (POSIX), for keys like `folder/sub/file.png`. */ function posixBasename(key) {
|
|
7
5
|
const normalized = key.replace(/^\/+/, '');
|
|
8
6
|
const lastSlash = normalized.lastIndexOf('/');
|
|
9
7
|
return lastSlash === -1 ? normalized : normalized.slice(lastSlash + 1);
|
|
10
8
|
}
|
|
11
9
|
export const VercelBlobClientUploadHandler = createClientUploadHandler({
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
serverURL
|
|
17
|
-
});
|
|
18
|
-
const { fileKey: pathname, sanitizedDocPrefix } = getFileKey({
|
|
19
|
-
collectionPrefix: prefix,
|
|
20
|
-
docPrefix,
|
|
21
|
-
filename: file.name,
|
|
22
|
-
useCompositePrefixes
|
|
23
|
-
});
|
|
24
|
-
// upload the file directly to Vercel Blob using the signed URL
|
|
25
|
-
const result = await upload(pathname, file, {
|
|
10
|
+
name: 'uploadToVercelBlob',
|
|
11
|
+
handler: async ({ data, file, updateFilename })=>{
|
|
12
|
+
const { pathname, token } = data;
|
|
13
|
+
const result = await put(pathname, file, {
|
|
26
14
|
access: 'public',
|
|
27
|
-
clientPayload: collectionSlug,
|
|
28
15
|
contentType: file.type,
|
|
29
|
-
|
|
16
|
+
token
|
|
30
17
|
});
|
|
31
|
-
|
|
32
|
-
if (
|
|
33
|
-
|
|
34
|
-
updateFilename(decodeURIComponent(posixBasename(pathname)));
|
|
18
|
+
const filename = decodeURIComponent(posixBasename(result.pathname.replace(/^\/+/, '')));
|
|
19
|
+
if (filename !== file.name) {
|
|
20
|
+
updateFilename(filename);
|
|
35
21
|
}
|
|
36
|
-
return {
|
|
37
|
-
prefix: sanitizedDocPrefix
|
|
38
|
-
};
|
|
39
22
|
}
|
|
40
23
|
});
|
|
41
24
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/VercelBlobClientUploadHandler.ts"],"sourcesContent":["'use client'\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\nimport {
|
|
1
|
+
{"version":3,"sources":["../../src/client/VercelBlobClientUploadHandler.ts"],"sourcesContent":["'use client'\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\nimport { put } from '@vercel/blob/client'\n\n/** Last path segment only (POSIX), for keys like `folder/sub/file.png`. */\nfunction posixBasename(key: string): string {\n const normalized = key.replace(/^\\/+/, '')\n const lastSlash = normalized.lastIndexOf('/')\n return lastSlash === -1 ? normalized : normalized.slice(lastSlash + 1)\n}\n\nexport const VercelBlobClientUploadHandler = createClientUploadHandler({\n name: 'uploadToVercelBlob',\n handler: async ({ data, file, updateFilename }) => {\n const { pathname, token } = data as { pathname: string; token: string }\n\n const result = await put(pathname, file, {\n access: 'public',\n contentType: file.type,\n token,\n })\n\n const filename = decodeURIComponent(posixBasename(result.pathname.replace(/^\\/+/, '')))\n if (filename !== file.name) {\n updateFilename(filename)\n }\n },\n})\n"],"names":["createClientUploadHandler","put","posixBasename","key","normalized","replace","lastSlash","lastIndexOf","slice","VercelBlobClientUploadHandler","name","handler","data","file","updateFilename","pathname","token","result","access","contentType","type","filename","decodeURIComponent"],"mappings":"AAAA;AACA,SAASA,yBAAyB,QAAQ,0CAAyC;AACnF,SAASC,GAAG,QAAQ,sBAAqB;AAEzC,yEAAyE,GACzE,SAASC,cAAcC,GAAW;IAChC,MAAMC,aAAaD,IAAIE,OAAO,CAAC,QAAQ;IACvC,MAAMC,YAAYF,WAAWG,WAAW,CAAC;IACzC,OAAOD,cAAc,CAAC,IAAIF,aAAaA,WAAWI,KAAK,CAACF,YAAY;AACtE;AAEA,OAAO,MAAMG,gCAAgCT,0BAA0B;IACrEU,MAAM;IACNC,SAAS,OAAO,EAAEC,IAAI,EAAEC,IAAI,EAAEC,cAAc,EAAE;QAC5C,MAAM,EAAEC,QAAQ,EAAEC,KAAK,EAAE,GAAGJ;QAE5B,MAAMK,SAAS,MAAMhB,IAAIc,UAAUF,MAAM;YACvCK,QAAQ;YACRC,aAAaN,KAAKO,IAAI;YACtBJ;QACF;QAEA,MAAMK,WAAWC,mBAAmBpB,cAAce,OAAOF,QAAQ,CAACV,OAAO,CAAC,QAAQ;QAClF,IAAIgB,aAAaR,KAAKH,IAAI,EAAE;YAC1BI,eAAeO;QACjB;IACF;AACF,GAAE"}
|
package/dist/deleteFile.d.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
interface DeleteFileArgs {
|
|
2
2
|
baseUrl: string;
|
|
3
|
-
|
|
4
|
-
docPrefix: string;
|
|
5
|
-
filename: string;
|
|
3
|
+
storageFilePath: string;
|
|
6
4
|
token: string;
|
|
7
|
-
useCompositePrefixes?: boolean;
|
|
8
5
|
}
|
|
9
|
-
export declare function deleteFile({ baseUrl,
|
|
6
|
+
export declare function deleteFile({ baseUrl, storageFilePath, token, }: DeleteFileArgs): Promise<void>;
|
|
10
7
|
export {};
|
|
11
8
|
//# sourceMappingURL=deleteFile.d.ts.map
|
package/dist/deleteFile.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deleteFile.d.ts","sourceRoot":"","sources":["../src/deleteFile.ts"],"names":[],"mappings":"AAIA,UAAU,cAAc;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,
|
|
1
|
+
{"version":3,"file":"deleteFile.d.ts","sourceRoot":"","sources":["../src/deleteFile.ts"],"names":[],"mappings":"AAIA,UAAU,cAAc;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,eAAe,EAAE,MAAM,CAAA;IACvB,KAAK,EAAE,MAAM,CAAA;CACd;AAED,wBAAsB,UAAU,CAAC,EAC/B,OAAO,EACP,eAAe,EACf,KAAK,GACN,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAEhC"}
|
package/dist/deleteFile.js
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import { del } from '@vercel/blob';
|
|
2
|
-
import {
|
|
3
|
-
export async function deleteFile({ baseUrl,
|
|
4
|
-
|
|
5
|
-
baseUrl,
|
|
6
|
-
collectionPrefix,
|
|
7
|
-
filename,
|
|
8
|
-
prefix: docPrefix,
|
|
9
|
-
useCompositePrefixes
|
|
10
|
-
});
|
|
11
|
-
await del(fileUrl, {
|
|
2
|
+
import { buildBlobUrl } from './generateURL.js';
|
|
3
|
+
export async function deleteFile({ baseUrl, storageFilePath, token }) {
|
|
4
|
+
await del(buildBlobUrl(baseUrl, storageFilePath), {
|
|
12
5
|
token
|
|
13
6
|
});
|
|
14
7
|
}
|
package/dist/deleteFile.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/deleteFile.ts"],"sourcesContent":["import { del } from '@vercel/blob'\n\nimport {
|
|
1
|
+
{"version":3,"sources":["../src/deleteFile.ts"],"sourcesContent":["import { del } from '@vercel/blob'\n\nimport { buildBlobUrl } from './generateURL.js'\n\ninterface DeleteFileArgs {\n baseUrl: string\n storageFilePath: string\n token: string\n}\n\nexport async function deleteFile({\n baseUrl,\n storageFilePath,\n token,\n}: DeleteFileArgs): Promise<void> {\n await del(buildBlobUrl(baseUrl, storageFilePath), { token })\n}\n"],"names":["del","buildBlobUrl","deleteFile","baseUrl","storageFilePath","token"],"mappings":"AAAA,SAASA,GAAG,QAAQ,eAAc;AAElC,SAASC,YAAY,QAAQ,mBAAkB;AAQ/C,OAAO,eAAeC,WAAW,EAC/BC,OAAO,EACPC,eAAe,EACfC,KAAK,EACU;IACf,MAAML,IAAIC,aAAaE,SAASC,kBAAkB;QAAEC;IAAM;AAC5D"}
|
package/dist/generateURL.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ interface GenerateURLArgs {
|
|
|
5
5
|
prefix: string;
|
|
6
6
|
useCompositePrefixes?: boolean;
|
|
7
7
|
}
|
|
8
|
+
export declare function buildBlobUrl(baseUrl: string, storageFilePath: string): string;
|
|
8
9
|
export declare function generateURL({ baseUrl, collectionPrefix, filename, prefix, useCompositePrefixes, }: GenerateURLArgs): string;
|
|
9
10
|
export {};
|
|
10
11
|
//# sourceMappingURL=generateURL.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateURL.d.ts","sourceRoot":"","sources":["../src/generateURL.ts"],"names":[],"mappings":"AAGA,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,gBAAqB,EACrB,QAAQ,EACR,MAAM,EACN,oBAA4B,GAC7B,EAAE,eAAe,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"generateURL.d.ts","sourceRoot":"","sources":["../src/generateURL.ts"],"names":[],"mappings":"AAGA,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAGD,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,MAAM,CAU7E;AAED,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,gBAAqB,EACrB,QAAQ,EACR,MAAM,EACN,oBAA4B,GAC7B,EAAE,eAAe,GAAG,MAAM,CAS1B"}
|
package/dist/generateURL.js
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { buildStoragePathData } from '@payloadcms/plugin-cloud-storage/utilities';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
// Builds the public blob URL for a storage path, URL-encoding only the filename segment.
|
|
4
|
+
export function buildBlobUrl(baseUrl, storageFilePath) {
|
|
5
|
+
// example: "my-collection/my-doc/my file.jpg" -> "my-collection/my-doc"
|
|
6
|
+
const dir = path.posix.dirname(storageFilePath);
|
|
7
|
+
// example: "my file.jpg" -> "my%20file.jpg"
|
|
8
|
+
const encodedFilename = encodeURIComponent(path.posix.basename(storageFilePath));
|
|
9
|
+
// example: "my-collection/my-doc/my%20file.jpg"
|
|
10
|
+
const storageFilePathWithEncodedFilename = dir === '.' ? encodedFilename : path.posix.join(dir, encodedFilename);
|
|
11
|
+
return `${baseUrl}/${storageFilePathWithEncodedFilename}`;
|
|
12
|
+
}
|
|
3
13
|
export function generateURL({ baseUrl, collectionPrefix = '', filename, prefix, useCompositePrefixes = false }) {
|
|
4
|
-
const {
|
|
14
|
+
const { storageFilePath } = buildStoragePathData({
|
|
5
15
|
collectionPrefix,
|
|
6
16
|
docPrefix: prefix,
|
|
7
17
|
filename,
|
|
8
18
|
useCompositePrefixes
|
|
9
19
|
});
|
|
10
|
-
|
|
11
|
-
const dir = path.posix.dirname(fileKeyWithPrefix);
|
|
12
|
-
// example: "my file.jpg" -> "my%20file.jpg"
|
|
13
|
-
const encodedFilename = encodeURIComponent(path.posix.basename(fileKeyWithPrefix));
|
|
14
|
-
// example: "my-collection/my-doc/my%20file.jpg"
|
|
15
|
-
const fileKeyWithEncodedFilename = dir === '.' ? encodedFilename : path.posix.join(dir, encodedFilename);
|
|
16
|
-
return `${baseUrl}/${fileKeyWithEncodedFilename}`;
|
|
20
|
+
return buildBlobUrl(baseUrl, storageFilePath);
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
//# sourceMappingURL=generateURL.js.map
|
package/dist/generateURL.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/generateURL.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"sources":["../src/generateURL.ts"],"sourcesContent":["import { buildStoragePathData } from '@payloadcms/plugin-cloud-storage/utilities'\nimport path from 'path'\n\ninterface GenerateURLArgs {\n baseUrl: string\n collectionPrefix?: string\n filename: string\n prefix: string\n useCompositePrefixes?: boolean\n}\n\n// Builds the public blob URL for a storage path, URL-encoding only the filename segment.\nexport function buildBlobUrl(baseUrl: string, storageFilePath: string): string {\n // example: \"my-collection/my-doc/my file.jpg\" -> \"my-collection/my-doc\"\n const dir = path.posix.dirname(storageFilePath)\n // example: \"my file.jpg\" -> \"my%20file.jpg\"\n const encodedFilename = encodeURIComponent(path.posix.basename(storageFilePath))\n // example: \"my-collection/my-doc/my%20file.jpg\"\n const storageFilePathWithEncodedFilename =\n dir === '.' ? encodedFilename : path.posix.join(dir, encodedFilename)\n\n return `${baseUrl}/${storageFilePathWithEncodedFilename}`\n}\n\nexport function generateURL({\n baseUrl,\n collectionPrefix = '',\n filename,\n prefix,\n useCompositePrefixes = false,\n}: GenerateURLArgs): string {\n const { storageFilePath } = buildStoragePathData({\n collectionPrefix,\n docPrefix: prefix,\n filename,\n useCompositePrefixes,\n })\n\n return buildBlobUrl(baseUrl, storageFilePath)\n}\n"],"names":["buildStoragePathData","path","buildBlobUrl","baseUrl","storageFilePath","dir","posix","dirname","encodedFilename","encodeURIComponent","basename","storageFilePathWithEncodedFilename","join","generateURL","collectionPrefix","filename","prefix","useCompositePrefixes","docPrefix"],"mappings":"AAAA,SAASA,oBAAoB,QAAQ,6CAA4C;AACjF,OAAOC,UAAU,OAAM;AAUvB,yFAAyF;AACzF,OAAO,SAASC,aAAaC,OAAe,EAAEC,eAAuB;IACnE,wEAAwE;IACxE,MAAMC,MAAMJ,KAAKK,KAAK,CAACC,OAAO,CAACH;IAC/B,4CAA4C;IAC5C,MAAMI,kBAAkBC,mBAAmBR,KAAKK,KAAK,CAACI,QAAQ,CAACN;IAC/D,gDAAgD;IAChD,MAAMO,qCACJN,QAAQ,MAAMG,kBAAkBP,KAAKK,KAAK,CAACM,IAAI,CAACP,KAAKG;IAEvD,OAAO,GAAGL,QAAQ,CAAC,EAAEQ,oCAAoC;AAC3D;AAEA,OAAO,SAASE,YAAY,EAC1BV,OAAO,EACPW,mBAAmB,EAAE,EACrBC,QAAQ,EACRC,MAAM,EACNC,uBAAuB,KAAK,EACZ;IAChB,MAAM,EAAEb,eAAe,EAAE,GAAGJ,qBAAqB;QAC/Cc;QACAI,WAAWF;QACXD;QACAE;IACF;IAEA,OAAOf,aAAaC,SAASC;AAC/B"}
|