@payloadcms/figma 0.0.1-alpha.40 → 0.0.1-alpha.42
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/db-content-api/generated/content-api-types.d.ts +168 -102
- 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 +82 -67
- 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/buildPathTypes.d.ts +12 -0
- package/dist/db-content-api/utilities/buildPathTypes.d.ts.map +1 -0
- package/dist/db-content-api/utilities/buildPathTypes.js +157 -0
- package/dist/db-content-api/utilities/buildPathTypes.js.map +1 -0
- 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 +18 -1
- package/dist/db-content-api/utilities/data/index.js.map +1 -1
- 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/generateSignedURL.js +1 -0
- 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/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/package.json +1 -1
- 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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/oauth/endpoints/getLoginEndpoint.ts"],"sourcesContent":["import type { CollectionConfig, Endpoint } from 'payload'\n\nimport { APIError, generateCookie, parseCookies } from 'payload'\nimport { getSafeRedirect } from 'payload/shared'\n\nimport type { Strategy } from '../strategy/index.js'\nimport type { CollectionOptions, PluginOptions, StateObj } from '../types.js'\n\nimport { getProjectToken } from '../../api/figma-api.js'\nimport { createCookieOptions } from '../utilities/createCookieOptions.js'\nimport { createDebugLogger } from '../utilities/createDebugLogger.js'\nimport { extractOrigin } from '../utilities/extractOrigin.js'\nimport { getAdminPath } from '../utilities/getAdminPath.js'\nimport { getCookieExpiration } from '../utilities/getCookieExpiration.js'\n\ninterface Args {\n collection: CollectionConfig\n collectionOptions: CollectionOptions\n endpointSlug: string\n pluginOptions: PluginOptions\n strategy: Strategy\n}\n\nconst isAbsoluteURL = (url: string) => /^[a-z][a-z\\d+.-]*:/.test(url)\n\nexport const getLoginEndpoint = ({\n collection,\n collectionOptions,\n endpointSlug,\n pluginOptions,\n strategy,\n}: Args): Endpoint => ({\n handler: async (req) => {\n const { config } = req.payload\n const adminPath = getAdminPath(config)\n try {\n await strategy.ensureMeta()\n\n const path = req.pathname\n const debugLogger = createDebugLogger(req.payload, pluginOptions.debug)\n\n let jsonBody: Record<string, unknown> = {}\n\n const contentType = req.headers.get('content-type')\n\n // Type narrow PayloadRequst's Partial<Request>\n if (typeof req.json !== 'function' || typeof req.formData !== 'function') {\n debugLogger.info({\n msg: 'Request does not have json or formData methods',\n path,\n })\n\n return Response.redirect(`${pluginOptions.redirectServerURL || config.serverURL}/login`)\n }\n\n if (collectionOptions.authorizationURLParams?.response_mode === 'query') {\n if (Object.keys(req.query).length > 0) {\n jsonBody = req.query\n }\n } else if (contentType === 'application/json') {\n jsonBody = await req.json()\n } else if (contentType === 'application/x-www-form-urlencoded') {\n const formData = await req.formData()\n jsonBody = Object.fromEntries(formData.entries())\n }\n\n const stateRaw = jsonBody.state as string\n const state: StateObj = JSON.parse(Buffer.from(stateRaw, 'base64').toString('utf-8'))\n\n const redirectDestination =\n state.serverURL || pluginOptions.redirectServerURL || config.serverURL\n let failedRedirect = `${redirectDestination}${adminPath}/login`\n let redirectToUse = `${redirectDestination}${adminPath}`\n\n if (state.redirect) {\n redirectToUse = state.redirect\n }\n\n if (state.failedRedirect) {\n failedRedirect = state.failedRedirect\n }\n\n failedRedirect = getSafeRedirect({ allowAbsoluteUrls: true, redirectTo: failedRedirect })\n redirectToUse = getSafeRedirect({ allowAbsoluteUrls: true, redirectTo: redirectToUse })\n\n if (!isAbsoluteURL(failedRedirect)) {\n return Response.json({ error: 'Invalid failed redirect provided' }, { status: 403 })\n }\n if (!isAbsoluteURL(redirectToUse)) {\n return Response.json({ error: 'Invalid redirect provided' }, { status: 403 })\n }\n\n if (Array.isArray(req.payload.config.csrf) && req.payload.config.csrf.length > 0) {\n const failedRedirectOrigin = extractOrigin(failedRedirect)\n const redirectToUseOrigin = extractOrigin(redirectToUse)\n\n if (failedRedirectOrigin && !req.payload.config.csrf.includes(failedRedirectOrigin)) {\n req.payload.logger.error(\n `Failed redirect origin ${failedRedirectOrigin} is not allowed by CSRF configuration.`,\n )\n return Response.json(\n { error: 'Failed redirect origin is not allowed by CSRF configuration.' },\n { status: 403 },\n )\n }\n\n if (redirectToUseOrigin && !req.payload.config.csrf.includes(redirectToUseOrigin)) {\n req.payload.logger.error(\n `Redirect origin ${redirectToUseOrigin} is not allowed by CSRF configuration.`,\n )\n return Response.json(\n { error: 'Redirect origin is not allowed by CSRF configuration.' },\n { status: 403 },\n )\n }\n }\n\n // Verify signature\n if (strategy.name !== req.payload.decrypt(state.sig)) {\n req.payload.logger.error('Signature mismatch. Failing login and redirecting.')\n return Response.redirect(failedRedirect)\n }\n\n if (!strategy.meta?.token_endpoint) {\n debugLogger.error({\n msg: `No metadata was obtained from the identity provider. Failing login and redirecting.`,\n path,\n })\n return Response.redirect(failedRedirect)\n }\n\n debugLogger.info({ body: jsonBody, msg: 'Received login response body', path })\n\n if (!jsonBody?.code) {\n debugLogger.error({\n msg: `No authorization code present in body. Failing login and redirecting.`,\n path,\n })\n return Response.redirect(failedRedirect)\n }\n\n // Exchange `code` from body for refresh token\n const { code } = jsonBody\n\n if (typeof code !== 'string') {\n debugLogger.error({\n msg: `Authorization code present in body is not a string. Failing login and redirecting.`,\n path,\n })\n return Response.redirect(failedRedirect)\n }\n\n const body: Record<string, string> = {\n client_id: strategy.clientID,\n code,\n code_verifier: req.payload.decrypt(state.verifier),\n grant_type: 'authorization_code',\n redirect_uri: `${redirectDestination}${config?.routes?.api || '/api'}/${\n collection.slug\n }/${endpointSlug}/login`,\n }\n\n if (strategy.collectionOptions.clientSecret) {\n body.client_secret = strategy.collectionOptions.clientSecret\n }\n\n debugLogger.info({\n msg: 'Exchanging code for access token',\n path,\n requestBody: body,\n })\n\n const tokenRes = await fetch(strategy.meta?.token_endpoint, {\n body: new URLSearchParams(body),\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n method: 'POST',\n }).then((r) => r.json())\n\n debugLogger.info({ msg: 'Token Response', path, tokenRes })\n\n const { access_token, error, error_description, expires_in: refreshTokenExpires } = tokenRes\n\n debugLogger.info({ msg: 'tokenResss', tokenRes })\n\n // Swap for project token\n const project_token = await getProjectToken(\n tokenRes.access_token,\n config.custom.figma.contentSystemId,\n )\n const refresh_token = access_token\n\n // Store project token as access_token\n\n const token = project_token.token\n const expiresIn = project_token.expiresAt\n\n if (!token) {\n const errMsg = `No ${\n collectionOptions.token || 'access_token'\n } was returned from the identity provider.`\n req.payload.logger.error({ msg: errMsg, path })\n throw new APIError(errMsg)\n }\n\n const cookieOptions = createCookieOptions({ collection, headers: req.headers, pluginOptions })\n\n if (error) {\n req.payload.logger.error('Error swapping access code with access token:')\n req.payload.logger.error({ err: error, error_description })\n return Response.redirect(failedRedirect)\n }\n\n // get the payloadRedirect cookie value\n const cookies = parseCookies(req.headers)\n\n const payloadRedirect =\n 'payloadRedirect' in cookies ? (cookies.payloadRedirect as string) : ''\n\n if (!req.responseHeaders) {\n req.responseHeaders = new Headers()\n }\n\n if (payloadRedirect) {\n if (isAbsoluteURL(payloadRedirect)) {\n redirectToUse = payloadRedirect\n } else {\n redirectToUse += payloadRedirect\n }\n\n req.responseHeaders.append(\n 'Set-Cookie',\n `payloadRedirect=${redirectToUse}; Max-Age=0; Path=/;`,\n )\n }\n\n if (refresh_token && !collectionOptions.usePayloadJWT) {\n debugLogger.info('Setting refresh token cookie')\n const refreshCookie = generateCookie({\n returnCookieAsObject: false,\n ...cookieOptions,\n name: `${strategy.cookieName}-refresh`,\n value: refresh_token,\n // Default to 1 year if no expiration\n expires: getCookieExpiration(cookieOptions?.expires ?? refreshTokenExpires ?? 31_556_952),\n }) as string\n\n req.responseHeaders.append('Set-Cookie', refreshCookie)\n }\n\n const tokenExpires = cookieOptions?.expires\n ? getCookieExpiration(cookieOptions.expires)\n : new Date(expiresIn) // Project token uses epoch time\n const tokenCookie = generateCookie({\n ...cookieOptions,\n name: strategy.cookieName,\n expires: tokenExpires,\n returnCookieAsObject: false,\n value: token,\n }) as string\n\n req.responseHeaders.append('Set-Cookie', tokenCookie)\n\n return Response.redirect(redirectToUse)\n } catch (err: unknown) {\n req.payload.logger.error({ err, msg: 'Error while attempting to login', path: req.pathname })\n\n return Response.json({ error: 'Error while attempting to login' }, { status: 500 })\n }\n },\n method: collectionOptions.authorizationURLParams?.response_mode === 'query' ? 'get' : 'post',\n path: `/${endpointSlug}/login`,\n})\n"],"names":["APIError","generateCookie","parseCookies","getSafeRedirect","getProjectToken","createCookieOptions","createDebugLogger","extractOrigin","getAdminPath","getCookieExpiration","isAbsoluteURL","url","test","getLoginEndpoint","collection","collectionOptions","endpointSlug","pluginOptions","strategy","handler","req","config","payload","adminPath","ensureMeta","path","pathname","debugLogger","debug","jsonBody","contentType","headers","get","json","formData","info","msg","Response","redirect","redirectServerURL","serverURL","authorizationURLParams","response_mode","Object","keys","query","length","fromEntries","entries","stateRaw","state","JSON","parse","Buffer","from","toString","redirectDestination","failedRedirect","redirectToUse","allowAbsoluteUrls","redirectTo","error","status","Array","isArray","csrf","failedRedirectOrigin","redirectToUseOrigin","includes","logger","name","decrypt","sig","meta","token_endpoint","body","code","client_id","clientID","code_verifier","verifier","grant_type","redirect_uri","routes","api","slug","clientSecret","client_secret","requestBody","tokenRes","fetch","URLSearchParams","method","then","r","access_token","error_description","expires_in","refreshTokenExpires","project_token","custom","figma","contentSystemId","refresh_token","token","expiresIn","expiresAt","errMsg","cookieOptions","err","cookies","payloadRedirect","responseHeaders","Headers","append","usePayloadJWT","refreshCookie","returnCookieAsObject","cookieName","value","expires","tokenExpires","Date","tokenCookie"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/oauth/endpoints/getLoginEndpoint.ts"],"sourcesContent":["import type { CollectionConfig, Endpoint } from 'payload'\n\nimport { APIError, generateCookie, parseCookies } from 'payload'\nimport { formatAdminURL, getSafeRedirect } from 'payload/shared'\n\nimport type { Strategy } from '../strategy/index.js'\nimport type { CollectionOptions, PluginOptions, StateObj } from '../types.js'\n\nimport { getProjectToken } from '../../api/figma-api.js'\nimport { createCookieOptions } from '../utilities/createCookieOptions.js'\nimport { createDebugLogger } from '../utilities/createDebugLogger.js'\nimport { extractOrigin } from '../utilities/extractOrigin.js'\nimport { getCookieExpiration } from '../utilities/getCookieExpiration.js'\n\ninterface Args {\n collection: CollectionConfig\n collectionOptions: CollectionOptions\n endpointSlug: string\n pluginOptions: PluginOptions\n strategy: Strategy\n}\n\nconst isAbsoluteURL = (url: string) => /^[a-z][a-z\\d+.-]*:/.test(url)\n\nexport const getLoginEndpoint = ({\n collection,\n collectionOptions,\n endpointSlug,\n pluginOptions,\n strategy,\n}: Args): Endpoint => ({\n handler: async (req) => {\n const { config } = req.payload\n const adminRoute = config.routes?.admin || '/admin'\n\n try {\n await strategy.ensureMeta()\n\n const path = req.pathname\n const debugLogger = createDebugLogger(req.payload, pluginOptions.debug)\n\n let jsonBody: Record<string, unknown> = {}\n\n const contentType = req.headers.get('content-type')\n\n // Type narrow PayloadRequst's Partial<Request>\n if (typeof req.json !== 'function' || typeof req.formData !== 'function') {\n debugLogger.info({\n msg: 'Request does not have json or formData methods',\n path,\n })\n\n return Response.redirect(`${pluginOptions.redirectServerURL || config.serverURL}/login`)\n }\n\n if (collectionOptions.authorizationURLParams?.response_mode === 'query') {\n if (Object.keys(req.query).length > 0) {\n jsonBody = req.query\n }\n } else if (contentType === 'application/json') {\n jsonBody = await req.json()\n } else if (contentType === 'application/x-www-form-urlencoded') {\n const formData = await req.formData()\n jsonBody = Object.fromEntries(formData.entries())\n }\n\n const stateRaw = jsonBody.state as string\n const state: StateObj = JSON.parse(Buffer.from(stateRaw, 'base64').toString('utf-8'))\n\n const redirectServerURL =\n state.serverURL || pluginOptions.redirectServerURL || config.serverURL\n let failedRedirect = formatAdminURL({\n adminRoute,\n path: '/login',\n serverURL: redirectServerURL,\n })\n let redirectToUse = formatAdminURL({\n adminRoute,\n serverURL: redirectServerURL,\n })\n\n if (state.redirect) {\n redirectToUse = state.redirect\n }\n\n if (state.failedRedirect) {\n failedRedirect = state.failedRedirect\n }\n\n failedRedirect = getSafeRedirect({ allowAbsoluteUrls: true, redirectTo: failedRedirect })\n redirectToUse = getSafeRedirect({ allowAbsoluteUrls: true, redirectTo: redirectToUse })\n\n if (!isAbsoluteURL(failedRedirect)) {\n return Response.json({ error: 'Invalid failed redirect provided' }, { status: 403 })\n }\n if (!isAbsoluteURL(redirectToUse)) {\n return Response.json({ error: 'Invalid redirect provided' }, { status: 403 })\n }\n\n if (Array.isArray(req.payload.config.csrf) && req.payload.config.csrf.length > 0) {\n const failedRedirectOrigin = extractOrigin(failedRedirect)\n const redirectToUseOrigin = extractOrigin(redirectToUse)\n\n if (failedRedirectOrigin && !req.payload.config.csrf.includes(failedRedirectOrigin)) {\n req.payload.logger.error(\n `Failed redirect origin ${failedRedirectOrigin} is not allowed by CSRF configuration.`,\n )\n return Response.json(\n { error: 'Failed redirect origin is not allowed by CSRF configuration.' },\n { status: 403 },\n )\n }\n\n if (redirectToUseOrigin && !req.payload.config.csrf.includes(redirectToUseOrigin)) {\n req.payload.logger.error(\n `Redirect origin ${redirectToUseOrigin} is not allowed by CSRF configuration.`,\n )\n return Response.json(\n { error: 'Redirect origin is not allowed by CSRF configuration.' },\n { status: 403 },\n )\n }\n }\n\n // Verify signature\n if (strategy.name !== req.payload.decrypt(state.sig)) {\n req.payload.logger.error('Signature mismatch. Failing login and redirecting.')\n return Response.redirect(failedRedirect)\n }\n\n if (!strategy.meta?.token_endpoint) {\n debugLogger.error({\n msg: `No metadata was obtained from the identity provider. Failing login and redirecting.`,\n path,\n })\n return Response.redirect(failedRedirect)\n }\n\n debugLogger.info({ body: jsonBody, msg: 'Received login response body', path })\n\n if (!jsonBody?.code) {\n debugLogger.error({\n msg: `No authorization code present in body. Failing login and redirecting.`,\n path,\n })\n return Response.redirect(failedRedirect)\n }\n\n // Exchange `code` from body for refresh token\n const { code } = jsonBody\n\n if (typeof code !== 'string') {\n debugLogger.error({\n msg: `Authorization code present in body is not a string. Failing login and redirecting.`,\n path,\n })\n return Response.redirect(failedRedirect)\n }\n\n const body: Record<string, string> = {\n client_id: strategy.clientID,\n code,\n code_verifier: req.payload.decrypt(state.verifier),\n grant_type: 'authorization_code',\n redirect_uri: formatAdminURL({\n apiRoute: config.routes?.api || '/api',\n path: `/${collection.slug}/${endpointSlug}/login`,\n serverURL: redirectServerURL,\n }),\n }\n\n if (strategy.collectionOptions.clientSecret) {\n body.client_secret = strategy.collectionOptions.clientSecret\n }\n\n debugLogger.info({\n msg: 'Exchanging code for access token',\n path,\n requestBody: body,\n })\n\n const tokenRes = await fetch(strategy.meta?.token_endpoint, {\n body: new URLSearchParams(body),\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n method: 'POST',\n }).then((r) => r.json())\n\n debugLogger.info({ msg: 'Token Response', path, tokenRes })\n\n const { access_token, error, error_description, expires_in: refreshTokenExpires } = tokenRes\n\n debugLogger.info({ msg: 'tokenResss', tokenRes })\n\n // Swap for project token\n const project_token = await getProjectToken(\n tokenRes.access_token,\n config.custom.figma.contentSystemId,\n )\n const refresh_token = access_token\n\n // Store project token as access_token\n\n const token = project_token.token\n const expiresIn = project_token.expiresAt\n\n if (!token) {\n const errMsg = `No ${\n collectionOptions.token || 'access_token'\n } was returned from the identity provider.`\n req.payload.logger.error({ msg: errMsg, path })\n throw new APIError(errMsg)\n }\n\n const cookieOptions = createCookieOptions({ collection, headers: req.headers, pluginOptions })\n\n if (error) {\n req.payload.logger.error('Error swapping access code with access token:')\n req.payload.logger.error({ err: error, error_description })\n return Response.redirect(failedRedirect)\n }\n\n // get the payloadRedirect cookie value\n const cookies = parseCookies(req.headers)\n\n const payloadRedirect =\n 'payloadRedirect' in cookies ? (cookies.payloadRedirect as string) : ''\n\n if (!req.responseHeaders) {\n req.responseHeaders = new Headers()\n }\n\n if (payloadRedirect) {\n if (isAbsoluteURL(payloadRedirect)) {\n redirectToUse = payloadRedirect\n } else {\n redirectToUse += payloadRedirect\n }\n\n req.responseHeaders.append(\n 'Set-Cookie',\n `payloadRedirect=${redirectToUse}; Max-Age=0; Path=/;`,\n )\n }\n\n if (refresh_token && !collectionOptions.usePayloadJWT) {\n debugLogger.info('Setting refresh token cookie')\n const refreshCookie = generateCookie({\n returnCookieAsObject: false,\n ...cookieOptions,\n name: `${strategy.cookieName}-refresh`,\n value: refresh_token,\n // Default to 1 year if no expiration\n expires: getCookieExpiration(cookieOptions?.expires ?? refreshTokenExpires ?? 31_556_952),\n }) as string\n\n req.responseHeaders.append('Set-Cookie', refreshCookie)\n }\n\n const tokenExpires = cookieOptions?.expires\n ? getCookieExpiration(cookieOptions.expires)\n : new Date(expiresIn) // Project token uses epoch time\n const tokenCookie = generateCookie({\n ...cookieOptions,\n name: strategy.cookieName,\n expires: tokenExpires,\n returnCookieAsObject: false,\n value: token,\n }) as string\n\n req.responseHeaders.append('Set-Cookie', tokenCookie)\n\n return Response.redirect(redirectToUse)\n } catch (err: unknown) {\n req.payload.logger.error({ err, msg: 'Error while attempting to login', path: req.pathname })\n\n return Response.json({ error: 'Error while attempting to login' }, { status: 500 })\n }\n },\n method: collectionOptions.authorizationURLParams?.response_mode === 'query' ? 'get' : 'post',\n path: `/${endpointSlug}/login`,\n})\n"],"names":["APIError","generateCookie","parseCookies","formatAdminURL","getSafeRedirect","getProjectToken","createCookieOptions","createDebugLogger","extractOrigin","getCookieExpiration","isAbsoluteURL","url","test","getLoginEndpoint","collection","collectionOptions","endpointSlug","pluginOptions","strategy","handler","req","config","payload","adminRoute","routes","admin","ensureMeta","path","pathname","debugLogger","debug","jsonBody","contentType","headers","get","json","formData","info","msg","Response","redirect","redirectServerURL","serverURL","authorizationURLParams","response_mode","Object","keys","query","length","fromEntries","entries","stateRaw","state","JSON","parse","Buffer","from","toString","failedRedirect","redirectToUse","allowAbsoluteUrls","redirectTo","error","status","Array","isArray","csrf","failedRedirectOrigin","redirectToUseOrigin","includes","logger","name","decrypt","sig","meta","token_endpoint","body","code","client_id","clientID","code_verifier","verifier","grant_type","redirect_uri","apiRoute","api","slug","clientSecret","client_secret","requestBody","tokenRes","fetch","URLSearchParams","method","then","r","access_token","error_description","expires_in","refreshTokenExpires","project_token","custom","figma","contentSystemId","refresh_token","token","expiresIn","expiresAt","errMsg","cookieOptions","err","cookies","payloadRedirect","responseHeaders","Headers","append","usePayloadJWT","refreshCookie","returnCookieAsObject","cookieName","value","expires","tokenExpires","Date","tokenCookie"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getLogoutEndpoint.d.ts","sourceRoot":"","sources":["../../../src/oauth/endpoints/getLogoutEndpoint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"getLogoutEndpoint.d.ts","sourceRoot":"","sources":["../../../src/oauth/endpoints/getLogoutEndpoint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAMzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAMhD,UAAU,IAAI;IACZ,UAAU,EAAE,gBAAgB,CAAA;IAC5B,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,aAAa,CAAA;IAC5B,QAAQ,EAAE,QAAQ,CAAA;CACnB;AAED,eAAO,MAAM,iBAAiB,2DAK3B,IAAI,KAAG,QA+ER,CAAA"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import jwt from 'jsonwebtoken';
|
|
2
2
|
import { parseCookies } from 'payload';
|
|
3
|
+
import { formatAdminURL } from 'payload/shared';
|
|
3
4
|
import { clearCookie } from '../utilities/clearCookie.js';
|
|
4
5
|
import { createCookieOptions } from '../utilities/createCookieOptions.js';
|
|
5
6
|
import { createDebugLogger } from '../utilities/createDebugLogger.js';
|
|
6
|
-
import { getAdminPath } from '../utilities/getAdminPath.js';
|
|
7
7
|
export const getLogoutEndpoint = ({ collection, endpointSlug, pluginOptions, strategy })=>({
|
|
8
8
|
handler: async (req)=>{
|
|
9
9
|
const debugLogger = createDebugLogger(req.payload, pluginOptions.debug);
|
|
@@ -23,12 +23,15 @@ export const getLogoutEndpoint = ({ collection, endpointSlug, pluginOptions, str
|
|
|
23
23
|
}
|
|
24
24
|
try {
|
|
25
25
|
await strategy.ensureMeta();
|
|
26
|
-
const
|
|
27
|
-
let successfulLogoutRedirectDest =
|
|
26
|
+
const redirectServerlURL = pluginOptions.redirectServerURL || config.serverURL;
|
|
27
|
+
let successfulLogoutRedirectDest = formatAdminURL({
|
|
28
|
+
adminRoute: config.routes?.admin || '/admin',
|
|
29
|
+
serverURL: redirectServerlURL
|
|
30
|
+
});
|
|
28
31
|
if (typeof req.query.redirect === 'string') {
|
|
29
32
|
successfulLogoutRedirectDest = req.query.redirect;
|
|
30
33
|
}
|
|
31
|
-
if (!
|
|
34
|
+
if (!redirectServerlURL && !req.query.redirect) {
|
|
32
35
|
debugLogger.error({
|
|
33
36
|
msg: 'No redirect URL provided',
|
|
34
37
|
path: req.pathname
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/oauth/endpoints/getLogoutEndpoint.ts"],"sourcesContent":["import type { CollectionConfig, Endpoint } from 'payload'\n\nimport jwt from 'jsonwebtoken'\nimport { parseCookies } from 'payload'\n\nimport type { Strategy } from '../strategy/index.js'\nimport type { PluginOptions } from '../types.js'\n\nimport { clearCookie } from '../utilities/clearCookie.js'\nimport { createCookieOptions } from '../utilities/createCookieOptions.js'\nimport { createDebugLogger } from '../utilities/createDebugLogger.js'\
|
|
1
|
+
{"version":3,"sources":["../../../src/oauth/endpoints/getLogoutEndpoint.ts"],"sourcesContent":["import type { CollectionConfig, Endpoint } from 'payload'\n\nimport jwt from 'jsonwebtoken'\nimport { parseCookies } from 'payload'\nimport { formatAdminURL } from 'payload/shared'\n\nimport type { Strategy } from '../strategy/index.js'\nimport type { PluginOptions } from '../types.js'\n\nimport { clearCookie } from '../utilities/clearCookie.js'\nimport { createCookieOptions } from '../utilities/createCookieOptions.js'\nimport { createDebugLogger } from '../utilities/createDebugLogger.js'\n\ninterface Args {\n collection: CollectionConfig\n endpointSlug: string\n pluginOptions: PluginOptions\n strategy: Strategy\n}\n\nexport const getLogoutEndpoint = ({\n collection,\n endpointSlug,\n pluginOptions,\n strategy,\n}: Args): Endpoint => ({\n handler: async (req) => {\n const debugLogger = createDebugLogger(req.payload, pluginOptions.debug)\n const { config } = req.payload\n\n if (!req) {\n debugLogger.error({ msg: 'Invalid request to logout endpoint' })\n return Response.json({ error: 'Invalid request to logout endpoint' }, { status: 400 })\n }\n\n if (!req.responseHeaders) {\n req.responseHeaders = new Headers()\n }\n\n try {\n await strategy.ensureMeta()\n\n const redirectServerlURL = pluginOptions.redirectServerURL || config.serverURL\n\n let successfulLogoutRedirectDest = formatAdminURL({\n adminRoute: config.routes?.admin || '/admin',\n serverURL: redirectServerlURL,\n })\n\n if (typeof req.query.redirect === 'string') {\n successfulLogoutRedirectDest = req.query.redirect\n }\n\n if (!redirectServerlURL && !req.query.redirect) {\n debugLogger.error({ msg: 'No redirect URL provided', path: req.pathname })\n return Response.json({ error: 'No redirect URL provided' }, { status: 400 })\n }\n\n const cookieOptions = createCookieOptions({ collection, headers: req.headers, pluginOptions })\n\n let redirectDestination = successfulLogoutRedirectDest\n if (strategy.meta?.end_session_endpoint) {\n const cookies = parseCookies(req.headers)\n\n const token = cookies.get(strategy.cookieName)\n if (!token) {\n debugLogger.error({ msg: 'Unable to parse token from cookie.' })\n return Response.json({ error: 'Unable to parse token from cookie.' }, { status: 400 })\n }\n\n const decodedToken = jwt.decode(token)\n const sid = typeof decodedToken === 'object' && decodedToken ? decodedToken.sid : undefined\n\n const body = {\n client_id: strategy.clientID,\n logout_hint: sid,\n post_logout_redirect_uri: successfulLogoutRedirectDest,\n }\n\n const qs = new URLSearchParams(body).toString()\n redirectDestination = `${strategy.meta.end_session_endpoint}?${qs}`\n }\n\n clearCookie({\n cookieName: strategy.cookieName,\n cookieOptions,\n req,\n })\n\n clearCookie({\n cookieName: `${strategy.cookieName}-refresh`,\n cookieOptions,\n req,\n })\n\n return Response.redirect(redirectDestination)\n } catch (err: unknown) {\n req.payload.logger.error({ err, msg: 'Error while attempting to logout', path: req.pathname })\n\n return Response.json({ error: 'Error while attempting to logout' }, { status: 500 })\n }\n },\n method: 'get',\n path: `/${endpointSlug}/logout`,\n})\n"],"names":["jwt","parseCookies","formatAdminURL","clearCookie","createCookieOptions","createDebugLogger","getLogoutEndpoint","collection","endpointSlug","pluginOptions","strategy","handler","req","debugLogger","payload","debug","config","error","msg","Response","json","status","responseHeaders","Headers","ensureMeta","redirectServerlURL","redirectServerURL","serverURL","successfulLogoutRedirectDest","adminRoute","routes","admin","query","redirect","path","pathname","cookieOptions","headers","redirectDestination","meta","end_session_endpoint","cookies","token","get","cookieName","decodedToken","decode","sid","undefined","body","client_id","clientID","logout_hint","post_logout_redirect_uri","qs","URLSearchParams","toString","err","logger","method"],"mappings":"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"}
|
|
@@ -52,7 +52,7 @@ import { contentApiStorageAdapter } from '../storage-content-api/index.js';
|
|
|
52
52
|
db = contentAPIAdapter({
|
|
53
53
|
auth: {
|
|
54
54
|
mode: 'tokenStore',
|
|
55
|
-
tokenStore: getTokenStore()
|
|
55
|
+
tokenStore: getTokenStore(getEnvironment())
|
|
56
56
|
},
|
|
57
57
|
contentSystemId,
|
|
58
58
|
url
|
|
@@ -74,7 +74,7 @@ import { contentApiStorageAdapter } from '../storage-content-api/index.js';
|
|
|
74
74
|
} : {
|
|
75
75
|
auth: {
|
|
76
76
|
mode: 'tokenStore',
|
|
77
|
-
tokenStore: getTokenStore()
|
|
77
|
+
tokenStore: getTokenStore(getEnvironment())
|
|
78
78
|
},
|
|
79
79
|
baseUrl: url,
|
|
80
80
|
contentSystemId
|
|
@@ -137,7 +137,7 @@ import { contentApiStorageAdapter } from '../storage-content-api/index.js';
|
|
|
137
137
|
response_mode: 'query',
|
|
138
138
|
response_type: 'code'
|
|
139
139
|
},
|
|
140
|
-
clientID: process.env.FIGMA_OAUTH_CLIENT_ID
|
|
140
|
+
clientID: process.env.FIGMA_OAUTH_CLIENT_ID,
|
|
141
141
|
clientSecret: process.env.FIGMA_OAUTH_CLIENT_SECRET,
|
|
142
142
|
identityMetadata: ENV_CONFIG[env].identityMetadata,
|
|
143
143
|
scope: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/plugin/build-config.ts"],"sourcesContent":["import type { Config, SanitizedConfig } from 'payload'\n\nimport { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage'\nimport { initClientUploads } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { lexicalEditor } from '@payloadcms/richtext-lexical'\nimport { buildConfig as payloadBuildConfig } from 'payload'\n\nimport type { StorageClientConfig } from '../storage-content-api/index.js'\n\nimport { getTokenStore } from '../auth/token-store.js'\nimport { ENV_CONFIG, getEnvironment } from '../constants.js'\nimport { contentAPIAdapter } from '../db-content-api/index.js'\nimport { health } from '../endpoints/health.js'\nimport { oAuth2Plugin } from '../oauth/index.js'\nimport { createStorageClient } from '../storage-content-api/client.js'\nimport { getGenerateSignedURLHandler } from '../storage-content-api/client-uploads/generateSignedURL.js'\nimport { contentApiStorageAdapter } from '../storage-content-api/index.js'\n\n/**\n * Configuration type for Payload projects on the Figma platform.\n *\n * Differences from standard Payload Config:\n * - `db`: Omitted (auto-injected by Figma platform)\n * - `secret`: Omitted (auto-injected by Figma platform)\n * - `editor`: Optional (defaults to lexicalEditor() if not provided)\n *\n * @example\n * // Minimal config\n * const config: FigmaConfig = {\n * figma: {\n * contentSystemId: process.env.FIGMA_CONTENT_SYSTEM_ID!\n * },\n * collections: [...]\n * }\n *\n * @example\n * // With custom editor\n * const config: FigmaConfig = {\n * figma: {\n * contentSystemId: process.env.FIGMA_CONTENT_SYSTEM_ID!\n * },\n * collections: [...],\n * editor: lexicalEditor({ features: [...] })\n * }\n *\n * @example\n * // Disable Content System (use custom db)\n * const config: FigmaConfig = {\n * figma: {\n * contentSystemId: process.env.FIGMA_CONTENT_SYSTEM_ID!,\n * useContentSystem: false\n * },\n * db: mongooseAdapter({ url: process.env.DATABASE_URI }),\n * collections: [...]\n * }\n */\nexport type FigmaConfig = {\n editor?: Config['editor']\n} & {\n figma: {\n contentSystemId: string\n storage?: boolean\n useContentSystem?: boolean\n }\n} & Omit<Config, 'db' | 'editor' | 'secret'> &\n Partial<Pick<Config, 'db'>>\n\n/**\n * Figma platform wrapper for Payload's buildConfig.\n * Automatically injects:\n * - Figma database adapter\n * - Secret (platform-provided)\n * - Default lexicalEditor (if no custom editor specified)\n *\n * @param config - Payload configuration (db and secret omitted, editor optional)\n * @returns Promise<SanitizedConfig> - Built Payload configuration\n */\nexport async function buildFigmaConfig(config: FigmaConfig): Promise<SanitizedConfig> {\n // Validate required environment variable\n if (!config.figma.contentSystemId) {\n throw new Error(\n 'FIGMA_CONTENT_SYSTEM_ID is required. ' +\n 'Ensure your .env file contains: FIGMA_CONTENT_SYSTEM_ID=your-content-system-id',\n )\n }\n\n const env = getEnvironment()\n const url = process.env.FIGMA_CONTENT_API_URL || ENV_CONFIG[env].contentApiUrl\n const { contentSystemId } = config.figma\n\n // Determine database adapter based on environment\n let db: Config['db']\n\n if (config.figma.useContentSystem === false) {\n db = config.db!\n } else if (process.env.FIGMA_CONTENT_API_ACCESS_KEY) {\n db = contentAPIAdapter({\n auth: { apiKey: process.env.FIGMA_CONTENT_API_ACCESS_KEY, mode: 'apiKey' },\n contentSystemId,\n url,\n })\n } else if (process.env.FIGMA_DEV_JWT === 'true') {\n db = contentAPIAdapter({\n auth: { mode: 'devJwt' },\n contentSystemId,\n url,\n })\n } else {\n db = contentAPIAdapter({\n auth: { mode: 'tokenStore', tokenStore: getTokenStore() },\n contentSystemId,\n url,\n })\n }\n\n // Build storage plugin if there are upload collections and storage is not disabled\n const uploadCollections = (config.collections || []).filter((c) => c.upload)\n let storagePlugin: ReturnType<typeof cloudStoragePlugin> | undefined\n\n if (uploadCollections.length > 0 && config.figma.storage !== false) {\n const storageConfig: StorageClientConfig = process.env.FIGMA_CONTENT_API_ACCESS_KEY\n ? { baseUrl: url, contentApiKey: process.env.FIGMA_CONTENT_API_ACCESS_KEY }\n : process.env.FIGMA_DEV_JWT === 'true'\n ? {\n auth: { mode: 'devJwt' },\n baseUrl: url,\n contentSystemId,\n }\n : {\n auth: { mode: 'tokenStore', tokenStore: getTokenStore() },\n baseUrl: url,\n contentSystemId,\n }\n\n const adapter = contentApiStorageAdapter(storageConfig)\n const storageClient = createStorageClient(storageConfig)\n\n const collectionsMap = uploadCollections.reduce(\n (acc, c) => {\n acc[c.slug] = { adapter, disableLocalStorage: true }\n return acc\n },\n {} as Record<string, { adapter: typeof adapter; disableLocalStorage: boolean }>,\n )\n\n initClientUploads({\n clientHandler: '@payloadcms/figma/client#ContentApiClientUploadHandler',\n collections: collectionsMap,\n config: config as unknown as Config,\n enabled: true,\n serverHandler: getGenerateSignedURLHandler({ client: storageClient }),\n serverHandlerPath: '/content-api-storage-signed-url',\n })\n\n storagePlugin = cloudStoragePlugin({ collections: collectionsMap })\n }\n\n // Build complete config with Figma platform defaults\n const configWithFigmaDefaults: Config = {\n ...config,\n admin: {\n // Auto-refresh tokens before expiry to prevent unexpected logouts.\n // The project token (access token) expires in ~15 minutes, so the client\n // will automatically refresh at ~14 minutes to keep the session alive.\n autoRefresh: true,\n ...config.admin,\n },\n custom: {\n figma: config.figma || {},\n },\n db,\n editor: config.editor ?? lexicalEditor(),\n endpoints: [...(config.endpoints ?? []), health],\n secret: 'MY_SECRET',\n // Add oauth to plugins if not already present\n plugins: [\n ...(config.plugins ?? []),\n ...(storagePlugin ? [storagePlugin] : []),\n oAuth2Plugin({\n collections: [\n {\n slug: 'users',\n // Use adapter because Figma app doesn't support OIDC scopes\n authorizationURLParams: {\n response_mode: 'query',\n response_type: 'code',\n },\n clientID: process.env.FIGMA_OAUTH_CLIENT_ID || ENV_CONFIG[env].clientId,\n clientSecret: process.env.FIGMA_OAUTH_CLIENT_SECRET,\n identityMetadata: ENV_CONFIG[env].identityMetadata,\n scope: ['current_user:read'],\n token: 'id_token', // Adapter creates JWT id_token from Figma user info\n usernameField: 'email',\n },\n ],\n debug: !!process.env.DEBUG,\n disabled: false,\n }),\n ],\n }\n\n // Delegate to Payload's buildConfig\n return await payloadBuildConfig(configWithFigmaDefaults)\n}\n"],"names":["cloudStoragePlugin","initClientUploads","lexicalEditor","buildConfig","payloadBuildConfig","getTokenStore","ENV_CONFIG","getEnvironment","contentAPIAdapter","health","oAuth2Plugin","createStorageClient","getGenerateSignedURLHandler","contentApiStorageAdapter","buildFigmaConfig","config","figma","contentSystemId","Error","env","url","process","FIGMA_CONTENT_API_URL","contentApiUrl","db","useContentSystem","FIGMA_CONTENT_API_ACCESS_KEY","auth","apiKey","mode","FIGMA_DEV_JWT","tokenStore","uploadCollections","collections","filter","c","upload","storagePlugin","length","storage","storageConfig","baseUrl","contentApiKey","adapter","storageClient","collectionsMap","reduce","acc","slug","disableLocalStorage","clientHandler","enabled","serverHandler","client","serverHandlerPath","configWithFigmaDefaults","admin","autoRefresh","custom","editor","endpoints","secret","plugins","authorizationURLParams","response_mode","response_type","clientID","FIGMA_OAUTH_CLIENT_ID","clientId","clientSecret","FIGMA_OAUTH_CLIENT_SECRET","identityMetadata","scope","token","usernameField","debug","DEBUG","disabled"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/plugin/build-config.ts"],"sourcesContent":["import type { Config, SanitizedConfig } from 'payload'\n\nimport { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage'\nimport { initClientUploads } from '@payloadcms/plugin-cloud-storage/utilities'\nimport { lexicalEditor } from '@payloadcms/richtext-lexical'\nimport { buildConfig as payloadBuildConfig } from 'payload'\n\nimport type { StorageClientConfig } from '../storage-content-api/index.js'\n\nimport { getTokenStore } from '../auth/token-store.js'\nimport { ENV_CONFIG, getEnvironment } from '../constants.js'\nimport { contentAPIAdapter } from '../db-content-api/index.js'\nimport { health } from '../endpoints/health.js'\nimport { oAuth2Plugin } from '../oauth/index.js'\nimport { createStorageClient } from '../storage-content-api/client.js'\nimport { getGenerateSignedURLHandler } from '../storage-content-api/client-uploads/generateSignedURL.js'\nimport { contentApiStorageAdapter } from '../storage-content-api/index.js'\n\n/**\n * Configuration type for Payload projects on the Figma platform.\n *\n * Differences from standard Payload Config:\n * - `db`: Omitted (auto-injected by Figma platform)\n * - `secret`: Omitted (auto-injected by Figma platform)\n * - `editor`: Optional (defaults to lexicalEditor() if not provided)\n *\n * @example\n * // Minimal config\n * const config: FigmaConfig = {\n * figma: {\n * contentSystemId: process.env.FIGMA_CONTENT_SYSTEM_ID!\n * },\n * collections: [...]\n * }\n *\n * @example\n * // With custom editor\n * const config: FigmaConfig = {\n * figma: {\n * contentSystemId: process.env.FIGMA_CONTENT_SYSTEM_ID!\n * },\n * collections: [...],\n * editor: lexicalEditor({ features: [...] })\n * }\n *\n * @example\n * // Disable Content System (use custom db)\n * const config: FigmaConfig = {\n * figma: {\n * contentSystemId: process.env.FIGMA_CONTENT_SYSTEM_ID!,\n * useContentSystem: false\n * },\n * db: mongooseAdapter({ url: process.env.DATABASE_URI }),\n * collections: [...]\n * }\n */\nexport type FigmaConfig = {\n editor?: Config['editor']\n} & {\n figma: {\n contentSystemId: string\n storage?: boolean\n useContentSystem?: boolean\n }\n} & Omit<Config, 'db' | 'editor' | 'secret'> &\n Partial<Pick<Config, 'db'>>\n\n/**\n * Figma platform wrapper for Payload's buildConfig.\n * Automatically injects:\n * - Figma database adapter\n * - Secret (platform-provided)\n * - Default lexicalEditor (if no custom editor specified)\n *\n * @param config - Payload configuration (db and secret omitted, editor optional)\n * @returns Promise<SanitizedConfig> - Built Payload configuration\n */\nexport async function buildFigmaConfig(config: FigmaConfig): Promise<SanitizedConfig> {\n // Validate required environment variable\n if (!config.figma.contentSystemId) {\n throw new Error(\n 'FIGMA_CONTENT_SYSTEM_ID is required. ' +\n 'Ensure your .env file contains: FIGMA_CONTENT_SYSTEM_ID=your-content-system-id',\n )\n }\n\n const env = getEnvironment()\n const url = process.env.FIGMA_CONTENT_API_URL || ENV_CONFIG[env].contentApiUrl\n const { contentSystemId } = config.figma\n\n // Determine database adapter based on environment\n let db: Config['db']\n\n if (config.figma.useContentSystem === false) {\n db = config.db!\n } else if (process.env.FIGMA_CONTENT_API_ACCESS_KEY) {\n db = contentAPIAdapter({\n auth: { apiKey: process.env.FIGMA_CONTENT_API_ACCESS_KEY, mode: 'apiKey' },\n contentSystemId,\n url,\n })\n } else if (process.env.FIGMA_DEV_JWT === 'true') {\n db = contentAPIAdapter({\n auth: { mode: 'devJwt' },\n contentSystemId,\n url,\n })\n } else {\n db = contentAPIAdapter({\n auth: { mode: 'tokenStore', tokenStore: getTokenStore(getEnvironment()) },\n contentSystemId,\n url,\n })\n }\n\n // Build storage plugin if there are upload collections and storage is not disabled\n const uploadCollections = (config.collections || []).filter((c) => c.upload)\n let storagePlugin: ReturnType<typeof cloudStoragePlugin> | undefined\n\n if (uploadCollections.length > 0 && config.figma.storage !== false) {\n const storageConfig: StorageClientConfig = process.env.FIGMA_CONTENT_API_ACCESS_KEY\n ? { baseUrl: url, contentApiKey: process.env.FIGMA_CONTENT_API_ACCESS_KEY }\n : process.env.FIGMA_DEV_JWT === 'true'\n ? {\n auth: { mode: 'devJwt' },\n baseUrl: url,\n contentSystemId,\n }\n : {\n auth: { mode: 'tokenStore', tokenStore: getTokenStore(getEnvironment()) },\n baseUrl: url,\n contentSystemId,\n }\n\n const adapter = contentApiStorageAdapter(storageConfig)\n const storageClient = createStorageClient(storageConfig)\n\n const collectionsMap = uploadCollections.reduce(\n (acc, c) => {\n acc[c.slug] = { adapter, disableLocalStorage: true }\n return acc\n },\n {} as Record<string, { adapter: typeof adapter; disableLocalStorage: boolean }>,\n )\n\n initClientUploads({\n clientHandler: '@payloadcms/figma/client#ContentApiClientUploadHandler',\n collections: collectionsMap,\n config: config as unknown as Config,\n enabled: true,\n serverHandler: getGenerateSignedURLHandler({ client: storageClient }),\n serverHandlerPath: '/content-api-storage-signed-url',\n })\n\n storagePlugin = cloudStoragePlugin({ collections: collectionsMap })\n }\n\n // Build complete config with Figma platform defaults\n const configWithFigmaDefaults: Config = {\n ...config,\n admin: {\n // Auto-refresh tokens before expiry to prevent unexpected logouts.\n // The project token (access token) expires in ~15 minutes, so the client\n // will automatically refresh at ~14 minutes to keep the session alive.\n autoRefresh: true,\n ...config.admin,\n },\n custom: {\n figma: config.figma || {},\n },\n db,\n editor: config.editor ?? lexicalEditor(),\n endpoints: [...(config.endpoints ?? []), health],\n secret: 'MY_SECRET',\n // Add oauth to plugins if not already present\n plugins: [\n ...(config.plugins ?? []),\n ...(storagePlugin ? [storagePlugin] : []),\n oAuth2Plugin({\n collections: [\n {\n slug: 'users',\n // Use adapter because Figma app doesn't support OIDC scopes\n authorizationURLParams: {\n response_mode: 'query',\n response_type: 'code',\n },\n clientID: process.env.FIGMA_OAUTH_CLIENT_ID!,\n clientSecret: process.env.FIGMA_OAUTH_CLIENT_SECRET,\n identityMetadata: ENV_CONFIG[env].identityMetadata,\n scope: ['current_user:read'],\n token: 'id_token', // Adapter creates JWT id_token from Figma user info\n usernameField: 'email',\n },\n ],\n debug: !!process.env.DEBUG,\n disabled: false,\n }),\n ],\n }\n\n // Delegate to Payload's buildConfig\n return await payloadBuildConfig(configWithFigmaDefaults)\n}\n"],"names":["cloudStoragePlugin","initClientUploads","lexicalEditor","buildConfig","payloadBuildConfig","getTokenStore","ENV_CONFIG","getEnvironment","contentAPIAdapter","health","oAuth2Plugin","createStorageClient","getGenerateSignedURLHandler","contentApiStorageAdapter","buildFigmaConfig","config","figma","contentSystemId","Error","env","url","process","FIGMA_CONTENT_API_URL","contentApiUrl","db","useContentSystem","FIGMA_CONTENT_API_ACCESS_KEY","auth","apiKey","mode","FIGMA_DEV_JWT","tokenStore","uploadCollections","collections","filter","c","upload","storagePlugin","length","storage","storageConfig","baseUrl","contentApiKey","adapter","storageClient","collectionsMap","reduce","acc","slug","disableLocalStorage","clientHandler","enabled","serverHandler","client","serverHandlerPath","configWithFigmaDefaults","admin","autoRefresh","custom","editor","endpoints","secret","plugins","authorizationURLParams","response_mode","response_type","clientID","FIGMA_OAUTH_CLIENT_ID","clientSecret","FIGMA_OAUTH_CLIENT_SECRET","identityMetadata","scope","token","usernameField","debug","DEBUG","disabled"],"mappings":"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"}
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
* These are kept separate to avoid breaking changes when the main spec is regenerated.
|
|
4
4
|
*/
|
|
5
5
|
export type UploadPaths = {
|
|
6
|
-
'/api/v0/uploads/{filename}': {
|
|
6
|
+
'/api/v0/uploads/{collectionId}/{filename}': {
|
|
7
7
|
delete: {
|
|
8
8
|
parameters: {
|
|
9
9
|
path: {
|
|
10
|
+
collectionId: string;
|
|
10
11
|
filename: string;
|
|
11
12
|
};
|
|
12
13
|
};
|
|
@@ -26,6 +27,7 @@ export type UploadPaths = {
|
|
|
26
27
|
requestBody: {
|
|
27
28
|
content: {
|
|
28
29
|
'application/json': {
|
|
30
|
+
collectionId: string;
|
|
29
31
|
expiresIn: number;
|
|
30
32
|
filename: string;
|
|
31
33
|
};
|
|
@@ -47,6 +49,7 @@ export type UploadPaths = {
|
|
|
47
49
|
requestBody: {
|
|
48
50
|
content: {
|
|
49
51
|
'application/json': {
|
|
52
|
+
collectionId: string;
|
|
50
53
|
filename: string;
|
|
51
54
|
uploadId: string;
|
|
52
55
|
};
|
|
@@ -68,6 +71,7 @@ export type UploadPaths = {
|
|
|
68
71
|
requestBody: {
|
|
69
72
|
content: {
|
|
70
73
|
'application/json': {
|
|
74
|
+
collectionId: string;
|
|
71
75
|
filename: string;
|
|
72
76
|
parts: Array<{
|
|
73
77
|
ETag: string;
|
|
@@ -93,6 +97,7 @@ export type UploadPaths = {
|
|
|
93
97
|
requestBody: {
|
|
94
98
|
content: {
|
|
95
99
|
'application/json': {
|
|
100
|
+
collectionId: string;
|
|
96
101
|
contentType: string;
|
|
97
102
|
filename: string;
|
|
98
103
|
};
|
|
@@ -114,6 +119,7 @@ export type UploadPaths = {
|
|
|
114
119
|
requestBody: {
|
|
115
120
|
content: {
|
|
116
121
|
'application/json': {
|
|
122
|
+
collectionId: string;
|
|
117
123
|
expiresIn: number;
|
|
118
124
|
filename: string;
|
|
119
125
|
partNumbers: number[];
|
|
@@ -140,6 +146,7 @@ export type UploadPaths = {
|
|
|
140
146
|
requestBody: {
|
|
141
147
|
content: {
|
|
142
148
|
'application/json': {
|
|
149
|
+
collectionId: string;
|
|
143
150
|
contentType?: string;
|
|
144
151
|
expiresIn: number;
|
|
145
152
|
filename: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-types.d.ts","sourceRoot":"","sources":["../../src/storage-content-api/api-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,WAAW,GAAG;IACxB,
|
|
1
|
+
{"version":3,"file":"api-types.d.ts","sourceRoot":"","sources":["../../src/storage-content-api/api-types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/storage-content-api/api-types.ts"],"sourcesContent":["/**\n * Upload API types extracted from Content API OpenAPI spec.\n * These are kept separate to avoid breaking changes when the main spec is regenerated.\n */\n\nexport type UploadPaths = {\n '/api/v0/uploads/{filename}': {\n delete: {\n parameters: {\n path: {\n filename: string\n }\n }\n responses: {\n 200: {\n content: {\n 'application/json': { success: true }\n }\n }\n }\n }\n }\n '/api/v0/uploads/download/sign': {\n post: {\n requestBody: {\n content: {\n 'application/json': {\n expiresIn: number\n filename: string\n }\n }\n }\n responses: {\n 200: {\n content: {\n 'application/json': { url: string }\n }\n }\n }\n }\n }\n '/api/v0/uploads/multipart/abort': {\n post: {\n requestBody: {\n content: {\n 'application/json': {\n filename: string\n uploadId: string\n }\n }\n }\n responses: {\n 200: {\n content: {\n 'application/json': { success: true }\n }\n }\n }\n }\n }\n '/api/v0/uploads/multipart/complete': {\n post: {\n requestBody: {\n content: {\n 'application/json': {\n filename: string\n parts: Array<{ ETag: string; PartNumber: number }>\n uploadId: string\n }\n }\n }\n responses: {\n 200: {\n content: {\n 'application/json': { success: true }\n }\n }\n }\n }\n }\n '/api/v0/uploads/multipart/initiate': {\n post: {\n requestBody: {\n content: {\n 'application/json': {\n contentType: string\n filename: string\n }\n }\n }\n responses: {\n 200: {\n content: {\n 'application/json': { uploadId: string }\n }\n }\n }\n }\n }\n '/api/v0/uploads/multipart/parts': {\n post: {\n requestBody: {\n content: {\n 'application/json': {\n expiresIn: number\n filename: string\n partNumbers: number[]\n uploadId: string\n }\n }\n }\n responses: {\n 200: {\n content: {\n 'application/json': {\n parts: Array<{ partNumber: number; url: string }>\n }\n }\n }\n }\n }\n }\n '/api/v0/uploads/sign': {\n post: {\n requestBody: {\n content: {\n 'application/json': {\n contentType?: string\n expiresIn: number\n filename: string\n }\n }\n }\n responses: {\n 200: {\n content: {\n 'application/json': { url: string }\n }\n }\n }\n }\n }\n}\n"],"names":[],"mappings":"AAAA;;;CAGC,GAED,
|
|
1
|
+
{"version":3,"sources":["../../src/storage-content-api/api-types.ts"],"sourcesContent":["/**\n * Upload API types extracted from Content API OpenAPI spec.\n * These are kept separate to avoid breaking changes when the main spec is regenerated.\n */\n\nexport type UploadPaths = {\n '/api/v0/uploads/{collectionId}/{filename}': {\n delete: {\n parameters: {\n path: {\n collectionId: string\n filename: string\n }\n }\n responses: {\n 200: {\n content: {\n 'application/json': { success: true }\n }\n }\n }\n }\n }\n '/api/v0/uploads/download/sign': {\n post: {\n requestBody: {\n content: {\n 'application/json': {\n collectionId: string\n expiresIn: number\n filename: string\n }\n }\n }\n responses: {\n 200: {\n content: {\n 'application/json': { url: string }\n }\n }\n }\n }\n }\n '/api/v0/uploads/multipart/abort': {\n post: {\n requestBody: {\n content: {\n 'application/json': {\n collectionId: string\n filename: string\n uploadId: string\n }\n }\n }\n responses: {\n 200: {\n content: {\n 'application/json': { success: true }\n }\n }\n }\n }\n }\n '/api/v0/uploads/multipart/complete': {\n post: {\n requestBody: {\n content: {\n 'application/json': {\n collectionId: string\n filename: string\n parts: Array<{ ETag: string; PartNumber: number }>\n uploadId: string\n }\n }\n }\n responses: {\n 200: {\n content: {\n 'application/json': { success: true }\n }\n }\n }\n }\n }\n '/api/v0/uploads/multipart/initiate': {\n post: {\n requestBody: {\n content: {\n 'application/json': {\n collectionId: string\n contentType: string\n filename: string\n }\n }\n }\n responses: {\n 200: {\n content: {\n 'application/json': { uploadId: string }\n }\n }\n }\n }\n }\n '/api/v0/uploads/multipart/parts': {\n post: {\n requestBody: {\n content: {\n 'application/json': {\n collectionId: string\n expiresIn: number\n filename: string\n partNumbers: number[]\n uploadId: string\n }\n }\n }\n responses: {\n 200: {\n content: {\n 'application/json': {\n parts: Array<{ partNumber: number; url: string }>\n }\n }\n }\n }\n }\n }\n '/api/v0/uploads/sign': {\n post: {\n requestBody: {\n content: {\n 'application/json': {\n collectionId: string\n contentType?: string\n expiresIn: number\n filename: string\n }\n }\n }\n responses: {\n 200: {\n content: {\n 'application/json': { url: string }\n }\n }\n }\n }\n }\n}\n"],"names":[],"mappings":"AAAA;;;CAGC,GAED,WAgJC"}
|
|
@@ -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,
|
|
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: { collectionId: collectionSlug, 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","collectionId","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,cAAcP;gBAAgBQ,aAAaN;gBAAUO,WAAW;gBAAMR;YAAS;QACzF;QAEA,IAAIG,SAAS,CAACD,MAAM;YAClB,MAAM,IAAIZ,SAAS,CAAC,oCAAoC,EAAEmB,KAAKC,SAAS,CAACP,QAAQ;QACnF;QAEA,OAAOQ,SAASb,IAAI,CAAC;YAAEc,KAAKV,KAAKU,GAAG;QAAC;IACvC;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
|
}
|