@payloadcms/figma 0.0.1-alpha.41 → 0.0.1-alpha.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/oauth-flow.d.ts.map +1 -1
- package/dist/auth/oauth-flow.js +6 -3
- package/dist/auth/oauth-flow.js.map +1 -1
- package/dist/auth/refresh.d.ts +2 -1
- package/dist/auth/refresh.d.ts.map +1 -1
- package/dist/auth/refresh.js +11 -3
- package/dist/auth/refresh.js.map +1 -1
- package/dist/auth/token-store.d.ts +6 -4
- package/dist/auth/token-store.d.ts.map +1 -1
- package/dist/auth/token-store.js +17 -14
- package/dist/auth/token-store.js.map +1 -1
- package/dist/auth/types.d.ts +7 -4
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/auth/types.js.map +1 -1
- package/dist/cli.js +8 -6
- package/dist/cli.js.map +1 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +2 -1
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +12 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/list-tokens.d.ts +3 -3
- package/dist/commands/list-tokens.d.ts.map +1 -1
- package/dist/commands/list-tokens.js +20 -8
- package/dist/commands/list-tokens.js.map +1 -1
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +12 -4
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logout.d.ts +5 -4
- package/dist/commands/logout.d.ts.map +1 -1
- package/dist/commands/logout.js +55 -12
- package/dist/commands/logout.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +19 -4
- package/dist/constants.js.map +1 -1
- package/dist/db-content-api/generated/content-api-types.d.ts +166 -277
- package/dist/db-content-api/generated/content-api-types.d.ts.map +1 -1
- package/dist/db-content-api/generated/content-api-types.js.map +1 -1
- package/dist/db-content-api/index.d.ts.map +1 -1
- package/dist/db-content-api/index.js +116 -64
- package/dist/db-content-api/index.js.map +1 -1
- package/dist/db-content-api/temp-utilities/unwrapDocument.d.ts +31 -1
- package/dist/db-content-api/temp-utilities/unwrapDocument.d.ts.map +1 -1
- package/dist/db-content-api/temp-utilities/unwrapDocument.js +24 -24
- package/dist/db-content-api/temp-utilities/unwrapDocument.js.map +1 -1
- package/dist/db-content-api/utilities/data/castFieldValue.d.ts.map +1 -1
- package/dist/db-content-api/utilities/data/castFieldValue.js +4 -1
- package/dist/db-content-api/utilities/data/castFieldValue.js.map +1 -1
- package/dist/db-content-api/utilities/data/index.d.ts +2 -0
- package/dist/db-content-api/utilities/data/index.d.ts.map +1 -1
- package/dist/db-content-api/utilities/data/index.js +23 -2
- package/dist/db-content-api/utilities/data/index.js.map +1 -1
- package/dist/db-content-api/utilities/meta/buildLocalizedPaths.d.ts +11 -0
- package/dist/db-content-api/utilities/meta/buildLocalizedPaths.d.ts.map +1 -0
- package/dist/db-content-api/utilities/meta/buildLocalizedPaths.js +55 -0
- package/dist/db-content-api/utilities/meta/buildLocalizedPaths.js.map +1 -0
- package/dist/db-content-api/utilities/meta/buildMeta.d.ts +22 -0
- package/dist/db-content-api/utilities/meta/buildMeta.d.ts.map +1 -0
- package/dist/db-content-api/utilities/meta/buildMeta.js +30 -0
- package/dist/db-content-api/utilities/meta/buildMeta.js.map +1 -0
- package/dist/db-content-api/utilities/meta/buildPathTypes.d.ts +7 -0
- package/dist/db-content-api/utilities/meta/buildPathTypes.d.ts.map +1 -0
- package/dist/db-content-api/utilities/meta/buildPathTypes.js +151 -0
- package/dist/db-content-api/utilities/meta/buildPathTypes.js.map +1 -0
- package/dist/db-content-api/utilities/meta/index.d.ts +5 -0
- package/dist/db-content-api/utilities/meta/index.d.ts.map +1 -0
- package/dist/db-content-api/utilities/meta/index.js +5 -0
- package/dist/db-content-api/utilities/meta/index.js.map +1 -0
- package/dist/db-content-api/utilities/where.d.ts.map +1 -1
- package/dist/db-content-api/utilities/where.js +17 -3
- package/dist/db-content-api/utilities/where.js.map +1 -1
- package/dist/oauth/components/LogoutButton/index.d.ts.map +1 -1
- package/dist/oauth/components/LogoutButton/index.js +5 -3
- package/dist/oauth/components/LogoutButton/index.js.map +1 -1
- package/dist/oauth/endpoints/getLoginEndpoint.d.ts.map +1 -1
- package/dist/oauth/endpoints/getLoginEndpoint.js +17 -7
- package/dist/oauth/endpoints/getLoginEndpoint.js.map +1 -1
- package/dist/oauth/endpoints/getLogoutEndpoint.d.ts.map +1 -1
- package/dist/oauth/endpoints/getLogoutEndpoint.js +7 -4
- package/dist/oauth/endpoints/getLogoutEndpoint.js.map +1 -1
- package/dist/plugin/build-config.js +3 -3
- package/dist/plugin/build-config.js.map +1 -1
- package/dist/storage-content-api/api-types.d.ts +8 -1
- package/dist/storage-content-api/api-types.d.ts.map +1 -1
- package/dist/storage-content-api/api-types.js.map +1 -1
- package/dist/storage-content-api/client-uploads/ClientUploadHandler.d.ts.map +1 -1
- package/dist/storage-content-api/client-uploads/ClientUploadHandler.js +3 -3
- package/dist/storage-content-api/client-uploads/ClientUploadHandler.js.map +1 -1
- package/dist/storage-content-api/client-uploads/generateSignedURL.d.ts.map +1 -1
- package/dist/storage-content-api/client-uploads/generateSignedURL.js +9 -1
- package/dist/storage-content-api/client-uploads/generateSignedURL.js.map +1 -1
- package/dist/storage-content-api/generateURL.d.ts.map +1 -1
- package/dist/storage-content-api/generateURL.js +3 -2
- package/dist/storage-content-api/generateURL.js.map +1 -1
- package/dist/storage-content-api/handleDelete.d.ts.map +1 -1
- package/dist/storage-content-api/handleDelete.js +4 -2
- package/dist/storage-content-api/handleDelete.js.map +1 -1
- package/dist/storage-content-api/handleUpload.d.ts +1 -1
- package/dist/storage-content-api/handleUpload.d.ts.map +1 -1
- package/dist/storage-content-api/handleUpload.js +11 -3
- package/dist/storage-content-api/handleUpload.js.map +1 -1
- package/dist/storage-content-api/staticHandler.js +1 -0
- package/dist/storage-content-api/staticHandler.js.map +1 -1
- package/dist/storage-content-api/utilities/getSafeFilename.d.ts +12 -0
- package/dist/storage-content-api/utilities/getSafeFilename.d.ts.map +1 -0
- package/dist/storage-content-api/utilities/getSafeFilename.js +61 -0
- package/dist/storage-content-api/utilities/getSafeFilename.js.map +1 -0
- package/dist/storage-content-api/utilities/index.d.ts +2 -0
- package/dist/storage-content-api/utilities/index.d.ts.map +1 -0
- package/dist/storage-content-api/utilities/index.js +3 -0
- package/dist/storage-content-api/utilities/index.js.map +1 -0
- package/dist/templates/.env.example +11 -0
- package/dist/utils/env-management.d.ts +9 -1
- package/dist/utils/env-management.d.ts.map +1 -1
- package/dist/utils/env-management.js +39 -17
- package/dist/utils/env-management.js.map +1 -1
- package/dist/utils/format-env-suffix.d.ts +12 -0
- package/dist/utils/format-env-suffix.d.ts.map +1 -0
- package/dist/utils/format-env-suffix.js +13 -0
- package/dist/utils/format-env-suffix.js.map +1 -0
- package/dist/utils/lambda-config.js +26 -12
- package/dist/utils/lambda-config.js.map +1 -1
- package/dist/utils/messages.d.ts.map +1 -1
- package/dist/utils/messages.js +1 -2
- package/dist/utils/messages.js.map +1 -1
- package/dist/utils/project.d.ts.map +1 -1
- package/dist/utils/project.js +3 -0
- package/dist/utils/project.js.map +1 -1
- package/package.json +2 -2
- package/dist/db-content-api/temp-utilities/pagination.d.ts +0 -13
- package/dist/db-content-api/temp-utilities/pagination.d.ts.map +0 -1
- package/dist/db-content-api/temp-utilities/pagination.js +0 -45
- package/dist/db-content-api/temp-utilities/pagination.js.map +0 -1
- package/dist/oauth/utilities/getAdminPath.d.ts +0 -8
- package/dist/oauth/utilities/getAdminPath.d.ts.map +0 -1
- package/dist/oauth/utilities/getAdminPath.js +0 -9
- package/dist/oauth/utilities/getAdminPath.js.map +0 -1
|
@@ -19,7 +19,8 @@ export const ContentApiClientUploadHandler = createClientUploadHandler({
|
|
|
19
19
|
if (!response.ok) {
|
|
20
20
|
throw new Error(`Failed to get presigned URL: ${response.status} ${response.statusText}`);
|
|
21
21
|
}
|
|
22
|
-
const { url } = await response.json();
|
|
22
|
+
const { filename: safeFilename, url } = await response.json();
|
|
23
|
+
const uploadFilename = safeFilename || file.name;
|
|
23
24
|
const uploadResponse = await fetch(url, {
|
|
24
25
|
body: file,
|
|
25
26
|
headers: {
|
|
@@ -30,10 +31,9 @@ export const ContentApiClientUploadHandler = createClientUploadHandler({
|
|
|
30
31
|
if (!uploadResponse.ok) {
|
|
31
32
|
throw new Error(`Failed to upload to S3: ${uploadResponse.status} ${uploadResponse.statusText}`);
|
|
32
33
|
}
|
|
33
|
-
// Include original filename so handleUpload can distinguish from resized versions
|
|
34
34
|
return {
|
|
35
35
|
clientUploaded: true,
|
|
36
|
-
filename:
|
|
36
|
+
filename: uploadFilename,
|
|
37
37
|
prefix
|
|
38
38
|
};
|
|
39
39
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/storage-content-api/client-uploads/ClientUploadHandler.tsx"],"sourcesContent":["'use client'\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\n\nexport const ContentApiClientUploadHandler = createClientUploadHandler({\n handler: async ({ apiRoute, collectionSlug, file, prefix, serverHandlerPath, serverURL }) => {\n // Build endpoint URL: serverURL + apiRoute + serverHandlerPath\n const endpointRoute = `${serverURL}${apiRoute}${serverHandlerPath}`\n\n const response = await fetch(endpointRoute, {\n body: JSON.stringify({\n collectionSlug,\n filename: file.name,\n mimeType: file.type,\n }),\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n method: 'POST',\n })\n\n if (!response.ok) {\n throw new Error(`Failed to get presigned URL: ${response.status} ${response.statusText}`)\n }\n\n const { url } = (await response.json()) as { url: string
|
|
1
|
+
{"version":3,"sources":["../../../src/storage-content-api/client-uploads/ClientUploadHandler.tsx"],"sourcesContent":["'use client'\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\n\nexport const ContentApiClientUploadHandler = createClientUploadHandler({\n handler: async ({ apiRoute, collectionSlug, file, prefix, serverHandlerPath, serverURL }) => {\n // Build endpoint URL: serverURL + apiRoute + serverHandlerPath\n const endpointRoute = `${serverURL}${apiRoute}${serverHandlerPath}`\n\n const response = await fetch(endpointRoute, {\n body: JSON.stringify({\n collectionSlug,\n filename: file.name,\n mimeType: file.type,\n }),\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n method: 'POST',\n })\n\n if (!response.ok) {\n throw new Error(`Failed to get presigned URL: ${response.status} ${response.statusText}`)\n }\n\n const { filename: safeFilename, url } = (await response.json()) as {\n filename?: string\n url: string\n }\n\n const uploadFilename = safeFilename || file.name\n\n const uploadResponse = await fetch(url, {\n body: file,\n headers: { 'Content-Type': file.type },\n method: 'PUT',\n })\n\n if (!uploadResponse.ok) {\n throw new Error(\n `Failed to upload to S3: ${uploadResponse.status} ${uploadResponse.statusText}`,\n )\n }\n\n return { clientUploaded: true, filename: uploadFilename, prefix }\n },\n})\n"],"names":["createClientUploadHandler","ContentApiClientUploadHandler","handler","apiRoute","collectionSlug","file","prefix","serverHandlerPath","serverURL","endpointRoute","response","fetch","body","JSON","stringify","filename","name","mimeType","type","credentials","headers","method","ok","Error","status","statusText","safeFilename","url","json","uploadFilename","uploadResponse","clientUploaded"],"mappings":"AAAA;AACA,SAASA,yBAAyB,QAAQ,0CAAyC;AAEnF,OAAO,MAAMC,gCAAgCD,0BAA0B;IACrEE,SAAS,OAAO,EAAEC,QAAQ,EAAEC,cAAc,EAAEC,IAAI,EAAEC,MAAM,EAAEC,iBAAiB,EAAEC,SAAS,EAAE;QACtF,+DAA+D;QAC/D,MAAMC,gBAAgB,GAAGD,YAAYL,WAAWI,mBAAmB;QAEnE,MAAMG,WAAW,MAAMC,MAAMF,eAAe;YAC1CG,MAAMC,KAAKC,SAAS,CAAC;gBACnBV;gBACAW,UAAUV,KAAKW,IAAI;gBACnBC,UAAUZ,KAAKa,IAAI;YACrB;YACAC,aAAa;YACbC,SAAS;gBAAE,gBAAgB;YAAmB;YAC9CC,QAAQ;QACV;QAEA,IAAI,CAACX,SAASY,EAAE,EAAE;YAChB,MAAM,IAAIC,MAAM,CAAC,6BAA6B,EAAEb,SAASc,MAAM,CAAC,CAAC,EAAEd,SAASe,UAAU,EAAE;QAC1F;QAEA,MAAM,EAAEV,UAAUW,YAAY,EAAEC,GAAG,EAAE,GAAI,MAAMjB,SAASkB,IAAI;QAK5D,MAAMC,iBAAiBH,gBAAgBrB,KAAKW,IAAI;QAEhD,MAAMc,iBAAiB,MAAMnB,MAAMgB,KAAK;YACtCf,MAAMP;YACNe,SAAS;gBAAE,gBAAgBf,KAAKa,IAAI;YAAC;YACrCG,QAAQ;QACV;QAEA,IAAI,CAACS,eAAeR,EAAE,EAAE;YACtB,MAAM,IAAIC,MACR,CAAC,wBAAwB,EAAEO,eAAeN,MAAM,CAAC,CAAC,EAAEM,eAAeL,UAAU,EAAE;QAEnF;QAEA,OAAO;YAAEM,gBAAgB;YAAMhB,UAAUc;YAAgBvB;QAAO;IAClE;AACF,GAAE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateSignedURL.d.ts","sourceRoot":"","sources":["../../../src/storage-content-api/client-uploads/generateSignedURL.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAI7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"generateSignedURL.d.ts","sourceRoot":"","sources":["../../../src/storage-content-api/client-uploads/generateSignedURL.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAI7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAIjD,KAAK,mBAAmB,GAAG,CAAC,IAAI,EAAE;IAChC,cAAc,EAAE,MAAM,CAAA;IACtB,GAAG,EAAE,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAA;CACnC,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;AAEhC,KAAK,IAAI,GAAG;IACV,MAAM,CAAC,EAAE,mBAAmB,CAAA;IAC5B,MAAM,EAAE,aAAa,CAAA;CACtB,CAAA;AAID,eAAO,MAAM,2BAA2B,wBAGrC,IAAI,KAAG,cAqCT,CAAA"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { APIError, Forbidden } from 'payload';
|
|
2
|
+
import { getSafeFilename } from '../utilities/index.js';
|
|
2
3
|
const defaultAccess = ({ req })=>!!req.user;
|
|
3
4
|
export const getGenerateSignedURLHandler = ({ access = defaultAccess, client })=>{
|
|
4
5
|
return async (req)=>{
|
|
@@ -12,17 +13,24 @@ export const getGenerateSignedURLHandler = ({ access = defaultAccess, client })=
|
|
|
12
13
|
})) {
|
|
13
14
|
throw new Forbidden();
|
|
14
15
|
}
|
|
16
|
+
const safeFilename = await getSafeFilename({
|
|
17
|
+
collectionSlug,
|
|
18
|
+
desiredFilename: filename,
|
|
19
|
+
req
|
|
20
|
+
});
|
|
15
21
|
const { data, error } = await client.POST('/api/v0/uploads/sign', {
|
|
16
22
|
body: {
|
|
23
|
+
collectionId: collectionSlug,
|
|
17
24
|
contentType: mimeType,
|
|
18
25
|
expiresIn: 3600,
|
|
19
|
-
filename
|
|
26
|
+
filename: safeFilename
|
|
20
27
|
}
|
|
21
28
|
});
|
|
22
29
|
if (error || !data) {
|
|
23
30
|
throw new APIError(`Failed to get presigned upload URL: ${JSON.stringify(error)}`);
|
|
24
31
|
}
|
|
25
32
|
return Response.json({
|
|
33
|
+
filename: safeFilename,
|
|
26
34
|
url: data.url
|
|
27
35
|
});
|
|
28
36
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/storage-content-api/client-uploads/generateSignedURL.ts"],"sourcesContent":["import type { PayloadHandler } from 'payload'\n\nimport { APIError, Forbidden } from 'payload'\n\nimport type { StorageClient } from '../client.js'\n\ntype ClientUploadsAccess = (args: {\n collectionSlug: string\n req: Parameters<PayloadHandler>[0]\n}) => boolean | Promise<boolean>\n\ntype Args = {\n access?: ClientUploadsAccess\n client: StorageClient\n}\n\nconst defaultAccess: ClientUploadsAccess = ({ req }) => !!req.user\n\nexport const getGenerateSignedURLHandler = ({\n access = defaultAccess,\n client,\n}: Args): PayloadHandler => {\n return async (req) => {\n if (!req.json) {\n throw new APIError('Content-Type expected to be application/json', 400)\n }\n\n const { collectionSlug, filename, mimeType } = (await req.json()) as {\n collectionSlug: string\n filename: string\n mimeType: string\n }\n\n if (!(await access({ collectionSlug, req }))) {\n throw new Forbidden()\n }\n\n const { data, error } = await client.POST('/api/v0/uploads/sign', {\n body: { contentType: mimeType
|
|
1
|
+
{"version":3,"sources":["../../../src/storage-content-api/client-uploads/generateSignedURL.ts"],"sourcesContent":["import type { PayloadHandler } from 'payload'\n\nimport { APIError, Forbidden } from 'payload'\n\nimport type { StorageClient } from '../client.js'\n\nimport { getSafeFilename } from '../utilities/index.js'\n\ntype ClientUploadsAccess = (args: {\n collectionSlug: string\n req: Parameters<PayloadHandler>[0]\n}) => boolean | Promise<boolean>\n\ntype Args = {\n access?: ClientUploadsAccess\n client: StorageClient\n}\n\nconst defaultAccess: ClientUploadsAccess = ({ req }) => !!req.user\n\nexport const getGenerateSignedURLHandler = ({\n access = defaultAccess,\n client,\n}: Args): PayloadHandler => {\n return async (req) => {\n if (!req.json) {\n throw new APIError('Content-Type expected to be application/json', 400)\n }\n\n const { collectionSlug, filename, mimeType } = (await req.json()) as {\n collectionSlug: string\n filename: string\n mimeType: string\n }\n\n if (!(await access({ collectionSlug, req }))) {\n throw new Forbidden()\n }\n\n const safeFilename = await getSafeFilename({\n collectionSlug,\n desiredFilename: filename,\n req,\n })\n\n const { data, error } = await client.POST('/api/v0/uploads/sign', {\n body: {\n collectionId: collectionSlug,\n contentType: mimeType,\n expiresIn: 3600,\n filename: safeFilename,\n },\n })\n\n if (error || !data) {\n throw new APIError(`Failed to get presigned upload URL: ${JSON.stringify(error)}`)\n }\n\n return Response.json({ filename: safeFilename, url: data.url })\n }\n}\n"],"names":["APIError","Forbidden","getSafeFilename","defaultAccess","req","user","getGenerateSignedURLHandler","access","client","json","collectionSlug","filename","mimeType","safeFilename","desiredFilename","data","error","POST","body","collectionId","contentType","expiresIn","JSON","stringify","Response","url"],"mappings":"AAEA,SAASA,QAAQ,EAAEC,SAAS,QAAQ,UAAS;AAI7C,SAASC,eAAe,QAAQ,wBAAuB;AAYvD,MAAMC,gBAAqC,CAAC,EAAEC,GAAG,EAAE,GAAK,CAAC,CAACA,IAAIC,IAAI;AAElE,OAAO,MAAMC,8BAA8B,CAAC,EAC1CC,SAASJ,aAAa,EACtBK,MAAM,EACD;IACL,OAAO,OAAOJ;QACZ,IAAI,CAACA,IAAIK,IAAI,EAAE;YACb,MAAM,IAAIT,SAAS,gDAAgD;QACrE;QAEA,MAAM,EAAEU,cAAc,EAAEC,QAAQ,EAAEC,QAAQ,EAAE,GAAI,MAAMR,IAAIK,IAAI;QAM9D,IAAI,CAAE,MAAMF,OAAO;YAAEG;YAAgBN;QAAI,IAAK;YAC5C,MAAM,IAAIH;QACZ;QAEA,MAAMY,eAAe,MAAMX,gBAAgB;YACzCQ;YACAI,iBAAiBH;YACjBP;QACF;QAEA,MAAM,EAAEW,IAAI,EAAEC,KAAK,EAAE,GAAG,MAAMR,OAAOS,IAAI,CAAC,wBAAwB;YAChEC,MAAM;gBACJC,cAAcT;gBACdU,aAAaR;gBACbS,WAAW;gBACXV,UAAUE;YACZ;QACF;QAEA,IAAIG,SAAS,CAACD,MAAM;YAClB,MAAM,IAAIf,SAAS,CAAC,oCAAoC,EAAEsB,KAAKC,SAAS,CAACP,QAAQ;QACnF;QAEA,OAAOQ,SAASf,IAAI,CAAC;YAAEE,UAAUE;YAAcY,KAAKV,KAAKU,GAAG;QAAC;IAC/D;AACF,EAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateURL.d.ts","sourceRoot":"","sources":["../../src/storage-content-api/generateURL.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAIzE,KAAK,IAAI,GAAG;IACV,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,eAAO,MAAM,cAAc,gBACX,IAAI,KAAG,
|
|
1
|
+
{"version":3,"file":"generateURL.d.ts","sourceRoot":"","sources":["../../src/storage-content-api/generateURL.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAIzE,KAAK,IAAI,GAAG;IACV,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,eAAO,MAAM,cAAc,gBACX,IAAI,KAAG,WAKpB,CAAA"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
-
export const getGenerateURL = ({ baseUrl })=>({ filename, prefix = '' })=>{
|
|
2
|
+
export const getGenerateURL = ({ baseUrl })=>({ collection, filename, prefix = '' })=>{
|
|
3
|
+
const collectionId = collection.slug;
|
|
3
4
|
const fileKey = path.posix.join(prefix, filename);
|
|
4
|
-
return `${baseUrl}/api/v0/uploads/download/${encodeURIComponent(fileKey)}`;
|
|
5
|
+
return `${baseUrl}/api/v0/uploads/download/${collectionId}/${encodeURIComponent(fileKey)}`;
|
|
5
6
|
};
|
|
6
7
|
|
|
7
8
|
//# sourceMappingURL=generateURL.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/storage-content-api/generateURL.ts"],"sourcesContent":["import type { GenerateURL } from '@payloadcms/plugin-cloud-storage/types'\n\nimport path from 'path'\n\ntype Args = {\n baseUrl: string\n}\n\nexport const getGenerateURL =\n ({ baseUrl }: Args): GenerateURL =>\n ({ filename, prefix = '' }) => {\n const fileKey = path.posix.join(prefix, filename)\n return `${baseUrl}/api/v0/uploads/download/${encodeURIComponent(fileKey)}`\n }\n"],"names":["path","getGenerateURL","baseUrl","filename","prefix","fileKey","posix","join","encodeURIComponent"],"mappings":"AAEA,OAAOA,UAAU,OAAM;AAMvB,OAAO,MAAMC,iBACX,CAAC,EAAEC,OAAO,EAAQ,GAClB,CAAC,EAAEC,QAAQ,EAAEC,SAAS,EAAE,EAAE;
|
|
1
|
+
{"version":3,"sources":["../../src/storage-content-api/generateURL.ts"],"sourcesContent":["import type { GenerateURL } from '@payloadcms/plugin-cloud-storage/types'\n\nimport path from 'path'\n\ntype Args = {\n baseUrl: string\n}\n\nexport const getGenerateURL =\n ({ baseUrl }: Args): GenerateURL =>\n ({ collection, filename, prefix = '' }) => {\n const collectionId = collection.slug\n const fileKey = path.posix.join(prefix, filename)\n return `${baseUrl}/api/v0/uploads/download/${collectionId}/${encodeURIComponent(fileKey)}`\n }\n"],"names":["path","getGenerateURL","baseUrl","collection","filename","prefix","collectionId","slug","fileKey","posix","join","encodeURIComponent"],"mappings":"AAEA,OAAOA,UAAU,OAAM;AAMvB,OAAO,MAAMC,iBACX,CAAC,EAAEC,OAAO,EAAQ,GAClB,CAAC,EAAEC,UAAU,EAAEC,QAAQ,EAAEC,SAAS,EAAE,EAAE;QACpC,MAAMC,eAAeH,WAAWI,IAAI;QACpC,MAAMC,UAAUR,KAAKS,KAAK,CAACC,IAAI,CAACL,QAAQD;QACxC,OAAO,GAAGF,QAAQ,yBAAyB,EAAEI,aAAa,CAAC,EAAEK,mBAAmBH,UAAU;IAC5F,EAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handleDelete.d.ts","sourceRoot":"","sources":["../../src/storage-content-api/handleDelete.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAA;AAI1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD,KAAK,IAAI,GAAG;IACV,MAAM,EAAE,aAAa,CAAA;CACtB,CAAA;AAED,eAAO,MAAM,eAAe,eAAgB,IAAI,KAAG,
|
|
1
|
+
{"version":3,"file":"handleDelete.d.ts","sourceRoot":"","sources":["../../src/storage-content-api/handleDelete.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAA;AAI1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD,KAAK,IAAI,GAAG;IACV,MAAM,EAAE,aAAa,CAAA;CACtB,CAAA;AAED,eAAO,MAAM,eAAe,eAAgB,IAAI,KAAG,YAUlD,CAAA"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
export const getHandleDelete = ({ client })=>{
|
|
3
|
-
return async ({ doc: { prefix = '' }, filename })=>{
|
|
3
|
+
return async ({ collection, doc: { prefix = '' }, filename })=>{
|
|
4
|
+
const collectionId = collection.slug;
|
|
4
5
|
const fileKey = path.posix.join(prefix, filename);
|
|
5
|
-
await client.DELETE('/api/v0/uploads/{filename}', {
|
|
6
|
+
await client.DELETE('/api/v0/uploads/{collectionId}/{filename}', {
|
|
6
7
|
params: {
|
|
7
8
|
path: {
|
|
9
|
+
collectionId,
|
|
8
10
|
filename: fileKey
|
|
9
11
|
}
|
|
10
12
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/storage-content-api/handleDelete.ts"],"sourcesContent":["import type { HandleDelete } from '@payloadcms/plugin-cloud-storage/types'\n\nimport path from 'path'\n\nimport type { StorageClient } from './client.js'\n\ntype Args = {\n client: StorageClient\n}\n\nexport const getHandleDelete = ({ client }: Args): HandleDelete => {\n return async ({ doc: { prefix = '' }, filename }) => {\n const fileKey = path.posix.join(prefix, filename)\n await client.DELETE('/api/v0/uploads/{filename}', {\n params: {
|
|
1
|
+
{"version":3,"sources":["../../src/storage-content-api/handleDelete.ts"],"sourcesContent":["import type { HandleDelete } from '@payloadcms/plugin-cloud-storage/types'\n\nimport path from 'path'\n\nimport type { StorageClient } from './client.js'\n\ntype Args = {\n client: StorageClient\n}\n\nexport const getHandleDelete = ({ client }: Args): HandleDelete => {\n return async ({ collection, doc: { prefix = '' }, filename }) => {\n const collectionId = collection.slug\n const fileKey = path.posix.join(prefix, filename)\n await client.DELETE('/api/v0/uploads/{collectionId}/{filename}', {\n params: {\n path: { collectionId, filename: fileKey },\n },\n })\n }\n}\n"],"names":["path","getHandleDelete","client","collection","doc","prefix","filename","collectionId","slug","fileKey","posix","join","DELETE","params"],"mappings":"AAEA,OAAOA,UAAU,OAAM;AAQvB,OAAO,MAAMC,kBAAkB,CAAC,EAAEC,MAAM,EAAQ;IAC9C,OAAO,OAAO,EAAEC,UAAU,EAAEC,KAAK,EAAEC,SAAS,EAAE,EAAE,EAAEC,QAAQ,EAAE;QAC1D,MAAMC,eAAeJ,WAAWK,IAAI;QACpC,MAAMC,UAAUT,KAAKU,KAAK,CAACC,IAAI,CAACN,QAAQC;QACxC,MAAMJ,OAAOU,MAAM,CAAC,6CAA6C;YAC/DC,QAAQ;gBACNb,MAAM;oBAAEO;oBAAcD,UAAUG;gBAAQ;YAC1C;QACF;IACF;AACF,EAAC"}
|
|
@@ -6,6 +6,6 @@ type Args = {
|
|
|
6
6
|
collection: CollectionConfig;
|
|
7
7
|
prefix?: string;
|
|
8
8
|
};
|
|
9
|
-
export declare const getHandleUpload: ({ client, prefix }: Args) => HandleUpload;
|
|
9
|
+
export declare const getHandleUpload: ({ client, collection, prefix }: Args) => HandleUpload;
|
|
10
10
|
export {};
|
|
11
11
|
//# sourceMappingURL=handleUpload.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handleUpload.d.ts","sourceRoot":"","sources":["../../src/storage-content-api/handleUpload.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAA;AAC1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAK/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD,KAAK,IAAI,GAAG;IACV,MAAM,EAAE,aAAa,CAAA;IACrB,UAAU,EAAE,gBAAgB,CAAA;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAKD,eAAO,MAAM,eAAe,
|
|
1
|
+
{"version":3,"file":"handleUpload.d.ts","sourceRoot":"","sources":["../../src/storage-content-api/handleUpload.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAA;AAC1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAK/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD,KAAK,IAAI,GAAG;IACV,MAAM,EAAE,aAAa,CAAA;IACrB,UAAU,EAAE,gBAAgB,CAAA;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAKD,eAAO,MAAM,eAAe,mCAAyC,IAAI,KAAG,YAoC3E,CAAA"}
|
|
@@ -4,7 +4,7 @@ const PART_SIZE = 64 * 1024 * 1024 // 64MB per part
|
|
|
4
4
|
;
|
|
5
5
|
const MAX_FILE_SIZE_BEFORE_MULTIPART = 100 * 1024 * 1024 // 100MB
|
|
6
6
|
;
|
|
7
|
-
export const getHandleUpload = ({ client, prefix = '' })=>{
|
|
7
|
+
export const getHandleUpload = ({ client, collection, prefix = '' })=>{
|
|
8
8
|
return async ({ clientUploadContext, data, file })=>{
|
|
9
9
|
// Skip if client already uploaded THIS SPECIFIC file
|
|
10
10
|
// clientUploadContext.filename contains the original filename that was client-uploaded
|
|
@@ -14,12 +14,14 @@ export const getHandleUpload = ({ client, prefix = '' })=>{
|
|
|
14
14
|
if (isOriginalClientUploaded) {
|
|
15
15
|
return data;
|
|
16
16
|
}
|
|
17
|
+
const collectionId = collection.slug;
|
|
17
18
|
const fileKey = path.posix.join(data.prefix || prefix, file.filename);
|
|
18
19
|
const fileData = file.tempFilePath ? fs.readFileSync(file.tempFilePath) : file.buffer;
|
|
19
20
|
const fileSize = file.filesize || fileData.length;
|
|
20
21
|
if (fileSize > MAX_FILE_SIZE_BEFORE_MULTIPART) {
|
|
21
22
|
await uploadMultipart({
|
|
22
23
|
client,
|
|
24
|
+
collectionId,
|
|
23
25
|
fileData,
|
|
24
26
|
fileKey,
|
|
25
27
|
mimeType: file.mimeType
|
|
@@ -27,6 +29,7 @@ export const getHandleUpload = ({ client, prefix = '' })=>{
|
|
|
27
29
|
} else {
|
|
28
30
|
await uploadSingleFile({
|
|
29
31
|
client,
|
|
32
|
+
collectionId,
|
|
30
33
|
fileData,
|
|
31
34
|
fileKey,
|
|
32
35
|
mimeType: file.mimeType
|
|
@@ -35,9 +38,10 @@ export const getHandleUpload = ({ client, prefix = '' })=>{
|
|
|
35
38
|
return data;
|
|
36
39
|
};
|
|
37
40
|
};
|
|
38
|
-
async function uploadSingleFile({ client, fileData, fileKey, mimeType }) {
|
|
41
|
+
async function uploadSingleFile({ client, collectionId, fileData, fileKey, mimeType }) {
|
|
39
42
|
const { data, error } = await client.POST('/api/v0/uploads/sign', {
|
|
40
43
|
body: {
|
|
44
|
+
collectionId,
|
|
41
45
|
contentType: mimeType,
|
|
42
46
|
expiresIn: 3600,
|
|
43
47
|
filename: fileKey
|
|
@@ -57,9 +61,10 @@ async function uploadSingleFile({ client, fileData, fileKey, mimeType }) {
|
|
|
57
61
|
throw new Error(`Failed to upload to S3: ${uploadResponse.status} ${uploadResponse.statusText}`);
|
|
58
62
|
}
|
|
59
63
|
}
|
|
60
|
-
async function uploadMultipart({ client, fileData, fileKey, mimeType }) {
|
|
64
|
+
async function uploadMultipart({ client, collectionId, fileData, fileKey, mimeType }) {
|
|
61
65
|
const { data: initiateData, error: initiateError } = await client.POST('/api/v0/uploads/multipart/initiate', {
|
|
62
66
|
body: {
|
|
67
|
+
collectionId,
|
|
63
68
|
contentType: mimeType,
|
|
64
69
|
filename: fileKey
|
|
65
70
|
}
|
|
@@ -75,6 +80,7 @@ async function uploadMultipart({ client, fileData, fileKey, mimeType }) {
|
|
|
75
80
|
}, (_, i)=>i + 1);
|
|
76
81
|
const { data: partsData, error: partsError } = await client.POST('/api/v0/uploads/multipart/parts', {
|
|
77
82
|
body: {
|
|
83
|
+
collectionId,
|
|
78
84
|
expiresIn: 3600,
|
|
79
85
|
filename: fileKey,
|
|
80
86
|
partNumbers,
|
|
@@ -110,6 +116,7 @@ async function uploadMultipart({ client, fileData, fileKey, mimeType }) {
|
|
|
110
116
|
}));
|
|
111
117
|
const { error: completeError } = await client.POST('/api/v0/uploads/multipart/complete', {
|
|
112
118
|
body: {
|
|
119
|
+
collectionId,
|
|
113
120
|
filename: fileKey,
|
|
114
121
|
parts: uploadedParts,
|
|
115
122
|
uploadId
|
|
@@ -122,6 +129,7 @@ async function uploadMultipart({ client, fileData, fileKey, mimeType }) {
|
|
|
122
129
|
try {
|
|
123
130
|
await client.POST('/api/v0/uploads/multipart/abort', {
|
|
124
131
|
body: {
|
|
132
|
+
collectionId,
|
|
125
133
|
filename: fileKey,
|
|
126
134
|
uploadId
|
|
127
135
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/storage-content-api/handleUpload.ts"],"sourcesContent":["import type { HandleUpload } from '@payloadcms/plugin-cloud-storage/types'\nimport type { CollectionConfig } from 'payload'\n\nimport fs from 'fs'\nimport path from 'path'\n\nimport type { StorageClient } from './client.js'\n\ntype Args = {\n client: StorageClient\n collection: CollectionConfig\n prefix?: string\n}\n\nconst PART_SIZE = 64 * 1024 * 1024 // 64MB per part\nconst MAX_FILE_SIZE_BEFORE_MULTIPART = 100 * 1024 * 1024 // 100MB\n\nexport const getHandleUpload = ({ client, prefix = '' }: Args): HandleUpload => {\n return async ({ clientUploadContext, data, file }) => {\n // Skip if client already uploaded THIS SPECIFIC file\n // clientUploadContext.filename contains the original filename that was client-uploaded\n // Resized versions (different filename) should still be uploaded\n const clientUploadedFilename =\n clientUploadContext &&\n typeof clientUploadContext === 'object' &&\n 'filename' in clientUploadContext\n ? clientUploadContext.filename\n : undefined\n\n const isOriginalClientUploaded =\n clientUploadContext &&\n typeof clientUploadContext === 'object' &&\n 'clientUploaded' in clientUploadContext &&\n clientUploadContext.clientUploaded &&\n file.filename === clientUploadedFilename\n\n if (isOriginalClientUploaded) {\n return data\n }\n\n const fileKey = path.posix.join(data.prefix || prefix, file.filename)\n const fileData = file.tempFilePath ? fs.readFileSync(file.tempFilePath) : file.buffer\n const fileSize = file.filesize || fileData.length\n\n if (fileSize > MAX_FILE_SIZE_BEFORE_MULTIPART) {\n await uploadMultipart({ client, fileData, fileKey, mimeType: file.mimeType })\n } else {\n await uploadSingleFile({ client, fileData, fileKey, mimeType: file.mimeType })\n }\n\n return data\n }\n}\n\nasync function uploadSingleFile({\n client,\n fileData,\n fileKey,\n mimeType,\n}: {\n client: StorageClient\n fileData: Buffer\n fileKey: string\n mimeType: string\n}): Promise<void> {\n const { data, error } = await client.POST('/api/v0/uploads/sign', {\n body: { contentType: mimeType, expiresIn: 3600, filename: fileKey },\n })\n\n if (error || !data) {\n throw new Error(`Failed to get presigned upload URL: ${JSON.stringify(error)}`)\n }\n\n const uploadResponse = await fetch(data.url, {\n body: fileData,\n headers: { 'Content-Type': mimeType },\n method: 'PUT',\n })\n\n if (!uploadResponse.ok) {\n throw new Error(`Failed to upload to S3: ${uploadResponse.status} ${uploadResponse.statusText}`)\n }\n}\n\nasync function uploadMultipart({\n client,\n fileData,\n fileKey,\n mimeType,\n}: {\n client: StorageClient\n fileData: Buffer\n fileKey: string\n mimeType: string\n}): Promise<void> {\n const { data: initiateData, error: initiateError } = await client.POST(\n '/api/v0/uploads/multipart/initiate',\n {\n body: { contentType: mimeType, filename: fileKey },\n },\n )\n\n if (initiateError || !initiateData) {\n throw new Error(`Failed to initiate multipart upload: ${JSON.stringify(initiateError)}`)\n }\n\n const { uploadId } = initiateData\n\n try {\n const numParts = Math.ceil(fileData.length / PART_SIZE)\n const partNumbers = Array.from({ length: numParts }, (_, i) => i + 1)\n\n const { data: partsData, error: partsError } = await client.POST(\n '/api/v0/uploads/multipart/parts',\n {\n body: { expiresIn: 3600, filename: fileKey, partNumbers, uploadId },\n },\n )\n\n if (partsError || !partsData) {\n throw new Error(`Failed to get part URLs: ${JSON.stringify(partsError)}`)\n }\n\n const { parts } = partsData\n\n if (!parts || !Array.isArray(parts)) {\n throw new Error('Invalid response from part URLs endpoint: parts is not an array')\n }\n\n const uploadedParts = await Promise.all(\n parts.map(async ({ partNumber, url }) => {\n const start = (partNumber - 1) * PART_SIZE\n const end = Math.min(start + PART_SIZE, fileData.length)\n const partData = fileData.slice(start, end)\n\n const uploadResponse = await fetch(url, {\n body: partData,\n headers: { 'Content-Type': mimeType },\n method: 'PUT',\n })\n\n const etag = uploadResponse.headers.get('ETag')\n if (!etag) {\n throw new Error(`No ETag returned for part ${partNumber}`)\n }\n\n return { ETag: etag, PartNumber: partNumber }\n }),\n )\n\n const { error: completeError } = await client.POST('/api/v0/uploads/multipart/complete', {\n body: { filename: fileKey, parts: uploadedParts, uploadId },\n })\n\n if (completeError) {\n throw new Error(`Failed to complete multipart upload: ${JSON.stringify(completeError)}`)\n }\n } catch (error) {\n try {\n await client.POST('/api/v0/uploads/multipart/abort', {\n body: { filename: fileKey, uploadId },\n })\n } catch {\n // Ignore abort errors\n }\n throw error\n }\n}\n"],"names":["fs","path","PART_SIZE","MAX_FILE_SIZE_BEFORE_MULTIPART","getHandleUpload","client","prefix","clientUploadContext","data","file","clientUploadedFilename","filename","undefined","isOriginalClientUploaded","clientUploaded","fileKey","posix","join","fileData","tempFilePath","readFileSync","buffer","fileSize","filesize","length","uploadMultipart","mimeType","uploadSingleFile","error","POST","body","contentType","expiresIn","Error","JSON","stringify","uploadResponse","fetch","url","headers","method","ok","status","statusText","initiateData","initiateError","uploadId","numParts","Math","ceil","partNumbers","Array","from","_","i","partsData","partsError","parts","isArray","uploadedParts","Promise","all","map","partNumber","start","end","min","partData","slice","etag","get","ETag","PartNumber","completeError"],"mappings":"AAGA,OAAOA,QAAQ,KAAI;AACnB,OAAOC,UAAU,OAAM;AAUvB,MAAMC,YAAY,KAAK,OAAO,KAAK,gBAAgB;;AACnD,MAAMC,iCAAiC,MAAM,OAAO,KAAK,QAAQ;;AAEjE,OAAO,MAAMC,kBAAkB,CAAC,EAAEC,MAAM,EAAEC,SAAS,EAAE,EAAQ;
|
|
1
|
+
{"version":3,"sources":["../../src/storage-content-api/handleUpload.ts"],"sourcesContent":["import type { HandleUpload } from '@payloadcms/plugin-cloud-storage/types'\nimport type { CollectionConfig } from 'payload'\n\nimport fs from 'fs'\nimport path from 'path'\n\nimport type { StorageClient } from './client.js'\n\ntype Args = {\n client: StorageClient\n collection: CollectionConfig\n prefix?: string\n}\n\nconst PART_SIZE = 64 * 1024 * 1024 // 64MB per part\nconst MAX_FILE_SIZE_BEFORE_MULTIPART = 100 * 1024 * 1024 // 100MB\n\nexport const getHandleUpload = ({ client, collection, prefix = '' }: Args): HandleUpload => {\n return async ({ clientUploadContext, data, file }) => {\n // Skip if client already uploaded THIS SPECIFIC file\n // clientUploadContext.filename contains the original filename that was client-uploaded\n // Resized versions (different filename) should still be uploaded\n const clientUploadedFilename =\n clientUploadContext &&\n typeof clientUploadContext === 'object' &&\n 'filename' in clientUploadContext\n ? clientUploadContext.filename\n : undefined\n\n const isOriginalClientUploaded =\n clientUploadContext &&\n typeof clientUploadContext === 'object' &&\n 'clientUploaded' in clientUploadContext &&\n clientUploadContext.clientUploaded &&\n file.filename === clientUploadedFilename\n\n if (isOriginalClientUploaded) {\n return data\n }\n\n const collectionId = collection.slug\n const fileKey = path.posix.join(data.prefix || prefix, file.filename)\n const fileData = file.tempFilePath ? fs.readFileSync(file.tempFilePath) : file.buffer\n const fileSize = file.filesize || fileData.length\n\n if (fileSize > MAX_FILE_SIZE_BEFORE_MULTIPART) {\n await uploadMultipart({ client, collectionId, fileData, fileKey, mimeType: file.mimeType })\n } else {\n await uploadSingleFile({ client, collectionId, fileData, fileKey, mimeType: file.mimeType })\n }\n\n return data\n }\n}\n\nasync function uploadSingleFile({\n client,\n collectionId,\n fileData,\n fileKey,\n mimeType,\n}: {\n client: StorageClient\n collectionId: string\n fileData: Buffer\n fileKey: string\n mimeType: string\n}): Promise<void> {\n const { data, error } = await client.POST('/api/v0/uploads/sign', {\n body: { collectionId, contentType: mimeType, expiresIn: 3600, filename: fileKey },\n })\n\n if (error || !data) {\n throw new Error(`Failed to get presigned upload URL: ${JSON.stringify(error)}`)\n }\n\n const uploadResponse = await fetch(data.url, {\n body: fileData,\n headers: { 'Content-Type': mimeType },\n method: 'PUT',\n })\n\n if (!uploadResponse.ok) {\n throw new Error(`Failed to upload to S3: ${uploadResponse.status} ${uploadResponse.statusText}`)\n }\n}\n\nasync function uploadMultipart({\n client,\n collectionId,\n fileData,\n fileKey,\n mimeType,\n}: {\n client: StorageClient\n collectionId: string\n fileData: Buffer\n fileKey: string\n mimeType: string\n}): Promise<void> {\n const { data: initiateData, error: initiateError } = await client.POST(\n '/api/v0/uploads/multipart/initiate',\n {\n body: { collectionId, contentType: mimeType, filename: fileKey },\n },\n )\n\n if (initiateError || !initiateData) {\n throw new Error(`Failed to initiate multipart upload: ${JSON.stringify(initiateError)}`)\n }\n\n const { uploadId } = initiateData\n\n try {\n const numParts = Math.ceil(fileData.length / PART_SIZE)\n const partNumbers = Array.from({ length: numParts }, (_, i) => i + 1)\n\n const { data: partsData, error: partsError } = await client.POST(\n '/api/v0/uploads/multipart/parts',\n {\n body: { collectionId, expiresIn: 3600, filename: fileKey, partNumbers, uploadId },\n },\n )\n\n if (partsError || !partsData) {\n throw new Error(`Failed to get part URLs: ${JSON.stringify(partsError)}`)\n }\n\n const { parts } = partsData\n\n if (!parts || !Array.isArray(parts)) {\n throw new Error('Invalid response from part URLs endpoint: parts is not an array')\n }\n\n const uploadedParts = await Promise.all(\n parts.map(async ({ partNumber, url }) => {\n const start = (partNumber - 1) * PART_SIZE\n const end = Math.min(start + PART_SIZE, fileData.length)\n const partData = fileData.slice(start, end)\n\n const uploadResponse = await fetch(url, {\n body: partData,\n headers: { 'Content-Type': mimeType },\n method: 'PUT',\n })\n\n const etag = uploadResponse.headers.get('ETag')\n if (!etag) {\n throw new Error(`No ETag returned for part ${partNumber}`)\n }\n\n return { ETag: etag, PartNumber: partNumber }\n }),\n )\n\n const { error: completeError } = await client.POST('/api/v0/uploads/multipart/complete', {\n body: { collectionId, filename: fileKey, parts: uploadedParts, uploadId },\n })\n\n if (completeError) {\n throw new Error(`Failed to complete multipart upload: ${JSON.stringify(completeError)}`)\n }\n } catch (error) {\n try {\n await client.POST('/api/v0/uploads/multipart/abort', {\n body: { collectionId, filename: fileKey, uploadId },\n })\n } catch {\n // Ignore abort errors\n }\n throw error\n }\n}\n"],"names":["fs","path","PART_SIZE","MAX_FILE_SIZE_BEFORE_MULTIPART","getHandleUpload","client","collection","prefix","clientUploadContext","data","file","clientUploadedFilename","filename","undefined","isOriginalClientUploaded","clientUploaded","collectionId","slug","fileKey","posix","join","fileData","tempFilePath","readFileSync","buffer","fileSize","filesize","length","uploadMultipart","mimeType","uploadSingleFile","error","POST","body","contentType","expiresIn","Error","JSON","stringify","uploadResponse","fetch","url","headers","method","ok","status","statusText","initiateData","initiateError","uploadId","numParts","Math","ceil","partNumbers","Array","from","_","i","partsData","partsError","parts","isArray","uploadedParts","Promise","all","map","partNumber","start","end","min","partData","slice","etag","get","ETag","PartNumber","completeError"],"mappings":"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"}
|
|
@@ -12,6 +12,7 @@ export const getHandler = ({ client, collection })=>{
|
|
|
12
12
|
const key = path.posix.join(prefix, filename);
|
|
13
13
|
const { data, error } = await client.POST('/api/v0/uploads/download/sign', {
|
|
14
14
|
body: {
|
|
15
|
+
collectionId: collection.slug,
|
|
15
16
|
expiresIn: 7200,
|
|
16
17
|
filename: key
|
|
17
18
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/storage-content-api/staticHandler.ts"],"sourcesContent":["import type { StaticHandler } from '@payloadcms/plugin-cloud-storage/types'\nimport type { CollectionConfig } from 'payload'\n\nimport { getFilePrefix } from '@payloadcms/plugin-cloud-storage/utilities'\nimport path from 'path'\n\nimport type { StorageClient } from './client.js'\n\ntype Args = {\n client: StorageClient\n collection: CollectionConfig\n}\n\nexport const getHandler = ({ client, collection }: Args): StaticHandler => {\n return async (req, { params: { clientUploadContext, filename } }) => {\n try {\n const prefix = await getFilePrefix({ clientUploadContext, collection, filename, req })\n const key = path.posix.join(prefix, filename)\n\n const { data, error } = await client.POST('/api/v0/uploads/download/sign', {\n body: { expiresIn: 7200, filename: key },\n })\n\n if (error || !data) {\n req.payload.logger.error(`Failed to get signed download URL: ${JSON.stringify(error)}`)\n return new Response('Internal Server Error', { status: 500 })\n }\n\n // Fetch file bytes from S3 (required for Payload's image processing)\n const fileResponse = await fetch(data.url)\n\n if (!fileResponse.ok) {\n req.payload.logger.error(\n `S3 fetch failed: ${fileResponse.status} ${fileResponse.statusText}`,\n )\n return new Response('Failed to fetch file from storage', { status: 502 })\n }\n\n const contentType = fileResponse.headers.get('content-type') || 'application/octet-stream'\n const contentLength = fileResponse.headers.get('content-length')\n\n return new Response(fileResponse.body, {\n headers: {\n 'Content-Type': contentType,\n ...(contentLength && { 'Content-Length': contentLength }),\n },\n })\n } catch (err) {\n req.payload.logger.error({ err, msg: 'Error in staticHandler' })\n return new Response('Internal Server Error', { status: 500 })\n }\n }\n}\n"],"names":["getFilePrefix","path","getHandler","client","collection","req","params","clientUploadContext","filename","prefix","key","posix","join","data","error","POST","body","expiresIn","payload","logger","JSON","stringify","Response","status","fileResponse","fetch","url","ok","statusText","contentType","headers","get","contentLength","err","msg"],"mappings":"AAGA,SAASA,aAAa,QAAQ,6CAA4C;AAC1E,OAAOC,UAAU,OAAM;AASvB,OAAO,MAAMC,aAAa,CAAC,EAAEC,MAAM,EAAEC,UAAU,EAAQ;IACrD,OAAO,OAAOC,KAAK,EAAEC,QAAQ,EAAEC,mBAAmB,EAAEC,QAAQ,EAAE,EAAE;QAC9D,IAAI;YACF,MAAMC,SAAS,MAAMT,cAAc;gBAAEO;gBAAqBH;gBAAYI;gBAAUH;YAAI;YACpF,MAAMK,MAAMT,KAAKU,KAAK,CAACC,IAAI,CAACH,QAAQD;YAEpC,MAAM,EAAEK,IAAI,EAAEC,KAAK,EAAE,GAAG,MAAMX,OAAOY,IAAI,CAAC,iCAAiC;gBACzEC,MAAM;oBAAEC,WAAW;
|
|
1
|
+
{"version":3,"sources":["../../src/storage-content-api/staticHandler.ts"],"sourcesContent":["import type { StaticHandler } from '@payloadcms/plugin-cloud-storage/types'\nimport type { CollectionConfig } from 'payload'\n\nimport { getFilePrefix } from '@payloadcms/plugin-cloud-storage/utilities'\nimport path from 'path'\n\nimport type { StorageClient } from './client.js'\n\ntype Args = {\n client: StorageClient\n collection: CollectionConfig\n}\n\nexport const getHandler = ({ client, collection }: Args): StaticHandler => {\n return async (req, { params: { clientUploadContext, filename } }) => {\n try {\n const prefix = await getFilePrefix({ clientUploadContext, collection, filename, req })\n const key = path.posix.join(prefix, filename)\n\n const { data, error } = await client.POST('/api/v0/uploads/download/sign', {\n body: { collectionId: collection.slug, expiresIn: 7200, filename: key },\n })\n\n if (error || !data) {\n req.payload.logger.error(`Failed to get signed download URL: ${JSON.stringify(error)}`)\n return new Response('Internal Server Error', { status: 500 })\n }\n\n // Fetch file bytes from S3 (required for Payload's image processing)\n const fileResponse = await fetch(data.url)\n\n if (!fileResponse.ok) {\n req.payload.logger.error(\n `S3 fetch failed: ${fileResponse.status} ${fileResponse.statusText}`,\n )\n return new Response('Failed to fetch file from storage', { status: 502 })\n }\n\n const contentType = fileResponse.headers.get('content-type') || 'application/octet-stream'\n const contentLength = fileResponse.headers.get('content-length')\n\n return new Response(fileResponse.body, {\n headers: {\n 'Content-Type': contentType,\n ...(contentLength && { 'Content-Length': contentLength }),\n },\n })\n } catch (err) {\n req.payload.logger.error({ err, msg: 'Error in staticHandler' })\n return new Response('Internal Server Error', { status: 500 })\n }\n }\n}\n"],"names":["getFilePrefix","path","getHandler","client","collection","req","params","clientUploadContext","filename","prefix","key","posix","join","data","error","POST","body","collectionId","slug","expiresIn","payload","logger","JSON","stringify","Response","status","fileResponse","fetch","url","ok","statusText","contentType","headers","get","contentLength","err","msg"],"mappings":"AAGA,SAASA,aAAa,QAAQ,6CAA4C;AAC1E,OAAOC,UAAU,OAAM;AASvB,OAAO,MAAMC,aAAa,CAAC,EAAEC,MAAM,EAAEC,UAAU,EAAQ;IACrD,OAAO,OAAOC,KAAK,EAAEC,QAAQ,EAAEC,mBAAmB,EAAEC,QAAQ,EAAE,EAAE;QAC9D,IAAI;YACF,MAAMC,SAAS,MAAMT,cAAc;gBAAEO;gBAAqBH;gBAAYI;gBAAUH;YAAI;YACpF,MAAMK,MAAMT,KAAKU,KAAK,CAACC,IAAI,CAACH,QAAQD;YAEpC,MAAM,EAAEK,IAAI,EAAEC,KAAK,EAAE,GAAG,MAAMX,OAAOY,IAAI,CAAC,iCAAiC;gBACzEC,MAAM;oBAAEC,cAAcb,WAAWc,IAAI;oBAAEC,WAAW;oBAAMX,UAAUE;gBAAI;YACxE;YAEA,IAAII,SAAS,CAACD,MAAM;gBAClBR,IAAIe,OAAO,CAACC,MAAM,CAACP,KAAK,CAAC,CAAC,mCAAmC,EAAEQ,KAAKC,SAAS,CAACT,QAAQ;gBACtF,OAAO,IAAIU,SAAS,yBAAyB;oBAAEC,QAAQ;gBAAI;YAC7D;YAEA,qEAAqE;YACrE,MAAMC,eAAe,MAAMC,MAAMd,KAAKe,GAAG;YAEzC,IAAI,CAACF,aAAaG,EAAE,EAAE;gBACpBxB,IAAIe,OAAO,CAACC,MAAM,CAACP,KAAK,CACtB,CAAC,iBAAiB,EAAEY,aAAaD,MAAM,CAAC,CAAC,EAAEC,aAAaI,UAAU,EAAE;gBAEtE,OAAO,IAAIN,SAAS,qCAAqC;oBAAEC,QAAQ;gBAAI;YACzE;YAEA,MAAMM,cAAcL,aAAaM,OAAO,CAACC,GAAG,CAAC,mBAAmB;YAChE,MAAMC,gBAAgBR,aAAaM,OAAO,CAACC,GAAG,CAAC;YAE/C,OAAO,IAAIT,SAASE,aAAaV,IAAI,EAAE;gBACrCgB,SAAS;oBACP,gBAAgBD;oBAChB,GAAIG,iBAAiB;wBAAE,kBAAkBA;oBAAc,CAAC;gBAC1D;YACF;QACF,EAAE,OAAOC,KAAK;YACZ9B,IAAIe,OAAO,CAACC,MAAM,CAACP,KAAK,CAAC;gBAAEqB;gBAAKC,KAAK;YAAyB;YAC9D,OAAO,IAAIZ,SAAS,yBAAyB;gBAAEC,QAAQ;YAAI;QAC7D;IACF;AACF,EAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PayloadRequest } from 'payload';
|
|
2
|
+
/**
|
|
3
|
+
* Get a safe filename that doesn't collide with existing documents
|
|
4
|
+
* Similar to Payload's getSafeFileName
|
|
5
|
+
*/
|
|
6
|
+
export declare function getSafeFilename({ collectionSlug, desiredFilename, prefix, req, }: {
|
|
7
|
+
collectionSlug: string;
|
|
8
|
+
desiredFilename: string;
|
|
9
|
+
prefix?: string;
|
|
10
|
+
req: PayloadRequest;
|
|
11
|
+
}): Promise<string>;
|
|
12
|
+
//# sourceMappingURL=getSafeFilename.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getSafeFilename.d.ts","sourceRoot":"","sources":["../../../src/storage-content-api/utilities/getSafeFilename.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAS,MAAM,SAAS,CAAA;AAkEpD;;;GAGG;AACH,wBAAsB,eAAe,CAAC,EACpC,cAAc,EACd,eAAe,EACf,MAAM,EACN,GAAG,GACJ,EAAE;IACD,cAAc,EAAE,MAAM,CAAA;IACtB,eAAe,EAAE,MAAM,CAAA;IACvB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,cAAc,CAAA;CACpB,GAAG,OAAO,CAAC,MAAM,CAAC,CAgBlB"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import sanitize from 'sanitize-filename';
|
|
2
|
+
/**
|
|
3
|
+
* Check if a document with the given filename already exists in the database
|
|
4
|
+
* Matches Payload's docWithFilenameExists
|
|
5
|
+
*/ async function docWithFilenameExists({ collectionSlug, filename, prefix, req }) {
|
|
6
|
+
const where = {
|
|
7
|
+
filename: {
|
|
8
|
+
equals: filename
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
if (prefix) {
|
|
12
|
+
where.prefix = {
|
|
13
|
+
equals: prefix
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
const doc = await req.payload.db.findOne({
|
|
17
|
+
collection: collectionSlug,
|
|
18
|
+
req,
|
|
19
|
+
where
|
|
20
|
+
});
|
|
21
|
+
return !!doc;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Increment filename by adding -1, -2, etc. suffix
|
|
25
|
+
* photo.jpg → photo-1.jpg
|
|
26
|
+
* photo-1.jpg → photo-2.jpg
|
|
27
|
+
* Matches Payload's incrementName
|
|
28
|
+
*/ function incrementFilename(name) {
|
|
29
|
+
const extension = name.split('.').pop();
|
|
30
|
+
const baseFilename = sanitize(name.substring(0, name.lastIndexOf('.')) || name);
|
|
31
|
+
let incrementedName = baseFilename;
|
|
32
|
+
const regex = /(.*)-(\d+)$/;
|
|
33
|
+
const found = baseFilename.match(regex);
|
|
34
|
+
if (found === null) {
|
|
35
|
+
incrementedName += '-1';
|
|
36
|
+
} else {
|
|
37
|
+
const matchedName = found[1];
|
|
38
|
+
const matchedNumber = found[2];
|
|
39
|
+
const incremented = Number(matchedNumber) + 1;
|
|
40
|
+
incrementedName = `${matchedName}-${incremented}`;
|
|
41
|
+
}
|
|
42
|
+
return `${incrementedName}.${extension}`;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Get a safe filename that doesn't collide with existing documents
|
|
46
|
+
* Similar to Payload's getSafeFileName
|
|
47
|
+
*/ export async function getSafeFilename({ collectionSlug, desiredFilename, prefix, req }) {
|
|
48
|
+
let modifiedFilename = desiredFilename;
|
|
49
|
+
// Keep incrementing until we find a filename that doesn't exist
|
|
50
|
+
while(await docWithFilenameExists({
|
|
51
|
+
collectionSlug,
|
|
52
|
+
filename: modifiedFilename,
|
|
53
|
+
prefix,
|
|
54
|
+
req
|
|
55
|
+
})){
|
|
56
|
+
modifiedFilename = incrementFilename(modifiedFilename);
|
|
57
|
+
}
|
|
58
|
+
return modifiedFilename;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
//# sourceMappingURL=getSafeFilename.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/storage-content-api/utilities/getSafeFilename.ts"],"sourcesContent":["import type { PayloadRequest, Where } from 'payload'\n\nimport sanitize from 'sanitize-filename'\n\n/**\n * Check if a document with the given filename already exists in the database\n * Matches Payload's docWithFilenameExists\n */\nasync function docWithFilenameExists({\n collectionSlug,\n filename,\n prefix,\n req,\n}: {\n collectionSlug: string\n filename: string\n prefix?: string\n req: PayloadRequest\n}): Promise<boolean> {\n const where: Where = {\n filename: {\n equals: filename,\n },\n }\n\n if (prefix) {\n where.prefix = {\n equals: prefix,\n }\n }\n\n const doc = await req.payload.db.findOne({\n collection: collectionSlug,\n req,\n where,\n })\n\n return !!doc\n}\n\n/**\n * Increment filename by adding -1, -2, etc. suffix\n * photo.jpg → photo-1.jpg\n * photo-1.jpg → photo-2.jpg\n * Matches Payload's incrementName\n */\nfunction incrementFilename(name: string): string {\n const extension = name.split('.').pop()\n const baseFilename = sanitize(name.substring(0, name.lastIndexOf('.')) || name)\n\n let incrementedName = baseFilename\n const regex = /(.*)-(\\d+)$/\n const found = baseFilename.match(regex)\n\n if (found === null) {\n incrementedName += '-1'\n } else {\n const matchedName = found[1]\n const matchedNumber = found[2]\n const incremented = Number(matchedNumber) + 1\n incrementedName = `${matchedName}-${incremented}`\n }\n\n return `${incrementedName}.${extension}`\n}\n\n/**\n * Get a safe filename that doesn't collide with existing documents\n * Similar to Payload's getSafeFileName\n */\nexport async function getSafeFilename({\n collectionSlug,\n desiredFilename,\n prefix,\n req,\n}: {\n collectionSlug: string\n desiredFilename: string\n prefix?: string\n req: PayloadRequest\n}): Promise<string> {\n let modifiedFilename = desiredFilename\n\n // Keep incrementing until we find a filename that doesn't exist\n while (\n await docWithFilenameExists({\n collectionSlug,\n filename: modifiedFilename,\n prefix,\n req,\n })\n ) {\n modifiedFilename = incrementFilename(modifiedFilename)\n }\n\n return modifiedFilename\n}\n"],"names":["sanitize","docWithFilenameExists","collectionSlug","filename","prefix","req","where","equals","doc","payload","db","findOne","collection","incrementFilename","name","extension","split","pop","baseFilename","substring","lastIndexOf","incrementedName","regex","found","match","matchedName","matchedNumber","incremented","Number","getSafeFilename","desiredFilename","modifiedFilename"],"mappings":"AAEA,OAAOA,cAAc,oBAAmB;AAExC;;;CAGC,GACD,eAAeC,sBAAsB,EACnCC,cAAc,EACdC,QAAQ,EACRC,MAAM,EACNC,GAAG,EAMJ;IACC,MAAMC,QAAe;QACnBH,UAAU;YACRI,QAAQJ;QACV;IACF;IAEA,IAAIC,QAAQ;QACVE,MAAMF,MAAM,GAAG;YACbG,QAAQH;QACV;IACF;IAEA,MAAMI,MAAM,MAAMH,IAAII,OAAO,CAACC,EAAE,CAACC,OAAO,CAAC;QACvCC,YAAYV;QACZG;QACAC;IACF;IAEA,OAAO,CAAC,CAACE;AACX;AAEA;;;;;CAKC,GACD,SAASK,kBAAkBC,IAAY;IACrC,MAAMC,YAAYD,KAAKE,KAAK,CAAC,KAAKC,GAAG;IACrC,MAAMC,eAAelB,SAASc,KAAKK,SAAS,CAAC,GAAGL,KAAKM,WAAW,CAAC,SAASN;IAE1E,IAAIO,kBAAkBH;IACtB,MAAMI,QAAQ;IACd,MAAMC,QAAQL,aAAaM,KAAK,CAACF;IAEjC,IAAIC,UAAU,MAAM;QAClBF,mBAAmB;IACrB,OAAO;QACL,MAAMI,cAAcF,KAAK,CAAC,EAAE;QAC5B,MAAMG,gBAAgBH,KAAK,CAAC,EAAE;QAC9B,MAAMI,cAAcC,OAAOF,iBAAiB;QAC5CL,kBAAkB,GAAGI,YAAY,CAAC,EAAEE,aAAa;IACnD;IAEA,OAAO,GAAGN,gBAAgB,CAAC,EAAEN,WAAW;AAC1C;AAEA;;;CAGC,GACD,OAAO,eAAec,gBAAgB,EACpC3B,cAAc,EACd4B,eAAe,EACf1B,MAAM,EACNC,GAAG,EAMJ;IACC,IAAI0B,mBAAmBD;IAEvB,gEAAgE;IAChE,MACE,MAAM7B,sBAAsB;QAC1BC;QACAC,UAAU4B;QACV3B;QACAC;IACF,GACA;QACA0B,mBAAmBlB,kBAAkBkB;IACvC;IAEA,OAAOA;AACT"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/storage-content-api/utilities/index.js"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/storage-content-api/utilities/index.js"],"sourcesContent":["export { getSafeFilename } from './getSafeFilename.ts'\n"],"names":["getSafeFilename"],"mappings":"AAAA,SAASA,eAAe,QAAQ,uBAAsB"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Content System ID for Content API access
|
|
2
|
+
FIGMA_CONTENT_SYSTEM_ID=
|
|
3
|
+
|
|
4
|
+
# Tenant ID for deploy operations
|
|
5
|
+
FIGMA_TENANT_ID=
|
|
6
|
+
|
|
7
|
+
# OAuth client ID for admin panel authentication
|
|
8
|
+
FIGMA_OAUTH_CLIENT_ID=
|
|
9
|
+
|
|
10
|
+
# OAuth client secret for admin panel authentication
|
|
11
|
+
FIGMA_OAUTH_CLIENT_SECRET=
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Get an environment variable value from .env file
|
|
2
|
+
* Get an environment variable value from .env file (async)
|
|
3
3
|
*
|
|
4
4
|
* @param projectPath - Path to project directory
|
|
5
5
|
* @param key - Environment variable name to retrieve
|
|
6
6
|
* @returns The value if found, null otherwise
|
|
7
7
|
*/
|
|
8
8
|
export declare function getEnvVar(projectPath: string, key: string): Promise<null | string>;
|
|
9
|
+
/**
|
|
10
|
+
* Get an environment variable value from .env file (sync)
|
|
11
|
+
*
|
|
12
|
+
* @param projectPath - Path to project directory
|
|
13
|
+
* @param key - Environment variable name to retrieve
|
|
14
|
+
* @returns The value if found, null otherwise
|
|
15
|
+
*/
|
|
16
|
+
export declare function getEnvVarSync(projectPath: string, key: string): null | string;
|
|
9
17
|
/**
|
|
10
18
|
* Add or update an environment variable in .env file
|
|
11
19
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env-management.d.ts","sourceRoot":"","sources":["../../src/utils/env-management.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"env-management.d.ts","sourceRoot":"","sources":["../../src/utils/env-management.ts"],"names":[],"mappings":"AAgCA;;;;;;GAMG;AACH,wBAAsB,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,CAUxF;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAU7E;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAmEf"}
|
|
@@ -1,7 +1,29 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
|
+
import fsSync from 'node:fs';
|
|
2
3
|
import path from 'path';
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
+
* Parse .env file contents and extract value for a key
|
|
6
|
+
*/ function parseEnvValue(contents, key) {
|
|
7
|
+
const lines = contents.split(/\r?\n/);
|
|
8
|
+
for (const line of lines){
|
|
9
|
+
// Skip empty lines and comments
|
|
10
|
+
if (!line.trim() || line.trim().startsWith('#')) {
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
// Skip lines without =
|
|
14
|
+
if (!line.includes('=')) {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
const equalsIndex = line.indexOf('=');
|
|
18
|
+
const lineKey = line.substring(0, equalsIndex).trim();
|
|
19
|
+
if (lineKey === key) {
|
|
20
|
+
return line.substring(equalsIndex + 1);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Get an environment variable value from .env file (async)
|
|
5
27
|
*
|
|
6
28
|
* @param projectPath - Path to project directory
|
|
7
29
|
* @param key - Environment variable name to retrieve
|
|
@@ -10,23 +32,23 @@ import path from 'path';
|
|
|
10
32
|
const envPath = path.join(projectPath, '.env');
|
|
11
33
|
try {
|
|
12
34
|
const contents = await fs.readFile(envPath, 'utf-8');
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (!line.trim() || line.trim().startsWith('#')) {
|
|
17
|
-
continue;
|
|
18
|
-
}
|
|
19
|
-
// Skip lines without =
|
|
20
|
-
if (!line.includes('=')) {
|
|
21
|
-
continue;
|
|
22
|
-
}
|
|
23
|
-
const equalsIndex = line.indexOf('=');
|
|
24
|
-
const lineKey = line.substring(0, equalsIndex).trim();
|
|
25
|
-
if (lineKey === key) {
|
|
26
|
-
return line.substring(equalsIndex + 1);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
35
|
+
return parseEnvValue(contents, key);
|
|
36
|
+
} catch {
|
|
37
|
+
// File doesn't exist or can't be read
|
|
29
38
|
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Get an environment variable value from .env file (sync)
|
|
43
|
+
*
|
|
44
|
+
* @param projectPath - Path to project directory
|
|
45
|
+
* @param key - Environment variable name to retrieve
|
|
46
|
+
* @returns The value if found, null otherwise
|
|
47
|
+
*/ export function getEnvVarSync(projectPath, key) {
|
|
48
|
+
const envPath = path.join(projectPath, '.env');
|
|
49
|
+
try {
|
|
50
|
+
const contents = fsSync.readFileSync(envPath, 'utf-8');
|
|
51
|
+
return parseEnvValue(contents, key);
|
|
30
52
|
} catch {
|
|
31
53
|
// File doesn't exist or can't be read
|
|
32
54
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/env-management.ts"],"sourcesContent":["import fs from 'fs/promises'\nimport path from 'path'\n\n/**\n * Get an environment variable value from .env file\n *\n * @param projectPath - Path to project directory\n * @param key - Environment variable name to retrieve\n * @returns The value if found, null otherwise\n */\nexport async function getEnvVar(projectPath: string, key: string): Promise<null | string> {\n const envPath = path.join(projectPath, '.env')\n\n try {\n const contents = await fs.readFile(envPath, 'utf-8')\n
|
|
1
|
+
{"version":3,"sources":["../../src/utils/env-management.ts"],"sourcesContent":["import fs from 'fs/promises'\nimport fsSync from 'node:fs'\nimport path from 'path'\n\n/**\n * Parse .env file contents and extract value for a key\n */\nfunction parseEnvValue(contents: string, key: string): null | string {\n const lines = contents.split(/\\r?\\n/)\n\n for (const line of lines) {\n // Skip empty lines and comments\n if (!line.trim() || line.trim().startsWith('#')) {\n continue\n }\n\n // Skip lines without =\n if (!line.includes('=')) {\n continue\n }\n\n const equalsIndex = line.indexOf('=')\n const lineKey = line.substring(0, equalsIndex).trim()\n\n if (lineKey === key) {\n return line.substring(equalsIndex + 1)\n }\n }\n\n return null\n}\n\n/**\n * Get an environment variable value from .env file (async)\n *\n * @param projectPath - Path to project directory\n * @param key - Environment variable name to retrieve\n * @returns The value if found, null otherwise\n */\nexport async function getEnvVar(projectPath: string, key: string): Promise<null | string> {\n const envPath = path.join(projectPath, '.env')\n\n try {\n const contents = await fs.readFile(envPath, 'utf-8')\n return parseEnvValue(contents, key)\n } catch {\n // File doesn't exist or can't be read\n return null\n }\n}\n\n/**\n * Get an environment variable value from .env file (sync)\n *\n * @param projectPath - Path to project directory\n * @param key - Environment variable name to retrieve\n * @returns The value if found, null otherwise\n */\nexport function getEnvVarSync(projectPath: string, key: string): null | string {\n const envPath = path.join(projectPath, '.env')\n\n try {\n const contents = fsSync.readFileSync(envPath, 'utf-8')\n return parseEnvValue(contents, key)\n } catch {\n // File doesn't exist or can't be read\n return null\n }\n}\n\n/**\n * Add or update an environment variable in .env file\n *\n * @param projectPath - Path to project directory\n * @param key - Environment variable name\n * @param value - Environment variable value\n */\nexport async function addOrUpdateEnvVar(\n projectPath: string,\n key: string,\n value: string,\n): Promise<void> {\n const envPath = path.join(projectPath, '.env')\n\n try {\n let contents = ''\n let envExists = false\n\n // Try to read existing .env file\n try {\n contents = await fs.readFile(envPath, 'utf-8')\n envExists = true\n } catch {\n // .env doesn't exist, will create it\n }\n\n const lines = contents.split(/\\r?\\n/)\n const seenKeys = new Set<string>()\n const processedLines: string[] = []\n let keyUpdated = false\n\n for (const line of lines) {\n // Skip empty lines at start if file didn't exist\n if (!envExists && !line.trim()) {\n continue\n }\n\n // Preserve empty lines and comments\n if (!line.trim() || line.trim().startsWith('#')) {\n processedLines.push(line)\n continue\n }\n\n // Skip lines without =\n if (!line.includes('=')) {\n processedLines.push(line)\n continue\n }\n\n const [lineKey] = line.split('=')\n const trimmedKey = lineKey.trim()\n\n // Skip duplicate keys (keep first occurrence)\n if (seenKeys.has(trimmedKey)) {\n continue\n }\n seenKeys.add(trimmedKey)\n\n // Update or keep the line\n if (trimmedKey === key) {\n processedLines.push(`${key}=${value}`)\n keyUpdated = true\n } else {\n processedLines.push(line)\n }\n }\n\n // Add key if not present\n if (!keyUpdated) {\n processedLines.push(`${key}=${value}`)\n }\n\n await fs.writeFile(envPath, processedLines.join('\\n'), 'utf-8')\n } catch (error) {\n throw new Error(\n `Failed to update .env file: ${error instanceof Error ? error.message : 'Unknown error'}`,\n )\n }\n}\n"],"names":["fs","fsSync","path","parseEnvValue","contents","key","lines","split","line","trim","startsWith","includes","equalsIndex","indexOf","lineKey","substring","getEnvVar","projectPath","envPath","join","readFile","getEnvVarSync","readFileSync","addOrUpdateEnvVar","value","envExists","seenKeys","Set","processedLines","keyUpdated","push","trimmedKey","has","add","writeFile","error","Error","message"],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Environment } from '../constants.js';
|
|
2
|
+
/**
|
|
3
|
+
* Format environment suffix for user-facing messages.
|
|
4
|
+
* Returns empty string for production (default), suffix for other environments.
|
|
5
|
+
*
|
|
6
|
+
* @param env - The environment
|
|
7
|
+
* @param prefix - Prefix before environment name (default: ' to ')
|
|
8
|
+
* @param suffix - Suffix after environment name (default: '')
|
|
9
|
+
* @returns Formatted string or empty string for production
|
|
10
|
+
*/
|
|
11
|
+
export declare function formatEnvSuffix(env: Environment, prefix?: string, suffix?: string): string;
|
|
12
|
+
//# sourceMappingURL=format-env-suffix.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-env-suffix.d.ts","sourceRoot":"","sources":["../../src/utils/format-env-suffix.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAElD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,WAAW,EAAE,MAAM,SAAS,EAAE,MAAM,SAAK,GAAG,MAAM,CAEtF"}
|