diff --git a/apps/docker-compose.yml b/apps/docker-compose.yml index 12d02b5661..77399d8bbc 100644 --- a/apps/docker-compose.yml +++ b/apps/docker-compose.yml @@ -129,6 +129,15 @@ services: - apps_network container_name: python-sandbox + node-sandbox: + build: + context: ./execution-service/execution/node + dockerfile: Dockerfile + networks: + - apps_network + container_name: node-sandbox + stdin_open: true # Enables interactive mode for passing standard input + networks: apps_network: diff --git a/apps/execution-service/constants/constant.go b/apps/execution-service/constants/constant.go index 46d3face08..1e3810a3e4 100644 --- a/apps/execution-service/constants/constant.go +++ b/apps/execution-service/constants/constant.go @@ -1,11 +1,11 @@ package constants const ( - JAVA = "Java" - PYTHON = "Python" - GOLANG = "Golang" - JAVASCRIPT = "Javascript" - CPP = "C++" + JAVA = "java" + PYTHON = "python" + GOLANG = "golang" + JAVASCRIPT = "javascript" + CPP = "c++" ) const ( @@ -17,6 +17,6 @@ var IS_VALID_LANGUAGE = map[string]bool{ PYTHON: true, //JAVA: true, //GOLANG: true, - //JAVASCRIPT: true, + JAVASCRIPT: true, //CPP: true, } diff --git a/apps/execution-service/execution/node/Dockerfile b/apps/execution-service/execution/node/Dockerfile new file mode 100644 index 0000000000..59ef6fcdee --- /dev/null +++ b/apps/execution-service/execution/node/Dockerfile @@ -0,0 +1,11 @@ +# Use a slim Node.js image +FROM node:18-slim + +# Set the working directory +WORKDIR /app + +# Install any dependencies if necessary (you can skip if no dependencies) +# COPY package*.json ./ +# RUN npm install + +# No entry point or CMD needed as you'll provide the command at runtime diff --git a/apps/execution-service/execution/node/javascript.go b/apps/execution-service/execution/node/javascript.go new file mode 100644 index 0000000000..c41c73c068 --- /dev/null +++ b/apps/execution-service/execution/node/javascript.go @@ -0,0 +1,33 @@ +package node + +import ( + "bytes" + "fmt" + "os/exec" + "strings" +) + +func RunJavaScriptCode(code string, input string) (string, string, error) { + cmd := exec.Command( + "docker", "run", "--rm", + "-i", // allows standard input to be passed in + "apps-node-sandbox", // Docker image with Node.js environment + "node", "-e", code, // Runs JavaScript code with Node.js + ) + + // Pass input to the JavaScript script + cmd.Stdin = bytes.NewBufferString(input) + + // Capture standard output and error output + var output bytes.Buffer + var errorOutput bytes.Buffer + cmd.Stdout = &output + cmd.Stderr = &errorOutput + + // Run the command + if err := cmd.Run(); err != nil { + return "", fmt.Sprintf("Command execution failed: %s: %v", errorOutput.String(), err), nil + } + + return strings.TrimSuffix(output.String(), "\n"), strings.TrimSuffix(errorOutput.String(), "\n"), nil +} diff --git a/apps/execution-service/utils/executeTest.go b/apps/execution-service/utils/executeTest.go index b411ba7298..f2bfd1cf50 100644 --- a/apps/execution-service/utils/executeTest.go +++ b/apps/execution-service/utils/executeTest.go @@ -2,6 +2,7 @@ package utils import ( "execution-service/constants" + "execution-service/execution/node" "execution-service/execution/python" "execution-service/models" "fmt" @@ -15,6 +16,8 @@ func ExecuteVisibleAndCustomTests(code models.Code, test models.Test) (models.Ex case constants.PYTHON: testResults, err = getVisibleAndCustomTestResults(code, test, python.RunPythonCode) break + case constants.JAVASCRIPT: + testResults, err = getVisibleAndCustomTestResults(code, test, node.RunJavaScriptCode) default: return models.ExecutionResults{}, fmt.Errorf("unsupported language: %s", code.Language) } @@ -33,6 +36,8 @@ func ExecuteVisibleAndHiddenTests(code models.Code, test models.Test) (models.Su case constants.PYTHON: testResults, err = getVisibleAndHiddenTestResults(code, test, python.RunPythonCode) break + case constants.JAVASCRIPT: + testResults, err = getVisibleAndHiddenTestResults(code, test, node.RunJavaScriptCode) default: return models.SubmissionResults{}, fmt.Errorf("unsupported language: %s", code.Language) } diff --git a/apps/frontend/src/app/collaboration/[id]/page.tsx b/apps/frontend/src/app/collaboration/[id]/page.tsx index b5df554935..f538285987 100644 --- a/apps/frontend/src/app/collaboration/[id]/page.tsx +++ b/apps/frontend/src/app/collaboration/[id]/page.tsx @@ -71,7 +71,7 @@ export default function CollaborationPage(props: CollaborationProps) { const [complexity, setComplexity] = useState(undefined); const [categories, setCategories] = useState([]); // Store the selected filter categories const [description, setDescription] = useState(undefined); - const [selectedLanguage, setSelectedLanguage] = useState("Python"); // State to hold the selected language item + const [selectedLanguage, setSelectedLanguage] = useState("python"); // State to hold the selected language item // Session states const [collaborationId, setCollaborationId] = useState( @@ -232,22 +232,29 @@ export default function CollaborationPage(props: CollaborationProps) { localStorage.setItem("visibleTestResults", JSON.stringify(data.visibleTestResults)); }; + const updateLangauge = (data: string) => { + setSelectedLanguage(data); + } + const handleRunTestCases = async () => { if (!questionDocRefId) { throw new Error("Question ID not found"); } setIsLoadingTestCase(true); sendExecutingStateToMatchedUser(true); - const data = await ExecuteVisibleAndCustomTests(questionDocRefId, { - code: code, - language: selectedLanguage, - customTestCases: "", - }); - updateExecutionResults(data); - infoMessage("Test cases executed. Review the results below."); - sendExecutionResultsToMatchedUser(data); - setIsLoadingTestCase(false); - sendExecutingStateToMatchedUser(false); + try { + const data = await ExecuteVisibleAndCustomTests(questionDocRefId, { + code: code, + language: selectedLanguage, + customTestCases: "", + }); + updateExecutionResults(data); + infoMessage("Test cases executed. Review the results below."); + sendExecutionResultsToMatchedUser(data); + } finally { + setIsLoadingTestCase(false); + sendExecutingStateToMatchedUser(false); + } }; const handleSubmitCode = async () => { @@ -256,25 +263,28 @@ export default function CollaborationPage(props: CollaborationProps) { } setIsLoadingSubmission(true); sendSubmittingStateToMatchedUser(true); - const data = await ExecuteVisibleAndHiddenTestsAndSubmit(questionDocRefId, { - code: code, - language: selectedLanguage, - user: currentUser ?? "", - matchedUser: matchedUser ?? "", - matchedTopics: matchedTopics ?? [], - title: questionTitle ?? "", - questionDifficulty: complexity ?? "", - questionTopics: categories, - }); - updateExecutionResults({ - visibleTestResults: data.visibleTestResults, - customTestResults: [], - }); - updateSubmissionResults(data); - sendSubmissionResultsToMatchedUser(data); - successMessage("Code saved successfully!"); - setIsLoadingSubmission(false); - sendSubmittingStateToMatchedUser(false); + try { + const data = await ExecuteVisibleAndHiddenTestsAndSubmit(questionDocRefId, { + code: code, + language: selectedLanguage, + user: currentUser ?? "", + matchedUser: matchedUser ?? "", + matchedTopics: matchedTopics ?? [], + title: questionTitle ?? "", + questionDifficulty: complexity ?? "", + questionTopics: categories, + }); + updateExecutionResults({ + visibleTestResults: data.visibleTestResults, + customTestResults: [], + }); + updateSubmissionResults(data); + sendSubmissionResultsToMatchedUser(data); + successMessage("Code saved successfully!"); + } finally { + setIsLoadingSubmission(false); + sendSubmittingStateToMatchedUser(false); + } }; const handleCodeChange = (code: string) => { @@ -505,7 +515,7 @@ export default function CollaborationPage(props: CollaborationProps) { ref={editorRef} user={currentUser} collaborationId={collaborationId} - language={selectedLanguage} + updateLanguage={updateLangauge} setMatchedUser={setMatchedUser} handleCloseCollaboration={handleCloseCollaboration} providerRef={providerRef} diff --git a/apps/frontend/src/components/CollaborativeEditor/CollaborativeEditor.tsx b/apps/frontend/src/components/CollaborativeEditor/CollaborativeEditor.tsx index fab13f68ac..b88531ebb3 100644 --- a/apps/frontend/src/components/CollaborativeEditor/CollaborativeEditor.tsx +++ b/apps/frontend/src/components/CollaborativeEditor/CollaborativeEditor.tsx @@ -32,7 +32,7 @@ import { ExecutionResults, SubmissionResults } from "@/app/services/execute"; interface CollaborativeEditorProps { user: string; collaborationId: string; - language: string; + updateLanguage: (language: string) => void; setMatchedUser: Dispatch>; handleCloseCollaboration: (type: string) => void; providerRef: MutableRefObject; @@ -203,6 +203,7 @@ const CollaborativeEditor = forwardRef( language: selectedLanguage, id: latestLanguageChangeId, }); + props.updateLanguage(selectedLanguage); success(`Changed Code Editor's language to ${selectedLanguage}`); } else { setMounted(true); @@ -385,7 +386,7 @@ const CollaborativeEditor = forwardRef( extensions: [ basicSetup, languageConf.of(python()), - // languageConf.of(javascript()), + // languageConf.of(node()), autoLanguage, yCollab(ytext, provider.awareness, { undoManager }), keymap.of([indentWithTab]),