From 23fed0b04e0d0fb493e09212c33409e83289e87e Mon Sep 17 00:00:00 2001 From: highjeans Date: Tue, 26 Nov 2024 22:28:33 +0000 Subject: [PATCH 01/11] Start engine code --- apps/engine/package.json | 6 +- apps/engine/src/index.ts | 30 ++++++- package-lock.json | 160 +++++++++++++++++++++++++++++++++++- packages/kafka/src/index.ts | 2 + 4 files changed, 195 insertions(+), 3 deletions(-) diff --git a/apps/engine/package.json b/apps/engine/package.json index fafb936..8ca3162 100644 --- a/apps/engine/package.json +++ b/apps/engine/package.json @@ -14,6 +14,10 @@ "dependencies": { "@repo/kafka": "*", "dotenv": "^16.4.5", - "esbuild": "^0.24.0" + "esbuild": "^0.24.0", + "node-ssh": "^13.2.0" + }, + "devDependencies": { + "@types/ssh2": "^1.15.1" } } diff --git a/apps/engine/src/index.ts b/apps/engine/src/index.ts index fc58014..e33661b 100644 --- a/apps/engine/src/index.ts +++ b/apps/engine/src/index.ts @@ -1,2 +1,30 @@ +import kafkaClient, { SUBMIT_TOPIC } from '@repo/kafka/client'; + // need to take input from the kafka queue and process it -console.log('hello'); +async function main() { + if (!SUBMIT_TOPIC) { + console.error('No topic name provided, exiting.'); + return; + } + try { + await kafkaClient.createTopic(SUBMIT_TOPIC); + + const consumer = kafkaClient + .getInstance() + .consumer({ groupId: 'engine-group' }); + await consumer.connect(); + + await consumer.subscribe({ topic: SUBMIT_TOPIC, fromBeginning: false }); + + await consumer.run({ + autoCommit: true, + eachMessage: async ({ message }) => { + const data = JSON.parse(message.value?.toString() || '{}'); + }, + }); + } catch (error) { + console.error(error); + } +} + +main(); diff --git a/package-lock.json b/package-lock.json index b552489..42e2e22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -82,7 +82,11 @@ "dependencies": { "@repo/kafka": "*", "dotenv": "^16.4.5", - "esbuild": "^0.24.0" + "esbuild": "^0.24.0", + "node-ssh": "^13.2.0" + }, + "devDependencies": { + "@types/ssh2": "^1.15.1" } }, "apps/engine/node_modules/@esbuild/aix-ppc64": { @@ -6288,6 +6292,33 @@ "@types/send": "*" } }, + "node_modules/@types/ssh2": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.15.1.tgz", + "integrity": "sha512-ZIbEqKAsi5gj35y4P4vkJYly642wIbY6PqoN0xiyQGshKUGXR9WQjF/iF9mXBQ8uBKy3ezfsCkcoHKhd0BzuDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "^18.11.18" + } + }, + "node_modules/@types/ssh2/node_modules/@types/node": { + "version": "18.19.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.66.tgz", + "integrity": "sha512-14HmtUdGxFUalGRfLLn9Gc1oNWvWh5zNbsyOLo5JV6WARSeN1QcEBKRnZm9QqNfrutgsl/hY4eJW63aZ44aBCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/ssh2/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/through": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.33.tgz", @@ -7456,6 +7487,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, "node_modules/ast-types": { "version": "0.13.4", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", @@ -7619,6 +7659,15 @@ "node": ">= 10.0.0" } }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -7799,6 +7848,15 @@ "license": "MIT", "peer": true }, + "node_modules/buildcheck": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz", + "integrity": "sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==", + "optional": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/builtin-modules": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", @@ -8382,6 +8440,20 @@ "node": ">= 6" } }, + "node_modules/cpu-features": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", + "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "buildcheck": "~0.0.6", + "nan": "^2.19.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -13862,6 +13934,13 @@ "thenify-all": "^1.0.0" } }, + "node_modules/nan": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz", + "integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==", + "license": "MIT", + "optional": true + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -14358,6 +14437,35 @@ "dev": true, "license": "MIT" }, + "node_modules/node-ssh": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/node-ssh/-/node-ssh-13.2.0.tgz", + "integrity": "sha512-7vsKR2Bbs66th6IWCy/7SN4MSwlVt+G6QrHB631BjRUM8/LmvDugtYhi0uAmgvHS/+PVurfNBOmELf30rm0MZg==", + "license": "MIT", + "dependencies": { + "is-stream": "^2.0.0", + "make-dir": "^3.1.0", + "sb-promise-queue": "^2.1.0", + "sb-scandir": "^3.1.0", + "shell-escape": "^0.2.0", + "ssh2": "^1.14.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/node-ssh/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", @@ -16393,6 +16501,27 @@ } } }, + "node_modules/sb-promise-queue": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sb-promise-queue/-/sb-promise-queue-2.1.0.tgz", + "integrity": "sha512-zwq4YuP1FQFkGx2Q7GIkZYZ6PqWpV+bg0nIO1sJhWOyGyhqbj0MsTvK6lCFo5TQwX5pZr6SCQ75e8PCDCuNvkg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/sb-scandir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/sb-scandir/-/sb-scandir-3.1.0.tgz", + "integrity": "sha512-70BVm2xz9jn94zSQdpvYrEG101/UV9TVGcfWr9T5iob3QhCK4lYXeculfBqPGFv3XTeKgx4dpWyYIDeZUqo4kg==", + "license": "MIT", + "dependencies": { + "sb-promise-queue": "^2.1.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -16585,6 +16714,12 @@ "node": ">=8" } }, + "node_modules/shell-escape": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/shell-escape/-/shell-escape-0.2.0.tgz", + "integrity": "sha512-uRRBT2MfEOyxuECseCZd28jC1AJ8hmqqneWQ4VWUTgCAFvb3wKU1jLqj6egC4Exrr88ogg3dp+zroH4wJuaXzw==", + "license": "MIT" + }, "node_modules/side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", @@ -16823,6 +16958,23 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/ssh2": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.16.0.tgz", + "integrity": "sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg==", + "hasInstallScript": true, + "dependencies": { + "asn1": "^0.2.6", + "bcrypt-pbkdf": "^1.0.2" + }, + "engines": { + "node": ">=10.16.0" + }, + "optionalDependencies": { + "cpu-features": "~0.0.10", + "nan": "^2.20.0" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -18526,6 +18678,12 @@ "dev": true, "license": "BSD" }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "license": "Unlicense" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/packages/kafka/src/index.ts b/packages/kafka/src/index.ts index 051ee99..5cc0c44 100644 --- a/packages/kafka/src/index.ts +++ b/packages/kafka/src/index.ts @@ -56,3 +56,5 @@ class KafkaSingleton { const kafkaClient = KafkaSingleton; export default kafkaClient; + +export const SUBMIT_TOPIC = process.env.SUBMIT_TOPIC_NAME; From c2c1618a2e6644bd2c1b7a98b379a916ac465b79 Mon Sep 17 00:00:00 2001 From: Ayush Acharjya Date: Sun, 8 Dec 2024 00:06:25 +0000 Subject: [PATCH 02/11] add logic to ssh into aws --- .../src/controllers/assignmentController.ts | 2 +- apps/engine/package.json | 5 +- apps/engine/src/index.ts | 7 +- apps/engine/src/utils/ssh.ts | 32 ++++ package-lock.json | 137 +++++++++++++++++- 5 files changed, 176 insertions(+), 7 deletions(-) create mode 100644 apps/engine/src/utils/ssh.ts diff --git a/apps/backend/src/controllers/assignmentController.ts b/apps/backend/src/controllers/assignmentController.ts index 9088489..a4e29e6 100644 --- a/apps/backend/src/controllers/assignmentController.ts +++ b/apps/backend/src/controllers/assignmentController.ts @@ -261,7 +261,7 @@ const submitAssignment = async (req: Request, res: Response) => { const payload = { markingScript: user.student.courses[0].assignments[0].markingScript, requiredFiles: user.student.courses[0].assignments[0].requiredFiles, - studentId: parsedData.data.userId, + userId: parsedData.data.userId, assignmentId: parsedData.data.assignmentId, }; diff --git a/apps/engine/package.json b/apps/engine/package.json index fafb936..ff4c904 100644 --- a/apps/engine/package.json +++ b/apps/engine/package.json @@ -13,7 +13,8 @@ "description": "", "dependencies": { "@repo/kafka": "*", - "dotenv": "^16.4.5", - "esbuild": "^0.24.0" + "dotenv": "^16.4.7", + "esbuild": "^0.24.0", + "node-ssh": "^13.2.0" } } diff --git a/apps/engine/src/index.ts b/apps/engine/src/index.ts index c2f4e58..616af42 100644 --- a/apps/engine/src/index.ts +++ b/apps/engine/src/index.ts @@ -1,5 +1,9 @@ import kafkaClient from '@repo/kafka/client'; import { SUBMIT } from '@repo/topics/topics'; +import dotenv from 'dotenv'; +import { sshIntoEC2 } from './utils/ssh'; + +dotenv.config(); const topic = SUBMIT; @@ -24,4 +28,5 @@ async function consumeMessages() { } } -consumeMessages(); +// consumeMessages(); +sshIntoEC2(); diff --git a/apps/engine/src/utils/ssh.ts b/apps/engine/src/utils/ssh.ts new file mode 100644 index 0000000..6b533e0 --- /dev/null +++ b/apps/engine/src/utils/ssh.ts @@ -0,0 +1,32 @@ +import fs from 'fs'; +import { NodeSSH } from 'node-ssh'; + +const ssh = new NodeSSH(); + +export async function sshIntoEC2() { + try { + const privateKeyPath = process.env.PRIVATE_KEY as string; + const privateKey = fs.readFileSync(privateKeyPath, 'utf-8'); + const host = process.env.HOST as string; + const username = process.env.USERNAME as string; + + console.log('Connecting to EC2 instance...'); + await ssh.connect({ + host, + username, + privateKey, + }); + + console.log('Connected to EC2 instance.'); + + await ssh.execCommand('cd'); + await ssh.execCommand('touch test.txt'); + await ssh.execCommand('echo "Hello World" > test.txt'); + await ssh.execCommand('ls -la'); + + ssh.dispose(); + console.log('Connection closed.'); + } catch (error) { + console.error('Failed to SSH into EC2 instance:', error); + } +} diff --git a/package-lock.json b/package-lock.json index ac083e9..81c06d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -86,8 +86,10 @@ "license": "ISC", "dependencies": { "@repo/kafka": "*", - "dotenv": "^16.4.5", - "esbuild": "^0.24.0" + "dotenv": "^16.4.7", + "esbuild": "^0.24.0", + "node-ssh": "^13.2.0", + "ssh2": "^1.16.0" } }, "apps/engine/node_modules/@esbuild/win32-x64": { @@ -105,7 +107,9 @@ } }, "apps/engine/node_modules/dotenv": { - "version": "16.4.5", + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -6707,6 +6711,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, "node_modules/ast-types": { "version": "0.13.4", "dev": true, @@ -6861,6 +6874,15 @@ "node": ">= 10.0.0" } }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, "node_modules/big.js": { "version": "5.2.2", "dev": true, @@ -7015,6 +7037,15 @@ "license": "MIT", "peer": true }, + "node_modules/buildcheck": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz", + "integrity": "sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==", + "optional": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/builtin-modules": { "version": "3.3.0", "dev": true, @@ -7515,6 +7546,20 @@ "node": ">= 6" } }, + "node_modules/cpu-features": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", + "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "buildcheck": "~0.0.6", + "nan": "^2.19.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/create-require": { "version": "1.1.1", "devOptional": true, @@ -12260,6 +12305,13 @@ "thenify-all": "^1.0.0" } }, + "node_modules/nan": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz", + "integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==", + "license": "MIT", + "optional": true + }, "node_modules/nanoid": { "version": "3.3.7", "funding": [ @@ -12687,6 +12739,35 @@ "dev": true, "license": "MIT" }, + "node_modules/node-ssh": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/node-ssh/-/node-ssh-13.2.0.tgz", + "integrity": "sha512-7vsKR2Bbs66th6IWCy/7SN4MSwlVt+G6QrHB631BjRUM8/LmvDugtYhi0uAmgvHS/+PVurfNBOmELf30rm0MZg==", + "license": "MIT", + "dependencies": { + "is-stream": "^2.0.0", + "make-dir": "^3.1.0", + "sb-promise-queue": "^2.1.0", + "sb-scandir": "^3.1.0", + "shell-escape": "^0.2.0", + "ssh2": "^1.14.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/node-ssh/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/nopt": { "version": "5.0.0", "license": "ISC", @@ -14478,6 +14559,27 @@ } } }, + "node_modules/sb-promise-queue": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sb-promise-queue/-/sb-promise-queue-2.1.0.tgz", + "integrity": "sha512-zwq4YuP1FQFkGx2Q7GIkZYZ6PqWpV+bg0nIO1sJhWOyGyhqbj0MsTvK6lCFo5TQwX5pZr6SCQ75e8PCDCuNvkg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/sb-scandir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/sb-scandir/-/sb-scandir-3.1.0.tgz", + "integrity": "sha512-70BVm2xz9jn94zSQdpvYrEG101/UV9TVGcfWr9T5iob3QhCK4lYXeculfBqPGFv3XTeKgx4dpWyYIDeZUqo4kg==", + "license": "MIT", + "dependencies": { + "sb-promise-queue": "^2.1.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/scheduler": { "version": "0.23.2", "license": "MIT", @@ -14638,6 +14740,12 @@ "node": ">=8" } }, + "node_modules/shell-escape": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/shell-escape/-/shell-escape-0.2.0.tgz", + "integrity": "sha512-uRRBT2MfEOyxuECseCZd28jC1AJ8hmqqneWQ4VWUTgCAFvb3wKU1jLqj6egC4Exrr88ogg3dp+zroH4wJuaXzw==", + "license": "MIT" + }, "node_modules/side-channel": { "version": "1.0.6", "license": "MIT", @@ -14834,6 +14942,23 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/ssh2": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.16.0.tgz", + "integrity": "sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg==", + "hasInstallScript": true, + "dependencies": { + "asn1": "^0.2.6", + "bcrypt-pbkdf": "^1.0.2" + }, + "engines": { + "node": ">=10.16.0" + }, + "optionalDependencies": { + "cpu-features": "~0.0.10", + "nan": "^2.20.0" + } + }, "node_modules/statuses": { "version": "2.0.1", "license": "MIT", @@ -15896,6 +16021,12 @@ "dev": true, "license": "BSD" }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "license": "Unlicense" + }, "node_modules/type-check": { "version": "0.4.0", "dev": true, From 9952d437f2a3693399adb83a3112a67241224d03 Mon Sep 17 00:00:00 2001 From: Ayush Acharjya Date: Sun, 8 Dec 2024 00:12:53 +0000 Subject: [PATCH 03/11] export const SUBMIT_TOPIC = process.env.SUBMIT_TOPIC_NAME; came in codebase during pull, remove that --- packages/kafka/src/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/kafka/src/index.ts b/packages/kafka/src/index.ts index 5cc0c44..051ee99 100644 --- a/packages/kafka/src/index.ts +++ b/packages/kafka/src/index.ts @@ -56,5 +56,3 @@ class KafkaSingleton { const kafkaClient = KafkaSingleton; export default kafkaClient; - -export const SUBMIT_TOPIC = process.env.SUBMIT_TOPIC_NAME; From 1c8d89dd05d0afd670db5a315770bbb5d3fae347 Mon Sep 17 00:00:00 2001 From: Ayush Acharjya Date: Sun, 8 Dec 2024 00:24:55 +0000 Subject: [PATCH 04/11] fix esbuild config for engine --- apps/engine/esbuild.config.js | 1 + package-lock.json | 2066 +++++++++++++++++++++++++-------- 2 files changed, 1598 insertions(+), 469 deletions(-) diff --git a/apps/engine/esbuild.config.js b/apps/engine/esbuild.config.js index 29c0095..1429dd4 100644 --- a/apps/engine/esbuild.config.js +++ b/apps/engine/esbuild.config.js @@ -3,6 +3,7 @@ const esbuild = require('esbuild'); esbuild .build({ entryPoints: ['./src/**/*.ts'], + loader: { '.node': 'file' }, bundle: true, platform: 'node', outdir: 'dist', diff --git a/package-lock.json b/package-lock.json index 63e833d..cea363c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -88,8 +88,10 @@ "@repo/kafka": "*", "dotenv": "^16.4.7", "esbuild": "^0.24.0", - "node-ssh": "^13.2.0", - "ssh2": "^1.16.0" + "node-ssh": "^13.2.0" + }, + "devDependencies": { + "esbuild-plugin-node-externals": "^1.0.1" } }, "apps/engine/node_modules/@esbuild/win32-x64": { @@ -120,6 +122,8 @@ }, "apps/engine/node_modules/esbuild": { "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", "hasInstallScript": true, "license": "MIT", "bin": { @@ -1398,328 +1402,696 @@ "version": "0.4.0", "license": "MIT" }, - "node_modules/@esbuild/win32-x64": { - "version": "0.23.1", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", "cpu": [ - "x64" + "ppc64" ], "license": "MIT", "optional": true, "os": [ - "win32" + "aix" ], "engines": { "node": ">=18" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.1", - "dev": true, + "node_modules/@esbuild/android-arm": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", + "cpu": [ + "arm" + ], "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "node": ">=18" } }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "dev": true, - "license": "Apache-2.0", + "node_modules/@esbuild/android-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=18" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "dev": true, + "node_modules/@esbuild/android-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", + "cpu": [ + "x64" + ], "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=18" } }, - "node_modules/@eslint/config-array": { - "version": "0.19.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.4", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@eslint/core": { - "version": "0.9.0", - "dev": true, - "license": "Apache-2.0", + "node_modules/@esbuild/darwin-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@eslint/eslintrc": { - "version": "3.2.0", - "dev": true, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=18" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "dev": true, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", + "cpu": [ + "x64" + ], "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/js": { - "version": "9.15.0", - "dev": true, + "node_modules/@esbuild/linux-arm": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", + "cpu": [ + "arm" + ], "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@eslint/object-schema": { - "version": "2.1.4", - "dev": true, - "license": "Apache-2.0", + "node_modules/@esbuild/linux-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@eslint/plugin-kit": { - "version": "0.2.3", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "levn": "^0.4.1" - }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@floating-ui/core": { - "version": "1.6.8", + "node_modules/@esbuild/linux-loong64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", + "cpu": [ + "loong64" + ], "license": "MIT", - "dependencies": { - "@floating-ui/utils": "^0.2.8" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@floating-ui/dom": { - "version": "1.6.12", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", + "cpu": [ + "mips64el" + ], "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.6.0", - "@floating-ui/utils": "^0.2.8" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.2", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", + "cpu": [ + "ppc64" + ], "license": "MIT", - "dependencies": { - "@floating-ui/dom": "^1.0.0" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@floating-ui/utils": { - "version": "0.2.8", - "license": "MIT" - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "dev": true, - "license": "Apache-2.0", + "node_modules/@esbuild/linux-riscv64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.18.0" + "node": ">=18" } }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" - }, - "engines": { - "node": ">=18.18.0" + "node_modules/@esbuild/linux-s390x": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "dev": true, - "license": "Apache-2.0", + "node_modules/@esbuild/linux-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "node": ">=18" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.13.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.3", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=10.10.0" + "node": ">=18" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "dev": true, - "license": "Apache-2.0", + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "node": ">=18" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "dev": true, - "license": "BSD-3-Clause" + "node_modules/@esbuild/openbsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.1", - "dev": true, - "license": "Apache-2.0", + "node_modules/@esbuild/sunos-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "node": ">=18" } }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "license": "MIT" + "node_modules/@esbuild/win32-ia32": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "dev": true, "license": "MIT", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://opencollective.com/eslint" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "dev": true, "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.19.0", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" }, "engines": { - "node": ">=6.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", + "node_modules/@eslint/core": { + "version": "0.9.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.2.0", + "dev": true, "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, "engines": { - "node": ">=6.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "dev": true, "license": "MIT", "engines": { - "node": ">=6.0.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", + "node_modules/@eslint/js": { + "version": "9.15.0", "dev": true, "license": "MIT", - "peer": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.3", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.6.8", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.8" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.12", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.8" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.2", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.8", + "license": "MIT" + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { @@ -8234,37 +8606,419 @@ "@esbuild/win32-x64": "0.23.1" } }, - "node_modules/escalade": { - "version": "3.2.0", + "node_modules/esbuild-plugin-node-externals": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esbuild-plugin-node-externals/-/esbuild-plugin-node-externals-1.0.1.tgz", + "integrity": "sha512-USzpK87OpVT21TsDfzPg8THB876GCFFMvSlY3Tg8y+ZBG+imiTkz7AchNgJIPUWKdI+0z1WXyX9QVWJCFxH7sg==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "find-up": "5.0.0", + "jsonfile": "^6.1.0" + }, + "peerDependencies": { + "esbuild": ">= 0.14.0" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "license": "MIT" + "node_modules/esbuild/node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", + "node_modules/esbuild/node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, - "node_modules/escodegen": { - "version": "2.1.0", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, + "node_modules/esbuild/node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, "bin": { "escodegen": "bin/escodegen.js", "esgenerate": "bin/esgenerate.js" @@ -16295,333 +17049,707 @@ "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { + "version": "8.15.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.15.0", + "@typescript-eslint/types": "8.15.0", + "@typescript-eslint/typescript-estree": "8.15.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.15.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.15.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "devOptional": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.1", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/update-check": { + "version": "1.5.4", + "dev": true, + "license": "MIT", + "dependencies": { + "registry-auth-token": "3.3.2", + "registry-url": "3.1.0" + } + }, + "node_modules/upper-case": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/upper-case-first": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "upper-case": "^1.1.1" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-callback-ref": { + "version": "1.3.2", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-resize-observer": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@juggle/resize-observer": "^3.3.1" + }, + "peerDependencies": { + "react": "16.8.0 - 18", + "react-dom": "16.8.0 - 18" + } + }, + "node_modules/use-sidecar": { + "version": "1.1.2", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "devOptional": true, + "license": "MIT" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "5.0.1", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { - "version": "8.15.0", + "node_modules/vary": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vite": { + "version": "5.4.11", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.15.0", - "@typescript-eslint/types": "8.15.0", - "@typescript-eslint/typescript-estree": "8.15.0" + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^18.0.0 || >=20.0.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" }, "peerDependenciesMeta": { - "typescript": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { "optional": true } } }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.15.0", + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.15.0", - "eslint-visitor-keys": "^4.2.0" - }, + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=12" } }, - "node_modules/typescript-eslint/node_modules/brace-expansion": { - "version": "2.0.1", + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "node_modules/typescript-eslint/node_modules/minimatch": { - "version": "9.0.5", + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=12" } }, - "node_modules/uglify-js": { - "version": "3.19.3", + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, + "os": [ + "android" + ], "engines": { - "node": ">=0.8.0" + "node": ">=12" } }, - "node_modules/unbox-primitive": { - "version": "1.0.2", + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" } }, - "node_modules/undici-types": { - "version": "6.19.8", - "devOptional": true, - "license": "MIT" + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } }, - "node_modules/universalify": { - "version": "2.0.1", + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">= 10.0.0" + "node": ">=12" } }, - "node_modules/unpipe": { - "version": "1.0.0", + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">= 0.8" + "node": ">=12" } }, - "node_modules/update-browserslist-db": { - "version": "1.1.1", + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" ], + "dev": true, "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/update-check": { - "version": "1.5.4", + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", - "dependencies": { - "registry-auth-token": "3.3.2", - "registry-url": "3.1.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/upper-case": { - "version": "1.1.3", + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } }, - "node_modules/upper-case-first": { - "version": "1.1.2", + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], "dev": true, "license": "MIT", - "dependencies": { - "upper-case": "^1.1.1" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/uri-js": { - "version": "4.4.1", + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/use-callback-ref": { - "version": "1.3.2", + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "node": ">=12" } }, - "node_modules/use-resize-observer": { - "version": "9.1.0", + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], "dev": true, "license": "MIT", - "dependencies": { - "@juggle/resize-observer": "^3.3.1" - }, - "peerDependencies": { - "react": "16.8.0 - 18", - "react-dom": "16.8.0 - 18" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/use-sidecar": { - "version": "1.1.2", + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "detect-node-es": "^1.1.0", - "tslib": "^2.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "node": ">=12" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "license": "MIT" - }, - "node_modules/utils-merge": { - "version": "1.0.1", + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">= 0.4.0" + "node": ">=12" } }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "devOptional": true, - "license": "MIT" - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" } }, - "node_modules/validate-npm-package-name": { - "version": "5.0.1", + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "ISC", + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=12" } }, - "node_modules/vary": { - "version": "1.1.2", + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 0.8" + "node": ">=12" } }, - "node_modules/vite": { - "version": "5.4.11", + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } + "node": ">=12" } }, "node_modules/vite/node_modules/@esbuild/win32-x64": { From d6b3144f780355288468c35eae19718906476ec4 Mon Sep 17 00:00:00 2001 From: Ayush Acharjya Date: Sun, 8 Dec 2024 20:12:20 +0000 Subject: [PATCH 05/11] update landing page to make the text center --- apps/frontend/components/LoginCard.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/apps/frontend/components/LoginCard.tsx b/apps/frontend/components/LoginCard.tsx index 8e15484..4a17e72 100644 --- a/apps/frontend/components/LoginCard.tsx +++ b/apps/frontend/components/LoginCard.tsx @@ -23,8 +23,10 @@ export function LoginCard() { > - Student Login - + + Student Login + + Access your assignments and submissions @@ -47,8 +49,12 @@ export function LoginCard() { > - Teacher Login - Manage assignments and grading + + Teacher Login + + + Manage assignments and grading +
From 95309a22d2ae18565c482135609e811b8728eb62 Mon Sep 17 00:00:00 2001 From: Ayush Acharjya Date: Sun, 8 Dec 2024 21:51:16 +0000 Subject: [PATCH 06/11] add ssh + file running condition --- .../src/controllers/assignmentController.ts | 1 + apps/engine/package.json | 1 + apps/engine/src/index.ts | 15 ++- apps/engine/src/utils/ssh.ts | 7 +- apps/engine/src/utils/submission.ts | 108 ++++++++++++++++++ package-lock.json | 22 +--- 6 files changed, 131 insertions(+), 23 deletions(-) create mode 100644 apps/engine/src/utils/submission.ts diff --git a/apps/backend/src/controllers/assignmentController.ts b/apps/backend/src/controllers/assignmentController.ts index a4e29e6..e8888b1 100644 --- a/apps/backend/src/controllers/assignmentController.ts +++ b/apps/backend/src/controllers/assignmentController.ts @@ -263,6 +263,7 @@ const submitAssignment = async (req: Request, res: Response) => { requiredFiles: user.student.courses[0].assignments[0].requiredFiles, userId: parsedData.data.userId, assignmentId: parsedData.data.assignmentId, + uploadLink: parsedData.data.assignmentZip, }; await prisma.submission.create({ diff --git a/apps/engine/package.json b/apps/engine/package.json index ff4c904..2e1ee5e 100644 --- a/apps/engine/package.json +++ b/apps/engine/package.json @@ -13,6 +13,7 @@ "description": "", "dependencies": { "@repo/kafka": "*", + "axios": "^1.7.9", "dotenv": "^16.4.7", "esbuild": "^0.24.0", "node-ssh": "^13.2.0" diff --git a/apps/engine/src/index.ts b/apps/engine/src/index.ts index 616af42..d96327b 100644 --- a/apps/engine/src/index.ts +++ b/apps/engine/src/index.ts @@ -1,6 +1,7 @@ import kafkaClient from '@repo/kafka/client'; import { SUBMIT } from '@repo/topics/topics'; import dotenv from 'dotenv'; +import { processSubmission } from './utils/submission'; import { sshIntoEC2 } from './utils/ssh'; dotenv.config(); @@ -10,15 +11,21 @@ const topic = SUBMIT; async function consumeMessages() { try { const kafka = kafkaClient.getInstance(); - const consumer = kafka.consumer({ groupId: 'your-consumer-group-id' }); + const consumer = kafka.consumer({ groupId: 'assignment-submission' }); await consumer.connect(); - await consumer.subscribe({ topic, fromBeginning: true }); + await consumer.subscribe({ topic, fromBeginning: false }); await consumer.run({ // @ts-ignore eachMessage: async ({ topic, partition, message }) => { const messageValue = message.value?.toString() || ''; console.log(`Received message: ${messageValue}`); + try { + const submission = JSON.parse(messageValue); + await processSubmission(submission); + } catch (err) { + console.log('Error processing message:', err); + } }, }); @@ -28,5 +35,5 @@ async function consumeMessages() { } } -// consumeMessages(); -sshIntoEC2(); +consumeMessages(); +// sshIntoEC2(); diff --git a/apps/engine/src/utils/ssh.ts b/apps/engine/src/utils/ssh.ts index 6b533e0..4e63947 100644 --- a/apps/engine/src/utils/ssh.ts +++ b/apps/engine/src/utils/ssh.ts @@ -1,5 +1,8 @@ import fs from 'fs'; import { NodeSSH } from 'node-ssh'; +import dotenv from 'dotenv'; + +dotenv.config(); const ssh = new NodeSSH(); @@ -8,7 +11,9 @@ export async function sshIntoEC2() { const privateKeyPath = process.env.PRIVATE_KEY as string; const privateKey = fs.readFileSync(privateKeyPath, 'utf-8'); const host = process.env.HOST as string; - const username = process.env.USERNAME as string; + const username = 'ubuntu'; + + console.log(host, username, privateKey); console.log('Connecting to EC2 instance...'); await ssh.connect({ diff --git a/apps/engine/src/utils/submission.ts b/apps/engine/src/utils/submission.ts new file mode 100644 index 0000000..d2b9775 --- /dev/null +++ b/apps/engine/src/utils/submission.ts @@ -0,0 +1,108 @@ +import fs from 'fs'; +import path from 'path'; +import os from 'os'; +import { execSync } from 'child_process'; +import axios from 'axios'; + +export async function processSubmission(submission: any) { + const { assignmentId, uploadLink, environment, testCommand } = submission; + + try { + const tmpDir = os.tmpdir(); + const filePath = path.join(tmpDir, `${assignmentId}`); + console.log(`Temp file path: ${filePath}`); + + console.log('Downloading submission...'); + const response = await axios.get(uploadLink, { responseType: 'stream' }); + const writer = fs.createWriteStream(filePath); + response.data.pipe(writer); + await new Promise((resolve, reject) => { + writer.on('finish', resolve); + writer.on('error', reject); + }); + + console.log('File downloaded successfully:', filePath); + + console.log('Determining file type...'); + let extractPath = path.join(tmpDir, `${assignmentId}_work`); + if (!fs.existsSync(extractPath)) { + fs.mkdirSync(extractPath, { recursive: true }); + } + + if (filePath.endsWith('.zip')) { + console.log('Extracting zip file...'); + execSync(`unzip ${filePath} -d ${extractPath}`); + } else { + console.log('Processing as a single file...'); + const destinationFilePath = path.join( + extractPath, + path.basename(filePath) + ); + fs.copyFileSync(filePath, destinationFilePath); + } + + console.log('Creating Dockerfile...'); + const dockerfilePath = path.join(extractPath, 'Dockerfile'); + const dockerfileContent = ` + FROM node:20.12.0-alpine3.19 + WORKDIR /app + COPY . . + CMD ["${testCommand}"] + `; + fs.writeFileSync(dockerfilePath, dockerfileContent); + console.log('Dockerfile created successfully.'); + + console.log('Building Docker image...'); + const imageName = `assignment_${assignmentId}`; + execSync(`docker build -t ${imageName} ${extractPath}`); + console.log(`Docker image ${imageName} built successfully.`); + + console.log('Saving Docker image...'); + const tarPath = path.join(tmpDir, `${imageName}.tar`); + execSync(`docker save -o ${tarPath} ${imageName}`); + console.log(`Docker image saved as ${tarPath}`); + + console.log('Transferring Docker image to EC2...'); + const { NodeSSH } = await import('node-ssh'); + const ssh = new NodeSSH(); + + const host = process.env.HOST as string; + const username = 'ubuntu'; + const privateKey = fs.readFileSync( + process.env.PRIVATE_KEY as string, + 'utf-8' + ); + + console.log('Connecting to EC2 instance...'); + await ssh.connect({ host, username, privateKey }); + console.log('Connected to EC2 instance.'); + + const remoteTarPath = `/tmp/${imageName}.tar`; + await ssh.putFile(tarPath, remoteTarPath); + console.log('Transferred Docker image to EC2.'); + + console.log('Running container on EC2...'); + await ssh.execCommand(`sudo docker load < ${remoteTarPath}`); + const runCommand = `sudo docker run ${imageName}`; + const runResult = await ssh.execCommand(runCommand); + + console.log('Container execution complete. Output:'); + console.log(runResult.stdout); + if (runResult.stderr) console.error('Errors:', runResult.stderr); + + // Clean Up + console.log('Cleaning up...'); + await ssh.execCommand(`docker rmi ${imageName}`); + await ssh.execCommand(`rm -f ${remoteTarPath}`); + ssh.dispose(); + + // Local cleanup + fs.rmSync(filePath); + fs.rmSync(extractPath, { recursive: true, force: true }); + fs.rmSync(tarPath); + + console.log('Marking complete.'); + } catch (error) { + console.error('Error processing submission:', error); + } +} diff --git a/package-lock.json b/package-lock.json index cea363c..1d67979 100644 --- a/package-lock.json +++ b/package-lock.json @@ -86,12 +86,10 @@ "license": "ISC", "dependencies": { "@repo/kafka": "*", + "axios": "^1.7.9", "dotenv": "^16.4.7", "esbuild": "^0.24.0", "node-ssh": "^13.2.0" - }, - "devDependencies": { - "esbuild-plugin-node-externals": "^1.0.1" } }, "apps/engine/node_modules/@esbuild/win32-x64": { @@ -7171,7 +7169,9 @@ } }, "node_modules/axios": { - "version": "1.7.8", + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -8606,20 +8606,6 @@ "@esbuild/win32-x64": "0.23.1" } }, - "node_modules/esbuild-plugin-node-externals": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/esbuild-plugin-node-externals/-/esbuild-plugin-node-externals-1.0.1.tgz", - "integrity": "sha512-USzpK87OpVT21TsDfzPg8THB876GCFFMvSlY3Tg8y+ZBG+imiTkz7AchNgJIPUWKdI+0z1WXyX9QVWJCFxH7sg==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "5.0.0", - "jsonfile": "^6.1.0" - }, - "peerDependencies": { - "esbuild": ">= 0.14.0" - } - }, "node_modules/esbuild/node_modules/@esbuild/aix-ppc64": { "version": "0.23.1", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", From 02bf0db4c82a95db517b18de59dc17c3703b2c44 Mon Sep 17 00:00:00 2001 From: Ayush Acharjya Date: Mon, 9 Dec 2024 20:46:31 +0000 Subject: [PATCH 07/11] complete engine stuff --- MarkingScripts/mark.sh | 102 ++++++++-------- MarkingScripts/mark1.sh | 63 ++++++++++ MarkingScripts/mul.js | 5 + MarkingScripts/sum.js | 5 + apps/engine/.env.example | 3 + apps/engine/src/utils/submission.ts | 174 +++++++++++++++------------- 6 files changed, 224 insertions(+), 128 deletions(-) create mode 100644 MarkingScripts/mark1.sh create mode 100644 MarkingScripts/mul.js create mode 100644 MarkingScripts/sum.js create mode 100644 apps/engine/.env.example diff --git a/MarkingScripts/mark.sh b/MarkingScripts/mark.sh index 1a43e39..cbb21cc 100644 --- a/MarkingScripts/mark.sh +++ b/MarkingScripts/mark.sh @@ -1,53 +1,63 @@ #!/bin/bash -# Marking script for Matrix Operations Assignment +# Setup +echo "Running tests for the mul function..." -# Ensure the script stops on the first error -set -e +# Path to the student code file +STUDENT_CODE="/app/mul.js" -# Directory where student submission is unzipped -STUDENT_DIR=$1 - -# Location of test cases -TEST_DIR="./test_cases" - -# Logs directory for output -LOGS_DIR="$STUDENT_DIR/logs" -mkdir -p "$LOGS_DIR" - -# Compilation step -echo "Compiling student code..." -gcc -o "$STUDENT_DIR/matrix_program" "$STUDENT_DIR/matrix.c" -lm > "$LOGS_DIR/compile.log" 2>&1 - -# Check if compilation was successful -if [ $? -ne 0 ]; then - echo "Compilation failed. See compile.log for details." > "$LOGS_DIR/result.log" +# Check if the file exists +if [ ! -f "$STUDENT_CODE" ]; then + echo "Error: $STUDENT_CODE not found!" exit 1 fi -# Run test cases -echo "Running test cases..." -SCORE=0 -MAX_SCORE=100 -TEST_COUNT=0 - -for INPUT in "$TEST_DIR"/*.in; do - BASENAME=$(basename "$INPUT" .in) - EXPECTED_OUTPUT="$TEST_DIR/$BASENAME.out" - STUDENT_OUTPUT="$LOGS_DIR/$BASENAME.output" - - # Run student program - "$STUDENT_DIR/matrix_program" < "$INPUT" > "$STUDENT_OUTPUT" - - # Compare output - if diff -q "$STUDENT_OUTPUT" "$EXPECTED_OUTPUT" > /dev/null; then - SCORE=$((SCORE + 20)) # 20 marks per test - else - echo "Test $BASENAME failed." >> "$LOGS_DIR/result.log" - fi - TEST_COUNT=$((TEST_COUNT + 1)) -done - -# Write final score -echo "Final Score: $SCORE/$MAX_SCORE" >> "$LOGS_DIR/result.log" -echo "Marking completed. See result.log for details." +# Run the test cases +node -e " +const { mul } = require('$STUDENT_CODE'); +const assert = require('assert'); + +// Initialize counter for passed tests +let PASSED_TESTS = 0; + +// Test cases +console.log('Running test cases...'); + +// Test 1: Positive numbers +try { + assert.strictEqual(mul(2, 3), 6, 'Test 1 Failed: mul(2, 3) should be 6'); + PASSED_TESTS++; + console.log('Test 1 Passed'); +} catch (error) { + console.error(error.message); +} + +// Test 2: Negative numbers +try { + assert.strictEqual(mul(-2, -3), 6, 'Test 2 Failed: mul(-2, -3) should be 6'); + PASSED_TESTS++; + console.log('Test 2 Passed'); +} catch (error) { + console.error(error.message); +} + +// Test 3: Mixed numbers +try { + assert.strictEqual(mul(-2, 3), -6, 'Test 3 Failed: mul(-2, 3) should be -6'); + PASSED_TESTS++; + console.log('Test 3 Passed'); +} catch (error) { + console.error(error.message); +} + +// Test 4: Zeros +try { + assert.strictEqual(mul(0, 0), 0, 'Test 4 Failed: mul(0, 0) should be 0'); + PASSED_TESTS++; + console.log('Test 4 Passed'); +} catch (error) { + console.error(error.message); +} + +console.log('Number of tests passed:', PASSED_TESTS); +" diff --git a/MarkingScripts/mark1.sh b/MarkingScripts/mark1.sh new file mode 100644 index 0000000..40ddc5c --- /dev/null +++ b/MarkingScripts/mark1.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +# Setup +echo "Running tests for the sum function..." + +# Path to the student code file +STUDENT_CODE="/app/sum.js" + +# Check if the file exists +if [ ! -f "$STUDENT_CODE" ]; then + echo "Error: $STUDENT_CODE not found!" + exit 1 +fi + +# Run the test cases +node -e " +const { sum } = require('$STUDENT_CODE'); +const assert = require('assert'); + +// Initialize counter for passed tests +let PASSED_TESTS = 0; + +// Test cases +console.log('Running test cases...'); + +// Test 1: Positive numbers +try { + assert.strictEqual(sum(2, 3), 5, 'Test 1 Failed: sum(2, 3) should be 5'); + PASSED_TESTS++; + console.log('Test 1 Passed'); +} catch (error) { + console.error(error.message); +} + +// Test 2: Negative numbers +try { + assert.strictEqual(sum(-2, -3), -5, 'Test 2 Failed: sum(-2, -3) should be -5'); + PASSED_TESTS++; + console.log('Test 2 Passed'); +} catch (error) { + console.error(error.message); +} + +// Test 3: Mixed numbers +try { + assert.strictEqual(sum(-2, 3), 1, 'Test 3 Failed: sum(-2, 3) should be 1'); + PASSED_TESTS++; + console.log('Test 3 Passed'); +} catch (error) { + console.error(error.message); +} + +// Test 4: Zeros +try { + assert.strictEqual(sum(0, 0), 0, 'Test 4 Failed: sum(0, 0) should be 0'); + PASSED_TESTS++; + console.log('Test 4 Passed'); +} catch (error) { + console.error(error.message); +} + +console.log('Number of tests passed:', PASSED_TESTS); +" diff --git a/MarkingScripts/mul.js b/MarkingScripts/mul.js new file mode 100644 index 0000000..dd6dae6 --- /dev/null +++ b/MarkingScripts/mul.js @@ -0,0 +1,5 @@ +function mul(a, b) { + return a + b; +} + +module.exports = { mul }; diff --git a/MarkingScripts/sum.js b/MarkingScripts/sum.js new file mode 100644 index 0000000..849a2a1 --- /dev/null +++ b/MarkingScripts/sum.js @@ -0,0 +1,5 @@ +function sum(a, b) { + return a + b; +} + +module.exports = { sum }; diff --git a/apps/engine/.env.example b/apps/engine/.env.example new file mode 100644 index 0000000..4d9749a --- /dev/null +++ b/apps/engine/.env.example @@ -0,0 +1,3 @@ +PRIVATE_KEY= +HOST= +USERNAME=ubuntu \ No newline at end of file diff --git a/apps/engine/src/utils/submission.ts b/apps/engine/src/utils/submission.ts index d2b9775..e717f51 100644 --- a/apps/engine/src/utils/submission.ts +++ b/apps/engine/src/utils/submission.ts @@ -1,108 +1,118 @@ +import { NodeSSH } from 'node-ssh'; import fs from 'fs'; -import path from 'path'; -import os from 'os'; -import { execSync } from 'child_process'; -import axios from 'axios'; +import prisma from '@repo/db/client'; export async function processSubmission(submission: any) { - const { assignmentId, uploadLink, environment, testCommand } = submission; + const { assignmentId, uploadLink, markingScript } = submission; try { - const tmpDir = os.tmpdir(); - const filePath = path.join(tmpDir, `${assignmentId}`); - console.log(`Temp file path: ${filePath}`); - - console.log('Downloading submission...'); - const response = await axios.get(uploadLink, { responseType: 'stream' }); - const writer = fs.createWriteStream(filePath); - response.data.pipe(writer); - await new Promise((resolve, reject) => { - writer.on('finish', resolve); - writer.on('error', reject); - }); - - console.log('File downloaded successfully:', filePath); - - console.log('Determining file type...'); - let extractPath = path.join(tmpDir, `${assignmentId}_work`); - if (!fs.existsSync(extractPath)) { - fs.mkdirSync(extractPath, { recursive: true }); - } - - if (filePath.endsWith('.zip')) { - console.log('Extracting zip file...'); - execSync(`unzip ${filePath} -d ${extractPath}`); - } else { - console.log('Processing as a single file...'); - const destinationFilePath = path.join( - extractPath, - path.basename(filePath) - ); - fs.copyFileSync(filePath, destinationFilePath); - } - - console.log('Creating Dockerfile...'); - const dockerfilePath = path.join(extractPath, 'Dockerfile'); - const dockerfileContent = ` - FROM node:20.12.0-alpine3.19 - WORKDIR /app - COPY . . - CMD ["${testCommand}"] - `; - fs.writeFileSync(dockerfilePath, dockerfileContent); - console.log('Dockerfile created successfully.'); - - console.log('Building Docker image...'); - const imageName = `assignment_${assignmentId}`; - execSync(`docker build -t ${imageName} ${extractPath}`); - console.log(`Docker image ${imageName} built successfully.`); - - console.log('Saving Docker image...'); - const tarPath = path.join(tmpDir, `${imageName}.tar`); - execSync(`docker save -o ${tarPath} ${imageName}`); - console.log(`Docker image saved as ${tarPath}`); - - console.log('Transferring Docker image to EC2...'); - const { NodeSSH } = await import('node-ssh'); + console.log('Setting up SSH connection...'); const ssh = new NodeSSH(); - const host = process.env.HOST as string; + const host = process.env.HOST; const username = 'ubuntu'; const privateKey = fs.readFileSync( process.env.PRIVATE_KEY as string, 'utf-8' ); - console.log('Connecting to EC2 instance...'); await ssh.connect({ host, username, privateKey }); console.log('Connected to EC2 instance.'); - const remoteTarPath = `/tmp/${imageName}.tar`; - await ssh.putFile(tarPath, remoteTarPath); - console.log('Transferred Docker image to EC2.'); + const remoteWorkDir = `/tmp/${assignmentId}_work`; + + console.log('Creating working directory on EC2...'); + await ssh.execCommand(`mkdir -p ${remoteWorkDir}`); + + console.log('Downloading submission on EC2...'); + const submissionFilePath = `${remoteWorkDir}/submission_file`; + const downloadSubmission = `curl -o ${submissionFilePath} '${uploadLink}'`; + await ssh.execCommand(downloadSubmission); + console.log('Submission file downloaded successfully.'); + + console.log('Downloading marking script...'); + const markingScriptPath = `${remoteWorkDir}/mark.sh`; + const downloadMarkingScript = `curl -o ${markingScriptPath} '${markingScript}'`; + await ssh.execCommand(downloadMarkingScript); + await ssh.execCommand(`chmod +x ${markingScriptPath}`); + console.log('Marking script downloaded and made executable.'); - console.log('Running container on EC2...'); - await ssh.execCommand(`sudo docker load < ${remoteTarPath}`); - const runCommand = `sudo docker run ${imageName}`; + // Ensure the script has Unix line endings + await ssh.execCommand(`sed -i 's/\\r$//' ${markingScriptPath}`); + + console.log('Creating Dockerfile...'); + const dockerfileContent = ` + FROM node:alpine + RUN apk add --no-cache bash + WORKDIR /app + COPY --chown=node:node submission_file ./mul.js + COPY --chown=node:node mark.sh ./mark.sh + RUN chmod +x mark.sh + CMD ["bash", "./mark.sh"] + `; + + await ssh.execCommand( + `echo '${dockerfileContent}' > ${remoteWorkDir}/Dockerfile` + ); + console.log('Dockerfile created.'); + + console.log('Building Docker image...'); + const imageName = `assignment_${assignmentId}:latest`; + const buildCommand = `cd ${remoteWorkDir} && sudo docker build -t ${imageName} .`; + const buildResult = await ssh.execCommand(buildCommand); + console.log('Build output:', buildResult.stdout); + if (buildResult.stderr) console.error('Build errors:', buildResult.stderr); + console.log(`Docker image ${imageName} built.`); + + console.log('Running Docker container...'); + const containerName = `assignment_${assignmentId}_container`; + const runCommand = `sudo docker run --name ${containerName} ${imageName}`; const runResult = await ssh.execCommand(runCommand); + console.log('Container output:', runResult.stdout); + if (runResult.stderr) console.error('Container errors:', runResult.stderr); - console.log('Container execution complete. Output:'); - console.log(runResult.stdout); - if (runResult.stderr) console.error('Errors:', runResult.stderr); + console.log('Validating test results from container output...'); + const testsOutput = runResult.stdout || ''; - // Clean Up - console.log('Cleaning up...'); - await ssh.execCommand(`docker rmi ${imageName}`); - await ssh.execCommand(`rm -f ${remoteTarPath}`); - ssh.dispose(); + const passedTestsMatch = testsOutput.match(/Number of tests passed: (\d+)/); + // @ts-ignore + const passedTests = passedTestsMatch ? parseInt(passedTestsMatch[1]) : 0; + const totalTests = 4; + + const marks = Math.round((passedTests / totalTests) * 100); + console.log(`Tests passed: ${passedTests}/${totalTests}`); + console.log(`Marks calculated: ${marks}`); - // Local cleanup - fs.rmSync(filePath); - fs.rmSync(extractPath, { recursive: true, force: true }); - fs.rmSync(tarPath); + const submissionRecord = await prisma.submission.findFirst({ + where: { assignmentId }, + select: { id: true }, + }); + + if (!submissionRecord) { + console.error(`Submission not found for assignmentId: ${assignmentId}`); + return; + } + + await prisma.submission.update({ + where: { id: submissionRecord.id }, + data: { + marksAchieved: marks, + logs: testsOutput, + }, + }); + console.log('Marks updated in the database.'); + + console.log('Cleaning up EC2 resources...'); + await ssh.execCommand( + `sudo docker stop ${containerName} && sudo docker rm ${containerName}` + ); + await ssh.execCommand(`sudo docker rmi ${imageName}`); + await ssh.execCommand(`rm -rf ${remoteWorkDir}`); - console.log('Marking complete.'); + ssh.dispose(); + console.log('Process complete.'); } catch (error) { console.error('Error processing submission:', error); + throw error; } } From dd5f802a78197189a65e296cdfde19676b840112 Mon Sep 17 00:00:00 2001 From: Ayush Acharjya Date: Mon, 9 Dec 2024 23:57:02 +0000 Subject: [PATCH 08/11] change the marker side to have dockerfiles as uploads --- MarkingScripts/multiply/Dockerfile | 12 ++++ MarkingScripts/{ => multiply}/mark.sh | 2 +- MarkingScripts/{mul.js => multiply/sol.js} | 0 MarkingScripts/sum/Dockerfile | 12 ++++ MarkingScripts/{mark1.sh => sum/mark.sh} | 2 +- MarkingScripts/{sum.js => sum/sol.js} | 0 .../src/controllers/assignmentController.ts | 16 +++-- .../marker/course/[courseId]/publish/page.tsx | 64 +++++++++++++------ .../migration.sql | 9 +++ packages/db/prisma/schema.prisma | 2 +- 10 files changed, 93 insertions(+), 26 deletions(-) create mode 100644 MarkingScripts/multiply/Dockerfile rename MarkingScripts/{ => multiply}/mark.sh (98%) rename MarkingScripts/{mul.js => multiply/sol.js} (100%) create mode 100644 MarkingScripts/sum/Dockerfile rename MarkingScripts/{mark1.sh => sum/mark.sh} (98%) rename MarkingScripts/{sum.js => sum/sol.js} (100%) create mode 100644 packages/db/prisma/migrations/20241209231811_change_require_file_to_docker_file/migration.sql diff --git a/MarkingScripts/multiply/Dockerfile b/MarkingScripts/multiply/Dockerfile new file mode 100644 index 0000000..db475d4 --- /dev/null +++ b/MarkingScripts/multiply/Dockerfile @@ -0,0 +1,12 @@ +FROM node:alpine + +RUN apk add --no-cache bash + +WORKDIR /app + +COPY sol.js ./ +COPY mark.sh ./ + +RUN chmod +x mark.sh + +CMD ["bash", "mark.sh"] \ No newline at end of file diff --git a/MarkingScripts/mark.sh b/MarkingScripts/multiply/mark.sh similarity index 98% rename from MarkingScripts/mark.sh rename to MarkingScripts/multiply/mark.sh index cbb21cc..debc2ef 100644 --- a/MarkingScripts/mark.sh +++ b/MarkingScripts/multiply/mark.sh @@ -4,7 +4,7 @@ echo "Running tests for the mul function..." # Path to the student code file -STUDENT_CODE="/app/mul.js" +STUDENT_CODE="/app/sol.js" # Check if the file exists if [ ! -f "$STUDENT_CODE" ]; then diff --git a/MarkingScripts/mul.js b/MarkingScripts/multiply/sol.js similarity index 100% rename from MarkingScripts/mul.js rename to MarkingScripts/multiply/sol.js diff --git a/MarkingScripts/sum/Dockerfile b/MarkingScripts/sum/Dockerfile new file mode 100644 index 0000000..db475d4 --- /dev/null +++ b/MarkingScripts/sum/Dockerfile @@ -0,0 +1,12 @@ +FROM node:alpine + +RUN apk add --no-cache bash + +WORKDIR /app + +COPY sol.js ./ +COPY mark.sh ./ + +RUN chmod +x mark.sh + +CMD ["bash", "mark.sh"] \ No newline at end of file diff --git a/MarkingScripts/mark1.sh b/MarkingScripts/sum/mark.sh similarity index 98% rename from MarkingScripts/mark1.sh rename to MarkingScripts/sum/mark.sh index 40ddc5c..e268ae6 100644 --- a/MarkingScripts/mark1.sh +++ b/MarkingScripts/sum/mark.sh @@ -4,7 +4,7 @@ echo "Running tests for the sum function..." # Path to the student code file -STUDENT_CODE="/app/sum.js" +STUDENT_CODE="/app/sol.js" # Check if the file exists if [ ! -f "$STUDENT_CODE" ]; then diff --git a/MarkingScripts/sum.js b/MarkingScripts/sum/sol.js similarity index 100% rename from MarkingScripts/sum.js rename to MarkingScripts/sum/sol.js diff --git a/apps/backend/src/controllers/assignmentController.ts b/apps/backend/src/controllers/assignmentController.ts index e8888b1..85598cf 100644 --- a/apps/backend/src/controllers/assignmentController.ts +++ b/apps/backend/src/controllers/assignmentController.ts @@ -240,7 +240,7 @@ const submitAssignment = async (req: Request, res: Response) => { }, select: { markingScript: true, - requiredFiles: true, + dockerFile: true, }, }, }, @@ -260,7 +260,7 @@ const submitAssignment = async (req: Request, res: Response) => { const assignmentZip = parsedData.data.assignmentZip; const payload = { markingScript: user.student.courses[0].assignments[0].markingScript, - requiredFiles: user.student.courses[0].assignments[0].requiredFiles, + dockerFile: user.student.courses[0].assignments[0].dockerFile, userId: parsedData.data.userId, assignmentId: parsedData.data.assignmentId, uploadLink: parsedData.data.assignmentZip, @@ -313,8 +313,15 @@ const submitAssignment = async (req: Request, res: Response) => { }; const createAssignment = async (req: Request, res: Response) => { - const { title, description, dueDate, maxMarks, courseId, markingScript } = - req.body; + const { + title, + description, + dueDate, + maxMarks, + courseId, + markingScript, + dockerFile, + } = req.body; if (!title || !dueDate || !maxMarks || !courseId) { return res.status(400).json({ @@ -341,6 +348,7 @@ const createAssignment = async (req: Request, res: Response) => { maxMarks: parseInt(maxMarks, 10), courseId, markingScript, + dockerFile, }, }); diff --git a/apps/frontend/app/marker/course/[courseId]/publish/page.tsx b/apps/frontend/app/marker/course/[courseId]/publish/page.tsx index 33e880a..ffbd0ae 100644 --- a/apps/frontend/app/marker/course/[courseId]/publish/page.tsx +++ b/apps/frontend/app/marker/course/[courseId]/publish/page.tsx @@ -6,6 +6,7 @@ import { motion } from 'framer-motion'; import { toast } from 'react-toastify'; import axios from 'axios'; import { UploadClient } from '@uploadcare/upload-client'; +import { Loader2 } from 'lucide-react'; const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL; @@ -16,20 +17,23 @@ export default function PublishAssignment() { const [description, setDescription] = useState(''); const [dueDate, setDueDate] = useState(''); const [maxMarks, setMaxMarks] = useState(''); - const [markingScript, setMarkingScript] = useState(null); + const [markingScript, setMarkingScript] = useState(null); + const [dockerFile, setDockerFile] = useState(null); + const [uploading, setUploading] = useState(false); - const handleFileUpload = async (file: File) => { + const handleFileUpload = async (file: File): Promise => { const client = new UploadClient({ publicKey: process.env.NEXT_PUBLIC_UPLOADCARE_PUBLIC_KEY!, }); try { const result = await client.uploadFile(file); - setMarkingScript(result.cdnUrl); - toast.success('Marking script uploaded successfully!'); + toast.success(`${file.name} uploaded successfully!`); + return result.cdnUrl; } catch (error) { console.error('File upload failed:', error); - toast.error('Failed to upload marking script.'); + toast.error(`Failed to upload ${file.name}.`); + return null; } }; @@ -39,7 +43,21 @@ export default function PublishAssignment() { return; } + if (!markingScript || !dockerFile) { + toast.error('Please upload both the Marking Script and Docker File.'); + return; + } + + setUploading(true); try { + const markingScriptUrl = await handleFileUpload(markingScript); + const dockerFileUrl = await handleFileUpload(dockerFile); + + if (!markingScriptUrl || !dockerFileUrl) { + toast.error('File uploads failed. Please try again.'); + return; + } + const response = await axios.post( `${API_BASE_URL}/api/v1/assignments`, { @@ -48,7 +66,8 @@ export default function PublishAssignment() { dueDate: new Date(dueDate).toISOString(), maxMarks: parseInt(maxMarks, 10), courseId, - markingScript, + markingScript: markingScriptUrl, + dockerFile: dockerFileUrl, }, { withCredentials: true, @@ -62,6 +81,8 @@ export default function PublishAssignment() { } catch (error) { console.error('Assignment creation failed:', error); toast.error('Failed to publish assignment.'); + } finally { + setUploading(false); } }; @@ -105,29 +126,34 @@ export default function PublishAssignment() { onChange={(e) => setMaxMarks(e.target.value)} /> - + + { + if (e.target.files?.[0]) setMarkingScript(e.target.files[0]); + }} + /> + + { - if (e.target.files?.[0]) handleFileUpload(e.target.files[0]); + if (e.target.files?.[0]) setDockerFile(e.target.files[0]); }} /> - {markingScript && ( -

- Uploaded successfully:{' '} - - {markingScript} - -

- )}
diff --git a/packages/db/prisma/migrations/20241209231811_change_require_file_to_docker_file/migration.sql b/packages/db/prisma/migrations/20241209231811_change_require_file_to_docker_file/migration.sql new file mode 100644 index 0000000..e1a1234 --- /dev/null +++ b/packages/db/prisma/migrations/20241209231811_change_require_file_to_docker_file/migration.sql @@ -0,0 +1,9 @@ +/* + Warnings: + + - You are about to drop the column `requiredFiles` on the `Assignment` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "Assignment" DROP COLUMN "requiredFiles", +ADD COLUMN "dockerFile" TEXT; diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma index 5ea5c8a..537fd49 100644 --- a/packages/db/prisma/schema.prisma +++ b/packages/db/prisma/schema.prisma @@ -60,7 +60,7 @@ model Assignment { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt markingScript String? - requiredFiles String? + dockerFile String? maxMarks Int courseId String course Course @relation(fields: [courseId], references: [id]) From 2d5c35ff33203bb65fc57f5dcbdeb052eb9e28c0 Mon Sep 17 00:00:00 2001 From: Ayush Acharjya Date: Tue, 10 Dec 2024 00:38:37 +0000 Subject: [PATCH 09/11] make the engine so that it now downloads the docker image and run them --- MarkingScripts/sum/Dockerfile | 6 +- apps/engine/src/utils/submission.ts | 98 +++++++++++++++++++---------- 2 files changed, 69 insertions(+), 35 deletions(-) diff --git a/MarkingScripts/sum/Dockerfile b/MarkingScripts/sum/Dockerfile index db475d4..37b5ec2 100644 --- a/MarkingScripts/sum/Dockerfile +++ b/MarkingScripts/sum/Dockerfile @@ -4,9 +4,9 @@ RUN apk add --no-cache bash WORKDIR /app -COPY sol.js ./ -COPY mark.sh ./ +COPY --chown=node:node sol.js ./sol.js +COPY --chown=node:node mark.sh ./mark.sh RUN chmod +x mark.sh -CMD ["bash", "mark.sh"] \ No newline at end of file +CMD ["bash", "./mark.sh"] \ No newline at end of file diff --git a/apps/engine/src/utils/submission.ts b/apps/engine/src/utils/submission.ts index e717f51..015f8f3 100644 --- a/apps/engine/src/utils/submission.ts +++ b/apps/engine/src/utils/submission.ts @@ -2,8 +2,23 @@ import { NodeSSH } from 'node-ssh'; import fs from 'fs'; import prisma from '@repo/db/client'; +async function convertLineEndings(ssh: NodeSSH, filePath: string) { + const result = await ssh.execCommand(` + if command -v dos2unix >/dev/null 2>&1; then + dos2unix ${filePath} + else + sed -i 's/\r$//' ${filePath} + fi + `); + + if (result.code !== 0) { + console.error('Failed to convert line endings:', result.stderr); + throw new Error('Failed to convert line endings'); + } +} + export async function processSubmission(submission: any) { - const { assignmentId, uploadLink, markingScript } = submission; + const { assignmentId, uploadLink, markingScript, dockerFile } = submission; try { console.log('Setting up SSH connection...'); @@ -24,46 +39,64 @@ export async function processSubmission(submission: any) { console.log('Creating working directory on EC2...'); await ssh.execCommand(`mkdir -p ${remoteWorkDir}`); + // Download and validate sol.js console.log('Downloading submission on EC2...'); - const submissionFilePath = `${remoteWorkDir}/submission_file`; - const downloadSubmission = `curl -o ${submissionFilePath} '${uploadLink}'`; + const downloadSubmission = `curl -o ${remoteWorkDir}/sol.js '${uploadLink}'`; await ssh.execCommand(downloadSubmission); - console.log('Submission file downloaded successfully.'); + const validateSubmission = `test -f ${remoteWorkDir}/sol.js && echo "File exists" || echo "File missing"`; + const submissionCheck = await ssh.execCommand(validateSubmission); + if (!submissionCheck.stdout.includes('File exists')) { + throw new Error('Failed to download sol.js'); + } + console.log('Submission file downloaded and renamed to sol.js.'); + + // Download and validate mark.sh console.log('Downloading marking script...'); - const markingScriptPath = `${remoteWorkDir}/mark.sh`; - const downloadMarkingScript = `curl -o ${markingScriptPath} '${markingScript}'`; + const downloadMarkingScript = `curl -o ${remoteWorkDir}/mark.sh '${markingScript}'`; await ssh.execCommand(downloadMarkingScript); - await ssh.execCommand(`chmod +x ${markingScriptPath}`); - console.log('Marking script downloaded and made executable.'); - - // Ensure the script has Unix line endings - await ssh.execCommand(`sed -i 's/\\r$//' ${markingScriptPath}`); - - console.log('Creating Dockerfile...'); - const dockerfileContent = ` - FROM node:alpine - RUN apk add --no-cache bash - WORKDIR /app - COPY --chown=node:node submission_file ./mul.js - COPY --chown=node:node mark.sh ./mark.sh - RUN chmod +x mark.sh - CMD ["bash", "./mark.sh"] - `; + await ssh.execCommand(`chmod +x ${remoteWorkDir}/mark.sh`); - await ssh.execCommand( - `echo '${dockerfileContent}' > ${remoteWorkDir}/Dockerfile` + console.log('Converting line endings...'); + await convertLineEndings(ssh, `${remoteWorkDir}/mark.sh`); + + const validateMarkingScript = `test -f ${remoteWorkDir}/mark.sh && echo "File exists" || echo "File missing"`; + const markingScriptCheck = await ssh.execCommand(validateMarkingScript); + if (!markingScriptCheck.stdout.includes('File exists')) { + throw new Error('Failed to download mark.sh'); + } + console.log( + 'Marking script downloaded, made executable, and line endings converted.' ); - console.log('Dockerfile created.'); + // Download and validate Dockerfile + console.log('Downloading Dockerfile...'); + const downloadDockerFile = `curl -o ${remoteWorkDir}/Dockerfile '${dockerFile}'`; + await ssh.execCommand(downloadDockerFile); + + const validateDockerFile = `test -f ${remoteWorkDir}/Dockerfile && echo "File exists" || echo "File missing"`; + const dockerFileCheck = await ssh.execCommand(validateDockerFile); + if (!dockerFileCheck.stdout.includes('File exists')) { + throw new Error('Failed to download Dockerfile'); + } + console.log('Dockerfile downloaded.'); + + // Build Docker image console.log('Building Docker image...'); const imageName = `assignment_${assignmentId}:latest`; const buildCommand = `cd ${remoteWorkDir} && sudo docker build -t ${imageName} .`; const buildResult = await ssh.execCommand(buildCommand); - console.log('Build output:', buildResult.stdout); - if (buildResult.stderr) console.error('Build errors:', buildResult.stderr); - console.log(`Docker image ${imageName} built.`); + if (buildResult.code !== 0) { + console.error('Build errors:', buildResult.stderr); + throw new Error( + 'Docker build failed. Check the Dockerfile or input files.' + ); + } + console.log(`Docker image ${imageName} built successfully.`); + console.log('Build output:', buildResult.stdout, buildResult.stderr); + + // Run Docker container console.log('Running Docker container...'); const containerName = `assignment_${assignmentId}_container`; const runCommand = `sudo docker run --name ${containerName} ${imageName}`; @@ -71,15 +104,14 @@ export async function processSubmission(submission: any) { console.log('Container output:', runResult.stdout); if (runResult.stderr) console.error('Container errors:', runResult.stderr); + // Process results console.log('Validating test results from container output...'); const testsOutput = runResult.stdout || ''; - const passedTestsMatch = testsOutput.match(/Number of tests passed: (\d+)/); - // @ts-ignore - const passedTests = passedTestsMatch ? parseInt(passedTestsMatch[1]) : 0; + const passedTests = passedTestsMatch ? parseInt(passedTestsMatch[1]!) : 0; const totalTests = 4; - const marks = Math.round((passedTests / totalTests) * 100); + console.log(`Tests passed: ${passedTests}/${totalTests}`); console.log(`Marks calculated: ${marks}`); @@ -116,3 +148,5 @@ export async function processSubmission(submission: any) { throw error; } } + +// sudo apt-get update && sudo apt-get install -y dos2unix From f417d3cb3f73c2742d10ef065b8b649757fafff3 Mon Sep 17 00:00:00 2001 From: Ayush Acharjya Date: Tue, 10 Dec 2024 12:39:28 +0000 Subject: [PATCH 10/11] make the submission more generic bt taking a boiler plate code from marker and creating a docker instance in aws server which finally works --- MarkingScripts/multiply/Dockerfile | 12 --- MarkingScripts/multiply/mark.sh | 63 ------------- MarkingScripts/multiply/sol.js | 5 -- MarkingScripts/sum/Dockerfile | 12 --- .../src/controllers/assignmentController.ts | 4 + apps/engine/src/utils/ssh.ts | 37 -------- apps/engine/src/utils/submission.ts | 17 ++-- .../marker/course/[courseId]/publish/page.tsx | 18 +++- .../assignments/[assignmentId]/page.tsx | 13 +++ .../migration.sql | 2 + packages/db/prisma/schema.prisma | 1 + test_assignments/cpp_max/Dockerfile | 20 +++++ test_assignments/cpp_max/boilerplate.cpp | 12 +++ test_assignments/cpp_max/mark.sh | 90 +++++++++++++++++++ test_assignments/cpp_max/sol.cpp | 27 ++++++ test_assignments/sum/Dockerfile | 20 +++++ .../sum/boilerplate.js | 2 +- .../sum/mark.sh | 3 +- test_assignments/sum/sol.js | 6 ++ 19 files changed, 224 insertions(+), 140 deletions(-) delete mode 100644 MarkingScripts/multiply/Dockerfile delete mode 100644 MarkingScripts/multiply/mark.sh delete mode 100644 MarkingScripts/multiply/sol.js delete mode 100644 MarkingScripts/sum/Dockerfile delete mode 100644 apps/engine/src/utils/ssh.ts create mode 100644 packages/db/prisma/migrations/20241210104752_add_new_field_for_boilerplate/migration.sql create mode 100644 test_assignments/cpp_max/Dockerfile create mode 100644 test_assignments/cpp_max/boilerplate.cpp create mode 100644 test_assignments/cpp_max/mark.sh create mode 100644 test_assignments/cpp_max/sol.cpp create mode 100644 test_assignments/sum/Dockerfile rename MarkingScripts/sum/sol.js => test_assignments/sum/boilerplate.js (71%) rename {MarkingScripts => test_assignments}/sum/mark.sh (97%) create mode 100644 test_assignments/sum/sol.js diff --git a/MarkingScripts/multiply/Dockerfile b/MarkingScripts/multiply/Dockerfile deleted file mode 100644 index db475d4..0000000 --- a/MarkingScripts/multiply/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM node:alpine - -RUN apk add --no-cache bash - -WORKDIR /app - -COPY sol.js ./ -COPY mark.sh ./ - -RUN chmod +x mark.sh - -CMD ["bash", "mark.sh"] \ No newline at end of file diff --git a/MarkingScripts/multiply/mark.sh b/MarkingScripts/multiply/mark.sh deleted file mode 100644 index debc2ef..0000000 --- a/MarkingScripts/multiply/mark.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/bash - -# Setup -echo "Running tests for the mul function..." - -# Path to the student code file -STUDENT_CODE="/app/sol.js" - -# Check if the file exists -if [ ! -f "$STUDENT_CODE" ]; then - echo "Error: $STUDENT_CODE not found!" - exit 1 -fi - -# Run the test cases -node -e " -const { mul } = require('$STUDENT_CODE'); -const assert = require('assert'); - -// Initialize counter for passed tests -let PASSED_TESTS = 0; - -// Test cases -console.log('Running test cases...'); - -// Test 1: Positive numbers -try { - assert.strictEqual(mul(2, 3), 6, 'Test 1 Failed: mul(2, 3) should be 6'); - PASSED_TESTS++; - console.log('Test 1 Passed'); -} catch (error) { - console.error(error.message); -} - -// Test 2: Negative numbers -try { - assert.strictEqual(mul(-2, -3), 6, 'Test 2 Failed: mul(-2, -3) should be 6'); - PASSED_TESTS++; - console.log('Test 2 Passed'); -} catch (error) { - console.error(error.message); -} - -// Test 3: Mixed numbers -try { - assert.strictEqual(mul(-2, 3), -6, 'Test 3 Failed: mul(-2, 3) should be -6'); - PASSED_TESTS++; - console.log('Test 3 Passed'); -} catch (error) { - console.error(error.message); -} - -// Test 4: Zeros -try { - assert.strictEqual(mul(0, 0), 0, 'Test 4 Failed: mul(0, 0) should be 0'); - PASSED_TESTS++; - console.log('Test 4 Passed'); -} catch (error) { - console.error(error.message); -} - -console.log('Number of tests passed:', PASSED_TESTS); -" diff --git a/MarkingScripts/multiply/sol.js b/MarkingScripts/multiply/sol.js deleted file mode 100644 index dd6dae6..0000000 --- a/MarkingScripts/multiply/sol.js +++ /dev/null @@ -1,5 +0,0 @@ -function mul(a, b) { - return a + b; -} - -module.exports = { mul }; diff --git a/MarkingScripts/sum/Dockerfile b/MarkingScripts/sum/Dockerfile deleted file mode 100644 index 37b5ec2..0000000 --- a/MarkingScripts/sum/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM node:alpine - -RUN apk add --no-cache bash - -WORKDIR /app - -COPY --chown=node:node sol.js ./sol.js -COPY --chown=node:node mark.sh ./mark.sh - -RUN chmod +x mark.sh - -CMD ["bash", "./mark.sh"] \ No newline at end of file diff --git a/apps/backend/src/controllers/assignmentController.ts b/apps/backend/src/controllers/assignmentController.ts index 85598cf..4d52930 100644 --- a/apps/backend/src/controllers/assignmentController.ts +++ b/apps/backend/src/controllers/assignmentController.ts @@ -112,6 +112,7 @@ const getAssignment = async (req: Request, res: Response) => { createdAt: true, updatedAt: true, maxMarks: true, + boilerplate: true, }, where: { id: parsedData.data.assignmentId, @@ -151,6 +152,7 @@ const getAssignment = async (req: Request, res: Response) => { }); } + console.log(assignmentDescription); return res.status(200).json({ ...assignmentDescription }); }; @@ -321,6 +323,7 @@ const createAssignment = async (req: Request, res: Response) => { courseId, markingScript, dockerFile, + boilerplate, } = req.body; if (!title || !dueDate || !maxMarks || !courseId) { @@ -349,6 +352,7 @@ const createAssignment = async (req: Request, res: Response) => { courseId, markingScript, dockerFile, + boilerplate, }, }); diff --git a/apps/engine/src/utils/ssh.ts b/apps/engine/src/utils/ssh.ts deleted file mode 100644 index 4e63947..0000000 --- a/apps/engine/src/utils/ssh.ts +++ /dev/null @@ -1,37 +0,0 @@ -import fs from 'fs'; -import { NodeSSH } from 'node-ssh'; -import dotenv from 'dotenv'; - -dotenv.config(); - -const ssh = new NodeSSH(); - -export async function sshIntoEC2() { - try { - const privateKeyPath = process.env.PRIVATE_KEY as string; - const privateKey = fs.readFileSync(privateKeyPath, 'utf-8'); - const host = process.env.HOST as string; - const username = 'ubuntu'; - - console.log(host, username, privateKey); - - console.log('Connecting to EC2 instance...'); - await ssh.connect({ - host, - username, - privateKey, - }); - - console.log('Connected to EC2 instance.'); - - await ssh.execCommand('cd'); - await ssh.execCommand('touch test.txt'); - await ssh.execCommand('echo "Hello World" > test.txt'); - await ssh.execCommand('ls -la'); - - ssh.dispose(); - console.log('Connection closed.'); - } catch (error) { - console.error('Failed to SSH into EC2 instance:', error); - } -} diff --git a/apps/engine/src/utils/submission.ts b/apps/engine/src/utils/submission.ts index 015f8f3..fa31d32 100644 --- a/apps/engine/src/utils/submission.ts +++ b/apps/engine/src/utils/submission.ts @@ -1,5 +1,6 @@ import { NodeSSH } from 'node-ssh'; import fs from 'fs'; +import path from 'path'; import prisma from '@repo/db/client'; async function convertLineEndings(ssh: NodeSSH, filePath: string) { @@ -25,7 +26,7 @@ export async function processSubmission(submission: any) { const ssh = new NodeSSH(); const host = process.env.HOST; - const username = 'ubuntu'; + const username = process.env.AWS_USERNAME; const privateKey = fs.readFileSync( process.env.PRIVATE_KEY as string, 'utf-8' @@ -39,17 +40,21 @@ export async function processSubmission(submission: any) { console.log('Creating working directory on EC2...'); await ssh.execCommand(`mkdir -p ${remoteWorkDir}`); - // Download and validate sol.js + // Extract original file extension from upload link + const fileExtension = path.extname(uploadLink); + const submissionFileName = `solution${fileExtension}`; + + // Download submission with original extension console.log('Downloading submission on EC2...'); - const downloadSubmission = `curl -o ${remoteWorkDir}/sol.js '${uploadLink}'`; + const downloadSubmission = `curl -o ${remoteWorkDir}/${submissionFileName} '${uploadLink}'`; await ssh.execCommand(downloadSubmission); - const validateSubmission = `test -f ${remoteWorkDir}/sol.js && echo "File exists" || echo "File missing"`; + const validateSubmission = `test -f ${remoteWorkDir}/${submissionFileName} && echo "File exists" || echo "File missing"`; const submissionCheck = await ssh.execCommand(validateSubmission); if (!submissionCheck.stdout.includes('File exists')) { - throw new Error('Failed to download sol.js'); + throw new Error(`Failed to download ${submissionFileName}`); } - console.log('Submission file downloaded and renamed to sol.js.'); + console.log(`Submission file downloaded as ${submissionFileName}`); // Download and validate mark.sh console.log('Downloading marking script...'); diff --git a/apps/frontend/app/marker/course/[courseId]/publish/page.tsx b/apps/frontend/app/marker/course/[courseId]/publish/page.tsx index ffbd0ae..8bc95a1 100644 --- a/apps/frontend/app/marker/course/[courseId]/publish/page.tsx +++ b/apps/frontend/app/marker/course/[courseId]/publish/page.tsx @@ -19,6 +19,7 @@ export default function PublishAssignment() { const [maxMarks, setMaxMarks] = useState(''); const [markingScript, setMarkingScript] = useState(null); const [dockerFile, setDockerFile] = useState(null); + const [boilerPlateCode, setBoilerPlateCode] = useState(null); const [uploading, setUploading] = useState(false); const handleFileUpload = async (file: File): Promise => { @@ -43,8 +44,8 @@ export default function PublishAssignment() { return; } - if (!markingScript || !dockerFile) { - toast.error('Please upload both the Marking Script and Docker File.'); + if (!markingScript || !dockerFile || !boilerPlateCode) { + toast.error('Please upload all the required files.'); return; } @@ -52,8 +53,9 @@ export default function PublishAssignment() { try { const markingScriptUrl = await handleFileUpload(markingScript); const dockerFileUrl = await handleFileUpload(dockerFile); + const boilerPlateCodeUrl = await handleFileUpload(boilerPlateCode); - if (!markingScriptUrl || !dockerFileUrl) { + if (!markingScriptUrl || !dockerFileUrl || !boilerPlateCodeUrl) { toast.error('File uploads failed. Please try again.'); return; } @@ -68,6 +70,7 @@ export default function PublishAssignment() { courseId, markingScript: markingScriptUrl, dockerFile: dockerFileUrl, + boilerplate: boilerPlateCodeUrl, }, { withCredentials: true, @@ -144,6 +147,15 @@ export default function PublishAssignment() { }} /> + + { + if (e.target.files?.[0]) setBoilerPlateCode(e.target.files[0]); + }} + /> +