diff --git a/frontend/app/(app)/code/[id]/page.tsx b/frontend/app/(app)/code/[id]/page.tsx index f8b58be..41c0f6f 100644 --- a/frontend/app/(app)/code/[id]/page.tsx +++ b/frontend/app/(app)/code/[id]/page.tsx @@ -62,14 +62,16 @@ export default async function CodePage({ params }: { params: { id: string } }) { return (
- {/* }> */}
- +
- {/*
*/}
); } diff --git a/frontend/components/editor/editor.tsx b/frontend/components/editor/editor.tsx index 2ae3a71..a18a149 100644 --- a/frontend/components/editor/editor.tsx +++ b/frontend/components/editor/editor.tsx @@ -35,19 +35,20 @@ import { ImperativePanelHandle } from "react-resizable-panels"; export default function CodeEditor({ userData, sandboxData, + ip, }: { userData: User; sandboxData: Sandbox; + ip: string; }) { const socket = io( // `ws://${sandboxData.id}.ws.ishaand.com?userId=${userData.id}&sandboxId=${sandboxData.id}` - `http://localhost:4000?userId=${userData.id}&sandboxId=${sandboxData.id}`, + `http://${ip}:4000?userId=${userData.id}&sandboxId=${sandboxData.id}`, { timeout: 2000, } ); - const [isAwaitingConnection, setIsAwaitingConnection] = useState(true); const [isPreviewCollapsed, setIsPreviewCollapsed] = useState(true); const [disableAccess, setDisableAccess] = useState({ isDisabled: false, @@ -362,9 +363,7 @@ export default function CodeEditor({ // Socket event listener effect useEffect(() => { - const onConnect = () => { - setIsAwaitingConnection(false); - }; + const onConnect = () => {}; const onDisconnect = () => { setTerminals([]); @@ -534,14 +533,6 @@ export default function CodeEditor({ }); }; - if (isAwaitingConnection) - return ( - - ); - // On disabled access for shared users, show un-interactable loading placeholder + info modal if (disableAccess.isDisabled) return ( diff --git a/frontend/components/editor/index.tsx b/frontend/components/editor/index.tsx index c344f9a..25b37fd 100644 --- a/frontend/components/editor/index.tsx +++ b/frontend/components/editor/index.tsx @@ -4,8 +4,9 @@ import dynamic from "next/dynamic"; import Loading from "@/components/editor/loading"; import { Sandbox, User } from "@/lib/types"; import { useEffect, useState } from "react"; -// import { startServer } from "@/lib/utils"; import { toast } from "sonner"; +import { getTaskIp, startServer } from "@/lib/actions"; +import { checkServiceStatus } from "@/lib/utils"; const CodeEditor = dynamic(() => import("@/components/editor/editor"), { ssr: false, @@ -13,29 +14,70 @@ const CodeEditor = dynamic(() => import("@/components/editor/editor"), { }); export default function Editor({ + isOwner, userData, sandboxData, }: { + isOwner: boolean; userData: User; sandboxData: Sandbox; }) { - const [isServerRunning, setIsServerRunning] = useState(false); + const [isServiceRunning, setIsServiceRunning] = useState(false); + const [isDeploymentActive, setIsDeploymentActive] = useState(false); + const [taskIp, setTaskIp] = useState(); const [didFail, setDidFail] = useState(false); useEffect(() => { - // startServer(sandboxData.id, userData.id, (success: boolean) => { - // if (!success) { - // toast.error("Failed to start server."); - // setDidFail(true); - // } else { - // setIsServerRunning(true); - // } - // }); - console.log("startServer"); + if (!isOwner) { + toast.error("You are not the owner of this sandbox. (TEMPORARY)"); + setDidFail(true); + return; + } + + startServer(sandboxData.id).then((response) => { + if (!response.success) { + toast.error(response.message); + setDidFail(true); + } else { + setIsServiceRunning(true); + + checkServiceStatus(sandboxData.id) + .then(() => { + setIsDeploymentActive(true); + + getTaskIp(sandboxData.id) + .then((ip) => { + setTaskIp(ip); + }) + .catch(() => { + setDidFail(true); + toast.error("An error occurred while getting your server IP."); + }); + }) + .catch(() => { + toast.error("An error occurred while initializing your server."); + setDidFail(true); + }); + } + }); }, []); - // if (!isServerRunning || didFail) - // return ; + if (didFail) return ; + if (!isServiceRunning || !isDeploymentActive || !taskIp) + return ( + + ); - return ; + return ( + + ); } diff --git a/frontend/components/editor/loading/index.tsx b/frontend/components/editor/loading/index.tsx index 1cc84ac..9d13cd5 100644 --- a/frontend/components/editor/loading/index.tsx +++ b/frontend/components/editor/loading/index.tsx @@ -48,12 +48,14 @@ export default function Loading({ )} {didFail ? ( - + Try again in a minute, or contact @ishaandey_ on Twitter/X if it still doesn't work. ) : description ? ( - {description} + + {description} + ) : null} diff --git a/frontend/lib/actions.ts b/frontend/lib/actions.ts index 9fef49c..432b859 100644 --- a/frontend/lib/actions.ts +++ b/frontend/lib/actions.ts @@ -1,8 +1,15 @@ "use server"; import { revalidatePath } from "next/cache"; -import ecsClient from "./ecs"; -import { CreateServiceCommand, StartTaskCommand } from "@aws-sdk/client-ecs"; +import ecsClient, { ec2Client } from "./ecs"; +import { + CreateServiceCommand, + DescribeClustersCommand, + DescribeServicesCommand, + DescribeTasksCommand, + ListTasksCommand, +} from "@aws-sdk/client-ecs"; +import { DescribeNetworkInterfacesCommand } from "@aws-sdk/client-ec2"; export async function createSandbox(body: { type: string; @@ -81,12 +88,105 @@ export async function unshareSandbox(sandboxId: string, userId: string) { revalidatePath(`/code/${sandboxId}`); } -export async function startServer(serviceName: string) { +export async function describeService(serviceName: string) { + const command = new DescribeServicesCommand({ + cluster: process.env.NEXT_PUBLIC_AWS_ECS_CLUSTER!, + services: [serviceName], + }); + + return await ecsClient.send(command); +} + +export async function getTaskIp(serviceName: string) { + const listCommand = new ListTasksCommand({ + cluster: process.env.NEXT_PUBLIC_AWS_ECS_CLUSTER!, + serviceName, + }); + + const listResponse = await ecsClient.send(listCommand); + const taskArns = listResponse.taskArns; + + const describeCommand = new DescribeTasksCommand({ + cluster: process.env.NEXT_PUBLIC_AWS_ECS_CLUSTER!, + tasks: taskArns, + }); + + const describeResponse = await ecsClient.send(describeCommand); + const tasks = describeResponse.tasks; + const taskAttachment = tasks?.[0].attachments?.[0].details; + if (!taskAttachment) { + throw new Error("Task attachment not found"); + } + + const eni = taskAttachment.find( + (detail) => detail.name === "networkInterfaceId" + )?.value; + if (!eni) { + throw new Error("Network interface not found"); + } + + const describeNetworkInterfacesCommand = new DescribeNetworkInterfacesCommand( + { + NetworkInterfaceIds: [eni], + } + ); + const describeNetworkInterfacesResponse = await ec2Client.send( + describeNetworkInterfacesCommand + ); + + const ip = + describeNetworkInterfacesResponse.NetworkInterfaces?.[0].Association + ?.PublicIp; + if (!ip) { + throw new Error("Public IP not found"); + } + + return ip; +} + +async function doesServiceExist(serviceName: string) { + const response = await describeService(serviceName); + const activeServices = response.services?.filter( + (service) => service.status === "ACTIVE" + ); + + console.log("activeServices: ", activeServices); + + return activeServices?.length === 1; +} + +async function countServices() { + const command = new DescribeClustersCommand({ + clusters: [process.env.NEXT_PUBLIC_AWS_ECS_CLUSTER!], + }); + + const response = await ecsClient.send(command); + return response.clusters?.[0].activeServicesCount!; +} + +export async function startServer( + serviceName: string +): Promise<{ success: boolean; message: string }> { + const serviceExists = await doesServiceExist(serviceName); + if (serviceExists) { + return { success: true, message: "" }; + } + + const activeServices = await countServices(); + if (activeServices >= 100) { + return { + success: false, + message: + "Too many servers are running! Please try again later or contact @ishaandey_ on Twitter/X.", + }; + } + const command = new CreateServiceCommand({ - cluster: process.env.AWS_ECS_CLUSTER!, + cluster: process.env.NEXT_PUBLIC_AWS_ECS_CLUSTER!, serviceName, taskDefinition: "Sandbox1", desiredCount: 1, + launchType: "FARGATE", networkConfiguration: { awsvpcConfiguration: { securityGroups: [process.env.AWS_ECS_SECURITY_GROUP!], @@ -107,6 +207,8 @@ export async function startServer(serviceName: string) { const response = await ecsClient.send(command); console.log("started server:", response.service?.serviceName); + return { success: true, message: "" }; + // store in workers kv: // { // userId: { @@ -116,7 +218,11 @@ export async function startServer(serviceName: string) { // } // } - } catch (error) { - console.error("Error starting server:", error); + } catch (error: any) { + // console.error("Error starting server:", error.message); + return { + success: false, + message: `Error starting server: ${error.message}. Try again in a minute, or contact @ishaandey_ on Twitter/X if it still doesn't work.`, + }; } } diff --git a/frontend/lib/ecs.ts b/frontend/lib/ecs.ts index 5866f7f..bc23a00 100644 --- a/frontend/lib/ecs.ts +++ b/frontend/lib/ecs.ts @@ -1,4 +1,5 @@ import { ECSClient } from "@aws-sdk/client-ecs"; +import { EC2Client } from "@aws-sdk/client-ec2"; const ecsClient = new ECSClient({ region: "us-east-1", @@ -8,4 +9,12 @@ const ecsClient = new ECSClient({ }, }); +export const ec2Client = new EC2Client({ + region: "us-east-1", + credentials: { + accessKeyId: process.env.AWS_ACCESS_KEY_ID!, + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!, + }, +}); + export default ecsClient; diff --git a/frontend/lib/utils.ts b/frontend/lib/utils.ts index 126e8b5..c94591c 100644 --- a/frontend/lib/utils.ts +++ b/frontend/lib/utils.ts @@ -2,8 +2,8 @@ import { type ClassValue, clsx } from "clsx"; // import { toast } from "sonner" import { twMerge } from "tailwind-merge"; import { Sandbox, TFile, TFolder } from "./types"; -import ecsClient from "./ecs"; -import { DescribeServicesCommand } from "@aws-sdk/client-ecs"; +import { Service } from "@aws-sdk/client-ecs"; +import { describeService } from "./actions"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); @@ -94,34 +94,44 @@ export function addNew( // } // } -const checkServiceStatus = (serviceName: string) => { +export function checkServiceStatus(serviceName: string): Promise { return new Promise((resolve, reject) => { - const command = new DescribeServicesCommand({ - cluster: process.env.NEXT_PUBLIC_AWS_ECS_CLUSTER, - services: [serviceName], - }); + let tries = 0; const interval = setInterval(async () => { try { - const response = await ecsClient.send(command); - console.log("Checking service status", response); + tries++; - if (response.services && response.services.length > 0) { - const service = response.services?.[0]; + if (tries > 40) { + clearInterval(interval); + reject(new Error("Timed out.")); + } + + const response = await describeService(serviceName); + const activeServices = response.services?.filter( + (service) => service.status === "ACTIVE" + ); + console.log("Checking activeServices status", activeServices); + + if (activeServices?.length === 1) { + const service = activeServices?.[0]; if ( service.runningCount === service.desiredCount && - service.deployments && - service.deployments.length === 1 && - service.deployments[0].rolloutState === "COMPLETED" + service.deployments?.length === 1 ) { - clearInterval(interval); - resolve(service); + if (service.deployments[0].rolloutState === "COMPLETED") { + clearInterval(interval); + resolve(service); + } else if (service.deployments[0].rolloutState === "FAILED") { + clearInterval(interval); + reject(new Error("Deployment failed.")); + } } } - } catch (error) { + } catch (error: any) { clearInterval(interval); reject(error); } - }, 5000); + }, 3000); }); -}; +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 4d50cfd..78da81b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@atlaskit/pragmatic-drag-and-drop": "^1.1.7", + "@aws-sdk/client-ec2": "^3.582.0", "@aws-sdk/client-ecs": "^3.577.0", "@clerk/nextjs": "^4.29.12", "@clerk/themes": "^1.7.12", @@ -165,6 +166,306 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, + "node_modules/@aws-sdk/client-ec2": { + "version": "3.582.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-ec2/-/client-ec2-3.582.0.tgz", + "integrity": "sha512-MwO4M+NyfKtKZs2PKEcwavpm+Hz17hx5YlEwmgg7ii8+TxJe+n4dNI8TnCfZtHycMc1dagKFtfgXm9nYwRRzxg==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sso-oidc": "3.582.0", + "@aws-sdk/client-sts": "3.582.0", + "@aws-sdk/core": "3.582.0", + "@aws-sdk/credential-provider-node": "3.582.0", + "@aws-sdk/middleware-host-header": "3.577.0", + "@aws-sdk/middleware-logger": "3.577.0", + "@aws-sdk/middleware-recursion-detection": "3.577.0", + "@aws-sdk/middleware-sdk-ec2": "3.582.0", + "@aws-sdk/middleware-user-agent": "3.577.0", + "@aws-sdk/region-config-resolver": "3.577.0", + "@aws-sdk/types": "3.577.0", + "@aws-sdk/util-endpoints": "3.577.0", + "@aws-sdk/util-user-agent-browser": "3.577.0", + "@aws-sdk/util-user-agent-node": "3.577.0", + "@smithy/config-resolver": "^3.0.0", + "@smithy/core": "^2.0.1", + "@smithy/fetch-http-handler": "^3.0.1", + "@smithy/hash-node": "^3.0.0", + "@smithy/invalid-dependency": "^3.0.0", + "@smithy/middleware-content-length": "^3.0.0", + "@smithy/middleware-endpoint": "^3.0.0", + "@smithy/middleware-retry": "^3.0.1", + "@smithy/middleware-serde": "^3.0.0", + "@smithy/middleware-stack": "^3.0.0", + "@smithy/node-config-provider": "^3.0.0", + "@smithy/node-http-handler": "^3.0.0", + "@smithy/protocol-http": "^4.0.0", + "@smithy/smithy-client": "^3.0.1", + "@smithy/types": "^3.0.0", + "@smithy/url-parser": "^3.0.0", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.1", + "@smithy/util-defaults-mode-node": "^3.0.1", + "@smithy/util-endpoints": "^2.0.0", + "@smithy/util-middleware": "^3.0.0", + "@smithy/util-retry": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "@smithy/util-waiter": "^3.0.0", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/client-sso": { + "version": "3.582.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.582.0.tgz", + "integrity": "sha512-C6G2vNREANe5uUCYrTs8vvGhIrrS1GRoTjr0f5qmkZDuAtuBsQNoTF6Rt+0mDwXXBYW3FcNhZntaNCGVhXlugA==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.582.0", + "@aws-sdk/middleware-host-header": "3.577.0", + "@aws-sdk/middleware-logger": "3.577.0", + "@aws-sdk/middleware-recursion-detection": "3.577.0", + "@aws-sdk/middleware-user-agent": "3.577.0", + "@aws-sdk/region-config-resolver": "3.577.0", + "@aws-sdk/types": "3.577.0", + "@aws-sdk/util-endpoints": "3.577.0", + "@aws-sdk/util-user-agent-browser": "3.577.0", + "@aws-sdk/util-user-agent-node": "3.577.0", + "@smithy/config-resolver": "^3.0.0", + "@smithy/core": "^2.0.1", + "@smithy/fetch-http-handler": "^3.0.1", + "@smithy/hash-node": "^3.0.0", + "@smithy/invalid-dependency": "^3.0.0", + "@smithy/middleware-content-length": "^3.0.0", + "@smithy/middleware-endpoint": "^3.0.0", + "@smithy/middleware-retry": "^3.0.1", + "@smithy/middleware-serde": "^3.0.0", + "@smithy/middleware-stack": "^3.0.0", + "@smithy/node-config-provider": "^3.0.0", + "@smithy/node-http-handler": "^3.0.0", + "@smithy/protocol-http": "^4.0.0", + "@smithy/smithy-client": "^3.0.1", + "@smithy/types": "^3.0.0", + "@smithy/url-parser": "^3.0.0", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.1", + "@smithy/util-defaults-mode-node": "^3.0.1", + "@smithy/util-endpoints": "^2.0.0", + "@smithy/util-middleware": "^3.0.0", + "@smithy/util-retry": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.582.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.582.0.tgz", + "integrity": "sha512-g4uiD4GUR03CqY6LwdocJxO+fHSBk/KNXBGJv1ENCcPmK3jpEI8xBggIQOQl3NWjDeP07bpIb8+UhgSoYAYtkg==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.582.0", + "@aws-sdk/core": "3.582.0", + "@aws-sdk/credential-provider-node": "3.582.0", + "@aws-sdk/middleware-host-header": "3.577.0", + "@aws-sdk/middleware-logger": "3.577.0", + "@aws-sdk/middleware-recursion-detection": "3.577.0", + "@aws-sdk/middleware-user-agent": "3.577.0", + "@aws-sdk/region-config-resolver": "3.577.0", + "@aws-sdk/types": "3.577.0", + "@aws-sdk/util-endpoints": "3.577.0", + "@aws-sdk/util-user-agent-browser": "3.577.0", + "@aws-sdk/util-user-agent-node": "3.577.0", + "@smithy/config-resolver": "^3.0.0", + "@smithy/core": "^2.0.1", + "@smithy/fetch-http-handler": "^3.0.1", + "@smithy/hash-node": "^3.0.0", + "@smithy/invalid-dependency": "^3.0.0", + "@smithy/middleware-content-length": "^3.0.0", + "@smithy/middleware-endpoint": "^3.0.0", + "@smithy/middleware-retry": "^3.0.1", + "@smithy/middleware-serde": "^3.0.0", + "@smithy/middleware-stack": "^3.0.0", + "@smithy/node-config-provider": "^3.0.0", + "@smithy/node-http-handler": "^3.0.0", + "@smithy/protocol-http": "^4.0.0", + "@smithy/smithy-client": "^3.0.1", + "@smithy/types": "^3.0.0", + "@smithy/url-parser": "^3.0.0", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.1", + "@smithy/util-defaults-mode-node": "^3.0.1", + "@smithy/util-endpoints": "^2.0.0", + "@smithy/util-middleware": "^3.0.0", + "@smithy/util-retry": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/client-sts": { + "version": "3.582.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.582.0.tgz", + "integrity": "sha512-3gaYyQkt8iTSStnjv6kJoPGDJUaPbhcgBOrXhUNbWUgAlgw7Y1aI1MYt3JqvVN4jtiCLwjuiAQATU/8elbqPdQ==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sso-oidc": "3.582.0", + "@aws-sdk/core": "3.582.0", + "@aws-sdk/credential-provider-node": "3.582.0", + "@aws-sdk/middleware-host-header": "3.577.0", + "@aws-sdk/middleware-logger": "3.577.0", + "@aws-sdk/middleware-recursion-detection": "3.577.0", + "@aws-sdk/middleware-user-agent": "3.577.0", + "@aws-sdk/region-config-resolver": "3.577.0", + "@aws-sdk/types": "3.577.0", + "@aws-sdk/util-endpoints": "3.577.0", + "@aws-sdk/util-user-agent-browser": "3.577.0", + "@aws-sdk/util-user-agent-node": "3.577.0", + "@smithy/config-resolver": "^3.0.0", + "@smithy/core": "^2.0.1", + "@smithy/fetch-http-handler": "^3.0.1", + "@smithy/hash-node": "^3.0.0", + "@smithy/invalid-dependency": "^3.0.0", + "@smithy/middleware-content-length": "^3.0.0", + "@smithy/middleware-endpoint": "^3.0.0", + "@smithy/middleware-retry": "^3.0.1", + "@smithy/middleware-serde": "^3.0.0", + "@smithy/middleware-stack": "^3.0.0", + "@smithy/node-config-provider": "^3.0.0", + "@smithy/node-http-handler": "^3.0.0", + "@smithy/protocol-http": "^4.0.0", + "@smithy/smithy-client": "^3.0.1", + "@smithy/types": "^3.0.0", + "@smithy/url-parser": "^3.0.0", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.1", + "@smithy/util-defaults-mode-node": "^3.0.1", + "@smithy/util-endpoints": "^2.0.0", + "@smithy/util-middleware": "^3.0.0", + "@smithy/util-retry": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/core": { + "version": "3.582.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.582.0.tgz", + "integrity": "sha512-ofmD96IQc9g1dbyqlCyxu5fCG7kIl9p1NoN5+vGBUyLdbmPCV3Pdg99nRHYEJuv2MgGx5AUFGDPMHcqbJpnZIw==", + "dependencies": { + "@smithy/core": "^2.0.1", + "@smithy/protocol-http": "^4.0.0", + "@smithy/signature-v4": "^3.0.0", + "@smithy/smithy-client": "^3.0.1", + "@smithy/types": "^3.0.0", + "fast-xml-parser": "4.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.582.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.582.0.tgz", + "integrity": "sha512-kGOUKw5ryPkDIYB69PjK3SicVLTbWB06ouFN2W1EvqUJpkQGPAUGzYcomKtt3mJaCTf/1kfoaHwARAl6KKSP8Q==", + "dependencies": { + "@aws-sdk/types": "3.577.0", + "@smithy/fetch-http-handler": "^3.0.1", + "@smithy/node-http-handler": "^3.0.0", + "@smithy/property-provider": "^3.0.0", + "@smithy/protocol-http": "^4.0.0", + "@smithy/smithy-client": "^3.0.1", + "@smithy/types": "^3.0.0", + "@smithy/util-stream": "^3.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.582.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.582.0.tgz", + "integrity": "sha512-GWcjHx6ErcZAi5GZ7kItX7E6ygYmklm9tD9dbCWdsnis7IiWfYZNMXFQEwKCubUmhT61zjGZGDUiRcqVeZu1Aw==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.577.0", + "@aws-sdk/credential-provider-process": "3.577.0", + "@aws-sdk/credential-provider-sso": "3.582.0", + "@aws-sdk/credential-provider-web-identity": "3.577.0", + "@aws-sdk/types": "3.577.0", + "@smithy/credential-provider-imds": "^3.0.0", + "@smithy/property-provider": "^3.0.0", + "@smithy/shared-ini-file-loader": "^3.0.0", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.582.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.582.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.582.0.tgz", + "integrity": "sha512-T8OLA/2xayRMT8z2eIZgo8tBAamTsBn7HWc8mL1a9yzv5OCPYvucNmbO915DY8u4cNbMl2dcB9frfVxIrahCXw==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.577.0", + "@aws-sdk/credential-provider-http": "3.582.0", + "@aws-sdk/credential-provider-ini": "3.582.0", + "@aws-sdk/credential-provider-process": "3.577.0", + "@aws-sdk/credential-provider-sso": "3.582.0", + "@aws-sdk/credential-provider-web-identity": "3.577.0", + "@aws-sdk/types": "3.577.0", + "@smithy/credential-provider-imds": "^3.0.0", + "@smithy/property-provider": "^3.0.0", + "@smithy/shared-ini-file-loader": "^3.0.0", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-ec2/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.582.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.582.0.tgz", + "integrity": "sha512-PSiBX6YvJaodGSVg6dReWfeYgK5Tl4fUi0GMuD9WXo/ckfxAPdDFtIfVR6VkSPUrkZj26uw1Pwqeefp2H5phag==", + "dependencies": { + "@aws-sdk/client-sso": "3.582.0", + "@aws-sdk/token-providers": "3.577.0", + "@aws-sdk/types": "3.577.0", + "@smithy/property-provider": "^3.0.0", + "@smithy/shared-ini-file-loader": "^3.0.0", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@aws-sdk/client-ecs": { "version": "3.577.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-ecs/-/client-ecs-3.577.0.tgz", @@ -551,6 +852,24 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-sdk/middleware-sdk-ec2": { + "version": "3.582.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-ec2/-/middleware-sdk-ec2-3.582.0.tgz", + "integrity": "sha512-0MXufDYUzOJk0K0fLwRk7Sq1L0EQI5qAngkeFuY8V66ZKWlb/lE0OmEei9CY2/fBsI4Aym0grRB6owGTETvpBQ==", + "dependencies": { + "@aws-sdk/types": "3.577.0", + "@aws-sdk/util-format-url": "3.577.0", + "@smithy/middleware-endpoint": "^3.0.0", + "@smithy/protocol-http": "^4.0.0", + "@smithy/signature-v4": "^3.0.0", + "@smithy/smithy-client": "^3.0.1", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@aws-sdk/middleware-user-agent": { "version": "3.577.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.577.0.tgz", @@ -626,6 +945,20 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-sdk/util-format-url": { + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.577.0.tgz", + "integrity": "sha512-SyEGC2J+y/krFRuPgiF02FmMYhqbiIkOjDE6k4nYLJQRyS6XEAGxZoG+OHeOVEM+bsDgbxokXZiM3XKGu6qFIg==", + "dependencies": { + "@aws-sdk/types": "3.577.0", + "@smithy/querystring-builder": "^3.0.0", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@aws-sdk/util-locate-window": { "version": "3.568.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.568.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 13ed68e..5d68434 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@atlaskit/pragmatic-drag-and-drop": "^1.1.7", + "@aws-sdk/client-ec2": "^3.582.0", "@aws-sdk/client-ecs": "^3.577.0", "@clerk/nextjs": "^4.29.12", "@clerk/themes": "^1.7.12",