@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.
Files changed (139) hide show
  1. package/dist/auth/oauth-flow.d.ts.map +1 -1
  2. package/dist/auth/oauth-flow.js +6 -3
  3. package/dist/auth/oauth-flow.js.map +1 -1
  4. package/dist/auth/refresh.d.ts +2 -1
  5. package/dist/auth/refresh.d.ts.map +1 -1
  6. package/dist/auth/refresh.js +11 -3
  7. package/dist/auth/refresh.js.map +1 -1
  8. package/dist/auth/token-store.d.ts +6 -4
  9. package/dist/auth/token-store.d.ts.map +1 -1
  10. package/dist/auth/token-store.js +17 -14
  11. package/dist/auth/token-store.js.map +1 -1
  12. package/dist/auth/types.d.ts +7 -4
  13. package/dist/auth/types.d.ts.map +1 -1
  14. package/dist/auth/types.js.map +1 -1
  15. package/dist/cli.js +8 -6
  16. package/dist/cli.js.map +1 -1
  17. package/dist/commands/deploy.d.ts.map +1 -1
  18. package/dist/commands/deploy.js +2 -1
  19. package/dist/commands/deploy.js.map +1 -1
  20. package/dist/commands/init.d.ts.map +1 -1
  21. package/dist/commands/init.js +12 -1
  22. package/dist/commands/init.js.map +1 -1
  23. package/dist/commands/list-tokens.d.ts +3 -3
  24. package/dist/commands/list-tokens.d.ts.map +1 -1
  25. package/dist/commands/list-tokens.js +20 -8
  26. package/dist/commands/list-tokens.js.map +1 -1
  27. package/dist/commands/login.d.ts.map +1 -1
  28. package/dist/commands/login.js +12 -4
  29. package/dist/commands/login.js.map +1 -1
  30. package/dist/commands/logout.d.ts +5 -4
  31. package/dist/commands/logout.d.ts.map +1 -1
  32. package/dist/commands/logout.js +55 -12
  33. package/dist/commands/logout.js.map +1 -1
  34. package/dist/constants.d.ts +1 -1
  35. package/dist/constants.d.ts.map +1 -1
  36. package/dist/constants.js +19 -4
  37. package/dist/constants.js.map +1 -1
  38. package/dist/db-content-api/generated/content-api-types.d.ts +166 -277
  39. package/dist/db-content-api/generated/content-api-types.d.ts.map +1 -1
  40. package/dist/db-content-api/generated/content-api-types.js.map +1 -1
  41. package/dist/db-content-api/index.d.ts.map +1 -1
  42. package/dist/db-content-api/index.js +116 -64
  43. package/dist/db-content-api/index.js.map +1 -1
  44. package/dist/db-content-api/temp-utilities/unwrapDocument.d.ts +31 -1
  45. package/dist/db-content-api/temp-utilities/unwrapDocument.d.ts.map +1 -1
  46. package/dist/db-content-api/temp-utilities/unwrapDocument.js +24 -24
  47. package/dist/db-content-api/temp-utilities/unwrapDocument.js.map +1 -1
  48. package/dist/db-content-api/utilities/data/castFieldValue.d.ts.map +1 -1
  49. package/dist/db-content-api/utilities/data/castFieldValue.js +4 -1
  50. package/dist/db-content-api/utilities/data/castFieldValue.js.map +1 -1
  51. package/dist/db-content-api/utilities/data/index.d.ts +2 -0
  52. package/dist/db-content-api/utilities/data/index.d.ts.map +1 -1
  53. package/dist/db-content-api/utilities/data/index.js +23 -2
  54. package/dist/db-content-api/utilities/data/index.js.map +1 -1
  55. package/dist/db-content-api/utilities/meta/buildLocalizedPaths.d.ts +11 -0
  56. package/dist/db-content-api/utilities/meta/buildLocalizedPaths.d.ts.map +1 -0
  57. package/dist/db-content-api/utilities/meta/buildLocalizedPaths.js +55 -0
  58. package/dist/db-content-api/utilities/meta/buildLocalizedPaths.js.map +1 -0
  59. package/dist/db-content-api/utilities/meta/buildMeta.d.ts +22 -0
  60. package/dist/db-content-api/utilities/meta/buildMeta.d.ts.map +1 -0
  61. package/dist/db-content-api/utilities/meta/buildMeta.js +30 -0
  62. package/dist/db-content-api/utilities/meta/buildMeta.js.map +1 -0
  63. package/dist/db-content-api/utilities/meta/buildPathTypes.d.ts +7 -0
  64. package/dist/db-content-api/utilities/meta/buildPathTypes.d.ts.map +1 -0
  65. package/dist/db-content-api/utilities/meta/buildPathTypes.js +151 -0
  66. package/dist/db-content-api/utilities/meta/buildPathTypes.js.map +1 -0
  67. package/dist/db-content-api/utilities/meta/index.d.ts +5 -0
  68. package/dist/db-content-api/utilities/meta/index.d.ts.map +1 -0
  69. package/dist/db-content-api/utilities/meta/index.js +5 -0
  70. package/dist/db-content-api/utilities/meta/index.js.map +1 -0
  71. package/dist/db-content-api/utilities/where.d.ts.map +1 -1
  72. package/dist/db-content-api/utilities/where.js +17 -3
  73. package/dist/db-content-api/utilities/where.js.map +1 -1
  74. package/dist/oauth/components/LogoutButton/index.d.ts.map +1 -1
  75. package/dist/oauth/components/LogoutButton/index.js +5 -3
  76. package/dist/oauth/components/LogoutButton/index.js.map +1 -1
  77. package/dist/oauth/endpoints/getLoginEndpoint.d.ts.map +1 -1
  78. package/dist/oauth/endpoints/getLoginEndpoint.js +17 -7
  79. package/dist/oauth/endpoints/getLoginEndpoint.js.map +1 -1
  80. package/dist/oauth/endpoints/getLogoutEndpoint.d.ts.map +1 -1
  81. package/dist/oauth/endpoints/getLogoutEndpoint.js +7 -4
  82. package/dist/oauth/endpoints/getLogoutEndpoint.js.map +1 -1
  83. package/dist/plugin/build-config.js +3 -3
  84. package/dist/plugin/build-config.js.map +1 -1
  85. package/dist/storage-content-api/api-types.d.ts +8 -1
  86. package/dist/storage-content-api/api-types.d.ts.map +1 -1
  87. package/dist/storage-content-api/api-types.js.map +1 -1
  88. package/dist/storage-content-api/client-uploads/ClientUploadHandler.d.ts.map +1 -1
  89. package/dist/storage-content-api/client-uploads/ClientUploadHandler.js +3 -3
  90. package/dist/storage-content-api/client-uploads/ClientUploadHandler.js.map +1 -1
  91. package/dist/storage-content-api/client-uploads/generateSignedURL.d.ts.map +1 -1
  92. package/dist/storage-content-api/client-uploads/generateSignedURL.js +9 -1
  93. package/dist/storage-content-api/client-uploads/generateSignedURL.js.map +1 -1
  94. package/dist/storage-content-api/generateURL.d.ts.map +1 -1
  95. package/dist/storage-content-api/generateURL.js +3 -2
  96. package/dist/storage-content-api/generateURL.js.map +1 -1
  97. package/dist/storage-content-api/handleDelete.d.ts.map +1 -1
  98. package/dist/storage-content-api/handleDelete.js +4 -2
  99. package/dist/storage-content-api/handleDelete.js.map +1 -1
  100. package/dist/storage-content-api/handleUpload.d.ts +1 -1
  101. package/dist/storage-content-api/handleUpload.d.ts.map +1 -1
  102. package/dist/storage-content-api/handleUpload.js +11 -3
  103. package/dist/storage-content-api/handleUpload.js.map +1 -1
  104. package/dist/storage-content-api/staticHandler.js +1 -0
  105. package/dist/storage-content-api/staticHandler.js.map +1 -1
  106. package/dist/storage-content-api/utilities/getSafeFilename.d.ts +12 -0
  107. package/dist/storage-content-api/utilities/getSafeFilename.d.ts.map +1 -0
  108. package/dist/storage-content-api/utilities/getSafeFilename.js +61 -0
  109. package/dist/storage-content-api/utilities/getSafeFilename.js.map +1 -0
  110. package/dist/storage-content-api/utilities/index.d.ts +2 -0
  111. package/dist/storage-content-api/utilities/index.d.ts.map +1 -0
  112. package/dist/storage-content-api/utilities/index.js +3 -0
  113. package/dist/storage-content-api/utilities/index.js.map +1 -0
  114. package/dist/templates/.env.example +11 -0
  115. package/dist/utils/env-management.d.ts +9 -1
  116. package/dist/utils/env-management.d.ts.map +1 -1
  117. package/dist/utils/env-management.js +39 -17
  118. package/dist/utils/env-management.js.map +1 -1
  119. package/dist/utils/format-env-suffix.d.ts +12 -0
  120. package/dist/utils/format-env-suffix.d.ts.map +1 -0
  121. package/dist/utils/format-env-suffix.js +13 -0
  122. package/dist/utils/format-env-suffix.js.map +1 -0
  123. package/dist/utils/lambda-config.js +26 -12
  124. package/dist/utils/lambda-config.js.map +1 -1
  125. package/dist/utils/messages.d.ts.map +1 -1
  126. package/dist/utils/messages.js +1 -2
  127. package/dist/utils/messages.js.map +1 -1
  128. package/dist/utils/project.d.ts.map +1 -1
  129. package/dist/utils/project.js +3 -0
  130. package/dist/utils/project.js.map +1 -1
  131. package/package.json +2 -2
  132. package/dist/db-content-api/temp-utilities/pagination.d.ts +0 -13
  133. package/dist/db-content-api/temp-utilities/pagination.d.ts.map +0 -1
  134. package/dist/db-content-api/temp-utilities/pagination.js +0 -45
  135. package/dist/db-content-api/temp-utilities/pagination.js.map +0 -1
  136. package/dist/oauth/utilities/getAdminPath.d.ts +0 -8
  137. package/dist/oauth/utilities/getAdminPath.d.ts.map +0 -1
  138. package/dist/oauth/utilities/getAdminPath.js +0 -9
  139. 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: file.name,
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 }\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 // Include original filename so handleUpload can distinguish from resized versions\n return { clientUploaded: true, filename: file.name, 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","url","json","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,EAAEC,GAAG,EAAE,GAAI,MAAMhB,SAASiB,IAAI;QAEpC,MAAMC,iBAAiB,MAAMjB,MAAMe,KAAK;YACtCd,MAAMP;YACNe,SAAS;gBAAE,gBAAgBf,KAAKa,IAAI;YAAC;YACrCG,QAAQ;QACV;QAEA,IAAI,CAACO,eAAeN,EAAE,EAAE;YACtB,MAAM,IAAIC,MACR,CAAC,wBAAwB,EAAEK,eAAeJ,MAAM,CAAC,CAAC,EAAEI,eAAeH,UAAU,EAAE;QAEnF;QAEA,kFAAkF;QAClF,OAAO;YAAEI,gBAAgB;YAAMd,UAAUV,KAAKW,IAAI;YAAEV;QAAO;IAC7D;AACF,GAAE"}
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;AAEjD,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,cA0BT,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, expiresIn: 3600, filename },\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({ url: data.url })\n }\n}\n"],"names":["APIError","Forbidden","defaultAccess","req","user","getGenerateSignedURLHandler","access","client","json","collectionSlug","filename","mimeType","data","error","POST","body","contentType","expiresIn","JSON","stringify","Response","url"],"mappings":"AAEA,SAASA,QAAQ,EAAEC,SAAS,QAAQ,UAAS;AAc7C,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,IAAIR,SAAS,gDAAgD;QACrE;QAEA,MAAM,EAAES,cAAc,EAAEC,QAAQ,EAAEC,QAAQ,EAAE,GAAI,MAAMR,IAAIK,IAAI;QAM9D,IAAI,CAAE,MAAMF,OAAO;YAAEG;YAAgBN;QAAI,IAAK;YAC5C,MAAM,IAAIF;QACZ;QAEA,MAAM,EAAEW,IAAI,EAAEC,KAAK,EAAE,GAAG,MAAMN,OAAOO,IAAI,CAAC,wBAAwB;YAChEC,MAAM;gBAAEC,aAAaL;gBAAUM,WAAW;gBAAMP;YAAS;QAC3D;QAEA,IAAIG,SAAS,CAACD,MAAM;YAClB,MAAM,IAAIZ,SAAS,CAAC,oCAAoC,EAAEkB,KAAKC,SAAS,CAACN,QAAQ;QACnF;QAEA,OAAOO,SAASZ,IAAI,CAAC;YAAEa,KAAKT,KAAKS,GAAG;QAAC;IACvC;AACF,EAAC"}
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,WAIpB,CAAA"}
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;QACxB,MAAMC,UAAUL,KAAKM,KAAK,CAACC,IAAI,CAACH,QAAQD;QACxC,OAAO,GAAGD,QAAQ,yBAAyB,EAAEM,mBAAmBH,UAAU;IAC5E,EAAC"}
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,YAOlD,CAAA"}
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: { path: { filename: fileKey } },\n })\n }\n}\n"],"names":["path","getHandleDelete","client","doc","prefix","filename","fileKey","posix","join","DELETE","params"],"mappings":"AAEA,OAAOA,UAAU,OAAM;AAQvB,OAAO,MAAMC,kBAAkB,CAAC,EAAEC,MAAM,EAAQ;IAC9C,OAAO,OAAO,EAAEC,KAAK,EAAEC,SAAS,EAAE,EAAE,EAAEC,QAAQ,EAAE;QAC9C,MAAMC,UAAUN,KAAKO,KAAK,CAACC,IAAI,CAACJ,QAAQC;QACxC,MAAMH,OAAOO,MAAM,CAAC,8BAA8B;YAChDC,QAAQ;gBAAEV,MAAM;oBAAEK,UAAUC;gBAAQ;YAAE;QACxC;IACF;AACF,EAAC"}
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,uBAA6B,IAAI,KAAG,YAmC/D,CAAA"}
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":"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"}
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;oBAAMT,UAAUE;gBAAI;YACzC;YAEA,IAAII,SAAS,CAACD,MAAM;gBAClBR,IAAIa,OAAO,CAACC,MAAM,CAACL,KAAK,CAAC,CAAC,mCAAmC,EAAEM,KAAKC,SAAS,CAACP,QAAQ;gBACtF,OAAO,IAAIQ,SAAS,yBAAyB;oBAAEC,QAAQ;gBAAI;YAC7D;YAEA,qEAAqE;YACrE,MAAMC,eAAe,MAAMC,MAAMZ,KAAKa,GAAG;YAEzC,IAAI,CAACF,aAAaG,EAAE,EAAE;gBACpBtB,IAAIa,OAAO,CAACC,MAAM,CAACL,KAAK,CACtB,CAAC,iBAAiB,EAAEU,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,aAAaR,IAAI,EAAE;gBACrCc,SAAS;oBACP,gBAAgBD;oBAChB,GAAIG,iBAAiB;wBAAE,kBAAkBA;oBAAc,CAAC;gBAC1D;YACF;QACF,EAAE,OAAOC,KAAK;YACZ5B,IAAIa,OAAO,CAACC,MAAM,CAACL,KAAK,CAAC;gBAAEmB;gBAAKC,KAAK;YAAyB;YAC9D,OAAO,IAAIZ,SAAS,yBAAyB;gBAAEC,QAAQ;YAAI;QAC7D;IACF;AACF,EAAC"}
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,2 @@
1
+ export { getSafeFilename } from "./getSafeFilename.ts";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/storage-content-api/utilities/index.js"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ export { getSafeFilename } from './getSafeFilename.ts';
2
+
3
+ //# sourceMappingURL=index.js.map
@@ -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":"AAGA;;;;;;GAMG;AACH,wBAAsB,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,CA+BxF;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAmEf"}
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
- * Get an environment variable value from .env file
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
- const lines = contents.split(/\r?\n/);
14
- for (const line of lines){
15
- // Skip empty lines and comments
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 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 } 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","path","getEnvVar","projectPath","key","envPath","join","contents","readFile","lines","split","line","trim","startsWith","includes","equalsIndex","indexOf","lineKey","substring","addOrUpdateEnvVar","value","envExists","seenKeys","Set","processedLines","keyUpdated","push","trimmedKey","has","add","writeFile","error","Error","message"],"mappings":"AAAA,OAAOA,QAAQ,cAAa;AAC5B,OAAOC,UAAU,OAAM;AAEvB;;;;;;CAMC,GACD,OAAO,eAAeC,UAAUC,WAAmB,EAAEC,GAAW;IAC9D,MAAMC,UAAUJ,KAAKK,IAAI,CAACH,aAAa;IAEvC,IAAI;QACF,MAAMI,WAAW,MAAMP,GAAGQ,QAAQ,CAACH,SAAS;QAC5C,MAAMI,QAAQF,SAASG,KAAK,CAAC;QAE7B,KAAK,MAAMC,QAAQF,MAAO;YACxB,gCAAgC;YAChC,IAAI,CAACE,KAAKC,IAAI,MAAMD,KAAKC,IAAI,GAAGC,UAAU,CAAC,MAAM;gBAC/C;YACF;YAEA,uBAAuB;YACvB,IAAI,CAACF,KAAKG,QAAQ,CAAC,MAAM;gBACvB;YACF;YAEA,MAAMC,cAAcJ,KAAKK,OAAO,CAAC;YACjC,MAAMC,UAAUN,KAAKO,SAAS,CAAC,GAAGH,aAAaH,IAAI;YAEnD,IAAIK,YAAYb,KAAK;gBACnB,OAAOO,KAAKO,SAAS,CAACH,cAAc;YACtC;QACF;QAEA,OAAO;IACT,EAAE,OAAM;QACN,sCAAsC;QACtC,OAAO;IACT;AACF;AAEA;;;;;;CAMC,GACD,OAAO,eAAeI,kBACpBhB,WAAmB,EACnBC,GAAW,EACXgB,KAAa;IAEb,MAAMf,UAAUJ,KAAKK,IAAI,CAACH,aAAa;IAEvC,IAAI;QACF,IAAII,WAAW;QACf,IAAIc,YAAY;QAEhB,iCAAiC;QACjC,IAAI;YACFd,WAAW,MAAMP,GAAGQ,QAAQ,CAACH,SAAS;YACtCgB,YAAY;QACd,EAAE,OAAM;QACN,qCAAqC;QACvC;QAEA,MAAMZ,QAAQF,SAASG,KAAK,CAAC;QAC7B,MAAMY,WAAW,IAAIC;QACrB,MAAMC,iBAA2B,EAAE;QACnC,IAAIC,aAAa;QAEjB,KAAK,MAAMd,QAAQF,MAAO;YACxB,iDAAiD;YACjD,IAAI,CAACY,aAAa,CAACV,KAAKC,IAAI,IAAI;gBAC9B;YACF;YAEA,oCAAoC;YACpC,IAAI,CAACD,KAAKC,IAAI,MAAMD,KAAKC,IAAI,GAAGC,UAAU,CAAC,MAAM;gBAC/CW,eAAeE,IAAI,CAACf;gBACpB;YACF;YAEA,uBAAuB;YACvB,IAAI,CAACA,KAAKG,QAAQ,CAAC,MAAM;gBACvBU,eAAeE,IAAI,CAACf;gBACpB;YACF;YAEA,MAAM,CAACM,QAAQ,GAAGN,KAAKD,KAAK,CAAC;YAC7B,MAAMiB,aAAaV,QAAQL,IAAI;YAE/B,8CAA8C;YAC9C,IAAIU,SAASM,GAAG,CAACD,aAAa;gBAC5B;YACF;YACAL,SAASO,GAAG,CAACF;YAEb,0BAA0B;YAC1B,IAAIA,eAAevB,KAAK;gBACtBoB,eAAeE,IAAI,CAAC,GAAGtB,IAAI,CAAC,EAAEgB,OAAO;gBACrCK,aAAa;YACf,OAAO;gBACLD,eAAeE,IAAI,CAACf;YACtB;QACF;QAEA,yBAAyB;QACzB,IAAI,CAACc,YAAY;YACfD,eAAeE,IAAI,CAAC,GAAGtB,IAAI,CAAC,EAAEgB,OAAO;QACvC;QAEA,MAAMpB,GAAG8B,SAAS,CAACzB,SAASmB,eAAelB,IAAI,CAAC,OAAO;IACzD,EAAE,OAAOyB,OAAO;QACd,MAAM,IAAIC,MACR,CAAC,4BAA4B,EAAED,iBAAiBC,QAAQD,MAAME,OAAO,GAAG,iBAAiB;IAE7F;AACF"}
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"}