diff --git a/package.json b/package.json index 307c0952c..15b2836d8 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "@dnd-kit/core": "^6.1.0", "@dnd-kit/sortable": "^8.0.0", "@dnd-kit/utilities": "^3.2.2", - "@google-cloud/storage": "^6.11.0", + "@google-cloud/storage": "^7.14.0", "@headlessui/react": "^1.7.15", "@heroicons/react": "2.0.13", "@hookform/resolvers": "^3.1.1", @@ -38,6 +38,7 @@ "daisyui": "^3.9.4", "date-fns": "^2.28.0", "embla-carousel-react": "^8.0.0", + "encoding": "^0.1.13", "file-saver": "^2.0.5", "fuse.js": "^6.6.2", "graphql": "^16.2.0", diff --git a/src/app/api/user/bulkImportTicks/route.ts b/src/app/api/user/bulkImportTicks/route.ts index 92afe244d..eb0b9961a 100644 --- a/src/app/api/user/bulkImportTicks/route.ts +++ b/src/app/api/user/bulkImportTicks/route.ts @@ -6,7 +6,7 @@ import { NextRequest, NextResponse } from 'next/server' import { updateUser } from '@/js/auth/ManagementClient' import { graphqlClient } from '@/js/graphql/Client' import { MUTATION_IMPORT_TICKS } from '@/js/graphql/gql/fragments' -import { withUserAuth } from '@/js/auth/withUserAuth' +import { withUserAuth, PREDEFINED_HEADERS } from '@/js/auth/withUserAuth' export interface Tick { name: string @@ -79,8 +79,8 @@ async function getMPTicks (profileUrl: string): Promise { } const postHandler = async (req: NextRequest): Promise => { - const uuid = req.headers.get('x-openbeta-user-uuid') - const auth0Userid = req.headers.get('x-auth0-userid') + const uuid = req.headers.get(PREDEFINED_HEADERS.user_uuid) + const auth0Userid = req.headers.get(PREDEFINED_HEADERS.auth0_id) const payload = await req.json() const profileUrl: string = payload.profileUrl diff --git a/src/app/api/user/get-signed-url/route.ts b/src/app/api/user/get-signed-url/route.ts new file mode 100644 index 000000000..380ad4de4 --- /dev/null +++ b/src/app/api/user/get-signed-url/route.ts @@ -0,0 +1,65 @@ +import { NextRequest, NextResponse } from 'next/server' +import { customAlphabet } from 'nanoid' +import { nolookalikesSafe } from 'nanoid-dictionary' +import { extname } from 'path' + +import { withUserAuth, PREDEFINED_HEADERS } from '@/js/auth/withUserAuth' +import { getSignedUrlForUpload } from '@/js/media/storageClient' + +export interface MediaPreSignedProps { + url: string + fullFilename: string +} + +/** + * Endpoint for getting a signed url to upload a media file to remote cloud storage. + * Usage: `/api/user/get-signed-url?filename=image001.jpg` + * See https://cloud.google.com/storage/docs/access-control/signed-urls + */ +const getHanlder = async (req: NextRequest): Promise => { + try { + const fullFilename = prepareFilenameFromRequest(req) + if (fullFilename == null) { + return NextResponse.json({ status: 400 }) + } + const url = await getSignedUrlForUpload(fullFilename) + + return NextResponse.json({ url, fullFilename: '/' + fullFilename }) + } catch (e) { + console.error('Uploading to media server failed', e) + return NextResponse.json({ status: 500 }) + } +} + +export const GET = withUserAuth(getHanlder) + +/** + * Random filename generator + */ +const safeFilename = (original: string): string => { + return safeRandomString() + extname(original) +} + +const safeRandomString = customAlphabet(nolookalikesSafe, 10) + +/** + * Construct the S3 path for a given media file and an authenticated user. Format: `u/{user_uuid}/{filename}`. + * It's super important **not** to have the leading slash '/'. + */ +export const prepareFilenameFromRequest = (req: NextRequest): string | null => { + const searchParams = req.nextUrl.searchParams + const filename = searchParams.get('filename') + if (filename == null) { + return null + } + + const uuid = req.headers.get(PREDEFINED_HEADERS.user_uuid) + if (uuid == null) { + return null + } + + /** + * Important: no starting slash / when working with buckets + */ + return `u/${uuid}/${safeFilename(filename)}` +} diff --git a/src/app/api/user/me/route.ts b/src/app/api/user/me/route.ts index 6644166ad..6ed790c8f 100644 --- a/src/app/api/user/me/route.ts +++ b/src/app/api/user/me/route.ts @@ -1,13 +1,13 @@ -import { withUserAuth } from '@/js/auth/withUserAuth' import { NextRequest, NextResponse } from 'next/server' +import { PREDEFINED_HEADERS, withUserAuth } from '@/js/auth/withUserAuth' import useUserProfileCmd from '@/js/hooks/useUserProfileCmd' /** * Direct `/api/user/me` to `/u/ => { - const uuid = req.headers.get('x-openbeta-user-uuid') - const accessToken = req.headers.get('x-auth0-access-token') + const uuid = req.headers.get(PREDEFINED_HEADERS.user_uuid) + const accessToken = req.headers.get(PREDEFINED_HEADERS.access_token) if (accessToken == null || uuid == null) { return NextResponse.json({ status: 500 }) diff --git a/src/app/api/user/remove-media/route.ts b/src/app/api/user/remove-media/route.ts new file mode 100644 index 000000000..824c49f08 --- /dev/null +++ b/src/app/api/user/remove-media/route.ts @@ -0,0 +1,24 @@ +import { NextRequest, NextResponse } from 'next/server' + +import { withUserAuth } from '@/js/auth/withUserAuth' +import { deleteMediaFromBucket } from '@/js/media/storageClient' +import { prepareFilenameFromRequest } from '../get-signed-url/route' + +/** + * Endpoint for removing a media object from remote cloud storage + */ +const postHandler = async (req: NextRequest): Promise => { + try { + const filename = prepareFilenameFromRequest(req) + if (filename == null) { + return NextResponse.json({ status: 400 }) + } + await deleteMediaFromBucket(filename) + return NextResponse.json({ status: 200 }) + } catch (e) { + console.log('Removing file from media server failed', e) + return NextResponse.json({ status: 500 }) + } +} + +export const POST = withUserAuth(postHandler) diff --git a/src/js/auth/withUserAuth.ts b/src/js/auth/withUserAuth.ts index 04e078bca..cd01199e5 100644 --- a/src/js/auth/withUserAuth.ts +++ b/src/js/auth/withUserAuth.ts @@ -13,12 +13,18 @@ export const withUserAuth = (handler: Next13APIHandler): Next13APIHandler => { const session = await getServerSession({ req, ...authOptions }) if (session != null) { // Passing useful session data downstream - req.headers.set('x-openbeta-user-uuid', session.user.metadata.uuid) - req.headers.set('x-auth0-userid', session.id) - req.headers.set('x-auth0-access-token', session.accessToken) + req.headers.set(PREDEFINED_HEADERS.user_uuid, session.user.metadata.uuid) + req.headers.set(PREDEFINED_HEADERS.auth0_id, session.id) + req.headers.set(PREDEFINED_HEADERS.access_token, session.accessToken) return await handler(req) } else { return NextResponse.json({ status: 401 }) } } } + +export enum PREDEFINED_HEADERS { + user_uuid = 'x-openbeta-user-uuid', + auth0_id = 'x-auth0-userid', // Example: 'auth0|1237492749372923498234' + access_token = 'x-auth0-access-token' // JWT token +} diff --git a/src/js/userApi/media.ts b/src/js/userApi/media.ts index 94ccf2b0d..20b03b203 100644 --- a/src/js/userApi/media.ts +++ b/src/js/userApi/media.ts @@ -1,6 +1,6 @@ import axios from 'axios' -import { MediaPreSignedProps } from '../../pages/api/media/get-signed-url' +import { MediaPreSignedProps } from '@/app/api/user/get-signed-url/route' const client = axios.create() @@ -12,7 +12,7 @@ const client = axios.create() */ export const uploadPhoto = async (filename: string, rawData: ArrayBuffer): Promise => { const res = await client.get( - '/api/media/get-signed-url?filename=' + encodeURIComponent(filename)) + '/api/user/get-signed-url?filename=' + encodeURIComponent(filename)) if (res.data?.url != null && res.data?.fullFilename != null) { const signedUploadUrl = res.data.url @@ -30,7 +30,7 @@ export const uploadPhoto = async (filename: string, rawData: ArrayBuffer): Promi export const deleteMediaFromStorage = async (filename: string): Promise => { const res = await client.post( - '/api/media/remove?filename=' + encodeURIComponent(filename), + '/api/user/remove-media?filename=' + encodeURIComponent(filename), { headers: { 'Content-Type': 'application/json' diff --git a/src/pages/api/media/get-signed-url.ts b/src/pages/api/media/get-signed-url.ts deleted file mode 100644 index c6afa0674..000000000 --- a/src/pages/api/media/get-signed-url.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { NextApiHandler } from 'next' -import { customAlphabet } from 'nanoid' -import { nolookalikesSafe } from 'nanoid-dictionary' -import { extname } from 'path' -import { getServerSession } from 'next-auth' -import withAuth from '../withAuth' -import { authOptions } from '@/app/api/auth/[...nextauth]/route' -import { getSignedUrlForUpload } from '../../../js/media/storageClient' - -export interface MediaPreSignedProps { - url: string - fullFilename: string -} - -/** - * Local API getting a signed url for uploading media to Google storage. - * - * Usage: `/api/media/get-signed-url?filename=image001.jpg` - * - * See https://cloud.google.com/storage/docs/access-control/signed-urls - */ -const handler: NextApiHandler = async (req, res) => { - if (req.method === 'GET') { - try { - if (req.query?.filename == null) { - throw new Error('Missing filename query param') - } - const { filename } = req.query - if (Array.isArray(filename)) { - throw new Error('Expect only 1 filename param') - } - const session = await getServerSession(req, res, authOptions) - if (session?.user?.metadata?.uuid == null) { - throw new Error('Missing user metadata') - } - const { uuid } = session.user.metadata - /** - * Important: no starting / when working with buckets - */ - const fullFilename = `u/${uuid}/${safeFilename(filename)}` - - const url = await getSignedUrlForUpload(fullFilename) - - return res.status(200).json({ url, fullFilename: '/' + fullFilename }) - } catch (e) { - console.log('Uploading to media server failed', e) - return res.status(500).end() - } - } - return res.status(400).end() -} - -export default withAuth(handler) - -/** - * Random filename generator - */ -const safeFilename = (original: string): string => { - return safeRandomString() + extname(original) -} - -const safeRandomString = customAlphabet(nolookalikesSafe, 10) diff --git a/src/pages/api/media/remove.ts b/src/pages/api/media/remove.ts deleted file mode 100644 index b980786a0..000000000 --- a/src/pages/api/media/remove.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { NextApiHandler } from 'next' -import withAuth from '../withAuth' - -import { deleteMediaFromBucket } from '../../../js/media/storageClient' - -// We need to disable the default body parser -export const config = { - api: { - bodyParser: false - } -} - -const handler: NextApiHandler = async (req, res) => { - if (req.method === 'POST') { - try { - if (req.query?.filename == null) { - throw new Error('Missing filename query param') - } - - const { filename } = req.query - - if (Array.isArray(filename)) { - throw new Error('Expect only 1 filename param') - } - - let filenameWithoutSlash = filename - if (filename.startsWith('/')) { - filenameWithoutSlash = filename.substring(1, filename.length) - } - await deleteMediaFromBucket(filenameWithoutSlash) - return res.status(200).end() - } catch (e) { - console.log('Removing file from media server failed', e) - return res.status(500).end() - } - } - return res.status(400).json({ error: 'Request not supported' }) -} - -export default withAuth(handler) diff --git a/yarn.lock b/yarn.lock index ed472c0c0..4cb06ef6e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1208,46 +1208,43 @@ resolved "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.4.tgz" integrity sha512-qprfWkn82Iw821mcKofJ5Pk9wgioHicxcQMxx+5zt5GSKoqdWvgG5AxVmpmUUjzTLPVSH5auBrhI93Deayn/DA== -"@google-cloud/paginator@^3.0.7": - version "3.0.7" - resolved "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-3.0.7.tgz" - integrity sha512-jJNutk0arIQhmpUUQJPJErsojqo834KcyB6X7a1mxuic8i1tKXxde8E69IZxNZawRIlZdIK2QY4WALvlK5MzYQ== +"@google-cloud/paginator@^5.0.0": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@google-cloud/paginator/-/paginator-5.0.2.tgz#86ad773266ce9f3b82955a8f75e22cd012ccc889" + integrity sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg== dependencies: arrify "^2.0.0" extend "^3.0.2" -"@google-cloud/projectify@^3.0.0": - version "3.0.0" - resolved "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-3.0.0.tgz" - integrity sha512-HRkZsNmjScY6Li8/kb70wjGlDDyLkVk3KvoEo9uIoxSjYLJasGiCch9+PqRVDOCGUFvEIqyogl+BeqILL4OJHA== +"@google-cloud/projectify@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@google-cloud/projectify/-/projectify-4.0.0.tgz#d600e0433daf51b88c1fa95ac7f02e38e80a07be" + integrity sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA== -"@google-cloud/promisify@^3.0.0": - version "3.0.1" - resolved "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-3.0.1.tgz" - integrity sha512-z1CjRjtQyBOYL+5Qr9DdYIfrdLBe746jRTYfaYU6MeXkqp7UfYs/jX16lFFVzZ7PGEJvqZNqYUEtb1mvDww4pA== +"@google-cloud/promisify@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@google-cloud/promisify/-/promisify-4.0.0.tgz#a906e533ebdd0f754dca2509933334ce58b8c8b1" + integrity sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g== -"@google-cloud/storage@^6.11.0": - version "6.12.0" - resolved "https://registry.npmjs.org/@google-cloud/storage/-/storage-6.12.0.tgz" - integrity sha512-78nNAY7iiZ4O/BouWMWTD/oSF2YtYgYB3GZirn0To6eBOugjXVoK+GXgUXOl+HlqbAOyHxAVXOlsj3snfbQ1dw== +"@google-cloud/storage@^7.14.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@google-cloud/storage/-/storage-7.14.0.tgz#eda9715f68507949214af804c906eba6d168a214" + integrity sha512-H41bPL2cMfSi4EEnFzKvg7XSb7T67ocSXrmF7MPjfgFB0L6CKGzfIYJheAZi1iqXjz6XaCT1OBf6HCG5vDBTOQ== dependencies: - "@google-cloud/paginator" "^3.0.7" - "@google-cloud/projectify" "^3.0.0" - "@google-cloud/promisify" "^3.0.0" + "@google-cloud/paginator" "^5.0.0" + "@google-cloud/projectify" "^4.0.0" + "@google-cloud/promisify" "^4.0.0" abort-controller "^3.0.0" async-retry "^1.3.3" - compressible "^2.0.12" - duplexify "^4.0.0" - ent "^2.2.0" - extend "^3.0.2" - fast-xml-parser "^4.2.2" - gaxios "^5.0.0" - google-auth-library "^8.0.1" + duplexify "^4.1.3" + fast-xml-parser "^4.4.1" + gaxios "^6.0.2" + google-auth-library "^9.6.3" + html-entities "^2.5.2" mime "^3.0.0" - mime-types "^2.0.8" p-limit "^3.0.1" - retry-request "^5.0.0" - teeny-request "^8.0.0" + retry-request "^7.0.0" + teeny-request "^9.0.0" uuid "^8.0.0" "@graphql-typed-document-node/core@^3.1.1": @@ -2514,6 +2511,11 @@ dependencies: "@babel/types" "^7.20.7" +"@types/caseless@*": + version "0.12.5" + resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.5.tgz#db9468cb1b1b5a925b8f34822f1669df0c5472f5" + integrity sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg== + "@types/d3-array@^3.0.3": version "3.0.7" resolved "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.7.tgz" @@ -2755,6 +2757,16 @@ "@types/scheduler" "*" csstype "^3.0.2" +"@types/request@^2.48.8": + version "2.48.12" + resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.12.tgz#0f590f615a10f87da18e9790ac94c29ec4c5ef30" + integrity sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw== + dependencies: + "@types/caseless" "*" + "@types/node" "*" + "@types/tough-cookie" "*" + form-data "^2.5.0" + "@types/scheduler@*": version "0.16.3" resolved "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz" @@ -2993,6 +3005,13 @@ agent-base@6: dependencies: debug "4" +agent-base@^7.0.2: + version "7.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" + integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== + dependencies: + debug "^4.3.4" + ajv@^6.12.4: version "6.12.6" resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" @@ -3611,7 +3630,7 @@ colord@^2.9: resolved "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz" integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== -combined-stream@^1.0.8: +combined-stream@^1.0.6, combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -3633,13 +3652,6 @@ commander@^7.2.0: resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== -compressible@^2.0.12: - version "2.0.18" - resolved "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz" - integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== - dependencies: - mime-db ">= 1.43.0 < 2" - concat-map@0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" @@ -4124,15 +4136,15 @@ domutils@^2.8.0: domelementtype "^2.2.0" domhandler "^4.2.0" -duplexify@^4.0.0: - version "4.1.2" - resolved "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz" - integrity sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw== +duplexify@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.3.tgz#a07e1c0d0a2c001158563d32592ba58bddb0236f" + integrity sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA== dependencies: end-of-stream "^1.4.1" inherits "^2.0.3" readable-stream "^3.1.1" - stream-shift "^1.0.0" + stream-shift "^1.0.2" earcut@^2.2.4: version "2.2.4" @@ -4179,6 +4191,13 @@ emoji-regex@^8.0.0: resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +encoding@^0.1.13: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== + dependencies: + iconv-lite "^0.6.2" + end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" @@ -4186,11 +4205,6 @@ end-of-stream@^1.4.1: dependencies: once "^1.4.0" -ent@^2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz" - integrity sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA== - entities@^2.0.0: version "2.2.0" resolved "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz" @@ -4697,15 +4711,10 @@ fast-shallow-equal@^1.0.0: resolved "https://registry.npmjs.org/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz" integrity sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw== -fast-text-encoding@^1.0.0: - version "1.0.6" - resolved "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz" - integrity sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w== - -fast-xml-parser@^4.2.2: - version "4.3.0" - resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.0.tgz" - integrity sha512-5Wln/SBrtlN37aboiNNFHfSALwLzpUx1vJhDgDVPKKG3JrNe8BWTUoNKqkeKk/HqNbKxC8nEAJaBydq30yHoLA== +fast-xml-parser@^4.4.1: + version "4.5.0" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz#2882b7d01a6825dfdf909638f2de0256351def37" + integrity sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg== dependencies: strnum "^1.0.5" @@ -4816,6 +4825,16 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +form-data@^2.5.0: + version "2.5.2" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.2.tgz#dc653743d1de2fcc340ceea38079daf6e9069fd2" + integrity sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + safe-buffer "^5.2.1" + form-data@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz" @@ -4865,22 +4884,23 @@ fuse.js@^6.6.2: resolved "https://registry.npmjs.org/fuse.js/-/fuse.js-6.6.2.tgz" integrity sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA== -gaxios@^5.0.0, gaxios@^5.0.1: - version "5.1.3" - resolved "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz" - integrity sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA== +gaxios@^6.0.0, gaxios@^6.0.2, gaxios@^6.1.1: + version "6.7.1" + resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-6.7.1.tgz#ebd9f7093ede3ba502685e73390248bb5b7f71fb" + integrity sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ== dependencies: extend "^3.0.2" - https-proxy-agent "^5.0.0" + https-proxy-agent "^7.0.1" is-stream "^2.0.0" node-fetch "^2.6.9" + uuid "^9.0.1" -gcp-metadata@^5.3.0: - version "5.3.0" - resolved "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz" - integrity sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w== +gcp-metadata@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-6.1.0.tgz#9b0dd2b2445258e7597f2024332d20611cbd6b8c" + integrity sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg== dependencies: - gaxios "^5.0.0" + gaxios "^6.0.0" json-bigint "^1.0.0" gensync@^1.0.0-beta.2: @@ -5029,27 +5049,17 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -google-auth-library@^8.0.1: - version "8.9.0" - resolved "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.9.0.tgz" - integrity sha512-f7aQCJODJFmYWN6PeNKzgvy9LI2tYmXnzpNDHEjG5sDNPgGb2FXQyTBnXeSH+PAtpKESFD+LmHw3Ox3mN7e1Fg== +google-auth-library@^9.6.3: + version "9.15.0" + resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-9.15.0.tgz#1b009c08557929c881d72f953f17e839e91b009b" + integrity sha512-7ccSEJFDFO7exFbO6NRyC+xH8/mZ1GZGG2xxx9iHxZWcjUjJpjWxIMw3cofAKcueZ6DATiukmmprD7yavQHOyQ== dependencies: - arrify "^2.0.0" base64-js "^1.3.0" ecdsa-sig-formatter "^1.0.11" - fast-text-encoding "^1.0.0" - gaxios "^5.0.0" - gcp-metadata "^5.3.0" - gtoken "^6.1.0" + gaxios "^6.1.1" + gcp-metadata "^6.1.0" + gtoken "^7.0.0" jws "^4.0.0" - lru-cache "^6.0.0" - -google-p12-pem@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.1.tgz" - integrity sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ== - dependencies: - node-forge "^1.3.1" gopd@^1.0.1: version "1.0.1" @@ -5085,13 +5095,12 @@ grid-index@^1.1.0: resolved "https://registry.npmjs.org/grid-index/-/grid-index-1.1.0.tgz" integrity sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA== -gtoken@^6.1.0: - version "6.1.2" - resolved "https://registry.npmjs.org/gtoken/-/gtoken-6.1.2.tgz" - integrity sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ== +gtoken@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-7.1.0.tgz#d61b4ebd10132222817f7222b1e6064bd463fc26" + integrity sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw== dependencies: - gaxios "^5.0.1" - google-p12-pem "^4.0.0" + gaxios "^6.0.0" jws "^4.0.0" has-bigints@^1.0.1, has-bigints@^1.0.2: @@ -5186,6 +5195,11 @@ html-encoding-sniffer@^3.0.0: dependencies: whatwg-encoding "^2.0.0" +html-entities@^2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.5.2.tgz#201a3cf95d3a15be7099521620d19dfb4f65359f" + integrity sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA== + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz" @@ -5213,6 +5227,14 @@ https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: agent-base "6" debug "4" +https-proxy-agent@^7.0.1: + version "7.0.5" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2" + integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw== + dependencies: + agent-base "^7.0.2" + debug "4" + human-signals@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" @@ -5235,7 +5257,7 @@ i18n-iso-countries@^7.5.0: dependencies: diacritics "1.3.0" -iconv-lite@0.6.3: +iconv-lite@0.6.3, iconv-lite@^0.6.2: version "0.6.3" resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -6686,12 +6708,12 @@ micromatch@^4.0.4, micromatch@^4.0.5: braces "^3.0.2" picomatch "^2.3.1" -mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": +mime-db@1.52.0: version "1.52.0" resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.0.8, mime-types@^2.1.12: +mime-types@^2.1.12: version "2.1.35" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -6826,18 +6848,13 @@ next@^13.5.6: "@next/swc-win32-ia32-msvc" "13.5.6" "@next/swc-win32-x64-msvc" "13.5.6" -node-fetch@^2.6.1, node-fetch@^2.6.9: +node-fetch@^2.6.9: version "2.7.0" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== dependencies: whatwg-url "^5.0.0" -node-forge@^1.3.1: - version "1.3.1" - resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz" - integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== - node-int64@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" @@ -7875,13 +7892,14 @@ response-iterator@^0.2.6: resolved "https://registry.npmjs.org/response-iterator/-/response-iterator-0.2.6.tgz" integrity sha512-pVzEEzrsg23Sh053rmDUvLSkGXluZio0qu8VT6ukrYuvtjVfCbDZH9d6PGXb8HZfzdNZt8feXv/jvUzlhRgLnw== -retry-request@^5.0.0: - version "5.0.2" - resolved "https://registry.npmjs.org/retry-request/-/retry-request-5.0.2.tgz" - integrity sha512-wfI3pk7EE80lCIXprqh7ym48IHYdwmAAzESdbU8Q9l7pnRCk9LEhpbOTNKjz6FARLm/Bl5m+4F0ABxOkYUujSQ== +retry-request@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/retry-request/-/retry-request-7.0.2.tgz#60bf48cfb424ec01b03fca6665dee91d06dd95f3" + integrity sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w== dependencies: - debug "^4.1.1" + "@types/request" "^2.48.8" extend "^3.0.2" + teeny-request "^9.0.0" retry@0.13.1: version "0.13.1" @@ -7929,7 +7947,7 @@ safe-array-concat@^1.0.1: has-symbols "^1.0.3" isarray "^2.0.5" -safe-buffer@^5.0.1, safe-buffer@~5.2.0: +safe-buffer@^5.0.1, safe-buffer@^5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -8186,10 +8204,10 @@ stream-events@^1.0.5: dependencies: stubs "^3.0.0" -stream-shift@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz" - integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== +stream-shift@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.3.tgz#85b8fab4d71010fc3ba8772e8046cc49b8a3864b" + integrity sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ== streamsearch@^1.1.0: version "1.1.0" @@ -8482,14 +8500,14 @@ tailwindcss@^3.1.8: resolve "^1.22.2" sucrase "^3.32.0" -teeny-request@^8.0.0: - version "8.0.3" - resolved "https://registry.npmjs.org/teeny-request/-/teeny-request-8.0.3.tgz" - integrity sha512-jJZpA5He2y52yUhA7pyAGZlgQpcB+xLjcN0eUFxr9c8hP/H7uOXbBNVo/O0C/xVfJLJs680jvkFgVJEEvk9+ww== +teeny-request@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-9.0.0.tgz#18140de2eb6595771b1b02203312dfad79a4716d" + integrity sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g== dependencies: http-proxy-agent "^5.0.0" https-proxy-agent "^5.0.0" - node-fetch "^2.6.1" + node-fetch "^2.6.9" stream-events "^1.0.5" uuid "^9.0.0" @@ -8935,7 +8953,7 @@ uuid@^8.0.0, uuid@^8.3.2: resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -uuid@^9.0.0: +uuid@^9.0.0, uuid@^9.0.1: version "9.0.1" resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==