transfer to ecs
This commit is contained in:
parent
509669ea30
commit
80547e9ae1
@ -1,20 +0,0 @@
|
|||||||
# This file specifies files that are *not* uploaded to Google Cloud
|
|
||||||
# using gcloud. It follows the same syntax as .gitignore, with the addition of
|
|
||||||
# "#!include" directives (which insert the entries of the given .gitignore-style
|
|
||||||
# file at that point).
|
|
||||||
#
|
|
||||||
# For more information, run:
|
|
||||||
# $ gcloud topic gcloudignore
|
|
||||||
#
|
|
||||||
.gcloudignore
|
|
||||||
# If you would like to upload your .git directory, .gitignore file or files
|
|
||||||
# from your .gitignore file, remove the corresponding line
|
|
||||||
# below:
|
|
||||||
.git
|
|
||||||
.gitignore
|
|
||||||
|
|
||||||
# Node.js dependencies:
|
|
||||||
node_modules/
|
|
||||||
.env
|
|
||||||
|
|
||||||
ingressController.yaml
|
|
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"watch": ["src"],
|
|
||||||
"ext": "ts",
|
|
||||||
"exec": "concurrently \"npx tsc --watch\" \"ts-node src/index.ts\""
|
|
||||||
}
|
|
2528
backend/orchestrator/package-lock.json
generated
2528
backend/orchestrator/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "orchestrator",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "",
|
|
||||||
"main": "src/index.js",
|
|
||||||
"scripts": {
|
|
||||||
"build": "npm i && npx tsc",
|
|
||||||
"start": "node dist/index.js",
|
|
||||||
"dev": "nodemon src/index.ts"
|
|
||||||
},
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"@kubernetes/client-node": "^0.21.0",
|
|
||||||
"cors": "^2.8.5",
|
|
||||||
"dotenv": "^16.4.5",
|
|
||||||
"express": "^4.19.2",
|
|
||||||
"yaml": "^2.4.2",
|
|
||||||
"zod": "^3.23.6"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"typescript": "^5.4.5",
|
|
||||||
"@types/cors": "^2.8.17",
|
|
||||||
"@types/express": "^4.17.21",
|
|
||||||
"@types/node": "^20.12.8",
|
|
||||||
"nodemon": "^3.1.0",
|
|
||||||
"ts-node": "^10.9.2"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,86 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: <SANDBOX>
|
|
||||||
labels:
|
|
||||||
app: <SANDBOX>
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: <SANDBOX>
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: <SANDBOX>
|
|
||||||
spec:
|
|
||||||
volumes:
|
|
||||||
- name: projects-volume
|
|
||||||
emptyDir: {}
|
|
||||||
containers:
|
|
||||||
- name: sandbox
|
|
||||||
image: ishaan1013/sandbox:latest
|
|
||||||
ports:
|
|
||||||
- containerPort: 4000
|
|
||||||
- containerPort: 3000
|
|
||||||
volumeMounts:
|
|
||||||
- name: projects-volume
|
|
||||||
mountPath: /projects
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: "1"
|
|
||||||
memory: "1Gi"
|
|
||||||
limits:
|
|
||||||
cpu: "1"
|
|
||||||
memory: "1Gi"
|
|
||||||
env:
|
|
||||||
- name: CF_AI_KEY
|
|
||||||
value: <CF_AI_KEY>
|
|
||||||
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: <SANDBOX>
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: <SANDBOX>
|
|
||||||
ports:
|
|
||||||
- protocol: TCP
|
|
||||||
name: ws
|
|
||||||
port: 4000
|
|
||||||
targetPort: 4000
|
|
||||||
- protocol: TCP
|
|
||||||
name: user
|
|
||||||
port: 3000
|
|
||||||
targetPort: 3000
|
|
||||||
---
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: <SANDBOX>
|
|
||||||
annotations:
|
|
||||||
kubernetes.io/ingress.class: "nginx"
|
|
||||||
spec:
|
|
||||||
ingressClassName: nginx
|
|
||||||
rules:
|
|
||||||
- host: <SANDBOX>.ws.ishaand.com
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: <SANDBOX>
|
|
||||||
port:
|
|
||||||
number: 4000
|
|
||||||
- host: <SANDBOX>.sandbox.ishaand.com
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: <SANDBOX>
|
|
||||||
port:
|
|
||||||
number: 3000
|
|
@ -1,221 +0,0 @@
|
|||||||
import express, { Express, Request, Response } from "express";
|
|
||||||
import dotenv from "dotenv";
|
|
||||||
|
|
||||||
import fs from "fs";
|
|
||||||
import yaml from "yaml";
|
|
||||||
import path from "path";
|
|
||||||
import cors from "cors";
|
|
||||||
import {
|
|
||||||
KubeConfig,
|
|
||||||
AppsV1Api,
|
|
||||||
CoreV1Api,
|
|
||||||
NetworkingV1Api,
|
|
||||||
} from "@kubernetes/client-node";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
const app = express();
|
|
||||||
const port = process.env.PORT || 4001;
|
|
||||||
app.use(express.json());
|
|
||||||
dotenv.config();
|
|
||||||
|
|
||||||
// const corsOptions = {
|
|
||||||
// origin: ['http://localhost:3000', 'https://s.ishaand.com', 'http://localhost:4000', /\.ws\.ishaand\.com$/],
|
|
||||||
// }
|
|
||||||
// app.use(cors(corsOptions))
|
|
||||||
|
|
||||||
app.use(cors());
|
|
||||||
|
|
||||||
const kubeconfig = new KubeConfig();
|
|
||||||
kubeconfig.loadFromOptions({
|
|
||||||
clusters: [
|
|
||||||
{
|
|
||||||
name: "gke_sylvan-epoch-422219-f9_us-central1_sandbox-cluster",
|
|
||||||
server: process.env.GKE_CLUSTER_SERVER!,
|
|
||||||
caData: process.env.GKE_CLUSTER_CA_DATA,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
users: [
|
|
||||||
{
|
|
||||||
name: "gke_sylvan-epoch-422219-f9_us-central1_sandbox-cluster",
|
|
||||||
exec: {
|
|
||||||
apiVersion: "client.authentication.k8s.io/v1beta1",
|
|
||||||
command: "gke-gcloud-auth-plugin",
|
|
||||||
installHint:
|
|
||||||
"Install gke-gcloud-auth-plugin for use with kubectl by following https://cloud.google.com/kubernetes-engine/docs/how-to/cluster-access-for-kubectl#install_plugin",
|
|
||||||
interactiveMode: "IfAvailable",
|
|
||||||
provideClusterInfo: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
contexts: [
|
|
||||||
{
|
|
||||||
name: "gke_sylvan-epoch-422219-f9_us-central1_sandbox-cluster",
|
|
||||||
cluster: "gke_sylvan-epoch-422219-f9_us-central1_sandbox-cluster",
|
|
||||||
user: "gke_sylvan-epoch-422219-f9_us-central1_sandbox-cluster",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
currentContext: "gke_sylvan-epoch-422219-f9_us-central1_sandbox-cluster",
|
|
||||||
});
|
|
||||||
|
|
||||||
const appsV1Api = kubeconfig.makeApiClient(AppsV1Api);
|
|
||||||
const coreV1Api = kubeconfig.makeApiClient(CoreV1Api);
|
|
||||||
const networkingV1Api = kubeconfig.makeApiClient(NetworkingV1Api);
|
|
||||||
|
|
||||||
const readAndParseKubeYaml = (
|
|
||||||
filePath: string,
|
|
||||||
sandboxId: string
|
|
||||||
): Array<any> => {
|
|
||||||
const fileContent = fs.readFileSync(filePath, "utf8");
|
|
||||||
const docs = yaml.parseAllDocuments(fileContent).map((doc) => {
|
|
||||||
let docString = doc.toString();
|
|
||||||
|
|
||||||
const regex = new RegExp(`<SANDBOX>`, "g");
|
|
||||||
docString = docString.replace(regex, sandboxId);
|
|
||||||
|
|
||||||
if (!process.env.CF_AI_KEY) {
|
|
||||||
throw new Error("CF_AI_KEY is not defined");
|
|
||||||
}
|
|
||||||
|
|
||||||
const regexEnv = new RegExp(`<CF_AI_KEY>`, "g");
|
|
||||||
docString = docString.replace(regexEnv, process.env.CF_AI_KEY);
|
|
||||||
|
|
||||||
return yaml.parse(docString);
|
|
||||||
});
|
|
||||||
return docs;
|
|
||||||
};
|
|
||||||
|
|
||||||
const dataSchema = z.object({
|
|
||||||
userId: z.string(),
|
|
||||||
sandboxId: z.string(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const namespace = "ingress-nginx";
|
|
||||||
|
|
||||||
app.get("/", async (req, res) => {
|
|
||||||
res.status(200).send({ message: "Orchestrator" });
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/test", async (req, res) => {
|
|
||||||
const pods = await coreV1Api.listNamespacedPod(namespace);
|
|
||||||
res.status(200).send({
|
|
||||||
pods: pods.body.items.map((item) => item?.metadata?.generateName),
|
|
||||||
message: "Orchestrator is up and running.",
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/start", async (req, res) => {
|
|
||||||
const { sandboxId } = dataSchema.parse(req.body);
|
|
||||||
|
|
||||||
try {
|
|
||||||
console.log("Creating resources for sandbox", sandboxId);
|
|
||||||
|
|
||||||
const kubeManifests = readAndParseKubeYaml(
|
|
||||||
path.join(__dirname, "../service.yaml"),
|
|
||||||
sandboxId
|
|
||||||
);
|
|
||||||
|
|
||||||
async function resourceExists(api: any, getMethod: string, name: string) {
|
|
||||||
try {
|
|
||||||
await api[getMethod](name, namespace);
|
|
||||||
return true;
|
|
||||||
} catch (e: any) {
|
|
||||||
if (e.response && e.response.statusCode === 404) {
|
|
||||||
console.log(
|
|
||||||
"Resource does not exist.",
|
|
||||||
e.response.body.message,
|
|
||||||
e.response.body.details
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const createResource = async (api: any, method: string, manifest: any) => {
|
|
||||||
const {
|
|
||||||
kind,
|
|
||||||
metadata: { name },
|
|
||||||
} = manifest;
|
|
||||||
if (!(await resourceExists(api, "readNamespaced" + kind, name))) {
|
|
||||||
await api["createNamespaced" + kind](namespace, manifest);
|
|
||||||
console.log(`Created ${kind.toLowerCase()}`, name);
|
|
||||||
} else {
|
|
||||||
console.log(`${kind} ${name} already exists.`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const promises = kubeManifests.map(async (manifest) => {
|
|
||||||
const {
|
|
||||||
kind,
|
|
||||||
metadata: { name },
|
|
||||||
} = manifest;
|
|
||||||
|
|
||||||
console.log("Kind:", kind);
|
|
||||||
|
|
||||||
switch (manifest.kind) {
|
|
||||||
case "Deployment":
|
|
||||||
return createResource(appsV1Api, "Deployment", manifest);
|
|
||||||
case "Service":
|
|
||||||
return createResource(coreV1Api, "Service", manifest);
|
|
||||||
case "Ingress":
|
|
||||||
return createResource(networkingV1Api, "Ingress", manifest);
|
|
||||||
default:
|
|
||||||
console.error("Unsupported kind:", manifest.kind);
|
|
||||||
return Promise.reject("Unsupported kind: " + manifest.kind);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all(promises);
|
|
||||||
|
|
||||||
console.log("All done!");
|
|
||||||
res.status(200).send({ message: "Resources created." });
|
|
||||||
} catch (error: any) {
|
|
||||||
const body = error.response.body;
|
|
||||||
console.log("Failed to create resources", error);
|
|
||||||
|
|
||||||
if (body.code === 409) {
|
|
||||||
return res.status(200).send({ message: "Resource already exists." });
|
|
||||||
}
|
|
||||||
res.status(500).send({ message: "Failed to create resources." });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/stop", async (req, res) => {
|
|
||||||
const { sandboxId } = dataSchema.parse(req.body);
|
|
||||||
console.log("Deleting resources for sandbox", sandboxId);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const kubeManifests = readAndParseKubeYaml(
|
|
||||||
path.join(__dirname, "../service.yaml"),
|
|
||||||
sandboxId
|
|
||||||
);
|
|
||||||
const promises = kubeManifests.map(async (manifest) => {
|
|
||||||
if (manifest.kind === "Deployment")
|
|
||||||
await appsV1Api.deleteNamespacedDeployment(
|
|
||||||
manifest.metadata?.name || "",
|
|
||||||
namespace
|
|
||||||
);
|
|
||||||
else if (manifest.kind === "Service")
|
|
||||||
await coreV1Api.deleteNamespacedService(
|
|
||||||
manifest.metadata?.name || "",
|
|
||||||
namespace
|
|
||||||
);
|
|
||||||
else if (manifest.kind === "Ingress")
|
|
||||||
await networkingV1Api.deleteNamespacedIngress(
|
|
||||||
manifest.metadata?.name || "",
|
|
||||||
namespace
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all(promises);
|
|
||||||
|
|
||||||
res.status(200).send({ message: "Resources deleted." });
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Failed to delete resources", error);
|
|
||||||
res.status(500).send({ message: "Failed to delete resources." });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.listen(port, () => {
|
|
||||||
console.log(`Listening on port: ${port}`);
|
|
||||||
});
|
|
@ -1,109 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
|
||||||
|
|
||||||
/* Projects */
|
|
||||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
|
||||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
|
||||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
|
||||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
|
||||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
|
||||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
|
||||||
|
|
||||||
/* Language and Environment */
|
|
||||||
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
|
||||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
|
||||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
|
||||||
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
|
||||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
|
||||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
|
||||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
|
||||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
|
||||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
|
||||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
|
||||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
|
||||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
|
||||||
|
|
||||||
/* Modules */
|
|
||||||
"module": "commonjs", /* Specify what module code is generated. */
|
|
||||||
// "rootDir": "./", /* Specify the root folder within your source files. */
|
|
||||||
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
|
|
||||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
|
||||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
|
||||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
|
||||||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
|
||||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
|
||||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
|
||||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
|
||||||
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
|
|
||||||
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
|
||||||
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
|
||||||
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
|
||||||
// "resolveJsonModule": true, /* Enable importing .json files. */
|
|
||||||
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
|
||||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
|
||||||
|
|
||||||
/* JavaScript Support */
|
|
||||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
|
||||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
|
||||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
|
||||||
|
|
||||||
/* Emit */
|
|
||||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
|
||||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
|
||||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
|
||||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
|
||||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
|
||||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
|
||||||
"outDir": "./dist",
|
|
||||||
// "removeComments": true, /* Disable emitting comments. */
|
|
||||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
|
||||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
|
||||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
|
||||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
|
||||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
|
||||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
|
||||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
|
||||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
|
||||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
|
||||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
|
||||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
|
||||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
|
||||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
|
||||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
|
||||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
|
||||||
|
|
||||||
/* Interop Constraints */
|
|
||||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
|
||||||
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
|
|
||||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
|
||||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
|
||||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
|
||||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
|
||||||
|
|
||||||
/* Type Checking */
|
|
||||||
"strict": true, /* Enable all strict type-checking options. */
|
|
||||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
|
||||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
|
||||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
|
||||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
|
||||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
|
||||||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
|
||||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
|
||||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
|
||||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
|
||||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
|
||||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
|
||||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
|
||||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
|
||||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
|
||||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
|
||||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
|
||||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
|
||||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
|
||||||
|
|
||||||
/* Completeness */
|
|
||||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
|
||||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
|
||||||
}
|
|
||||||
}
|
|
@ -21,5 +21,6 @@ RUN mkdir projects && chown -R myuser:myuser projects
|
|||||||
USER myuser
|
USER myuser
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
EXPOSE 4000
|
||||||
|
|
||||||
CMD [ "node", "dist/index.js" ]
|
CMD [ "node", "dist/index.js" ]
|
1157
backend/server/package-lock.json
generated
1157
backend/server/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -11,6 +11,7 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@aws-sdk/client-ecs": "^3.577.0",
|
||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
|
@ -18,6 +18,7 @@ import {
|
|||||||
renameFile,
|
renameFile,
|
||||||
saveFile,
|
saveFile,
|
||||||
stopServer,
|
stopServer,
|
||||||
|
testDescribe,
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
import { IDisposable, IPty, spawn } from "node-pty";
|
import { IDisposable, IPty, spawn } from "node-pty";
|
||||||
import {
|
import {
|
||||||
@ -115,6 +116,10 @@ io.on("connection", async (socket) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("describing service:");
|
||||||
|
const describeService = await testDescribe();
|
||||||
|
console.log(describeService);
|
||||||
|
|
||||||
const sandboxFiles = await getSandboxFiles(data.sandboxId);
|
const sandboxFiles = await getSandboxFiles(data.sandboxId);
|
||||||
sandboxFiles.fileData.forEach((file) => {
|
sandboxFiles.fileData.forEach((file) => {
|
||||||
const filePath = path.join(dirName, file.id);
|
const filePath = path.join(dirName, file.id);
|
||||||
@ -290,9 +295,9 @@ io.on("connection", async (socket) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("renameFolder", async (folderId: string, newName: string) => {
|
// todo
|
||||||
// todo
|
// socket.on("renameFolder", async (folderId: string, newName: string) => {
|
||||||
});
|
// });
|
||||||
|
|
||||||
socket.on("deleteFolder", async (folderId: string, callback) => {
|
socket.on("deleteFolder", async (folderId: string, callback) => {
|
||||||
const files = await getFolder(folderId);
|
const files = await getFolder(folderId);
|
||||||
@ -329,7 +334,6 @@ io.on("connection", async (socket) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const onData = pty.onData((data) => {
|
const onData = pty.onData((data) => {
|
||||||
// console.log("terminalResponse", id, data)
|
|
||||||
io.emit("terminalResponse", {
|
io.emit("terminalResponse", {
|
||||||
id,
|
id,
|
||||||
data,
|
data,
|
||||||
@ -430,7 +434,6 @@ io.on("connection", async (socket) => {
|
|||||||
socket.on("disconnect", async () => {
|
socket.on("disconnect", async () => {
|
||||||
console.log("disconnected", data.userId, data.sandboxId);
|
console.log("disconnected", data.userId, data.sandboxId);
|
||||||
if (data.isOwner) {
|
if (data.isOwner) {
|
||||||
// console.log("deleting all terminals")
|
|
||||||
Object.entries(terminals).forEach((t) => {
|
Object.entries(terminals).forEach((t) => {
|
||||||
const { terminal, onData, onExit } = t[1];
|
const { terminal, onData, onExit } = t[1];
|
||||||
onData.dispose();
|
onData.dispose();
|
||||||
@ -453,9 +456,8 @@ io.on("connection", async (socket) => {
|
|||||||
inactivityTimeout = setTimeout(() => {
|
inactivityTimeout = setTimeout(() => {
|
||||||
io.fetchSockets().then(async (sockets) => {
|
io.fetchSockets().then(async (sockets) => {
|
||||||
if (sockets.length === 0) {
|
if (sockets.length === 0) {
|
||||||
// close server
|
|
||||||
console.log("Closing server due to inactivity.");
|
console.log("Closing server due to inactivity.");
|
||||||
const res = await stopServer(data.sandboxId, data.userId);
|
// const res = await stopServer(data.sandboxId, data.userId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, 20000);
|
}, 20000);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import e from "cors";
|
import * as dotenv from "dotenv";
|
||||||
import {
|
import {
|
||||||
R2FileBody,
|
R2FileBody,
|
||||||
R2Files,
|
R2Files,
|
||||||
@ -9,6 +9,33 @@ import {
|
|||||||
User,
|
User,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
|
import {
|
||||||
|
DescribeServicesCommand,
|
||||||
|
ECSClient,
|
||||||
|
StartTaskCommand,
|
||||||
|
StopTaskCommand,
|
||||||
|
} from "@aws-sdk/client-ecs";
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
const client = new ECSClient({
|
||||||
|
region: "us-east-1",
|
||||||
|
credentials: {
|
||||||
|
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
||||||
|
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const testDescribe = async () => {
|
||||||
|
const command = new DescribeServicesCommand({
|
||||||
|
cluster: "Sandbox",
|
||||||
|
services: ["Sandbox"],
|
||||||
|
});
|
||||||
|
const response = await client.send(command);
|
||||||
|
console.log("describing: ", response);
|
||||||
|
return response;
|
||||||
|
};
|
||||||
|
|
||||||
export const getSandboxFiles = async (id: string) => {
|
export const getSandboxFiles = async (id: string) => {
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`https://storage.ishaan1013.workers.dev/api?sandboxId=${id}`
|
`https://storage.ishaan1013.workers.dev/api?sandboxId=${id}`
|
||||||
@ -151,18 +178,16 @@ export const getProjectSize = async (id: string) => {
|
|||||||
return (await res.json()).size;
|
return (await res.json()).size;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const stopServer = async (sandboxId: string, userId: string) => {
|
export const stopServer = async (task: string) => {
|
||||||
const res = await fetch("http://localhost:4001/stop", {
|
const command = new StopTaskCommand({
|
||||||
method: "POST",
|
cluster: "arn:aws:ecs:us-east-1:767398085538:service/Sandbox/Sandbox",
|
||||||
headers: {
|
task,
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
sandboxId,
|
|
||||||
userId,
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
const data = await res.json();
|
|
||||||
|
|
||||||
return data;
|
try {
|
||||||
|
const response = await client.send(command);
|
||||||
|
console.log("Stopped server:", response);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error stopping server:", error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user