diff --git a/apps/webapp/app/routes/admin.api.v1.snapshot.ts b/apps/webapp/app/routes/admin.api.v1.snapshot.ts index 3b345978f5..a9f25fc3fe 100644 --- a/apps/webapp/app/routes/admin.api.v1.snapshot.ts +++ b/apps/webapp/app/routes/admin.api.v1.snapshot.ts @@ -1,11 +1,15 @@ +import { S3Client } from "@aws-sdk/client-s3"; +import { Upload } from "@aws-sdk/lib-storage"; import { type DataFunctionArgs } from "@remix-run/node"; -import fs from "fs"; -import os from "os"; -import path from "path"; -import { PassThrough } from "stream"; import v8 from "v8"; import { prisma } from "~/db.server"; import { authenticateApiRequestWithPersonalAccessToken } from "~/services/personalAccessToken.server"; +import { logger } from "~/services/logger.server"; + +// Use 100MB parts for faster parallel uploads of large snapshots +const PART_SIZE = 100 * 1024 * 1024; +// Use high parallelism to maximize upload speed +const QUEUE_SIZE = 8; // Format date as yyyy-MM-dd HH_mm_ss_SSS function formatDate(date: Date) { @@ -24,6 +28,32 @@ function formatDate(date: Date) { .padStart(2, "0")}_${milliseconds.toString().padStart(3, "0")}`; } +function getS3Config() { + const bucket = process.env.SNAPSHOT_S3_BUCKET; + const region = process.env.SNAPSHOT_S3_REGION ?? "us-east-1"; + + if (!bucket) { + return undefined; + } + + // Optional - only needed for non-AWS S3 (MinIO, R2, etc.) or local dev + const endpoint = process.env.SNAPSHOT_S3_ENDPOINT; + const accessKeyId = process.env.SNAPSHOT_S3_ACCESS_KEY_ID; + const secretAccessKey = process.env.SNAPSHOT_S3_SECRET_ACCESS_KEY; + + // If explicit credentials provided, use them (local dev / non-AWS) + // Otherwise, SDK uses default credential chain (IAM role, env vars, etc.) + const credentials = + accessKeyId && secretAccessKey ? { accessKeyId, secretAccessKey } : undefined; + + return { + bucket, + region, + endpoint, + credentials, + }; +} + export async function loader({ request }: DataFunctionArgs) { const authenticationResult = await authenticateApiRequestWithPersonalAccessToken(request); @@ -41,31 +71,94 @@ export async function loader({ request }: DataFunctionArgs) { throw new Response("You must be an admin to perform this action", { status: 403 }); } - const tempDir = os.tmpdir(); - const filepath = path.join( - tempDir, - `${getTaskIdentifier()}-${formatDate(new Date())}.heapsnapshot` - ); + const s3Config = getS3Config(); - const snapshotPath = v8.writeHeapSnapshot(filepath); - if (!snapshotPath) { - throw new Response("No snapshot saved", { status: 500 }); + if (!s3Config) { + throw new Response( + "S3 is not configured. Set SNAPSHOT_S3_ENDPOINT, SNAPSHOT_S3_BUCKET, SNAPSHOT_S3_ACCESS_KEY_ID, and SNAPSHOT_S3_SECRET_ACCESS_KEY.", + { status: 500 } + ); } - const body = new PassThrough(); - const stream = fs.createReadStream(snapshotPath); - stream.on("open", () => stream.pipe(body)); - stream.on("error", (err) => body.end(err)); - stream.on("end", () => body.end()); - - return new Response(body as any, { - status: 200, - headers: { - "Content-Type": "application/octet-stream", - "Content-Disposition": `attachment; filename="${path.basename(snapshotPath)}"`, - "Content-Length": (await fs.promises.stat(snapshotPath)).size.toString(), - }, + const s3Client = new S3Client({ + region: s3Config.region, + ...(s3Config.credentials && { credentials: s3Config.credentials }), + ...(s3Config.endpoint && { endpoint: s3Config.endpoint, forcePathStyle: true }), + }); + + const filename = `${getTaskIdentifier()}-${formatDate(new Date())}.heapsnapshot`; + const s3Key = `snapshots/${filename}`; + + logger.info("Taking heap snapshot and streaming to S3", { + bucket: s3Config.bucket, + key: s3Key, }); + + try { + const startTime = Date.now(); + const snapshotStream = v8.getHeapSnapshot(); + + const upload = new Upload({ + client: s3Client, + params: { + Bucket: s3Config.bucket, + Key: s3Key, + Body: snapshotStream, + ContentType: "application/octet-stream", + }, + queueSize: QUEUE_SIZE, + partSize: PART_SIZE, + leavePartsOnError: false, + }); + + let totalBytes = 0; + upload.on("httpUploadProgress", (progress) => { + totalBytes = progress.loaded ?? totalBytes; + logger.info("Upload progress", { + loaded: progress.loaded, + part: progress.part, + }); + }); + + await upload.done(); + const duration = Date.now() - startTime; + + logger.info("Heap snapshot uploaded to S3", { + bucket: s3Config.bucket, + key: s3Key, + durationMs: duration, + durationSec: Math.round(duration / 1000), + totalBytes, + uploadSpeedMBps: totalBytes > 0 ? Math.round((totalBytes / 1024 / 1024 / (duration / 1000)) * 10) / 10 : 0, + }); + + return new Response( + JSON.stringify({ + success: true, + bucket: s3Config.bucket, + key: s3Key, + sizeBytes: totalBytes, + durationMs: duration, + }), + { + status: 200, + headers: { + "Content-Type": "application/json", + }, + } + ); + } catch (error) { + logger.error("Failed to upload heap snapshot to S3", { + error: error instanceof Error ? error.message : String(error), + bucket: s3Config.bucket, + key: s3Key, + }); + + throw new Response( + `Failed to upload snapshot to S3: ${error instanceof Error ? error.message : String(error)}`, + { status: 500 } + ); + } } function getTaskIdentifier() { diff --git a/apps/webapp/package.json b/apps/webapp/package.json index 97f4533348..02099e3f60 100644 --- a/apps/webapp/package.json +++ b/apps/webapp/package.json @@ -31,6 +31,7 @@ "@ariakit/react-core": "^0.4.6", "@aws-sdk/client-ecr": "^3.931.0", "@aws-sdk/client-s3": "^3.936.0", + "@aws-sdk/lib-storage": "^3.936.0", "@aws-sdk/client-sqs": "^3.445.0", "@aws-sdk/client-sts": "^3.840.0", "@aws-sdk/credential-provider-node": "^3.936.0", diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index c80648d710..100b5304a2 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -8,6 +8,7 @@ volumes: clickhouse-logs: prometheus-data: grafana-data: + minio-data: networks: app_network: @@ -214,3 +215,40 @@ services: - app_network depends_on: - prometheus + + minio: + container_name: minio + image: minio/minio:latest + restart: always + volumes: + - minio-data:/data + ports: + - "9002:9000" + - "9003:9001" + environment: + MINIO_ROOT_USER: admin + MINIO_ROOT_PASSWORD: adminpassword + networks: + - app_network + command: server /data --console-address ":9001" + healthcheck: + test: ["CMD", "mc", "ready", "local"] + interval: 5s + timeout: 5s + retries: 5 + + minio-setup: + container_name: minio-setup + image: minio/mc:latest + depends_on: + minio: + condition: service_healthy + networks: + - app_network + entrypoint: > + /bin/sh -c " + mc alias set local http://minio:9000 admin adminpassword; + mc mb --ignore-existing local/snapshots; + mc mb --ignore-existing local/packets; + exit 0; + " diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 76f12a8774..24804dd75b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -232,6 +232,9 @@ importers: '@aws-sdk/credential-provider-node': specifier: ^3.936.0 version: 3.940.0 + '@aws-sdk/lib-storage': + specifier: ^3.936.0 + version: 3.975.0(@aws-sdk/client-s3@3.940.0) '@aws-sdk/s3-presigned-post': specifier: ^3.936.0 version: 3.940.0 @@ -1088,7 +1091,7 @@ importers: version: 18.3.1 react-email: specifier: ^2.1.1 - version: 2.1.2(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.15)(bufferutil@4.0.9)(eslint@8.31.0) + version: 2.1.2(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.15)(eslint@8.31.0) resend: specifier: ^3.2.0 version: 3.2.0 @@ -3281,6 +3284,12 @@ packages: resolution: {integrity: sha512-9QLTIkDJHHaYL0nyymO41H8g3ui1yz6Y3GmAN1gYQa6plXisuFBnGAbmKVj7zNvjWaOKdF0dV3dd3AFKEDoJ/w==} engines: {node: '>=18.0.0'} + '@aws-sdk/lib-storage@3.975.0': + resolution: {integrity: sha512-F6vrnZ3F7oqr3oONCIpx+uZDTwXWfh8sBoNNJollDn5pIn7TI+R+7WxVIXAMq/JWLXE6N8T3M6ogWk4Y4JWPPw==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@aws-sdk/client-s3': 3.975.0 + '@aws-sdk/middleware-bucket-endpoint@3.936.0': resolution: {integrity: sha512-XLSVVfAorUxZh6dzF+HTOp4R1B5EQcdpGcPliWr0KUj2jukgjZEcqbBmjyMF/p9bmyQsONX80iURF1HLAlW0qg==} engines: {node: '>=18.0.0'} @@ -9402,6 +9411,10 @@ packages: resolution: {integrity: sha512-j7HwVkBw68YW8UmFRcjZOmssE77Rvk0GWAIN1oFBhsaovQmZWYCIcGa9/pwRB0ExI8Sk9MWNALTjftjHZea7VA==} engines: {node: '>=18.0.0'} + '@smithy/abort-controller@4.2.8': + resolution: {integrity: sha512-peuVfkYHAmS5ybKxWcfraK7WBBP0J+rkfUcbHJJKQ4ir3UAUNQI+Y4Vt/PqSzGqgloJ5O1dk7+WzNL8wcCSXbw==} + engines: {node: '>=18.0.0'} + '@smithy/chunked-blob-reader-native@4.2.1': resolution: {integrity: sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ==} engines: {node: '>=18.0.0'} @@ -9431,6 +9444,10 @@ packages: resolution: {integrity: sha512-6gnIz3h+PEPQGDj8MnRSjDvKBah042jEoPgjFGJ4iJLBE78L4lY/n98x14XyPF4u3lN179Ub/ZKFY5za9GeLQw==} engines: {node: '>=18.0.0'} + '@smithy/core@3.21.1': + resolution: {integrity: sha512-NUH8R4O6FkN8HKMojzbGg/5pNjsfTjlMmeFclyPfPaXXUrbr5TzhWgbf7t92wfrpCHRgpjyz7ffASIS3wX28aA==} + engines: {node: '>=18.0.0'} + '@smithy/core@3.6.0': resolution: {integrity: sha512-Pgvfb+TQ4wUNLyHzvgCP4aYZMh16y7GcfF59oirRHcgGgkH1e/s9C0nv/v3WP+Quymyr5je71HeFQCwh+44XLg==} engines: {node: '>=18.0.0'} @@ -9481,6 +9498,10 @@ packages: resolution: {integrity: sha512-3+RG3EA6BBJ/ofZUeTFJA7mHfSYrZtQIrDP9dI8Lf7X6Jbos2jptuLrAAteDiFVrmbEmLSuRG/bUKzfAXk7dhg==} engines: {node: '>=18.0.0'} + '@smithy/fetch-http-handler@5.3.9': + resolution: {integrity: sha512-I4UhmcTYXBrct03rwzQX1Y/iqQlzVQaPxWjCjula++5EmWq9YGBrx6bbGqluGc1f0XEfhSkiY4jhLgbsJUMKRA==} + engines: {node: '>=18.0.0'} + '@smithy/hash-blob-browser@4.2.6': resolution: {integrity: sha512-8P//tA8DVPk+3XURk2rwcKgYwFvwGwmJH/wJqQiSKwXZtf/LiZK+hbUZmPj/9KzM+OVSwe4o85KTp5x9DUZTjw==} engines: {node: '>=18.0.0'} @@ -9555,6 +9576,10 @@ packages: resolution: {integrity: sha512-9pAX/H+VQPzNbouhDhkW723igBMLgrI8OtX+++M7iKJgg/zY/Ig3i1e6seCcx22FWhE6Q/S61BRdi2wXBORT+A==} engines: {node: '>=18.0.0'} + '@smithy/middleware-endpoint@4.4.11': + resolution: {integrity: sha512-/WqsrycweGGfb9sSzME4CrsuayjJF6BueBmkKlcbeU5q18OhxRrvvKlmfw3tpDsK5ilx2XUJvoukwxHB0nHs/Q==} + engines: {node: '>=18.0.0'} + '@smithy/middleware-retry@2.0.21': resolution: {integrity: sha512-EZS1EXv1k6IJX6hyu/0yNQuPcPaXwG8SWljQHYueyRbOxmqYgoWMWPtfZj0xRRQ4YtLawQSpBgAeiJltq8/MPw==} engines: {node: '>=14.0.0'} @@ -9587,6 +9612,10 @@ packages: resolution: {integrity: sha512-VkLoE/z7e2g8pirwisLz8XJWedUSY8my/qrp81VmAdyrhi94T+riBfwP+AOEEFR9rFTSonC/5D2eWNmFabHyGQ==} engines: {node: '>=18.0.0'} + '@smithy/middleware-serde@4.2.9': + resolution: {integrity: sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ==} + engines: {node: '>=18.0.0'} + '@smithy/middleware-stack@2.0.8': resolution: {integrity: sha512-7/N59j0zWqVEKExJcA14MrLDZ/IeN+d6nbkN8ucs+eURyaDUXWYlZrQmMOd/TyptcQv0+RDlgag/zSTTV62y/Q==} engines: {node: '>=14.0.0'} @@ -9599,6 +9628,10 @@ packages: resolution: {integrity: sha512-bYrutc+neOyWxtZdbB2USbQttZN0mXaOyYLIsaTbJhFsfpXyGWUxJpEuO1rJ8IIJm2qH4+xJT0mxUSsEDTYwdQ==} engines: {node: '>=18.0.0'} + '@smithy/middleware-stack@4.2.8': + resolution: {integrity: sha512-w6LCfOviTYQjBctOKSwy6A8FIkQy7ICvglrZFl6Bw4FmcQ1Z420fUtIhxaUZZshRe0VCq4kvDiPiXrPZAe8oRA==} + engines: {node: '>=18.0.0'} + '@smithy/node-config-provider@2.1.6': resolution: {integrity: sha512-HLqTs6O78m3M3z1cPLFxddxhEPv5MkVatfPuxoVO3A+cHZanNd/H5I6btcdHy6N2CB1MJ/lihJC92h30SESsBA==} engines: {node: '>=14.0.0'} @@ -9611,6 +9644,10 @@ packages: resolution: {integrity: sha512-UTurh1C4qkVCtqggI36DGbLB2Kv8UlcFdMXDcWMbqVY2uRg0XmT9Pb4Vj6oSQ34eizO1fvR0RnFV4Axw4IrrAg==} engines: {node: '>=18.0.0'} + '@smithy/node-config-provider@4.3.8': + resolution: {integrity: sha512-aFP1ai4lrbVlWjfpAfRSL8KFcnJQYfTl5QxLJXY32vghJrDuFyPZ6LtUL+JEGYiFRG1PfPLHLoxj107ulncLIg==} + engines: {node: '>=18.0.0'} + '@smithy/node-http-handler@2.1.10': resolution: {integrity: sha512-lkALAwtN6odygIM4nB8aHDahINM6WXXjNrZmWQAh0RSossySRT2qa31cFv0ZBuAYVWeprskRk13AFvvLmf1WLw==} engines: {node: '>=14.0.0'} @@ -9623,6 +9660,10 @@ packages: resolution: {integrity: sha512-CMnzM9R2WqlqXQGtIlsHMEZfXKJVTIrqCNoSd/QpAyp+Dw0a1Vps13l6ma1fH8g7zSPNsA59B/kWgeylFuA/lw==} engines: {node: '>=18.0.0'} + '@smithy/node-http-handler@4.4.8': + resolution: {integrity: sha512-q9u+MSbJVIJ1QmJ4+1u+cERXkrhuILCBDsJUBAW1MPE6sFonbCNaegFuwW9ll8kh5UdyY3jOkoOGlc7BesoLpg==} + engines: {node: '>=18.0.0'} + '@smithy/property-provider@2.0.15': resolution: {integrity: sha512-YbRFBn8oiiC3o1Kn3a4KjGa6k47rCM9++5W9cWqYn9WnkyH+hBWgfJAckuxpyA2Hq6Ys4eFrWzXq6fqHEw7iew==} engines: {node: '>=14.0.0'} @@ -9635,6 +9676,10 @@ packages: resolution: {integrity: sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg==} engines: {node: '>=18.0.0'} + '@smithy/property-provider@4.2.8': + resolution: {integrity: sha512-EtCTbyIveCKeOXDSWSdze3k612yCPq1YbXsbqX3UHhkOSW8zKsM9NOJG5gTIya0vbY2DIaieG8pKo1rITHYL0w==} + engines: {node: '>=18.0.0'} + '@smithy/protocol-http@3.0.10': resolution: {integrity: sha512-6+tjNk7rXW7YTeGo9qwxXj/2BFpJTe37kTj3EnZCoX/nH+NP/WLA7O83fz8XhkGqsaAhLUPo/bB12vvd47nsmg==} engines: {node: '>=14.0.0'} @@ -9647,6 +9692,10 @@ packages: resolution: {integrity: sha512-RlaL+sA0LNMp03bf7XPbFmT5gN+w3besXSWMkA8rcmxLSVfiEXElQi4O2IWwPfxzcHkxqrwBFMbngB8yx/RvaQ==} engines: {node: '>=18.0.0'} + '@smithy/protocol-http@5.3.8': + resolution: {integrity: sha512-QNINVDhxpZ5QnP3aviNHQFlRogQZDfYlCkQT+7tJnErPQbDhysondEjhikuANxgMsZrkGeiAxXy4jguEGsDrWQ==} + engines: {node: '>=18.0.0'} + '@smithy/querystring-builder@2.0.14': resolution: {integrity: sha512-lQ4pm9vTv9nIhl5jt6uVMPludr6syE2FyJmHsIJJuOD7QPIJnrf9HhUGf1iHh9KJ4CUv21tpOU3X6s0rB6uJ0g==} engines: {node: '>=14.0.0'} @@ -9659,6 +9708,10 @@ packages: resolution: {integrity: sha512-y98otMI1saoajeik2kLfGyRp11e5U/iJYH/wLCh3aTV/XutbGT9nziKGkgCaMD1ghK7p6htHMm6b6scl9JRUWg==} engines: {node: '>=18.0.0'} + '@smithy/querystring-builder@4.2.8': + resolution: {integrity: sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw==} + engines: {node: '>=18.0.0'} + '@smithy/querystring-parser@2.0.14': resolution: {integrity: sha512-+cbtXWI9tNtQjlgQg3CA+pvL3zKTAxPnG3Pj6MP89CR3vi3QMmD0SOWoq84tqZDnJCxlsusbgIXk1ngMReXo+A==} engines: {node: '>=14.0.0'} @@ -9671,6 +9724,10 @@ packages: resolution: {integrity: sha512-031WCTdPYgiQRYNPXznHXof2YM0GwL6SeaSyTH/P72M1Vz73TvCNH2Nq8Iu2IEPq9QP2yx0/nrw5YmSeAi/AjQ==} engines: {node: '>=18.0.0'} + '@smithy/querystring-parser@4.2.8': + resolution: {integrity: sha512-vUurovluVy50CUlazOiXkPq40KGvGWSdmusa3130MwrR1UNnNgKAlj58wlOe61XSHRpUfIIh6cE0zZ8mzKaDPA==} + engines: {node: '>=18.0.0'} + '@smithy/service-error-classification@2.0.7': resolution: {integrity: sha512-LLxgW12qGz8doYto15kZ4x1rHjtXl0BnCG6T6Wb8z2DI4PT9cJfOSvzbuLzy7+5I24PAepKgFeWHRd9GYy3Z9w==} engines: {node: '>=14.0.0'} @@ -9695,6 +9752,10 @@ packages: resolution: {integrity: sha512-5WmZ5+kJgJDjwXXIzr1vDTG+RhF9wzSODQBfkrQ2VVkYALKGvZX1lgVSxEkgicSAFnFhPj5rudJV0zoinqS0bA==} engines: {node: '>=18.0.0'} + '@smithy/shared-ini-file-loader@4.4.3': + resolution: {integrity: sha512-DfQjxXQnzC5UbCUPeC3Ie8u+rIWZTvuDPAGU/BxzrOGhRvgUanaP68kDZA+jaT3ZI+djOf+4dERGlm9mWfFDrg==} + engines: {node: '>=18.0.0'} + '@smithy/signature-v4@2.0.16': resolution: {integrity: sha512-ilLY85xS2kZZzTb83diQKYLIYALvart0KnBaKnIRnMBHAGEio5aHSlANQoxVn0VsonwmQ3CnWhnCT0sERD8uTg==} engines: {node: '>=14.0.0'} @@ -9711,6 +9772,10 @@ packages: resolution: {integrity: sha512-Lw67+yQSpLl4YkDLUzI2KgS8TXclXmbzSeOJUmRFS4ueT56B4pw3RZRF/SRzvgyxM/HxgkUan8oSHXCujPDafQ==} engines: {node: '>=14.0.0'} + '@smithy/smithy-client@4.10.12': + resolution: {integrity: sha512-VKO/HKoQ5OrSHW6AJUmEnUKeXI1/5LfCwO9cwyao7CmLvGnZeM1i36Lyful3LK1XU7HwTVieTqO1y2C/6t3qtA==} + engines: {node: '>=18.0.0'} + '@smithy/smithy-client@4.4.5': resolution: {integrity: sha512-+lynZjGuUFJaMdDYSTMnP/uPBBXXukVfrJlP+1U/Dp5SFTEI++w6NMga8DjOENxecOF71V9Z2DllaVDYRnGlkg==} engines: {node: '>=18.0.0'} @@ -9727,6 +9792,10 @@ packages: resolution: {integrity: sha512-PgqxJq2IcdMF9iAasxcqZqqoOXBHufEfmbEUdN1pmJrJltT42b0Sc8UiYSWWzKkciIp9/mZDpzYi4qYG1qqg6g==} engines: {node: '>=14.0.0'} + '@smithy/types@4.12.0': + resolution: {integrity: sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw==} + engines: {node: '>=18.0.0'} + '@smithy/types@4.3.1': resolution: {integrity: sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==} engines: {node: '>=18.0.0'} @@ -9746,6 +9815,10 @@ packages: resolution: {integrity: sha512-VaxMGsilqFnK1CeBX+LXnSuaMx4sTL/6znSZh2829txWieazdVxr54HmiyTsIbpOTLcf5nYpq9lpzmwRdxj6rQ==} engines: {node: '>=18.0.0'} + '@smithy/url-parser@4.2.8': + resolution: {integrity: sha512-NQho9U68TGMEU639YkXnVMV3GEFFULmmaWdlu1E9qzyIePOHsoSnagTGSDv1Zi8DCNN6btxOSdgmy5E/hsZwhA==} + engines: {node: '>=18.0.0'} + '@smithy/util-base64@2.0.1': resolution: {integrity: sha512-DlI6XFYDMsIVN+GH9JtcRp3j02JEVuWIn/QOZisVzpIAprdsxGveFed0bjbMRCqmIFe8uetn5rxzNrBtIGrPIQ==} engines: {node: '>=14.0.0'} @@ -9873,6 +9946,10 @@ packages: resolution: {integrity: sha512-6Y3+rvBF7+PZOc40ybeZMcGln6xJGVeY60E7jy9Mv5iKpMJpHgRE6dKy9ScsVxvfAYuEX4Q9a65DQX90KaQ3bA==} engines: {node: '>=18.0.0'} + '@smithy/util-middleware@4.2.8': + resolution: {integrity: sha512-PMqfeJxLcNPMDgvPbbLl/2Vpin+luxqTGPpW3NAQVLbRrFRzTa4rNAASYeIGjRV9Ytuhzny39SpyU04EQreF+A==} + engines: {node: '>=18.0.0'} + '@smithy/util-retry@2.0.7': resolution: {integrity: sha512-fIe5yARaF0+xVT1XKcrdnHKTJ1Vc4+3e3tLDjCuIcE9b6fkBzzGFY7AFiX4M+vj6yM98DrwkuZeHf7/hmtVp0Q==} engines: {node: '>= 14.0.0'} @@ -9893,6 +9970,10 @@ packages: resolution: {integrity: sha512-aI+GLi7MJoVxg24/3J1ipwLoYzgkB4kUfogZfnslcYlynj3xsQ0e7vk4TnTro9hhsS5PvX1mwmkRqqHQjwcU7w==} engines: {node: '>=18.0.0'} + '@smithy/util-stream@4.5.10': + resolution: {integrity: sha512-jbqemy51UFSZSp2y0ZmRfckmrzuKww95zT9BYMmuJ8v3altGcqjwoV1tzpOwuHaKrwQrCjIzOib499ymr2f98g==} + engines: {node: '>=18.0.0'} + '@smithy/util-stream@4.5.6': resolution: {integrity: sha512-qWw/UM59TiaFrPevefOZ8CNBKbYEP6wBAIlLqxn3VAIo9rgnTNc4ASbVrqDmhuwI87usnjhdQrxodzAGFFzbRQ==} engines: {node: '>=18.0.0'} @@ -11697,6 +11778,9 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + buffer@5.6.0: + resolution: {integrity: sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==} + buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} @@ -18329,6 +18413,9 @@ packages: std-env@3.9.0: resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} + stream-browserify@3.0.0: + resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + stream-buffers@3.0.2: resolution: {integrity: sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ==} engines: {node: '>= 0.10.0'} @@ -21293,6 +21380,17 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/lib-storage@3.975.0(@aws-sdk/client-s3@3.940.0)': + dependencies: + '@aws-sdk/client-s3': 3.940.0 + '@smithy/abort-controller': 4.2.8 + '@smithy/middleware-endpoint': 4.4.11 + '@smithy/smithy-client': 4.10.12 + buffer: 5.6.0 + events: 3.3.0 + stream-browserify: 3.0.0 + tslib: 2.8.1 + '@aws-sdk/middleware-bucket-endpoint@3.936.0': dependencies: '@aws-sdk/types': 3.936.0 @@ -29039,6 +29137,11 @@ snapshots: '@smithy/types': 4.9.0 tslib: 2.8.1 + '@smithy/abort-controller@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + '@smithy/chunked-blob-reader-native@4.2.1': dependencies: '@smithy/util-base64': 4.3.0 @@ -29099,6 +29202,19 @@ snapshots: '@smithy/uuid': 1.1.0 tslib: 2.8.1 + '@smithy/core@3.21.1': + dependencies: + '@smithy/middleware-serde': 4.2.9 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + '@smithy/util-base64': 4.3.0 + '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-middleware': 4.2.8 + '@smithy/util-stream': 4.5.10 + '@smithy/util-utf8': 4.2.0 + '@smithy/uuid': 1.1.0 + tslib: 2.8.1 + '@smithy/core@3.6.0': dependencies: '@smithy/middleware-serde': 4.0.8 @@ -29196,6 +29312,14 @@ snapshots: '@smithy/util-base64': 4.3.0 tslib: 2.8.1 + '@smithy/fetch-http-handler@5.3.9': + dependencies: + '@smithy/protocol-http': 5.3.8 + '@smithy/querystring-builder': 4.2.8 + '@smithy/types': 4.12.0 + '@smithy/util-base64': 4.3.0 + tslib: 2.8.1 + '@smithy/hash-blob-browser@4.2.6': dependencies: '@smithy/chunked-blob-reader': 5.2.0 @@ -29326,6 +29450,17 @@ snapshots: '@smithy/util-middleware': 4.2.5 tslib: 2.8.1 + '@smithy/middleware-endpoint@4.4.11': + dependencies: + '@smithy/core': 3.21.1 + '@smithy/middleware-serde': 4.2.9 + '@smithy/node-config-provider': 4.3.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + '@smithy/url-parser': 4.2.8 + '@smithy/util-middleware': 4.2.8 + tslib: 2.8.1 + '@smithy/middleware-retry@2.0.21': dependencies: '@smithy/node-config-provider': 2.1.6 @@ -29396,6 +29531,12 @@ snapshots: '@smithy/types': 4.9.0 tslib: 2.8.1 + '@smithy/middleware-serde@4.2.9': + dependencies: + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + '@smithy/middleware-stack@2.0.8': dependencies: '@smithy/types': 2.6.0 @@ -29411,6 +29552,11 @@ snapshots: '@smithy/types': 4.9.0 tslib: 2.8.1 + '@smithy/middleware-stack@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + '@smithy/node-config-provider@2.1.6': dependencies: '@smithy/property-provider': 2.0.15 @@ -29432,6 +29578,13 @@ snapshots: '@smithy/types': 4.9.0 tslib: 2.8.1 + '@smithy/node-config-provider@4.3.8': + dependencies: + '@smithy/property-provider': 4.2.8 + '@smithy/shared-ini-file-loader': 4.4.3 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + '@smithy/node-http-handler@2.1.10': dependencies: '@smithy/abort-controller': 2.0.14 @@ -29456,6 +29609,14 @@ snapshots: '@smithy/types': 4.9.0 tslib: 2.8.1 + '@smithy/node-http-handler@4.4.8': + dependencies: + '@smithy/abort-controller': 4.2.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/querystring-builder': 4.2.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + '@smithy/property-provider@2.0.15': dependencies: '@smithy/types': 2.6.0 @@ -29471,6 +29632,11 @@ snapshots: '@smithy/types': 4.9.0 tslib: 2.8.1 + '@smithy/property-provider@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + '@smithy/protocol-http@3.0.10': dependencies: '@smithy/types': 2.6.0 @@ -29486,6 +29652,11 @@ snapshots: '@smithy/types': 4.9.0 tslib: 2.8.1 + '@smithy/protocol-http@5.3.8': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + '@smithy/querystring-builder@2.0.14': dependencies: '@smithy/types': 2.6.0 @@ -29504,6 +29675,12 @@ snapshots: '@smithy/util-uri-escape': 4.2.0 tslib: 2.8.1 + '@smithy/querystring-builder@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + '@smithy/util-uri-escape': 4.2.0 + tslib: 2.8.1 + '@smithy/querystring-parser@2.0.14': dependencies: '@smithy/types': 2.6.0 @@ -29519,6 +29696,11 @@ snapshots: '@smithy/types': 4.9.0 tslib: 2.8.1 + '@smithy/querystring-parser@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + '@smithy/service-error-classification@2.0.7': dependencies: '@smithy/types': 2.6.0 @@ -29546,6 +29728,11 @@ snapshots: '@smithy/types': 4.9.0 tslib: 2.8.1 + '@smithy/shared-ini-file-loader@4.4.3': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + '@smithy/signature-v4@2.0.16': dependencies: '@smithy/eventstream-codec': 2.0.14 @@ -29586,6 +29773,16 @@ snapshots: '@smithy/util-stream': 2.0.21 tslib: 2.8.1 + '@smithy/smithy-client@4.10.12': + dependencies: + '@smithy/core': 3.21.1 + '@smithy/middleware-endpoint': 4.4.11 + '@smithy/middleware-stack': 4.2.8 + '@smithy/protocol-http': 5.3.8 + '@smithy/types': 4.12.0 + '@smithy/util-stream': 4.5.10 + tslib: 2.8.1 + '@smithy/smithy-client@4.4.5': dependencies: '@smithy/core': 3.6.0 @@ -29620,6 +29817,10 @@ snapshots: dependencies: tslib: 2.8.1 + '@smithy/types@4.12.0': + dependencies: + tslib: 2.8.1 + '@smithy/types@4.3.1': dependencies: tslib: 2.8.1 @@ -29646,6 +29847,12 @@ snapshots: '@smithy/types': 4.9.0 tslib: 2.8.1 + '@smithy/url-parser@4.2.8': + dependencies: + '@smithy/querystring-parser': 4.2.8 + '@smithy/types': 4.12.0 + tslib: 2.8.1 + '@smithy/util-base64@2.0.1': dependencies: '@smithy/util-buffer-from': 2.0.0 @@ -29830,6 +30037,11 @@ snapshots: '@smithy/types': 4.9.0 tslib: 2.8.1 + '@smithy/util-middleware@4.2.8': + dependencies: + '@smithy/types': 4.12.0 + tslib: 2.8.1 + '@smithy/util-retry@2.0.7': dependencies: '@smithy/service-error-classification': 2.0.7 @@ -29870,6 +30082,17 @@ snapshots: '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 + '@smithy/util-stream@4.5.10': + dependencies: + '@smithy/fetch-http-handler': 5.3.9 + '@smithy/node-http-handler': 4.4.8 + '@smithy/types': 4.12.0 + '@smithy/util-base64': 4.3.0 + '@smithy/util-buffer-from': 4.2.0 + '@smithy/util-hex-encoding': 4.2.0 + '@smithy/util-utf8': 4.2.0 + tslib: 2.8.1 + '@smithy/util-stream@4.5.6': dependencies: '@smithy/fetch-http-handler': 5.3.6 @@ -31963,6 +32186,11 @@ snapshots: buffer-from@1.1.2: {} + buffer@5.6.0: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + buffer@5.7.1: dependencies: base64-js: 1.5.1 @@ -38600,7 +38828,7 @@ snapshots: react: 19.1.0 scheduler: 0.26.0 - react-email@2.1.2(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.15)(bufferutil@4.0.9)(eslint@8.31.0): + react-email@2.1.2(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.15)(eslint@8.31.0): dependencies: '@babel/parser': 7.24.1 '@radix-ui/colors': 1.0.1 @@ -38637,8 +38865,8 @@ snapshots: react: 18.3.1 react-dom: 18.2.0(react@18.3.1) shelljs: 0.8.5 - socket.io: 4.7.3(bufferutil@4.0.9) - socket.io-client: 4.7.3(bufferutil@4.0.9) + socket.io: 4.7.3 + socket.io-client: 4.7.3 sonner: 1.3.1(react-dom@18.2.0(react@18.3.1))(react@18.3.1) source-map-js: 1.0.2 stacktrace-parser: 0.1.10 @@ -39785,7 +40013,7 @@ snapshots: - supports-color - utf-8-validate - socket.io-client@4.7.3(bufferutil@4.0.9): + socket.io-client@4.7.3: dependencies: '@socket.io/component-emitter': 3.1.0 debug: 4.3.7(supports-color@10.0.0) @@ -39814,7 +40042,7 @@ snapshots: transitivePeerDependencies: - supports-color - socket.io@4.7.3(bufferutil@4.0.9): + socket.io@4.7.3: dependencies: accepts: 1.3.8 base64id: 2.0.0 @@ -39995,6 +40223,11 @@ snapshots: std-env@3.9.0: {} + stream-browserify@3.0.0: + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + stream-buffers@3.0.2: {} stream-shift@1.0.3: {}