@payloadcms/storage-gcs 3.83.0-internal.06ac84e → 3.83.0-internal.86b7bfb

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.
@@ -0,0 +1,12 @@
1
+ import type { Storage } from '@google-cloud/storage';
2
+ import type { Adapter, ClientUploadsConfig } from '@payloadcms/plugin-cloud-storage/types';
3
+ interface CreateGcsAdapterArgs {
4
+ acl?: 'Private' | 'Public';
5
+ bucket: string;
6
+ clientUploads?: ClientUploadsConfig;
7
+ getStorageClient: () => Storage;
8
+ useCompositePrefixes?: boolean;
9
+ }
10
+ export declare function createGcsAdapter({ acl, bucket, clientUploads, getStorageClient, useCompositePrefixes, }: CreateGcsAdapterArgs): Adapter;
11
+ export {};
12
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,KAAK,EACV,OAAO,EACP,mBAAmB,EAEpB,MAAM,wCAAwC,CAAA;AAO/C,UAAU,oBAAoB;IAC5B,GAAG,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC,gBAAgB,EAAE,MAAM,OAAO,CAAA;IAC/B,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,GAAG,EACH,MAAM,EACN,aAAa,EACb,gBAAgB,EAChB,oBAA4B,GAC7B,EAAE,oBAAoB,GAAG,OAAO,CA0DhC"}
@@ -0,0 +1,54 @@
1
+ import { deleteFile } from './deleteFile.js';
2
+ import { generateURL } from './generateURL.js';
3
+ import { getFile } from './getFile.js';
4
+ import { uploadFile } from './uploadFile.js';
5
+ export function createGcsAdapter({ acl, bucket, clientUploads, getStorageClient, useCompositePrefixes = false }) {
6
+ return ({ collection, prefix = '' })=>({
7
+ name: 'gcs',
8
+ clientUploads,
9
+ generateURL: ({ filename, prefix: urlPrefix = '' })=>generateURL({
10
+ bucket,
11
+ client: getStorageClient(),
12
+ collectionPrefix: prefix,
13
+ filename,
14
+ prefix: urlPrefix,
15
+ useCompositePrefixes
16
+ }),
17
+ handleDelete: ({ doc: { prefix: docPrefix = '' }, filename })=>deleteFile({
18
+ bucket,
19
+ client: getStorageClient(),
20
+ collectionPrefix: prefix,
21
+ docPrefix,
22
+ filename,
23
+ useCompositePrefixes
24
+ }),
25
+ handleUpload: async ({ data, file })=>{
26
+ await uploadFile({
27
+ acl,
28
+ bucket,
29
+ buffer: file.buffer,
30
+ client: getStorageClient(),
31
+ collectionPrefix: prefix,
32
+ docPrefix: data.prefix,
33
+ filename: file.filename,
34
+ mimeType: file.mimeType,
35
+ useCompositePrefixes
36
+ });
37
+ return data;
38
+ },
39
+ staticHandler: (req, { headers, params: { clientUploadContext, filename, prefix: prefixQueryParam } })=>getFile({
40
+ bucket,
41
+ client: getStorageClient(),
42
+ clientUploadContext,
43
+ collection,
44
+ collectionPrefix: prefix,
45
+ filename,
46
+ incomingHeaders: headers,
47
+ prefixQueryParam,
48
+ req,
49
+ useCompositePrefixes
50
+ })
51
+ });
52
+ }
53
+
54
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapter.ts"],"sourcesContent":["import type { Storage } from '@google-cloud/storage'\nimport 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 CreateGcsAdapterArgs {\n acl?: 'Private' | 'Public'\n bucket: string\n clientUploads?: ClientUploadsConfig\n getStorageClient: () => Storage\n useCompositePrefixes?: boolean\n}\n\nexport function createGcsAdapter({\n acl,\n bucket,\n clientUploads,\n getStorageClient,\n useCompositePrefixes = false,\n}: CreateGcsAdapterArgs): Adapter {\n return ({ collection, prefix = '' }): GeneratedAdapter => ({\n name: 'gcs',\n clientUploads,\n\n generateURL: ({ filename, prefix: urlPrefix = '' }) =>\n generateURL({\n bucket,\n client: getStorageClient(),\n collectionPrefix: prefix,\n filename,\n prefix: urlPrefix,\n useCompositePrefixes,\n }),\n\n handleDelete: ({ doc: { prefix: docPrefix = '' }, filename }) =>\n deleteFile({\n bucket,\n client: getStorageClient(),\n collectionPrefix: prefix,\n docPrefix,\n filename,\n useCompositePrefixes,\n }),\n\n handleUpload: async ({ data, file }) => {\n await uploadFile({\n acl,\n bucket,\n buffer: file.buffer,\n client: getStorageClient(),\n collectionPrefix: prefix,\n docPrefix: data.prefix,\n filename: file.filename,\n mimeType: file.mimeType,\n useCompositePrefixes,\n })\n\n return data\n },\n\n staticHandler: (\n req,\n { headers, params: { clientUploadContext, filename, prefix: prefixQueryParam } },\n ) =>\n getFile({\n bucket,\n client: getStorageClient(),\n clientUploadContext,\n collection,\n collectionPrefix: prefix,\n filename,\n incomingHeaders: headers,\n prefixQueryParam,\n req,\n useCompositePrefixes,\n }),\n })\n}\n"],"names":["deleteFile","generateURL","getFile","uploadFile","createGcsAdapter","acl","bucket","clientUploads","getStorageClient","useCompositePrefixes","collection","prefix","name","filename","urlPrefix","client","collectionPrefix","handleDelete","doc","docPrefix","handleUpload","data","file","buffer","mimeType","staticHandler","req","headers","params","clientUploadContext","prefixQueryParam","incomingHeaders"],"mappings":"AAOA,SAASA,UAAU,QAAQ,kBAAiB;AAC5C,SAASC,WAAW,QAAQ,mBAAkB;AAC9C,SAASC,OAAO,QAAQ,eAAc;AACtC,SAASC,UAAU,QAAQ,kBAAiB;AAU5C,OAAO,SAASC,iBAAiB,EAC/BC,GAAG,EACHC,MAAM,EACNC,aAAa,EACbC,gBAAgB,EAChBC,uBAAuB,KAAK,EACP;IACrB,OAAO,CAAC,EAAEC,UAAU,EAAEC,SAAS,EAAE,EAAE,GAAwB,CAAA;YACzDC,MAAM;YACNL;YAEAN,aAAa,CAAC,EAAEY,QAAQ,EAAEF,QAAQG,YAAY,EAAE,EAAE,GAChDb,YAAY;oBACVK;oBACAS,QAAQP;oBACRQ,kBAAkBL;oBAClBE;oBACAF,QAAQG;oBACRL;gBACF;YAEFQ,cAAc,CAAC,EAAEC,KAAK,EAAEP,QAAQQ,YAAY,EAAE,EAAE,EAAEN,QAAQ,EAAE,GAC1Db,WAAW;oBACTM;oBACAS,QAAQP;oBACRQ,kBAAkBL;oBAClBQ;oBACAN;oBACAJ;gBACF;YAEFW,cAAc,OAAO,EAAEC,IAAI,EAAEC,IAAI,EAAE;gBACjC,MAAMnB,WAAW;oBACfE;oBACAC;oBACAiB,QAAQD,KAAKC,MAAM;oBACnBR,QAAQP;oBACRQ,kBAAkBL;oBAClBQ,WAAWE,KAAKV,MAAM;oBACtBE,UAAUS,KAAKT,QAAQ;oBACvBW,UAAUF,KAAKE,QAAQ;oBACvBf;gBACF;gBAEA,OAAOY;YACT;YAEAI,eAAe,CACbC,KACA,EAAEC,OAAO,EAAEC,QAAQ,EAAEC,mBAAmB,EAAEhB,QAAQ,EAAEF,QAAQmB,gBAAgB,EAAE,EAAE,GAEhF5B,QAAQ;oBACNI;oBACAS,QAAQP;oBACRqB;oBACAnB;oBACAM,kBAAkBL;oBAClBE;oBACAkB,iBAAiBJ;oBACjBG;oBACAJ;oBACAjB;gBACF;QACJ,CAAA;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"GcsClientUploadHandler.d.ts","sourceRoot":"","sources":["../../src/client/GcsClientUploadHandler.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,sBAAsB;;;;;;;aAsB7B,OAAK,aAOT,CAAA"}
1
+ {"version":3,"file":"GcsClientUploadHandler.d.ts","sourceRoot":"","sources":["../../src/client/GcsClientUploadHandler.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,sBAAsB;;;;;;;aAyB/B,OAAM,aAIR,CAAA"}
@@ -0,0 +1,12 @@
1
+ import type { Storage } from '@google-cloud/storage';
2
+ interface DeleteFileArgs {
3
+ bucket: string;
4
+ client: Storage;
5
+ collectionPrefix?: string;
6
+ docPrefix: string;
7
+ filename: string;
8
+ useCompositePrefixes?: boolean;
9
+ }
10
+ export declare function deleteFile({ bucket, client, collectionPrefix, docPrefix, filename, useCompositePrefixes, }: DeleteFileArgs): Promise<void>;
11
+ export {};
12
+ //# sourceMappingURL=deleteFile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deleteFile.d.ts","sourceRoot":"","sources":["../src/deleteFile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AAIpD,UAAU,cAAc;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAsB,UAAU,CAAC,EAC/B,MAAM,EACN,MAAM,EACN,gBAAqB,EACrB,SAAS,EACT,QAAQ,EACR,oBAA4B,GAC7B,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAWhC"}
@@ -0,0 +1,14 @@
1
+ import { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities';
2
+ export async function deleteFile({ bucket, client, collectionPrefix = '', docPrefix, filename, useCompositePrefixes = false }) {
3
+ const fileKey = getFileKey({
4
+ collectionPrefix,
5
+ docPrefix,
6
+ filename,
7
+ useCompositePrefixes
8
+ });
9
+ await client.bucket(bucket).file(fileKey).delete({
10
+ ignoreNotFound: true
11
+ });
12
+ }
13
+
14
+ //# sourceMappingURL=deleteFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/deleteFile.ts"],"sourcesContent":["import type { Storage } from '@google-cloud/storage'\n\nimport { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities'\n\ninterface DeleteFileArgs {\n bucket: string\n client: Storage\n collectionPrefix?: string\n docPrefix: string\n filename: string\n useCompositePrefixes?: boolean\n}\n\nexport async function deleteFile({\n bucket,\n client,\n collectionPrefix = '',\n docPrefix,\n filename,\n useCompositePrefixes = false,\n}: DeleteFileArgs): Promise<void> {\n const fileKey = getFileKey({\n collectionPrefix,\n docPrefix,\n filename,\n useCompositePrefixes,\n })\n\n await client.bucket(bucket).file(fileKey).delete({\n ignoreNotFound: true,\n })\n}\n"],"names":["getFileKey","deleteFile","bucket","client","collectionPrefix","docPrefix","filename","useCompositePrefixes","fileKey","file","delete","ignoreNotFound"],"mappings":"AAEA,SAASA,UAAU,QAAQ,6CAA4C;AAWvE,OAAO,eAAeC,WAAW,EAC/BC,MAAM,EACNC,MAAM,EACNC,mBAAmB,EAAE,EACrBC,SAAS,EACTC,QAAQ,EACRC,uBAAuB,KAAK,EACb;IACf,MAAMC,UAAUR,WAAW;QACzBI;QACAC;QACAC;QACAC;IACF;IAEA,MAAMJ,OAAOD,MAAM,CAACA,QAAQO,IAAI,CAACD,SAASE,MAAM,CAAC;QAC/CC,gBAAgB;IAClB;AACF"}
@@ -1,9 +1,12 @@
1
1
  import type { Storage } from '@google-cloud/storage';
2
- import type { GenerateURL } from '@payloadcms/plugin-cloud-storage/types';
3
- interface Args {
2
+ interface GenerateURLArgs {
4
3
  bucket: string;
5
- getStorageClient: () => Storage;
4
+ client: Storage;
5
+ collectionPrefix?: string;
6
+ filename: string;
7
+ prefix: string;
8
+ useCompositePrefixes?: boolean;
6
9
  }
7
- export declare const getGenerateURL: ({ bucket, getStorageClient }: Args) => GenerateURL;
10
+ export declare function generateURL({ bucket, client, collectionPrefix, filename, prefix, useCompositePrefixes, }: GenerateURLArgs): string;
8
11
  export {};
9
12
  //# sourceMappingURL=generateURL.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"generateURL.d.ts","sourceRoot":"","sources":["../src/generateURL.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAIzE,UAAU,IAAI;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,gBAAgB,EAAE,MAAM,OAAO,CAAA;CAChC;AAED,eAAO,MAAM,cAAc,iCACM,IAAI,KAAG,WAKrC,CAAA"}
1
+ {"version":3,"file":"generateURL.d.ts","sourceRoot":"","sources":["../src/generateURL.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AAIpD,UAAU,eAAe;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,OAAO,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,MAAM,EACN,MAAM,EACN,gBAAqB,EACrB,QAAQ,EACR,MAAM,EACN,oBAA4B,GAC7B,EAAE,eAAe,GAAG,MAAM,CAS1B"}
@@ -1,6 +1,12 @@
1
- import path from 'path';
2
- export const getGenerateURL = ({ bucket, getStorageClient })=>({ filename, prefix = '' })=>{
3
- return decodeURIComponent(getStorageClient().bucket(bucket).file(path.posix.join(prefix, filename)).publicUrl());
4
- };
1
+ import { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities';
2
+ export function generateURL({ bucket, client, collectionPrefix = '', filename, prefix, useCompositePrefixes = false }) {
3
+ const fileKey = getFileKey({
4
+ collectionPrefix,
5
+ docPrefix: prefix,
6
+ filename,
7
+ useCompositePrefixes
8
+ });
9
+ return decodeURIComponent(client.bucket(bucket).file(fileKey).publicUrl());
10
+ }
5
11
 
6
12
  //# sourceMappingURL=generateURL.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/generateURL.ts"],"sourcesContent":["import type { Storage } from '@google-cloud/storage'\nimport type { GenerateURL } from '@payloadcms/plugin-cloud-storage/types'\n\nimport path from 'path'\n\ninterface Args {\n bucket: string\n getStorageClient: () => Storage\n}\n\nexport const getGenerateURL =\n ({ bucket, getStorageClient }: Args): GenerateURL =>\n ({ filename, prefix = '' }) => {\n return decodeURIComponent(\n getStorageClient().bucket(bucket).file(path.posix.join(prefix, filename)).publicUrl(),\n )\n }\n"],"names":["path","getGenerateURL","bucket","getStorageClient","filename","prefix","decodeURIComponent","file","posix","join","publicUrl"],"mappings":"AAGA,OAAOA,UAAU,OAAM;AAOvB,OAAO,MAAMC,iBACX,CAAC,EAAEC,MAAM,EAAEC,gBAAgB,EAAQ,GACnC,CAAC,EAAEC,QAAQ,EAAEC,SAAS,EAAE,EAAE;QACxB,OAAOC,mBACLH,mBAAmBD,MAAM,CAACA,QAAQK,IAAI,CAACP,KAAKQ,KAAK,CAACC,IAAI,CAACJ,QAAQD,WAAWM,SAAS;IAEvF,EAAC"}
1
+ {"version":3,"sources":["../src/generateURL.ts"],"sourcesContent":["import type { Storage } from '@google-cloud/storage'\n\nimport { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities'\n\ninterface GenerateURLArgs {\n bucket: string\n client: Storage\n collectionPrefix?: string\n filename: string\n prefix: string\n useCompositePrefixes?: boolean\n}\n\nexport function generateURL({\n bucket,\n client,\n collectionPrefix = '',\n filename,\n prefix,\n useCompositePrefixes = false,\n}: GenerateURLArgs): string {\n const fileKey = getFileKey({\n collectionPrefix,\n docPrefix: prefix,\n filename,\n useCompositePrefixes,\n })\n\n return decodeURIComponent(client.bucket(bucket).file(fileKey).publicUrl())\n}\n"],"names":["getFileKey","generateURL","bucket","client","collectionPrefix","filename","prefix","useCompositePrefixes","fileKey","docPrefix","decodeURIComponent","file","publicUrl"],"mappings":"AAEA,SAASA,UAAU,QAAQ,6CAA4C;AAWvE,OAAO,SAASC,YAAY,EAC1BC,MAAM,EACNC,MAAM,EACNC,mBAAmB,EAAE,EACrBC,QAAQ,EACRC,MAAM,EACNC,uBAAuB,KAAK,EACZ;IAChB,MAAMC,UAAUR,WAAW;QACzBI;QACAK,WAAWH;QACXD;QACAE;IACF;IAEA,OAAOG,mBAAmBP,OAAOD,MAAM,CAACA,QAAQS,IAAI,CAACH,SAASI,SAAS;AACzE"}
@@ -0,0 +1,17 @@
1
+ import type { Storage } from '@google-cloud/storage';
2
+ import type { CollectionConfig, PayloadRequest } from 'payload';
3
+ interface GetFileArgs {
4
+ bucket: string;
5
+ client: Storage;
6
+ clientUploadContext?: unknown;
7
+ collection: CollectionConfig;
8
+ collectionPrefix?: string;
9
+ filename: string;
10
+ incomingHeaders?: Headers;
11
+ prefixQueryParam?: string;
12
+ req: PayloadRequest;
13
+ useCompositePrefixes?: boolean;
14
+ }
15
+ export declare function getFile({ bucket, client, clientUploadContext, collection, collectionPrefix, filename, incomingHeaders, prefixQueryParam, req, useCompositePrefixes, }: GetFileArgs): Promise<Response>;
16
+ export {};
17
+ //# sourceMappingURL=getFile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getFile.d.ts","sourceRoot":"","sources":["../src/getFile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAU/D,UAAU,WAAW;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;IACf,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,UAAU,EAAE,gBAAgB,CAAA;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,GAAG,EAAE,cAAc,CAAA;IACnB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAsB,OAAO,CAAC,EAC5B,MAAM,EACN,MAAM,EACN,mBAAmB,EACnB,UAAU,EACV,gBAAqB,EACrB,QAAQ,EACR,eAAe,EACf,gBAAgB,EAChB,GAAG,EACH,oBAA4B,GAC7B,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAiGjC"}
@@ -0,0 +1,96 @@
1
+ import { ApiError } from '@google-cloud/storage';
2
+ import { getFilePrefix as getDocPrefix, getFileKey } from '@payloadcms/plugin-cloud-storage/utilities';
3
+ import { getRangeRequestInfo } from 'payload/internal';
4
+ import { sanitizeFilename } from 'payload/shared';
5
+ export async function getFile({ bucket, client, clientUploadContext, collection, collectionPrefix = '', filename, incomingHeaders, prefixQueryParam, req, useCompositePrefixes = false }) {
6
+ try {
7
+ const docPrefix = await getDocPrefix({
8
+ clientUploadContext,
9
+ collection,
10
+ filename,
11
+ prefixQueryParam,
12
+ req
13
+ });
14
+ const key = getFileKey({
15
+ collectionPrefix,
16
+ docPrefix,
17
+ filename: sanitizeFilename(filename),
18
+ useCompositePrefixes
19
+ });
20
+ const file = client.bucket(bucket).file(key);
21
+ const [metadata] = await file.getMetadata();
22
+ // Handle range request
23
+ const rangeHeader = req.headers.get('range');
24
+ const fileSize = Number(metadata.size);
25
+ const rangeResult = getRangeRequestInfo({
26
+ fileSize,
27
+ rangeHeader
28
+ });
29
+ if (rangeResult.type === 'invalid') {
30
+ return new Response(null, {
31
+ headers: new Headers(rangeResult.headers),
32
+ status: rangeResult.status
33
+ });
34
+ }
35
+ const etagFromHeaders = req.headers.get('etag') || req.headers.get('if-none-match');
36
+ const objectEtag = metadata.etag;
37
+ let headers = new Headers(incomingHeaders);
38
+ // Add range-related headers from the result
39
+ for (const [key, value] of Object.entries(rangeResult.headers)){
40
+ headers.append(key, value);
41
+ }
42
+ headers.append('Content-Type', String(metadata.contentType));
43
+ headers.append('ETag', String(metadata.etag));
44
+ // Add Content-Security-Policy header for SVG files to prevent executable code
45
+ if (metadata.contentType === 'image/svg+xml') {
46
+ headers.append('Content-Security-Policy', "script-src 'none'");
47
+ }
48
+ if (collection.upload && typeof collection.upload === 'object' && typeof collection.upload.modifyResponseHeaders === 'function') {
49
+ headers = collection.upload.modifyResponseHeaders({
50
+ headers
51
+ }) || headers;
52
+ }
53
+ if (etagFromHeaders && etagFromHeaders === objectEtag) {
54
+ return new Response(null, {
55
+ headers,
56
+ status: 304
57
+ });
58
+ }
59
+ // Manually create a ReadableStream for the web from a Node.js stream.
60
+ const readableStream = new ReadableStream({
61
+ start (controller) {
62
+ const streamOptions = rangeResult.type === 'partial' ? {
63
+ end: rangeResult.rangeEnd,
64
+ start: rangeResult.rangeStart
65
+ } : {};
66
+ const nodeStream = file.createReadStream(streamOptions);
67
+ nodeStream.on('data', (chunk)=>{
68
+ controller.enqueue(new Uint8Array(chunk));
69
+ });
70
+ nodeStream.on('end', ()=>{
71
+ controller.close();
72
+ });
73
+ nodeStream.on('error', (err)=>{
74
+ controller.error(err);
75
+ });
76
+ }
77
+ });
78
+ return new Response(readableStream, {
79
+ headers,
80
+ status: rangeResult.status
81
+ });
82
+ } catch (err) {
83
+ if (err instanceof ApiError && err.code === 404) {
84
+ return new Response(null, {
85
+ status: 404,
86
+ statusText: 'Not Found'
87
+ });
88
+ }
89
+ req.payload.logger.error(err);
90
+ return new Response('Internal Server Error', {
91
+ status: 500
92
+ });
93
+ }
94
+ }
95
+
96
+ //# sourceMappingURL=getFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/getFile.ts"],"sourcesContent":["import type { Storage } from '@google-cloud/storage'\nimport type { CollectionConfig, PayloadRequest } from 'payload'\n\nimport { ApiError } from '@google-cloud/storage'\nimport {\n getFilePrefix as getDocPrefix,\n getFileKey,\n} from '@payloadcms/plugin-cloud-storage/utilities'\nimport { getRangeRequestInfo } from 'payload/internal'\nimport { sanitizeFilename } from 'payload/shared'\n\ninterface GetFileArgs {\n bucket: string\n client: Storage\n clientUploadContext?: unknown\n collection: CollectionConfig\n collectionPrefix?: string\n filename: string\n incomingHeaders?: Headers\n prefixQueryParam?: string\n req: PayloadRequest\n useCompositePrefixes?: boolean\n}\n\nexport async function getFile({\n bucket,\n client,\n clientUploadContext,\n collection,\n collectionPrefix = '',\n filename,\n incomingHeaders,\n prefixQueryParam,\n req,\n useCompositePrefixes = false,\n}: GetFileArgs): Promise<Response> {\n try {\n const docPrefix = await getDocPrefix({\n clientUploadContext,\n collection,\n filename,\n prefixQueryParam,\n req,\n })\n\n const key = getFileKey({\n collectionPrefix,\n docPrefix,\n filename: sanitizeFilename(filename),\n useCompositePrefixes,\n })\n\n const file = client.bucket(bucket).file(key)\n\n const [metadata] = await file.getMetadata()\n\n // Handle range request\n const rangeHeader = req.headers.get('range')\n const fileSize = Number(metadata.size)\n const rangeResult = getRangeRequestInfo({ fileSize, rangeHeader })\n\n if (rangeResult.type === 'invalid') {\n return new Response(null, {\n headers: new Headers(rangeResult.headers),\n status: rangeResult.status,\n })\n }\n\n const etagFromHeaders = req.headers.get('etag') || req.headers.get('if-none-match')\n const objectEtag = metadata.etag\n\n let headers = new Headers(incomingHeaders)\n\n // Add range-related headers from the result\n for (const [key, value] of Object.entries(rangeResult.headers)) {\n headers.append(key, value)\n }\n\n headers.append('Content-Type', String(metadata.contentType))\n headers.append('ETag', String(metadata.etag))\n\n // Add Content-Security-Policy header for SVG files to prevent executable code\n if (metadata.contentType === 'image/svg+xml') {\n headers.append('Content-Security-Policy', \"script-src 'none'\")\n }\n\n if (\n collection.upload &&\n typeof collection.upload === 'object' &&\n typeof collection.upload.modifyResponseHeaders === 'function'\n ) {\n headers = collection.upload.modifyResponseHeaders({ headers }) || headers\n }\n\n if (etagFromHeaders && etagFromHeaders === objectEtag) {\n return new Response(null, {\n headers,\n status: 304,\n })\n }\n\n // Manually create a ReadableStream for the web from a Node.js stream.\n const readableStream = new ReadableStream({\n start(controller) {\n const streamOptions =\n rangeResult.type === 'partial'\n ? { end: rangeResult.rangeEnd, start: rangeResult.rangeStart }\n : {}\n const nodeStream = file.createReadStream(streamOptions)\n nodeStream.on('data', (chunk) => {\n controller.enqueue(new Uint8Array(chunk))\n })\n nodeStream.on('end', () => {\n controller.close()\n })\n nodeStream.on('error', (err) => {\n controller.error(err)\n })\n },\n })\n\n return new Response(readableStream, {\n headers,\n status: rangeResult.status,\n })\n } catch (err: unknown) {\n if (err instanceof ApiError && err.code === 404) {\n return new Response(null, { status: 404, statusText: 'Not Found' })\n }\n req.payload.logger.error(err)\n return new Response('Internal Server Error', { status: 500 })\n }\n}\n"],"names":["ApiError","getFilePrefix","getDocPrefix","getFileKey","getRangeRequestInfo","sanitizeFilename","getFile","bucket","client","clientUploadContext","collection","collectionPrefix","filename","incomingHeaders","prefixQueryParam","req","useCompositePrefixes","docPrefix","key","file","metadata","getMetadata","rangeHeader","headers","get","fileSize","Number","size","rangeResult","type","Response","Headers","status","etagFromHeaders","objectEtag","etag","value","Object","entries","append","String","contentType","upload","modifyResponseHeaders","readableStream","ReadableStream","start","controller","streamOptions","end","rangeEnd","rangeStart","nodeStream","createReadStream","on","chunk","enqueue","Uint8Array","close","err","error","code","statusText","payload","logger"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -44,6 +44,20 @@ export interface GcsStorageOptions {
44
44
  * @see https://github.com/googleapis/nodejs-storage
45
45
  */
46
46
  options: StorageOptions;
47
+ /**
48
+ * When true, the collection-level prefix and document-level prefix are combined
49
+ * (compositional). When false (default), document prefix overrides collection
50
+ * prefix entirely.
51
+ *
52
+ * Example:
53
+ * - collection prefix: `collection-prefix/`
54
+ * - document prefix: `document-prefix/`
55
+ * - resulting prefix with useCompositePrefixes=true: `collection-prefix/document-prefix/`
56
+ * - resulting prefix with useCompositePrefixes=false: `document-prefix/`
57
+ *
58
+ * @default false
59
+ */
60
+ useCompositePrefixes?: boolean;
47
61
  }
48
62
  type GcsStoragePlugin = (gcsStorageArgs: GcsStorageOptions) => Plugin;
49
63
  export declare const gcsStorage: GcsStoragePlugin;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAC3D,OAAO,KAAK,EAEV,mBAAmB,EAEnB,iBAAiB,EAElB,MAAM,wCAAwC,CAAA;AAC/C,OAAO,KAAK,EAAU,MAAM,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAYnE,MAAM,WAAW,iBAAiB;IAChC,GAAG,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;IAE1B;;;;;;;;OAQG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAE5B;;OAEG;IACH,MAAM,EAAE,MAAM,CAAA;IACd;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB;;OAEG;IACH,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC;;OAEG;IACH,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,oBAAoB,EAAE,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC,CAAA;IAC7F;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB;;;;OAIG;IACH,OAAO,EAAE,cAAc,CAAA;CACxB;AAED,KAAK,gBAAgB,GAAG,CAAC,cAAc,EAAE,iBAAiB,KAAK,MAAM,CAAA;AAIrE,eAAO,MAAM,UAAU,EAAE,gBA2EtB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAC3D,OAAO,KAAK,EACV,mBAAmB,EAEnB,iBAAiB,EAClB,MAAM,wCAAwC,CAAA;AAC/C,OAAO,KAAK,EAAU,MAAM,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AASnE,MAAM,WAAW,iBAAiB;IAChC,GAAG,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;IAE1B;;;;;;;;OAQG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAE5B;;OAEG;IACH,MAAM,EAAE,MAAM,CAAA;IACd;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB;;OAEG;IACH,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC;;OAEG;IACH,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,oBAAoB,EAAE,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC,CAAA;IAC7F;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;OAIG;IACH,OAAO,EAAE,cAAc,CAAA;IAEvB;;;;;;;;;;;;OAYG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,KAAK,gBAAgB,GAAG,CAAC,cAAc,EAAE,iBAAiB,KAAK,MAAM,CAAA;AAIrE,eAAO,MAAM,UAAU,EAAE,gBAkFtB,CAAA"}
package/dist/index.js CHANGED
@@ -1,11 +1,8 @@
1
1
  import { Storage } from '@google-cloud/storage';
2
2
  import { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage';
3
3
  import { initClientUploads } from '@payloadcms/plugin-cloud-storage/utilities';
4
+ import { createGcsAdapter } from './adapter.js';
4
5
  import { getGenerateSignedURLHandler } from './generateSignedURL.js';
5
- import { getGenerateURL } from './generateURL.js';
6
- import { getHandleDelete } from './handleDelete.js';
7
- import { getHandleUpload } from './handleUpload.js';
8
- import { getHandler } from './staticHandler.js';
9
6
  const gcsClients = new Map();
10
7
  export const gcsStorage = (gcsStorageOptions)=>(incomingConfig)=>{
11
8
  const cacheKey = gcsStorageOptions.clientCacheKey || `gcs:${gcsStorageOptions.bucket}`;
@@ -16,7 +13,13 @@ export const gcsStorage = (gcsStorageOptions)=>(incomingConfig)=>{
16
13
  gcsClients.set(cacheKey, new Storage(gcsStorageOptions.options));
17
14
  return gcsClients.get(cacheKey);
18
15
  };
19
- const adapter = gcsStorageInternal(getStorageClient, gcsStorageOptions);
16
+ const adapter = createGcsAdapter({
17
+ acl: gcsStorageOptions.acl,
18
+ bucket: gcsStorageOptions.bucket,
19
+ clientUploads: gcsStorageOptions.clientUploads,
20
+ getStorageClient,
21
+ useCompositePrefixes: gcsStorageOptions.useCompositePrefixes
22
+ });
20
23
  const isPluginDisabled = gcsStorageOptions.enabled === false;
21
24
  initClientUploads({
22
25
  clientHandler: '@payloadcms/storage-gcs/client#GcsClientUploadHandler',
@@ -60,36 +63,9 @@ export const gcsStorage = (gcsStorageOptions)=>(incomingConfig)=>{
60
63
  };
61
64
  return cloudStoragePlugin({
62
65
  alwaysInsertFields: gcsStorageOptions.alwaysInsertFields,
63
- collections: collectionsWithAdapter
66
+ collections: collectionsWithAdapter,
67
+ useCompositePrefixes: gcsStorageOptions.useCompositePrefixes
64
68
  })(config);
65
69
  };
66
- function gcsStorageInternal(getStorageClient, { acl, bucket, clientUploads }) {
67
- return ({ collection, prefix })=>{
68
- return {
69
- name: 'gcs',
70
- clientUploads,
71
- generateURL: getGenerateURL({
72
- bucket,
73
- getStorageClient
74
- }),
75
- handleDelete: getHandleDelete({
76
- bucket,
77
- getStorageClient
78
- }),
79
- handleUpload: getHandleUpload({
80
- acl,
81
- bucket,
82
- collection,
83
- getStorageClient,
84
- prefix
85
- }),
86
- staticHandler: getHandler({
87
- bucket,
88
- collection,
89
- getStorageClient
90
- })
91
- };
92
- };
93
- }
94
70
 
95
71
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { StorageOptions } from '@google-cloud/storage'\nimport type {\n Adapter,\n ClientUploadsConfig,\n PluginOptions as CloudStoragePluginOptions,\n CollectionOptions,\n GeneratedAdapter,\n} from '@payloadcms/plugin-cloud-storage/types'\nimport type { Config, Plugin, UploadCollectionSlug } from 'payload'\n\nimport { Storage } from '@google-cloud/storage'\nimport { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage'\nimport { initClientUploads } from '@payloadcms/plugin-cloud-storage/utilities'\n\nimport { getGenerateSignedURLHandler } from './generateSignedURL.js'\nimport { getGenerateURL } from './generateURL.js'\nimport { getHandleDelete } from './handleDelete.js'\nimport { getHandleUpload } from './handleUpload.js'\nimport { getHandler } from './staticHandler.js'\n\nexport interface GcsStorageOptions {\n acl?: 'Private' | 'Public'\n\n /**\n * When enabled, fields (like the prefix field) will always be inserted into\n * the collection schema regardless of whether the plugin is enabled. This\n * ensures a consistent schema across all environments.\n *\n * This will be enabled by default in Payload v4.\n *\n * @default false\n */\n alwaysInsertFields?: boolean\n\n /**\n * The name of the bucket to use.\n */\n bucket: string\n /**\n * Optional cache key to identify the GCS storage client instance.\n * If not provided, a default key will be used.\n *\n * @default `gcs:containerName`\n */\n clientCacheKey?: string\n /**\n * Do uploads directly on the client to bypass limits on Vercel. You must allow CORS PUT method for the bucket to your website.\n */\n clientUploads?: ClientUploadsConfig\n /**\n * Collection options to apply the S3 adapter to.\n */\n collections: Partial<Record<UploadCollectionSlug, Omit<CollectionOptions, 'adapter'> | true>>\n /**\n * Whether or not to enable the plugin\n *\n * Default: true\n */\n enabled?: boolean\n\n /**\n * Google Cloud Storage client configuration.\n *\n * @see https://github.com/googleapis/nodejs-storage\n */\n options: StorageOptions\n}\n\ntype GcsStoragePlugin = (gcsStorageArgs: GcsStorageOptions) => Plugin\n\nconst gcsClients = new Map<string, Storage>()\n\nexport const gcsStorage: GcsStoragePlugin =\n (gcsStorageOptions: GcsStorageOptions) =>\n (incomingConfig: Config): Config => {\n const cacheKey = gcsStorageOptions.clientCacheKey || `gcs:${gcsStorageOptions.bucket}`\n\n const getStorageClient = (): Storage => {\n if (gcsClients.has(cacheKey)) {\n return gcsClients.get(cacheKey)!\n }\n gcsClients.set(cacheKey, new Storage(gcsStorageOptions.options))\n\n return gcsClients.get(cacheKey)!\n }\n\n const adapter = gcsStorageInternal(getStorageClient, gcsStorageOptions)\n\n const isPluginDisabled = gcsStorageOptions.enabled === false\n\n initClientUploads({\n clientHandler: '@payloadcms/storage-gcs/client#GcsClientUploadHandler',\n collections: gcsStorageOptions.collections,\n config: incomingConfig,\n enabled: !isPluginDisabled && Boolean(gcsStorageOptions.clientUploads),\n serverHandler: getGenerateSignedURLHandler({\n access:\n typeof gcsStorageOptions.clientUploads === 'object'\n ? gcsStorageOptions.clientUploads.access\n : undefined,\n bucket: gcsStorageOptions.bucket,\n collections: gcsStorageOptions.collections,\n getStorageClient,\n }),\n serverHandlerPath: '/storage-gcs-generate-signed-url',\n })\n\n if (isPluginDisabled) {\n return incomingConfig\n }\n\n // Add adapter to each collection option object\n const collectionsWithAdapter: CloudStoragePluginOptions['collections'] = Object.entries(\n gcsStorageOptions.collections,\n ).reduce(\n (acc, [slug, collOptions]) => ({\n ...acc,\n [slug]: {\n ...(collOptions === true ? {} : collOptions),\n adapter,\n },\n }),\n {} as Record<string, CollectionOptions>,\n )\n\n // Set disableLocalStorage: true for collections specified in the plugin options\n const config = {\n ...incomingConfig,\n collections: (incomingConfig.collections || []).map((collection) => {\n if (!collectionsWithAdapter[collection.slug]) {\n return collection\n }\n\n return {\n ...collection,\n upload: {\n ...(typeof collection.upload === 'object' ? collection.upload : {}),\n disableLocalStorage: true,\n },\n }\n }),\n }\n\n return cloudStoragePlugin({\n alwaysInsertFields: gcsStorageOptions.alwaysInsertFields,\n collections: collectionsWithAdapter,\n })(config)\n }\n\nfunction gcsStorageInternal(\n getStorageClient: () => Storage,\n { acl, bucket, clientUploads }: GcsStorageOptions,\n): Adapter {\n return ({ collection, prefix }): GeneratedAdapter => {\n return {\n name: 'gcs',\n clientUploads,\n generateURL: getGenerateURL({ bucket, getStorageClient }),\n handleDelete: getHandleDelete({ bucket, getStorageClient }),\n handleUpload: getHandleUpload({\n acl,\n bucket,\n collection,\n getStorageClient,\n prefix,\n }),\n staticHandler: getHandler({ bucket, collection, getStorageClient }),\n }\n }\n}\n"],"names":["Storage","cloudStoragePlugin","initClientUploads","getGenerateSignedURLHandler","getGenerateURL","getHandleDelete","getHandleUpload","getHandler","gcsClients","Map","gcsStorage","gcsStorageOptions","incomingConfig","cacheKey","clientCacheKey","bucket","getStorageClient","has","get","set","options","adapter","gcsStorageInternal","isPluginDisabled","enabled","clientHandler","collections","config","Boolean","clientUploads","serverHandler","access","undefined","serverHandlerPath","collectionsWithAdapter","Object","entries","reduce","acc","slug","collOptions","map","collection","upload","disableLocalStorage","alwaysInsertFields","acl","prefix","name","generateURL","handleDelete","handleUpload","staticHandler"],"mappings":"AAUA,SAASA,OAAO,QAAQ,wBAAuB;AAC/C,SAASC,kBAAkB,QAAQ,mCAAkC;AACrE,SAASC,iBAAiB,QAAQ,6CAA4C;AAE9E,SAASC,2BAA2B,QAAQ,yBAAwB;AACpE,SAASC,cAAc,QAAQ,mBAAkB;AACjD,SAASC,eAAe,QAAQ,oBAAmB;AACnD,SAASC,eAAe,QAAQ,oBAAmB;AACnD,SAASC,UAAU,QAAQ,qBAAoB;AAoD/C,MAAMC,aAAa,IAAIC;AAEvB,OAAO,MAAMC,aACX,CAACC,oBACD,CAACC;QACC,MAAMC,WAAWF,kBAAkBG,cAAc,IAAI,CAAC,IAAI,EAAEH,kBAAkBI,MAAM,EAAE;QAEtF,MAAMC,mBAAmB;YACvB,IAAIR,WAAWS,GAAG,CAACJ,WAAW;gBAC5B,OAAOL,WAAWU,GAAG,CAACL;YACxB;YACAL,WAAWW,GAAG,CAACN,UAAU,IAAIb,QAAQW,kBAAkBS,OAAO;YAE9D,OAAOZ,WAAWU,GAAG,CAACL;QACxB;QAEA,MAAMQ,UAAUC,mBAAmBN,kBAAkBL;QAErD,MAAMY,mBAAmBZ,kBAAkBa,OAAO,KAAK;QAEvDtB,kBAAkB;YAChBuB,eAAe;YACfC,aAAaf,kBAAkBe,WAAW;YAC1CC,QAAQf;YACRY,SAAS,CAACD,oBAAoBK,QAAQjB,kBAAkBkB,aAAa;YACrEC,eAAe3B,4BAA4B;gBACzC4B,QACE,OAAOpB,kBAAkBkB,aAAa,KAAK,WACvClB,kBAAkBkB,aAAa,CAACE,MAAM,GACtCC;gBACNjB,QAAQJ,kBAAkBI,MAAM;gBAChCW,aAAaf,kBAAkBe,WAAW;gBAC1CV;YACF;YACAiB,mBAAmB;QACrB;QAEA,IAAIV,kBAAkB;YACpB,OAAOX;QACT;QAEA,+CAA+C;QAC/C,MAAMsB,yBAAmEC,OAAOC,OAAO,CACrFzB,kBAAkBe,WAAW,EAC7BW,MAAM,CACN,CAACC,KAAK,CAACC,MAAMC,YAAY,GAAM,CAAA;gBAC7B,GAAGF,GAAG;gBACN,CAACC,KAAK,EAAE;oBACN,GAAIC,gBAAgB,OAAO,CAAC,IAAIA,WAAW;oBAC3CnB;gBACF;YACF,CAAA,GACA,CAAC;QAGH,gFAAgF;QAChF,MAAMM,SAAS;YACb,GAAGf,cAAc;YACjBc,aAAa,AAACd,CAAAA,eAAec,WAAW,IAAI,EAAE,AAAD,EAAGe,GAAG,CAAC,CAACC;gBACnD,IAAI,CAACR,sBAAsB,CAACQ,WAAWH,IAAI,CAAC,EAAE;oBAC5C,OAAOG;gBACT;gBAEA,OAAO;oBACL,GAAGA,UAAU;oBACbC,QAAQ;wBACN,GAAI,OAAOD,WAAWC,MAAM,KAAK,WAAWD,WAAWC,MAAM,GAAG,CAAC,CAAC;wBAClEC,qBAAqB;oBACvB;gBACF;YACF;QACF;QAEA,OAAO3C,mBAAmB;YACxB4C,oBAAoBlC,kBAAkBkC,kBAAkB;YACxDnB,aAAaQ;QACf,GAAGP;IACL,EAAC;AAEH,SAASL,mBACPN,gBAA+B,EAC/B,EAAE8B,GAAG,EAAE/B,MAAM,EAAEc,aAAa,EAAqB;IAEjD,OAAO,CAAC,EAAEa,UAAU,EAAEK,MAAM,EAAE;QAC5B,OAAO;YACLC,MAAM;YACNnB;YACAoB,aAAa7C,eAAe;gBAAEW;gBAAQC;YAAiB;YACvDkC,cAAc7C,gBAAgB;gBAAEU;gBAAQC;YAAiB;YACzDmC,cAAc7C,gBAAgB;gBAC5BwC;gBACA/B;gBACA2B;gBACA1B;gBACA+B;YACF;YACAK,eAAe7C,WAAW;gBAAEQ;gBAAQ2B;gBAAY1B;YAAiB;QACnE;IACF;AACF"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { StorageOptions } from '@google-cloud/storage'\nimport type {\n ClientUploadsConfig,\n PluginOptions as CloudStoragePluginOptions,\n CollectionOptions,\n} from '@payloadcms/plugin-cloud-storage/types'\nimport type { Config, Plugin, UploadCollectionSlug } from 'payload'\n\nimport { Storage } from '@google-cloud/storage'\nimport { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage'\nimport { initClientUploads } from '@payloadcms/plugin-cloud-storage/utilities'\n\nimport { createGcsAdapter } from './adapter.js'\nimport { getGenerateSignedURLHandler } from './generateSignedURL.js'\n\nexport interface GcsStorageOptions {\n acl?: 'Private' | 'Public'\n\n /**\n * When enabled, fields (like the prefix field) will always be inserted into\n * the collection schema regardless of whether the plugin is enabled. This\n * ensures a consistent schema across all environments.\n *\n * This will be enabled by default in Payload v4.\n *\n * @default false\n */\n alwaysInsertFields?: boolean\n\n /**\n * The name of the bucket to use.\n */\n bucket: string\n /**\n * Optional cache key to identify the GCS storage client instance.\n * If not provided, a default key will be used.\n *\n * @default `gcs:containerName`\n */\n clientCacheKey?: string\n /**\n * Do uploads directly on the client to bypass limits on Vercel. You must allow CORS PUT method for the bucket to your website.\n */\n clientUploads?: ClientUploadsConfig\n /**\n * Collection options to apply the S3 adapter to.\n */\n collections: Partial<Record<UploadCollectionSlug, Omit<CollectionOptions, 'adapter'> | true>>\n /**\n * Whether or not to enable the plugin\n *\n * Default: true\n */\n enabled?: boolean\n /**\n * Google Cloud Storage client configuration.\n *\n * @see https://github.com/googleapis/nodejs-storage\n */\n options: StorageOptions\n\n /**\n * When true, the collection-level prefix and document-level prefix are combined\n * (compositional). When false (default), document prefix overrides collection\n * prefix entirely.\n *\n * Example:\n * - collection prefix: `collection-prefix/`\n * - document prefix: `document-prefix/`\n * - resulting prefix with useCompositePrefixes=true: `collection-prefix/document-prefix/`\n * - resulting prefix with useCompositePrefixes=false: `document-prefix/`\n *\n * @default false\n */\n useCompositePrefixes?: boolean\n}\n\ntype GcsStoragePlugin = (gcsStorageArgs: GcsStorageOptions) => Plugin\n\nconst gcsClients = new Map<string, Storage>()\n\nexport const gcsStorage: GcsStoragePlugin =\n (gcsStorageOptions: GcsStorageOptions) =>\n (incomingConfig: Config): Config => {\n const cacheKey = gcsStorageOptions.clientCacheKey || `gcs:${gcsStorageOptions.bucket}`\n\n const getStorageClient = (): Storage => {\n if (gcsClients.has(cacheKey)) {\n return gcsClients.get(cacheKey)!\n }\n gcsClients.set(cacheKey, new Storage(gcsStorageOptions.options))\n\n return gcsClients.get(cacheKey)!\n }\n\n const adapter = createGcsAdapter({\n acl: gcsStorageOptions.acl,\n bucket: gcsStorageOptions.bucket,\n clientUploads: gcsStorageOptions.clientUploads,\n getStorageClient,\n useCompositePrefixes: gcsStorageOptions.useCompositePrefixes,\n })\n\n const isPluginDisabled = gcsStorageOptions.enabled === false\n\n initClientUploads({\n clientHandler: '@payloadcms/storage-gcs/client#GcsClientUploadHandler',\n collections: gcsStorageOptions.collections,\n config: incomingConfig,\n enabled: !isPluginDisabled && Boolean(gcsStorageOptions.clientUploads),\n serverHandler: getGenerateSignedURLHandler({\n access:\n typeof gcsStorageOptions.clientUploads === 'object'\n ? gcsStorageOptions.clientUploads.access\n : undefined,\n bucket: gcsStorageOptions.bucket,\n collections: gcsStorageOptions.collections,\n getStorageClient,\n }),\n serverHandlerPath: '/storage-gcs-generate-signed-url',\n })\n\n if (isPluginDisabled) {\n return incomingConfig\n }\n\n // Add adapter to each collection option object\n const collectionsWithAdapter: CloudStoragePluginOptions['collections'] = Object.entries(\n gcsStorageOptions.collections,\n ).reduce(\n (acc, [slug, collOptions]) => ({\n ...acc,\n [slug]: {\n ...(collOptions === true ? {} : collOptions),\n adapter,\n },\n }),\n {} as Record<string, CollectionOptions>,\n )\n\n // Set disableLocalStorage: true for collections specified in the plugin options\n const config = {\n ...incomingConfig,\n collections: (incomingConfig.collections || []).map((collection) => {\n if (!collectionsWithAdapter[collection.slug]) {\n return collection\n }\n\n return {\n ...collection,\n upload: {\n ...(typeof collection.upload === 'object' ? collection.upload : {}),\n disableLocalStorage: true,\n },\n }\n }),\n }\n\n return cloudStoragePlugin({\n alwaysInsertFields: gcsStorageOptions.alwaysInsertFields,\n collections: collectionsWithAdapter,\n useCompositePrefixes: gcsStorageOptions.useCompositePrefixes,\n })(config)\n }\n"],"names":["Storage","cloudStoragePlugin","initClientUploads","createGcsAdapter","getGenerateSignedURLHandler","gcsClients","Map","gcsStorage","gcsStorageOptions","incomingConfig","cacheKey","clientCacheKey","bucket","getStorageClient","has","get","set","options","adapter","acl","clientUploads","useCompositePrefixes","isPluginDisabled","enabled","clientHandler","collections","config","Boolean","serverHandler","access","undefined","serverHandlerPath","collectionsWithAdapter","Object","entries","reduce","acc","slug","collOptions","map","collection","upload","disableLocalStorage","alwaysInsertFields"],"mappings":"AAQA,SAASA,OAAO,QAAQ,wBAAuB;AAC/C,SAASC,kBAAkB,QAAQ,mCAAkC;AACrE,SAASC,iBAAiB,QAAQ,6CAA4C;AAE9E,SAASC,gBAAgB,QAAQ,eAAc;AAC/C,SAASC,2BAA2B,QAAQ,yBAAwB;AAkEpE,MAAMC,aAAa,IAAIC;AAEvB,OAAO,MAAMC,aACX,CAACC,oBACD,CAACC;QACC,MAAMC,WAAWF,kBAAkBG,cAAc,IAAI,CAAC,IAAI,EAAEH,kBAAkBI,MAAM,EAAE;QAEtF,MAAMC,mBAAmB;YACvB,IAAIR,WAAWS,GAAG,CAACJ,WAAW;gBAC5B,OAAOL,WAAWU,GAAG,CAACL;YACxB;YACAL,WAAWW,GAAG,CAACN,UAAU,IAAIV,QAAQQ,kBAAkBS,OAAO;YAE9D,OAAOZ,WAAWU,GAAG,CAACL;QACxB;QAEA,MAAMQ,UAAUf,iBAAiB;YAC/BgB,KAAKX,kBAAkBW,GAAG;YAC1BP,QAAQJ,kBAAkBI,MAAM;YAChCQ,eAAeZ,kBAAkBY,aAAa;YAC9CP;YACAQ,sBAAsBb,kBAAkBa,oBAAoB;QAC9D;QAEA,MAAMC,mBAAmBd,kBAAkBe,OAAO,KAAK;QAEvDrB,kBAAkB;YAChBsB,eAAe;YACfC,aAAajB,kBAAkBiB,WAAW;YAC1CC,QAAQjB;YACRc,SAAS,CAACD,oBAAoBK,QAAQnB,kBAAkBY,aAAa;YACrEQ,eAAexB,4BAA4B;gBACzCyB,QACE,OAAOrB,kBAAkBY,aAAa,KAAK,WACvCZ,kBAAkBY,aAAa,CAACS,MAAM,GACtCC;gBACNlB,QAAQJ,kBAAkBI,MAAM;gBAChCa,aAAajB,kBAAkBiB,WAAW;gBAC1CZ;YACF;YACAkB,mBAAmB;QACrB;QAEA,IAAIT,kBAAkB;YACpB,OAAOb;QACT;QAEA,+CAA+C;QAC/C,MAAMuB,yBAAmEC,OAAOC,OAAO,CACrF1B,kBAAkBiB,WAAW,EAC7BU,MAAM,CACN,CAACC,KAAK,CAACC,MAAMC,YAAY,GAAM,CAAA;gBAC7B,GAAGF,GAAG;gBACN,CAACC,KAAK,EAAE;oBACN,GAAIC,gBAAgB,OAAO,CAAC,IAAIA,WAAW;oBAC3CpB;gBACF;YACF,CAAA,GACA,CAAC;QAGH,gFAAgF;QAChF,MAAMQ,SAAS;YACb,GAAGjB,cAAc;YACjBgB,aAAa,AAAChB,CAAAA,eAAegB,WAAW,IAAI,EAAE,AAAD,EAAGc,GAAG,CAAC,CAACC;gBACnD,IAAI,CAACR,sBAAsB,CAACQ,WAAWH,IAAI,CAAC,EAAE;oBAC5C,OAAOG;gBACT;gBAEA,OAAO;oBACL,GAAGA,UAAU;oBACbC,QAAQ;wBACN,GAAI,OAAOD,WAAWC,MAAM,KAAK,WAAWD,WAAWC,MAAM,GAAG,CAAC,CAAC;wBAClEC,qBAAqB;oBACvB;gBACF;YACF;QACF;QAEA,OAAOzC,mBAAmB;YACxB0C,oBAAoBnC,kBAAkBmC,kBAAkB;YACxDlB,aAAaO;YACbX,sBAAsBb,kBAAkBa,oBAAoB;QAC9D,GAAGK;IACL,EAAC"}
@@ -0,0 +1,15 @@
1
+ import type { Storage } from '@google-cloud/storage';
2
+ interface UploadFileArgs {
3
+ acl?: 'Private' | 'Public';
4
+ bucket: string;
5
+ buffer: Buffer;
6
+ client: Storage;
7
+ collectionPrefix?: string;
8
+ docPrefix?: string;
9
+ filename: string;
10
+ mimeType: string;
11
+ useCompositePrefixes?: boolean;
12
+ }
13
+ export declare function uploadFile({ acl, bucket, buffer, client, collectionPrefix, docPrefix, filename, mimeType, useCompositePrefixes, }: UploadFileArgs): Promise<void>;
14
+ export {};
15
+ //# sourceMappingURL=uploadFile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uploadFile.d.ts","sourceRoot":"","sources":["../src/uploadFile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AAIpD,UAAU,cAAc;IACtB,GAAG,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAsB,UAAU,CAAC,EAC/B,GAAG,EACH,MAAM,EACN,MAAM,EACN,MAAM,EACN,gBAAqB,EACrB,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,oBAA4B,GAC7B,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBhC"}
@@ -0,0 +1,20 @@
1
+ import { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities';
2
+ export async function uploadFile({ acl, bucket, buffer, client, collectionPrefix = '', docPrefix, filename, mimeType, useCompositePrefixes = false }) {
3
+ const fileKey = getFileKey({
4
+ collectionPrefix,
5
+ docPrefix: docPrefix || '',
6
+ filename,
7
+ useCompositePrefixes
8
+ });
9
+ const gcsFile = client.bucket(bucket).file(fileKey);
10
+ await gcsFile.save(buffer, {
11
+ metadata: {
12
+ contentType: mimeType
13
+ }
14
+ });
15
+ if (acl) {
16
+ await gcsFile[`make${acl}`]();
17
+ }
18
+ }
19
+
20
+ //# sourceMappingURL=uploadFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/uploadFile.ts"],"sourcesContent":["import type { Storage } from '@google-cloud/storage'\n\nimport { getFileKey } from '@payloadcms/plugin-cloud-storage/utilities'\n\ninterface UploadFileArgs {\n acl?: 'Private' | 'Public'\n bucket: string\n buffer: Buffer\n client: Storage\n collectionPrefix?: string\n docPrefix?: string\n filename: string\n mimeType: string\n useCompositePrefixes?: boolean\n}\n\nexport async function uploadFile({\n acl,\n bucket,\n buffer,\n client,\n collectionPrefix = '',\n docPrefix,\n filename,\n mimeType,\n useCompositePrefixes = false,\n}: UploadFileArgs): Promise<void> {\n const fileKey = getFileKey({\n collectionPrefix,\n docPrefix: docPrefix || '',\n filename,\n useCompositePrefixes,\n })\n\n const gcsFile = client.bucket(bucket).file(fileKey)\n\n await gcsFile.save(buffer, {\n metadata: {\n contentType: mimeType,\n },\n })\n\n if (acl) {\n await gcsFile[`make${acl}`]()\n }\n}\n"],"names":["getFileKey","uploadFile","acl","bucket","buffer","client","collectionPrefix","docPrefix","filename","mimeType","useCompositePrefixes","fileKey","gcsFile","file","save","metadata","contentType"],"mappings":"AAEA,SAASA,UAAU,QAAQ,6CAA4C;AAcvE,OAAO,eAAeC,WAAW,EAC/BC,GAAG,EACHC,MAAM,EACNC,MAAM,EACNC,MAAM,EACNC,mBAAmB,EAAE,EACrBC,SAAS,EACTC,QAAQ,EACRC,QAAQ,EACRC,uBAAuB,KAAK,EACb;IACf,MAAMC,UAAUX,WAAW;QACzBM;QACAC,WAAWA,aAAa;QACxBC;QACAE;IACF;IAEA,MAAME,UAAUP,OAAOF,MAAM,CAACA,QAAQU,IAAI,CAACF;IAE3C,MAAMC,QAAQE,IAAI,CAACV,QAAQ;QACzBW,UAAU;YACRC,aAAaP;QACf;IACF;IAEA,IAAIP,KAAK;QACP,MAAMU,OAAO,CAAC,CAAC,IAAI,EAAEV,KAAK,CAAC;IAC7B;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@payloadcms/storage-gcs",
3
- "version": "3.83.0-internal.06ac84e",
3
+ "version": "3.83.0-internal.86b7bfb",
4
4
  "description": "Payload storage adapter for Google Cloud Storage",
5
5
  "homepage": "https://payloadcms.com",
6
6
  "repository": {
@@ -38,13 +38,13 @@
38
38
  ],
39
39
  "dependencies": {
40
40
  "@google-cloud/storage": "7.19.0",
41
- "@payloadcms/plugin-cloud-storage": "3.83.0-internal.06ac84e"
41
+ "@payloadcms/plugin-cloud-storage": "3.83.0-internal.86b7bfb"
42
42
  },
43
43
  "devDependencies": {
44
- "payload": "3.83.0-internal.06ac84e"
44
+ "payload": "3.83.0-internal.86b7bfb"
45
45
  },
46
46
  "peerDependencies": {
47
- "payload": "3.83.0-internal.06ac84e"
47
+ "payload": "3.83.0-internal.86b7bfb"
48
48
  },
49
49
  "engines": {
50
50
  "node": "^18.20.2 || >=20.9.0"
@@ -1,9 +0,0 @@
1
- import type { Storage } from '@google-cloud/storage';
2
- import type { HandleDelete } from '@payloadcms/plugin-cloud-storage/types';
3
- interface Args {
4
- bucket: string;
5
- getStorageClient: () => Storage;
6
- }
7
- export declare const getHandleDelete: ({ bucket, getStorageClient }: Args) => HandleDelete;
8
- export {};
9
- //# sourceMappingURL=handleDelete.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"handleDelete.d.ts","sourceRoot":"","sources":["../src/handleDelete.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAA;AAI1E,UAAU,IAAI;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,gBAAgB,EAAE,MAAM,OAAO,CAAA;CAChC;AAED,eAAO,MAAM,eAAe,iCAAkC,IAAI,KAAG,YAMpE,CAAA"}
@@ -1,10 +0,0 @@
1
- import path from 'path';
2
- export const getHandleDelete = ({ bucket, getStorageClient })=>{
3
- return async ({ doc: { prefix = '' }, filename })=>{
4
- await getStorageClient().bucket(bucket).file(path.posix.join(prefix, filename)).delete({
5
- ignoreNotFound: true
6
- });
7
- };
8
- };
9
-
10
- //# sourceMappingURL=handleDelete.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/handleDelete.ts"],"sourcesContent":["import type { Storage } from '@google-cloud/storage'\nimport type { HandleDelete } from '@payloadcms/plugin-cloud-storage/types'\n\nimport path from 'path'\n\ninterface Args {\n bucket: string\n getStorageClient: () => Storage\n}\n\nexport const getHandleDelete = ({ bucket, getStorageClient }: Args): HandleDelete => {\n return async ({ doc: { prefix = '' }, filename }) => {\n await getStorageClient().bucket(bucket).file(path.posix.join(prefix, filename)).delete({\n ignoreNotFound: true,\n })\n }\n}\n"],"names":["path","getHandleDelete","bucket","getStorageClient","doc","prefix","filename","file","posix","join","delete","ignoreNotFound"],"mappings":"AAGA,OAAOA,UAAU,OAAM;AAOvB,OAAO,MAAMC,kBAAkB,CAAC,EAAEC,MAAM,EAAEC,gBAAgB,EAAQ;IAChE,OAAO,OAAO,EAAEC,KAAK,EAAEC,SAAS,EAAE,EAAE,EAAEC,QAAQ,EAAE;QAC9C,MAAMH,mBAAmBD,MAAM,CAACA,QAAQK,IAAI,CAACP,KAAKQ,KAAK,CAACC,IAAI,CAACJ,QAAQC,WAAWI,MAAM,CAAC;YACrFC,gBAAgB;QAClB;IACF;AACF,EAAC"}
@@ -1,13 +0,0 @@
1
- import type { Storage } from '@google-cloud/storage';
2
- import type { HandleUpload } from '@payloadcms/plugin-cloud-storage/types';
3
- import type { CollectionConfig } from 'payload';
4
- interface Args {
5
- acl?: 'Private' | 'Public';
6
- bucket: string;
7
- collection: CollectionConfig;
8
- getStorageClient: () => Storage;
9
- prefix?: string;
10
- }
11
- export declare const getHandleUpload: ({ acl, bucket, getStorageClient, prefix, }: Args) => HandleUpload;
12
- export {};
13
- //# sourceMappingURL=handleUpload.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"handleUpload.d.ts","sourceRoot":"","sources":["../src/handleUpload.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAA;AAC1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAI/C,UAAU,IAAI;IACZ,GAAG,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,gBAAgB,CAAA;IAC5B,gBAAgB,EAAE,MAAM,OAAO,CAAA;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,eAAO,MAAM,eAAe,+CAKzB,IAAI,KAAG,YAiBT,CAAA"}
@@ -1,18 +0,0 @@
1
- import path from 'path';
2
- export const getHandleUpload = ({ acl, bucket, getStorageClient, prefix = '' })=>{
3
- return async ({ data, file })=>{
4
- const fileKey = path.posix.join(data.prefix || prefix, file.filename);
5
- const gcsFile = getStorageClient().bucket(bucket).file(fileKey);
6
- await gcsFile.save(file.buffer, {
7
- metadata: {
8
- contentType: file.mimeType
9
- }
10
- });
11
- if (acl) {
12
- await gcsFile[`make${acl}`]();
13
- }
14
- return data;
15
- };
16
- };
17
-
18
- //# sourceMappingURL=handleUpload.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/handleUpload.ts"],"sourcesContent":["import type { Storage } from '@google-cloud/storage'\nimport type { HandleUpload } from '@payloadcms/plugin-cloud-storage/types'\nimport type { CollectionConfig } from 'payload'\n\nimport path from 'path'\n\ninterface Args {\n acl?: 'Private' | 'Public'\n bucket: string\n collection: CollectionConfig\n getStorageClient: () => Storage\n prefix?: string\n}\n\nexport const getHandleUpload = ({\n acl,\n bucket,\n getStorageClient,\n prefix = '',\n}: Args): HandleUpload => {\n return async ({ data, file }) => {\n const fileKey = path.posix.join(data.prefix || prefix, file.filename)\n\n const gcsFile = getStorageClient().bucket(bucket).file(fileKey)\n await gcsFile.save(file.buffer, {\n metadata: {\n contentType: file.mimeType,\n },\n })\n\n if (acl) {\n await gcsFile[`make${acl}`]()\n }\n\n return data\n }\n}\n"],"names":["path","getHandleUpload","acl","bucket","getStorageClient","prefix","data","file","fileKey","posix","join","filename","gcsFile","save","buffer","metadata","contentType","mimeType"],"mappings":"AAIA,OAAOA,UAAU,OAAM;AAUvB,OAAO,MAAMC,kBAAkB,CAAC,EAC9BC,GAAG,EACHC,MAAM,EACNC,gBAAgB,EAChBC,SAAS,EAAE,EACN;IACL,OAAO,OAAO,EAAEC,IAAI,EAAEC,IAAI,EAAE;QAC1B,MAAMC,UAAUR,KAAKS,KAAK,CAACC,IAAI,CAACJ,KAAKD,MAAM,IAAIA,QAAQE,KAAKI,QAAQ;QAEpE,MAAMC,UAAUR,mBAAmBD,MAAM,CAACA,QAAQI,IAAI,CAACC;QACvD,MAAMI,QAAQC,IAAI,CAACN,KAAKO,MAAM,EAAE;YAC9BC,UAAU;gBACRC,aAAaT,KAAKU,QAAQ;YAC5B;QACF;QAEA,IAAIf,KAAK;YACP,MAAMU,OAAO,CAAC,CAAC,IAAI,EAAEV,KAAK,CAAC;QAC7B;QAEA,OAAOI;IACT;AACF,EAAC"}
@@ -1,11 +0,0 @@
1
- import type { StaticHandler } from '@payloadcms/plugin-cloud-storage/types';
2
- import type { CollectionConfig } from 'payload';
3
- import { type Storage } from '@google-cloud/storage';
4
- interface Args {
5
- bucket: string;
6
- collection: CollectionConfig;
7
- getStorageClient: () => Storage;
8
- }
9
- export declare const getHandler: ({ bucket, collection, getStorageClient }: Args) => StaticHandler;
10
- export {};
11
- //# sourceMappingURL=staticHandler.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"staticHandler.d.ts","sourceRoot":"","sources":["../src/staticHandler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAC3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE/C,OAAO,EAAY,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAA;AAM9D,UAAU,IAAI;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,gBAAgB,CAAA;IAC5B,gBAAgB,EAAE,MAAM,OAAO,CAAA;CAChC;AAED,eAAO,MAAM,UAAU,6CAA8C,IAAI,KAAG,aAmG3E,CAAA"}
@@ -1,93 +0,0 @@
1
- import { ApiError } from '@google-cloud/storage';
2
- import { getFilePrefix } from '@payloadcms/plugin-cloud-storage/utilities';
3
- import path from 'path';
4
- import { getRangeRequestInfo } from 'payload/internal';
5
- import { sanitizeFilename } from 'payload/shared';
6
- export const getHandler = ({ bucket, collection, getStorageClient })=>{
7
- return async (req, { headers: incomingHeaders, params: { clientUploadContext, filename, prefix: prefixQueryParam } })=>{
8
- try {
9
- const prefix = await getFilePrefix({
10
- clientUploadContext,
11
- collection,
12
- filename,
13
- prefixQueryParam,
14
- req
15
- });
16
- const file = getStorageClient().bucket(bucket).file(path.posix.join(prefix, sanitizeFilename(filename)));
17
- const [metadata] = await file.getMetadata();
18
- // Handle range request
19
- const rangeHeader = req.headers.get('range');
20
- const fileSize = Number(metadata.size);
21
- const rangeResult = getRangeRequestInfo({
22
- fileSize,
23
- rangeHeader
24
- });
25
- if (rangeResult.type === 'invalid') {
26
- return new Response(null, {
27
- headers: new Headers(rangeResult.headers),
28
- status: rangeResult.status
29
- });
30
- }
31
- const etagFromHeaders = req.headers.get('etag') || req.headers.get('if-none-match');
32
- const objectEtag = metadata.etag;
33
- let headers = new Headers(incomingHeaders);
34
- // Add range-related headers from the result
35
- for (const [key, value] of Object.entries(rangeResult.headers)){
36
- headers.append(key, value);
37
- }
38
- headers.append('Content-Type', String(metadata.contentType));
39
- headers.append('ETag', String(metadata.etag));
40
- // Add Content-Security-Policy header for SVG files to prevent executable code
41
- if (metadata.contentType === 'image/svg+xml') {
42
- headers.append('Content-Security-Policy', "script-src 'none'");
43
- }
44
- if (collection.upload && typeof collection.upload === 'object' && typeof collection.upload.modifyResponseHeaders === 'function') {
45
- headers = collection.upload.modifyResponseHeaders({
46
- headers
47
- }) || headers;
48
- }
49
- if (etagFromHeaders && etagFromHeaders === objectEtag) {
50
- return new Response(null, {
51
- headers,
52
- status: 304
53
- });
54
- }
55
- // Manually create a ReadableStream for the web from a Node.js stream.
56
- const readableStream = new ReadableStream({
57
- start (controller) {
58
- const streamOptions = rangeResult.type === 'partial' ? {
59
- end: rangeResult.rangeEnd,
60
- start: rangeResult.rangeStart
61
- } : {};
62
- const nodeStream = file.createReadStream(streamOptions);
63
- nodeStream.on('data', (chunk)=>{
64
- controller.enqueue(new Uint8Array(chunk));
65
- });
66
- nodeStream.on('end', ()=>{
67
- controller.close();
68
- });
69
- nodeStream.on('error', (err)=>{
70
- controller.error(err);
71
- });
72
- }
73
- });
74
- return new Response(readableStream, {
75
- headers,
76
- status: rangeResult.status
77
- });
78
- } catch (err) {
79
- if (err instanceof ApiError && err.code === 404) {
80
- return new Response(null, {
81
- status: 404,
82
- statusText: 'Not Found'
83
- });
84
- }
85
- req.payload.logger.error(err);
86
- return new Response('Internal Server Error', {
87
- status: 500
88
- });
89
- }
90
- };
91
- };
92
-
93
- //# sourceMappingURL=staticHandler.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/staticHandler.ts"],"sourcesContent":["import type { StaticHandler } from '@payloadcms/plugin-cloud-storage/types'\nimport type { CollectionConfig } from 'payload'\n\nimport { ApiError, type Storage } from '@google-cloud/storage'\nimport { getFilePrefix } from '@payloadcms/plugin-cloud-storage/utilities'\nimport path from 'path'\nimport { getRangeRequestInfo } from 'payload/internal'\nimport { sanitizeFilename } from 'payload/shared'\n\ninterface Args {\n bucket: string\n collection: CollectionConfig\n getStorageClient: () => Storage\n}\n\nexport const getHandler = ({ bucket, collection, getStorageClient }: Args): StaticHandler => {\n return async (\n req,\n {\n headers: incomingHeaders,\n params: { clientUploadContext, filename, prefix: prefixQueryParam },\n },\n ) => {\n try {\n const prefix = await getFilePrefix({\n clientUploadContext,\n collection,\n filename,\n prefixQueryParam,\n req,\n })\n const file = getStorageClient()\n .bucket(bucket)\n .file(path.posix.join(prefix, sanitizeFilename(filename)))\n\n const [metadata] = await file.getMetadata()\n\n // Handle range request\n const rangeHeader = req.headers.get('range')\n const fileSize = Number(metadata.size)\n const rangeResult = getRangeRequestInfo({ fileSize, rangeHeader })\n\n if (rangeResult.type === 'invalid') {\n return new Response(null, {\n headers: new Headers(rangeResult.headers),\n status: rangeResult.status,\n })\n }\n\n const etagFromHeaders = req.headers.get('etag') || req.headers.get('if-none-match')\n const objectEtag = metadata.etag\n\n let headers = new Headers(incomingHeaders)\n\n // Add range-related headers from the result\n for (const [key, value] of Object.entries(rangeResult.headers)) {\n headers.append(key, value)\n }\n\n headers.append('Content-Type', String(metadata.contentType))\n headers.append('ETag', String(metadata.etag))\n\n // Add Content-Security-Policy header for SVG files to prevent executable code\n if (metadata.contentType === 'image/svg+xml') {\n headers.append('Content-Security-Policy', \"script-src 'none'\")\n }\n\n if (\n collection.upload &&\n typeof collection.upload === 'object' &&\n typeof collection.upload.modifyResponseHeaders === 'function'\n ) {\n headers = collection.upload.modifyResponseHeaders({ headers }) || headers\n }\n\n if (etagFromHeaders && etagFromHeaders === objectEtag) {\n return new Response(null, {\n headers,\n status: 304,\n })\n }\n\n // Manually create a ReadableStream for the web from a Node.js stream.\n const readableStream = new ReadableStream({\n start(controller) {\n const streamOptions =\n rangeResult.type === 'partial'\n ? { end: rangeResult.rangeEnd, start: rangeResult.rangeStart }\n : {}\n const nodeStream = file.createReadStream(streamOptions)\n nodeStream.on('data', (chunk) => {\n controller.enqueue(new Uint8Array(chunk))\n })\n nodeStream.on('end', () => {\n controller.close()\n })\n nodeStream.on('error', (err) => {\n controller.error(err)\n })\n },\n })\n\n return new Response(readableStream, {\n headers,\n status: rangeResult.status,\n })\n } catch (err: unknown) {\n if (err instanceof ApiError && err.code === 404) {\n return new Response(null, { status: 404, statusText: 'Not Found' })\n }\n req.payload.logger.error(err)\n return new Response('Internal Server Error', { status: 500 })\n }\n }\n}\n"],"names":["ApiError","getFilePrefix","path","getRangeRequestInfo","sanitizeFilename","getHandler","bucket","collection","getStorageClient","req","headers","incomingHeaders","params","clientUploadContext","filename","prefix","prefixQueryParam","file","posix","join","metadata","getMetadata","rangeHeader","get","fileSize","Number","size","rangeResult","type","Response","Headers","status","etagFromHeaders","objectEtag","etag","key","value","Object","entries","append","String","contentType","upload","modifyResponseHeaders","readableStream","ReadableStream","start","controller","streamOptions","end","rangeEnd","rangeStart","nodeStream","createReadStream","on","chunk","enqueue","Uint8Array","close","err","error","code","statusText","payload","logger"],"mappings":"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"}