diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..44c2ab6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.idea/ +.mvn/ +.vscode/ +src/frontend/editor/node_modules +target/ +.DS_Store +src/frontend/editor/package-lock.json \ No newline at end of file diff --git a/CollabCode.iml b/CollabCode.iml new file mode 100644 index 0000000..78db2b1 --- /dev/null +++ b/CollabCode.iml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Dockerfiles/backend/Dockerfile b/Dockerfiles/backend/Dockerfile new file mode 100644 index 0000000..d3a6ad6 --- /dev/null +++ b/Dockerfiles/backend/Dockerfile @@ -0,0 +1,2 @@ +FROM openjdk:latest + diff --git a/Dockerfiles/db/Dockerfile b/Dockerfiles/db/Dockerfile new file mode 100644 index 0000000..f6857c1 --- /dev/null +++ b/Dockerfiles/db/Dockerfile @@ -0,0 +1,7 @@ +FROM mysql:latest + + +ENV MYSQL_ROOT_PASSWORD=root +ENV MYSQL_DATABASE=collabcode + +EXPOSE 3306 \ No newline at end of file diff --git a/Dockerfiles/docker-compose.yml b/Dockerfiles/docker-compose.yml new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md new file mode 100644 index 0000000..e56e492 --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +Collaborative coding platform. Supports real-time collaboration, on multple programming languages, temporary code storage, and more. + +### Landing page: +--- + + +### Create a new room: +--- + + +### Code in any language: +--- + + +### Save your code: +--- + diff --git a/api_tests/samples.md b/api_tests/samples.md new file mode 100644 index 0000000..554aadf --- /dev/null +++ b/api_tests/samples.md @@ -0,0 +1,223 @@ +## Sample programs to test execution + +```java +import java.util.Arrays; + +public class Main { + public static void main(String[] args) { + int n = 1000; // Find all primes less than 1000 + boolean[] isPrime = new boolean[n]; + Arrays.fill(isPrime, true); + isPrime[0] = isPrime[1] = false; + + for (int i = 2; i * i < n; i++) { + if (isPrime[i]) { + for (int j = i * i; j < n; j += i) { + isPrime[j] = false; + } + } + } + + for (int i = 2; i < n; i++) { + if (isPrime[i]) { + System.out.println(i + " "); + } + } + } +} + + +``` +--- +```cpp +#include +#include +#include + +using namespace std; + +const int INF = 1e9; + +vector>> adj; + +void dijkstra(int start, vector& dist) { + priority_queue, vector>, greater>> pq; + dist[start] = 0; + pq.push({0, start}); + + while (!pq.empty()) { + int d = pq.top().first; + int u = pq.top().second; + pq.pop(); + + if (d != dist[u]) + continue; + + for (auto edge : adj[u]) { + int v = edge.first; + int len = edge.second; + + if (dist[u] + len < dist[v]) { + dist[v] = dist[u] + len; + pq.push({dist[v], v}); + } + } + } +} + +int main() { + int n = 5; + adj.resize(n); + + // Example graph + adj[0].push_back({1, 10}); + adj[0].push_back({4, 5}); + adj[1].push_back({2, 1}); + adj[2].push_back({3, 4}); + adj[3].push_back({0, 7}); + adj[4].push_back({1, 3}); + adj[4].push_back({2, 9}); + adj[4].push_back({3, 2}); + + vector dist(n, INF); + dijkstra(0, dist); + + for (int i = 0; i < n; i++) { + cout << "Distance to node " << i << " is " << dist[i] << endl; + } + + return 0; +} + + +``` + + +--- +```golang +package main + +import ( + "fmt" + "time" +) + +func task(name string, ch chan string) { + time.Sleep(time.Second * 2) + ch <- fmt.Sprintf("Task %s completed", name) +} + +func main() { + ch := make(chan string) + go task("A", ch) + go task("B", ch) + + fmt.Println(<-ch) // Receive message from any goroutine + fmt.Println(<-ch) // Receive message from any goroutine (may print in different order) +} +``` +--- +```python +import cmath + +def fft(x): + N = len(x) + if N <= 1: + return x + even = fft(x[0::2]) + odd = fft(x[1::2]) + T = [cmath.exp(-2j * cmath.pi * k / N) * odd[k] for k in range(N // 2)] + return [even[k] + T[k] for k in range(N // 2)] + [even[k] - T[k] for k in range(N // 2)] + +# Test the FFT function +x = [cmath.exp(2j * cmath.pi * i / 8) for i in range(8)] +fft_result = fft(x) + +# Print the results +for value in fft_result: + print(value) + + +``` +--- +```c +#include +#include + +#define N 4 + +void multiply(int a[N][N], int b[N][N], int result[N][N]) { + int i, j, k; + for (i = 0; i < N; i++) { + for (j = 0; j < N; j++) { + result[i][j] = 0; + for (k = 0; k < N; k++) { + result[i][j] += a[i][k] * b[j][k]; + } + } + } +} + +int main() { + int a[N][N] = { + {1, 2, 3, 4}, + {5, 6, 7, 8}, + {9, 10, 11, 12}, + {13, 14, 15, 16} + }; + int b[N][N] = { + {16, 15, 14, 13}, + {12, 11, 10, 9}, + {8, 7, 6, 5}, + {4, 3, 2, 1} + }; + int result[N][N]; + + multiply(a, b, result); + + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + printf("%d ", result[i][j]); + } + printf("\n"); + } + + return 0; +} + +``` +--- +```rust +fn quicksort(arr: &mut [i32]) { + if arr.len() <= 1 { + return; + } + + let pivot_index = partition(arr); + quicksort(&mut arr[0..pivot_index]); + quicksort(&mut arr[pivot_index + 1..arr.len()]); +} + +fn partition(arr: &mut [i32]) -> usize { + let pivot_index = arr.len() / 2; + arr.swap(pivot_index, arr.len() - 1); + + let mut store_index = 0; + for i in 0..arr.len() - 1 { + if arr[i] < arr[arr.len() - 1] { + arr.swap(i, store_index); + store_index += 1; + } + } + arr.swap(store_index, arr.len() - 1); + store_index +} + +fn main() { + let mut arr = [3, 6, 8, 10, 1, 2, 1]; + quicksort(&mut arr); + println!("{:?}", arr); +} + + +``` \ No newline at end of file diff --git a/api_tests/thread.py b/api_tests/thread.py new file mode 100644 index 0000000..858570e --- /dev/null +++ b/api_tests/thread.py @@ -0,0 +1,28 @@ +import requests +import threading + +url = "http://127.1:8080/exec" +headers = {'Content-Type': 'application/json'} + +data1 = { + "lang": "python", + "code": "def bubbleSort(arr):\n n = len(arr)\n # optimize code, so if the array is already sorted, it doesn't need\n # to go through the entire process\n # Traverse through all array elements\n for i in range(n-1):\n\n # range(n) also work but outer loop will\n # repeat one time more than needed.\n # Last i elements are already in place\n swapped = False\n for j in range(0, n-i-1):\n\n # traverse the array from 0 to n-i-1\n # Swap if the element found is greater\n # than the next element\n if arr[j] > arr[j + 1]:\n swapped = True\n arr[j], arr[j + 1] = arr[j + 1], arr[j]\n\n if not swapped:\n # if we haven't needed to make a single swap, we\n # can just exit the main loop.\n return\n\n\n# Driver code to test above\narr = [64, 34, 25, 12, 22, 11, 90]\n\nbubbleSort(arr)\n\nprint(\"Sorted array is:\")\nfor i in range(len(arr)):\n print(\"% d\" % arr[i], end=\" \")" +} + +data2 = { + "lang": "python", + "code": "def insertionSort(arr):\n n = len(arr) # Get the length of the array\n \n if n <= 1:\n return # If the array has 0 or 1 element, it is already sorted, so return\n \n for i in range(1, n): # Iterate over the array starting from the second element\n key = arr[i] # Store the current element as the key to be inserted in the right position\n j = i-1\n while j >= 0 and key < arr[j]: # Move elements greater than key one position ahead\n arr[j+1] = arr[j] # Shift elements to the right\n j -= 1\n arr[j+1] = key # Insert the key in the correct position\n \n# Sorting the array [12, 11, 13, 5, 6] using insertionSort\narr = [12, 11, 13, 5, 6]\ninsertionSort(arr)\nprint(arr)" +} + +def send_post(data): + response = requests.post(url, headers=headers, json=data) + print(response.json()) + +thread1 = threading.Thread(target=send_post, args=(data1,)) +thread2 = threading.Thread(target=send_post, args=(data2,)) + +thread1.start() +thread2.start() + +thread1.join() +thread2.join() diff --git a/mvnw b/mvnw new file mode 100755 index 0000000..66df285 --- /dev/null +++ b/mvnw @@ -0,0 +1,308 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.2.0 +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "$(uname)" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --unix "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --unix "$CLASSPATH") +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && + JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=$(which readlink) + if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then + if $darwin ; then + javaHome="$(dirname "\"$javaExecutable\"")" + javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" + else + javaExecutable="$(readlink -f "\"$javaExecutable\"")" + fi + javaHome="$(dirname "\"$javaExecutable\"")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=$(cd "$wdir/.." || exit 1; pwd) + fi + # end of workaround + done + printf '%s' "$(cd "$basedir" || exit 1; pwd)" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + # Remove \r in case we run on Windows within Git Bash + # and check out the repository with auto CRLF management + # enabled. Otherwise, we may read lines that are delimited with + # \r\n and produce $'-Xarg\r' rather than -Xarg due to word + # splitting rules. + tr -s '\r\n' ' ' < "$1" + fi +} + +log() { + if [ "$MVNW_VERBOSE" = true ]; then + printf '%s\n' "$1" + fi +} + +BASE_DIR=$(find_maven_basedir "$(dirname "$0")") +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +log "$MAVEN_PROJECTBASEDIR" + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" +if [ -r "$wrapperJarPath" ]; then + log "Found $wrapperJarPath" +else + log "Couldn't find $wrapperJarPath, downloading it ..." + + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + fi + while IFS="=" read -r key value; do + # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) + safeValue=$(echo "$value" | tr -d '\r') + case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; + esac + done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" + log "Downloading from: $wrapperUrl" + + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget > /dev/null; then + log "Found wget ... using wget" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + log "Found curl ... using curl" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + fi + else + log "Falling back to using Java to download" + javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=$(cygpath --path --windows "$javaSource") + javaClass=$(cygpath --path --windows "$javaClass") + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + log " - Compiling MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + log " - Running MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +# If specified, validate the SHA-256 sum of the Maven wrapper jar file +wrapperSha256Sum="" +while IFS="=" read -r key value; do + case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; + esac +done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" +if [ -n "$wrapperSha256Sum" ]; then + wrapperSha256Result=false + if command -v sha256sum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + elif command -v shasum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." + echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." + exit 1 + fi + if [ $wrapperSha256Result = false ]; then + echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 + echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 + echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 + exit 1 + fi +fi + +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --windows "$CLASSPATH") + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +# shellcheck disable=SC2086 # safe args +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..95ba6f5 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,205 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.2.0 +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file +SET WRAPPER_SHA_256_SUM="" +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B +) +IF NOT %WRAPPER_SHA_256_SUM%=="" ( + powershell -Command "&{"^ + "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ + "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ + " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ + " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ + " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ + " exit 1;"^ + "}"^ + "}" + if ERRORLEVEL 1 goto error +) + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/nogitlog.sh b/nogitlog.sh new file mode 100755 index 0000000..c8fc5a0 --- /dev/null +++ b/nogitlog.sh @@ -0,0 +1,11 @@ +git checkout --orphan temp_branch + +git add -A + +git commit -am "purged commits" + +git branch -D main + +git branch -m main + +git push -f origin main \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..b6b7fcb --- /dev/null +++ b/pom.xml @@ -0,0 +1,70 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.2.4 + + + com.debxrshi + CollabCode + 0.0.1-SNAPSHOT + war + CollabCode + CollabCode + + 17 + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-websocket + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + jakarta.validation + jakarta.validation-api + + + org.springframework.boot + spring-boot-starter-tomcat + provided + + + com.mysql + mysql-connector-j + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/src/frontend/editor/index.html b/src/frontend/editor/index.html new file mode 100644 index 0000000..4b457a7 --- /dev/null +++ b/src/frontend/editor/index.html @@ -0,0 +1,13 @@ + + + + + + + CollabCode + + + + + + diff --git a/src/frontend/editor/package.json b/src/frontend/editor/package.json new file mode 100644 index 0000000..e440d79 --- /dev/null +++ b/src/frontend/editor/package.json @@ -0,0 +1,36 @@ +{ + "name": "collabcode-frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite --host", + "build": "vite build", + "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" + }, + "dependencies": { + "@fortawesome/fontawesome-free": "^6.5.2", + "@fortawesome/free-regular-svg-icons": "^6.5.2", + "@fortawesome/free-solid-svg-icons": "^6.5.2", + "@fortawesome/react-fontawesome": "^0.2.0", + "@monaco-editor/react": "^4.6.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.23.0", + "sweetalert2": "^11.10.8" + }, + "devDependencies": { + "@types/react": "^18.2.66", + "@types/react-dom": "^18.2.22", + "@vitejs/plugin-react": "^4.2.1", + "autoprefixer": "^10.4.19", + "eslint": "^8.57.0", + "eslint-plugin-react": "^7.34.1", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.6", + "postcss": "^8.4.38", + "tailwindcss": "^3.4.3", + "vite": "^5.2.0" + } +} diff --git a/src/frontend/editor/postcss.config.js b/src/frontend/editor/postcss.config.js new file mode 100644 index 0000000..2e7af2b --- /dev/null +++ b/src/frontend/editor/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/src/frontend/editor/public/vite.svg b/src/frontend/editor/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/src/frontend/editor/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/frontend/editor/src/App.jsx b/src/frontend/editor/src/App.jsx new file mode 100644 index 0000000..e536e45 --- /dev/null +++ b/src/frontend/editor/src/App.jsx @@ -0,0 +1,16 @@ +import { useState } from 'react' +import reactLogo from './assets/react.svg' +import viteLogo from '/vite.svg' +import Room from './Room' + + +function App() { + + return ( + <> + + > + ) +} + +export default App diff --git a/src/frontend/editor/src/CodeEditor.jsx b/src/frontend/editor/src/CodeEditor.jsx new file mode 100644 index 0000000..396bd8e --- /dev/null +++ b/src/frontend/editor/src/CodeEditor.jsx @@ -0,0 +1,208 @@ +import { useState, useRef, useEffect } from "react"; +import { useNavigate, useParams } from 'react-router-dom'; +import { Editor } from "@monaco-editor/react"; +import Swal from 'sweetalert2'; +import Terminal from "./Terminal"; + +function CodeEditor() { + const navigate = useNavigate(); + const editorRef = useRef(null); + const [code, setCode] = useState(''); + const [id, setId] = useState(''); + const [language, setLanguage] = useState('javascript'); + const [output, setOutput] = useState(""); + const [time, setTime] = useState(""); + const { uuid } = useParams(); + const [key, setKey] = useState(localStorage.getItem("key")); + + useEffect(() => { + if (!uuid || !key) { + navigate("/"); + } else { + fetch(`http://192.168.1.4:8080/api/joinRoom?uuid=${uuid}&roomKey=${key}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + }, + }).then(response => { + if (response.ok) { + return response.json(); + } else { + navigate('/'); + } + }).then(data => { + setLanguage(data.lang); + setId(data.id); + setCode(data.code); + }).catch(error => { + console.error(error); + navigate('/'); + }); + } + }, [uuid, key, navigate]); + + function handleEditorDidMount(editor, monaco) { + editorRef.current = editor; + const socket = new WebSocket(`ws://192.168.1.4:8080/websocket/${uuid}`); + + socket.onopen = () => { + console.log("WebSocket connection opened"); + }; + + socket.onclose = () => { + console.log("WebSocket connection closed"); + }; + + let isReceivedChanged = false; + + editor.onDidChangeModelContent(() => { + if (!isReceivedChanged) { + const value = editor.getValue(); + socket.send(value); + } + }); + + socket.onmessage = (event) => { + const content = event.data; + const selection = editor.getSelection(); + + isReceivedChanged = true; + editor.setValue(content); + isReceivedChanged = false; + + editor.setSelection(selection); + }; + + return () => { + socket.close(); + }; + } + + const handleLanguageChange = (event) => { + setLanguage(event.target.value); + setCode(''); + }; + + const runCode = () => { + let timerInterval; + Swal.fire({ + title: "Executing", + background: 'black', + timerProgressBar: true, + didOpen: () => { + Swal.showLoading(); + const timer = Swal.getPopup().querySelector("b"); + timerInterval = setInterval(() => { + timer.textContent = `${Swal.getTimerLeft()}`; + }, 100); + }, + willClose: () => { + clearInterval(timerInterval); + } + }); + + const codes = editorRef.current.getValue(); + const obj = { code: codes, lang: language }; + setOutput(""); + setTime(""); + fetch('http://192.168.1.4:8080/api/exec', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(obj) + }).then(response => response.json()) + .then(data => { + setOutput(data.out); + setTime(data.tte); + Swal.close(); + }).catch(error => { + console.error(error); + Swal.close(); + }); + }; + + const saveCode = () => { + fetch('http://192.168.1.4:8080/api/saveState', { + method: 'PUT', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ id: id, lang: language, code: editorRef.current.getValue(), uuid: uuid, roomKey: key }) + }).then(response => { + if (response.ok) { + Swal.fire({ + title: "Saved", + background: 'black', + showClass: { + popup: ` + animate__animated + animate__fadeInUp + animate__faster + ` + }, + hideClass: { + popup: ` + animate__animated + animate__fadeOutDown + animate__faster + ` + } + }); + } else { + Swal.fire({ + icon: 'error', + title: 'Error', + text: 'Failed to save code. Please try again later.' + }); + } + }).catch(error => { + console.error(error); + Swal.fire({ + icon: 'error', + title: 'Error', + text: 'Failed to save code. Please try again later.' + }); + }); + }; + return ( + <> + + + + + JavaScript + Python + Go + C++ + C + Java + Rust + {/* Add more options for other languages */} + + + Save + Run Code + + + + + + + + + + > + + + ); +} + +export default CodeEditor; diff --git a/src/frontend/editor/src/Layout.jsx b/src/frontend/editor/src/Layout.jsx new file mode 100644 index 0000000..d462bcb --- /dev/null +++ b/src/frontend/editor/src/Layout.jsx @@ -0,0 +1,17 @@ +import React, { useState, useEffect ,useContext} from 'react'; +import { Outlet } from 'react-router-dom'; + + + +function Layout(){ + let x=window.location.href.toLowerCase() + return( + <> + + > + ) + } + + + +export default Layout \ No newline at end of file diff --git a/src/frontend/editor/src/PopUp.jsx b/src/frontend/editor/src/PopUp.jsx new file mode 100644 index 0000000..1b32ef3 --- /dev/null +++ b/src/frontend/editor/src/PopUp.jsx @@ -0,0 +1,130 @@ +import React, { useRef, useState } from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faXmark } from '@fortawesome/free-solid-svg-icons'; +import { useNavigate } from 'react-router-dom'; + + + + +function PopUp({ onClose, textValue ,type}) { + const [borderColor, setBorderColor] = useState('border-blue-500'); + const [fontColor, setFontColor] = useState(); + const inputRef = useRef(); + const [value, setValue] = useState(''); + const navigate = useNavigate(); + + const handleError = async (e) => { + e.preventDefault(); + const roomId = textValue; + const secretKey = inputRef.current.value; + + + if (!roomId || !secretKey || roomId.trim().length === 0 || secretKey.trim().length === 0) { + setBorderColor('border-red-500'); + setFontColor('placeholder-red-600'); + setTimeout(() => { + setBorderColor('border-blue-500'); + setFontColor(); + }, 2000); + return; + } + + try { + const response = await fetch('http://192.168.1.4:8080/api/createRoom', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ uuid:roomId, roomKey:secretKey, code:"", lang:"python" }), + }); + + if (response.ok) { + const data = await response.text(); + + console.log('Room joined:', data); + localStorage.setItem("key",secretKey) + navigate(`/CodeEditor/${textValue}`) + } else { + console.log('Failed to join room:', response.statusText); + setBorderColor('border-red-500'); + setFontColor('placeholder-red-600'); + } + } catch (error) { + console.log('Error joining room:', error); + setBorderColor('border-red-500'); + setFontColor('placeholder-red-600'); + } + }; + const handleErrorJoin = async (e) => { + e.preventDefault(); + const roomId = textValue; + const secretKey = inputRef.current.value; + + if (!roomId || !secretKey || roomId.trim().length === 0 || secretKey.trim().length === 0) { + setBorderColor('border-red-500'); + setFontColor('placeholder-red-600'); + setTimeout(() => { + setBorderColor('border-blue-500'); + setFontColor(); + }, 2000); + return; + } + + try { + const response = await fetch(`http://192.168.1.4:8080/api/joinRoom?uuid=${textValue}&roomKey=${value}`, { + method: 'Get', + headers: { 'Content-Type': 'application/json' }, + //body: JSON.stringify({ uuid:roomId, roomKey:secretKey, code:"", lang:"python" }), + }); + + if (response.ok) { + const data = await response.json() + + console.log('Room joined:', data); + localStorage.setItem("key",secretKey) + navigate(`/CodeEditor/${textValue}`) + } else { + console.log('Failed to join room:', response.statusText); + setBorderColor('border-red-500'); + setFontColor('placeholder-red-600'); + } + } catch (error) { + console.log('Error joining room:', error); + setBorderColor('border-red-500'); + setFontColor('placeholder-red-600'); + } + }; + + return ( + + + + Room ID : {textValue} + + + + setValue(e.target.value)} + /> + {type=='new' && + Create + } + {type=='existing' && + Join + } + + + + ); +} + +export default PopUp; \ No newline at end of file diff --git a/src/frontend/editor/src/Room.jsx b/src/frontend/editor/src/Room.jsx new file mode 100644 index 0000000..d88ceda --- /dev/null +++ b/src/frontend/editor/src/Room.jsx @@ -0,0 +1,97 @@ +import React, { useState } from 'react' +import team from './images/team.jpg' +import PopUp from './PopUp'; +import { faL } from '@fortawesome/free-solid-svg-icons'; + + + +function Room() { + + const [buttonDisabled, setButtonDisabled] = useState(true); + const [textColor, setTextColor] = useState('text-slate-400'); + const [isOpen, setIsOpen] = useState(false); + const [isJoin, setIsjoin] = useState(false); + const [value, setValue] = useState("") + + const handleInputChange = (e) => { + setValue(e.target.value) + const value = e.target.value; + const v = value.trim(); + + if (v.length > 0) { + setButtonDisabled(false); + setTextColor('text-blue-500'); + } + else { + setButtonDisabled(true); + setTextColor('text-slate-400'); + } + }; + + const joinClick = (e) => { + setIsjoin(true) + setIsOpen(true) + + } + + const createRoom=(e)=>{ + setValue(crypto.randomUUID()) + setIsOpen(true); + setIsjoin(false) + + + } + + + return ( + + + + CollabCode + + Home + About + + + + + + + + + + + Collaborative coding platform + + + Collaborate and try out multiple languages with just a click, free. + + + + Create Room + + + Or + + + + Join + + + {console.log(value)} + {isOpen && {setIsOpen(false)}} />} + {isOpen && isJoin && {setIsOpen(false)}} />} + + + + + + + + + + + ) +} + +export default Room \ No newline at end of file diff --git a/src/frontend/editor/src/Terminal.jsx b/src/frontend/editor/src/Terminal.jsx new file mode 100644 index 0000000..8bfe312 --- /dev/null +++ b/src/frontend/editor/src/Terminal.jsx @@ -0,0 +1,23 @@ +function Terminal({ output, time }) { // Destructure output from props + const formattedOutput = output.split('\n').map((line, index) => {line}); + + if (time.length != 0){ + return ( + + Output: + {formattedOutput} + + Execution completed in {time} seconds. + + ); + } + else{ + return ( + + Output: + + ); + } +} + +export default Terminal; \ No newline at end of file diff --git a/src/frontend/editor/src/assets/react.svg b/src/frontend/editor/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/src/frontend/editor/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/frontend/editor/src/images/team.jpg b/src/frontend/editor/src/images/team.jpg new file mode 100644 index 0000000..e1d0537 Binary files /dev/null and b/src/frontend/editor/src/images/team.jpg differ diff --git a/src/frontend/editor/src/index.css b/src/frontend/editor/src/index.css new file mode 100644 index 0000000..f59f302 --- /dev/null +++ b/src/frontend/editor/src/index.css @@ -0,0 +1,82 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + /* background-color: rgb(30, 30, 30); */ + } + .App{ + display: inline-block; + + } + + code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; + } + + .container { + display: flex; + } + + .CodeEditor { + float: left; + width: 50%; /* or another appropriate width */ + } + + .Terminal { + background-color: rgb(22, 22, 22); + height: 95vh; + min-width: 50%; + color: white; + display: inline-block; + overflow: auto; /* Add this line */ + } + + .nex{ + display: flex; + justify-content: space-evenly; + } + .Nav{ + background-color: rgb(35, 32, 32); + color: rgb(234, 234, 227); + height: 40px; + } + .edits{ + display: flex; + justify-content: space-between; + } + .runcode{ + border: none; + background-color: rgb(27, 115, 37); + color: aliceblue; + font-weight: bolder; + padding: 1%; + border-radius: 9%; + } + .runcode:hover{ + background-color: rgb(21, 163, 38); + } + .output{ + padding-top: 1%; + padding-left: 2%; + font-size: large; + padding-bottom: 1.5%; + border-bottom: 2px solid rgb(228, 219, 219); + } + select{ + background-color: rgb(243, 236, 236); + border-radius: 9%; + outline: none; + border: none; + } + + + + \ No newline at end of file diff --git a/src/frontend/editor/src/main.jsx b/src/frontend/editor/src/main.jsx new file mode 100644 index 0000000..633e3e9 --- /dev/null +++ b/src/frontend/editor/src/main.jsx @@ -0,0 +1,31 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App.jsx' +import './index.css' +import Room from './Room.jsx' +import PopUp from './PopUp.jsx' +import CodeEditor from './CodeEditor.jsx' +import { RouterProvider, createBrowserRouter } from 'react-router-dom' +import Layout from './Layout.jsx' + + + + +const router=createBrowserRouter([ + + { path:'/', + element:, + children:[ + {path:'', + element:}, + {path:'CodeEditor/:uuid', + element:}, + ] + } +]) + +ReactDOM.createRoot(document.getElementById('root')).render( + + + +) diff --git a/src/frontend/editor/tailwind.config.js b/src/frontend/editor/tailwind.config.js new file mode 100644 index 0000000..4ed030a --- /dev/null +++ b/src/frontend/editor/tailwind.config.js @@ -0,0 +1,12 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: [ + "./index.html", + "./src/**/*.{js,ts,jsx,tsx}", + ], + theme: { + extend: {}, + }, + plugins: [], +} + diff --git a/src/frontend/editor/vite.config.js b/src/frontend/editor/vite.config.js new file mode 100644 index 0000000..5a33944 --- /dev/null +++ b/src/frontend/editor/vite.config.js @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}) diff --git a/src/main/java/com/debxrshi/collabcode/CollabCodeApplication.java b/src/main/java/com/debxrshi/collabcode/CollabCodeApplication.java new file mode 100644 index 0000000..459ba84 --- /dev/null +++ b/src/main/java/com/debxrshi/collabcode/CollabCodeApplication.java @@ -0,0 +1,13 @@ +package com.debxrshi.collabcode; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class CollabCodeApplication { + + public static void main(String[] args) { + SpringApplication.run(CollabCodeApplication.class, args); + } + +} diff --git a/src/main/java/com/debxrshi/collabcode/config/WebSocketConfig.java b/src/main/java/com/debxrshi/collabcode/config/WebSocketConfig.java new file mode 100644 index 0000000..0a2e883 --- /dev/null +++ b/src/main/java/com/debxrshi/collabcode/config/WebSocketConfig.java @@ -0,0 +1,23 @@ +package com.debxrshi.collabcode.config; + +import com.debxrshi.collabcode.repository.RoomRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.WebSocketHandler; +import org.springframework.web.socket.config.annotation.EnableWebSocket; +import org.springframework.web.socket.config.annotation.WebSocketConfigurer; +import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; + +@Configuration +@EnableWebSocket +public class WebSocketConfig implements WebSocketConfigurer { + + @Autowired + WebSocketHandler handler; + + @Override + public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { + registry.addHandler(handler, "/websocket/{uuid}") + .setAllowedOrigins("*"); + } +} \ No newline at end of file diff --git a/src/main/java/com/debxrshi/collabcode/controller/ExecutionController.java b/src/main/java/com/debxrshi/collabcode/controller/ExecutionController.java new file mode 100644 index 0000000..0b744d1 --- /dev/null +++ b/src/main/java/com/debxrshi/collabcode/controller/ExecutionController.java @@ -0,0 +1,22 @@ +package com.debxrshi.collabcode.controller; + +import com.debxrshi.collabcode.model.Code; +import com.debxrshi.collabcode.model.ExecResult; +import com.debxrshi.collabcode.service.Impl.CodeExecutorImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +@CrossOrigin +@RestController +@RequestMapping("/api") +public class ExecutionController { + + @Autowired + private CodeExecutorImpl codeExecutor ; + + @PostMapping(value = "/exec", produces = "application/json", consumes = "application/json") + public ExecResult executeCode(@RequestBody Code code) { + return codeExecutor.codeExecutor(code); + } +} \ No newline at end of file diff --git a/src/main/java/com/debxrshi/collabcode/controller/RoomController.java b/src/main/java/com/debxrshi/collabcode/controller/RoomController.java new file mode 100644 index 0000000..32317e7 --- /dev/null +++ b/src/main/java/com/debxrshi/collabcode/controller/RoomController.java @@ -0,0 +1,46 @@ +package com.debxrshi.collabcode.controller; + +import com.debxrshi.collabcode.model.Room; +import com.debxrshi.collabcode.repository.RoomRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + + +@RestController +@RequestMapping("/api") +@CrossOrigin +public class RoomController { + + @Autowired + private RoomRepository roomRepository; + + @PostMapping(value = "/createRoom", consumes = "application/json", produces = "application/json") + public ResponseEntity createRoom(@Validated @RequestBody Room room){ + roomRepository.save(room); + return ResponseEntity.status(HttpStatus.OK).body("Room Created!"); + } + + @GetMapping(value = "/joinRoom", consumes = "application/json", produces = "application/json") + public ResponseEntity joinRoom(@Validated @RequestParam String uuid, String roomKey) { + Room roomObj = roomRepository.findByUuidAndRoomKey(uuid, roomKey); + if (roomObj != null) { + return ResponseEntity.status(HttpStatus.OK).body(roomObj); + } + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Check your credentials! Wrong ID or KEY"); + } + + @PutMapping(value = "/saveState", consumes = "application/json", produces = "application/json") + public ResponseEntity saveState(@RequestBody Room room ){ + if(roomRepository.findByUuidAndRoomKey(room.getUuid(),room.getRoomKey()) != null){ + roomRepository.save(room); + return ResponseEntity.status(HttpStatus.OK).body(room); + } + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Check your credentials! Wrong ID or KEY"); + } + +} + + diff --git a/src/main/java/com/debxrshi/collabcode/handler/WebSocketHandler.java b/src/main/java/com/debxrshi/collabcode/handler/WebSocketHandler.java new file mode 100644 index 0000000..5f42324 --- /dev/null +++ b/src/main/java/com/debxrshi/collabcode/handler/WebSocketHandler.java @@ -0,0 +1,79 @@ +package com.debxrshi.collabcode.handler; + +import com.debxrshi.collabcode.model.Room; +import com.debxrshi.collabcode.repository.RoomRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.socket.CloseStatus; +import org.springframework.web.socket.TextMessage; +import org.springframework.web.socket.WebSocketSession; +import org.springframework.web.socket.handler.TextWebSocketHandler; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +@Service +public class WebSocketHandler extends TextWebSocketHandler { + + + @Autowired + private RoomRepository roomRepository; + private static final Map> roomSessions = new HashMap<>(); + + @Override + public void afterConnectionEstablished(WebSocketSession session) { + try { + String roomId = getRoomId(session); + Room room = roomRepository.findByUuid(roomId); + if (room != null) { + roomSessions.computeIfAbsent(roomId, k -> new HashSet<>()).add(session); + } else { + session.close(CloseStatus.NOT_ACCEPTABLE); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + protected void handleTextMessage(WebSocketSession session, TextMessage textMessage) { + try { + String roomId = getRoomId(session); + if (roomId != null) { + Set sessions = roomSessions.get(roomId); + if (sessions != null) { + for (WebSocketSession webSocketSession : sessions) { + if (webSocketSession.isOpen()) { + webSocketSession.sendMessage(textMessage); + } + } + } + } + } catch (Exception e) { + System.out.println("Failed to send message"); + e.printStackTrace(); + } + } + + @Override + public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { + String roomId = getRoomId(session); + if (roomId != null) { + Set sessions = roomSessions.get(roomId); + if (sessions != null) { + sessions.remove(session); + if (sessions.isEmpty()) { + roomSessions.remove(roomId); + } + } + } + } + + private String getRoomId(WebSocketSession session) { + String uri = session.getUri().toString(); + String[] parts = uri.split("/"); + return parts.length > 1 ? parts[parts.length - 1] : null; + } +} \ No newline at end of file diff --git a/src/main/java/com/debxrshi/collabcode/model/Code.java b/src/main/java/com/debxrshi/collabcode/model/Code.java new file mode 100644 index 0000000..1f217bd --- /dev/null +++ b/src/main/java/com/debxrshi/collabcode/model/Code.java @@ -0,0 +1,22 @@ +package com.debxrshi.collabcode.model; + +public class Code { + private String code; + private String lang; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getLang() { + return lang; + } + + public void setLang(String lang) { + this.lang = lang; + } +} diff --git a/src/main/java/com/debxrshi/collabcode/model/ExecResult.java b/src/main/java/com/debxrshi/collabcode/model/ExecResult.java new file mode 100644 index 0000000..948b563 --- /dev/null +++ b/src/main/java/com/debxrshi/collabcode/model/ExecResult.java @@ -0,0 +1,29 @@ +package com.debxrshi.collabcode.model; + +public class ExecResult { + private String out; + private float tte; + + public ExecResult() { + } + public ExecResult(String out, float tte) { + this.out = out; + this.tte = tte; + } + + public String getOut() { + return out; + } + + public void setOut(String out) { + this.out = out; + } + + public float getTte() { + return tte; + } + + public void setTte(float tte) { + this.tte = tte; + } +} diff --git a/src/main/java/com/debxrshi/collabcode/model/Room.java b/src/main/java/com/debxrshi/collabcode/model/Room.java new file mode 100644 index 0000000..06766a7 --- /dev/null +++ b/src/main/java/com/debxrshi/collabcode/model/Room.java @@ -0,0 +1,65 @@ +package com.debxrshi.collabcode.model; + +import jakarta.persistence.*; +import jakarta.validation.constraints.NotNull; + +@Entity +@Table(name = "Rooms") +public class Room { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "uuid", unique = true, nullable = false) + private String uuid; + + @Column(name = "code", columnDefinition = "text") + private String code; + + @Column(name = "lang") + private String lang; + + @Column(name = "roomKey", nullable = false) + private String roomKey; + + public String getRoomKey() { + return roomKey; + } + + public void setRoomKey(String roomKey) { + this.roomKey = roomKey; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getLang() { + return lang; + } + + public void setLang(String lang) { + this.lang = lang; + } +} \ No newline at end of file diff --git a/src/main/java/com/debxrshi/collabcode/repository/RoomRepository.java b/src/main/java/com/debxrshi/collabcode/repository/RoomRepository.java new file mode 100644 index 0000000..9381887 --- /dev/null +++ b/src/main/java/com/debxrshi/collabcode/repository/RoomRepository.java @@ -0,0 +1,12 @@ +package com.debxrshi.collabcode.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import com.debxrshi.collabcode.model.Room; +import org.springframework.stereotype.Repository; + +@Repository +public interface RoomRepository extends JpaRepository { + Room findByUuidAndRoomKey(String uuid, String roomKey); + + Room findByUuid(String uuid); +} diff --git a/src/main/java/com/debxrshi/collabcode/service/Impl/CodeExecutorImpl.java b/src/main/java/com/debxrshi/collabcode/service/Impl/CodeExecutorImpl.java new file mode 100644 index 0000000..155a1c6 --- /dev/null +++ b/src/main/java/com/debxrshi/collabcode/service/Impl/CodeExecutorImpl.java @@ -0,0 +1,27 @@ +package com.debxrshi.collabcode.service.Impl; + +import com.debxrshi.collabcode.model.Code; +import com.debxrshi.collabcode.model.ExecResult; +import com.debxrshi.collabcode.service.strategy.CodeExecutionStrategy; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Map; + + +@Service +public class CodeExecutorImpl { + + @Autowired + private Map executors; + + public ExecResult codeExecutor(Code code) { + String lang = code.getLang(); + CodeExecutionStrategy executor = executors.get(lang); + if (executor != null) { + return executor.execCode(code); + } else { + return new ExecResult("Unsupported Language: " + code.getLang(), 0.00F); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/debxrshi/collabcode/service/strategy/CExecutor.java b/src/main/java/com/debxrshi/collabcode/service/strategy/CExecutor.java new file mode 100644 index 0000000..4b49306 --- /dev/null +++ b/src/main/java/com/debxrshi/collabcode/service/strategy/CExecutor.java @@ -0,0 +1,25 @@ +package com.debxrshi.collabcode.service.strategy; + +import com.debxrshi.collabcode.model.Code; +import com.debxrshi.collabcode.model.ExecResult; +import com.debxrshi.collabcode.service.util.ContainerManager; +import org.springframework.stereotype.Component; + +import java.util.UUID; + +@Component("c") +public class CExecutor implements CodeExecutionStrategy { + + @Override + public ExecResult execCode(Code code) { + String containerName = code.getLang() + UUID.randomUUID(); + String dockerCommand = String.format("echo \"%s\" > main.c && gcc main.c -o main && timeout -s SIGKILL 10 ./main ; exit", code.getCode().replace("\"", "\\\"")); + ProcessBuilder pb = new ProcessBuilder() + .command("docker", "run", "--rm", "--name", containerName, "--network", "none", + "--memory", "150m", "cc-gxx:dev", "sh", "-c", dockerCommand) + .redirectErrorStream(true); + ExecResult result = new ExecResult(); + return ContainerManager.initContainer(pb, containerName, result); + } +} + diff --git a/src/main/java/com/debxrshi/collabcode/service/strategy/CodeExecutionStrategy.java b/src/main/java/com/debxrshi/collabcode/service/strategy/CodeExecutionStrategy.java new file mode 100644 index 0000000..17fd22b --- /dev/null +++ b/src/main/java/com/debxrshi/collabcode/service/strategy/CodeExecutionStrategy.java @@ -0,0 +1,9 @@ +package com.debxrshi.collabcode.service.strategy; + +import com.debxrshi.collabcode.model.Code; +import com.debxrshi.collabcode.model.ExecResult; + + +public interface CodeExecutionStrategy { + ExecResult execCode(Code code); +} diff --git a/src/main/java/com/debxrshi/collabcode/service/strategy/CppExecutor.java b/src/main/java/com/debxrshi/collabcode/service/strategy/CppExecutor.java new file mode 100644 index 0000000..25fbbd4 --- /dev/null +++ b/src/main/java/com/debxrshi/collabcode/service/strategy/CppExecutor.java @@ -0,0 +1,24 @@ +package com.debxrshi.collabcode.service.strategy; + +import com.debxrshi.collabcode.model.Code; +import com.debxrshi.collabcode.model.ExecResult; +import com.debxrshi.collabcode.service.util.ContainerManager; +import org.springframework.stereotype.Component; + +import java.util.UUID; + +@Component("cpp") +public class CppExecutor implements CodeExecutionStrategy { + + @Override + public ExecResult execCode(Code code) { + String containerName = code.getLang() + UUID.randomUUID(); + String dockerCommand = String.format("echo \"%s\" > main.cpp && g++ main.cpp -o main && timeout -s SIGKILL 10 ./main ; exit", code.getCode().replace("\"", "\\\"")); + ProcessBuilder pb = new ProcessBuilder() + .command("docker", "run", "--rm", "--name", containerName, "--network", "none", + "--memory", "150m", "cc-gxx:dev", "sh", "-c", dockerCommand) + .redirectErrorStream(true); + ExecResult result = new ExecResult(); + return ContainerManager.initContainer(pb, containerName, result); + } +} diff --git a/src/main/java/com/debxrshi/collabcode/service/strategy/GoExecutor.java b/src/main/java/com/debxrshi/collabcode/service/strategy/GoExecutor.java new file mode 100644 index 0000000..253e971 --- /dev/null +++ b/src/main/java/com/debxrshi/collabcode/service/strategy/GoExecutor.java @@ -0,0 +1,24 @@ +package com.debxrshi.collabcode.service.strategy; + +import com.debxrshi.collabcode.model.Code; +import com.debxrshi.collabcode.model.ExecResult; +import com.debxrshi.collabcode.service.util.ContainerManager; +import org.springframework.stereotype.Component; + +import java.util.UUID; + +@Component("go") +public class GoExecutor implements CodeExecutionStrategy { + + @Override + public ExecResult execCode(Code code) { + + String containerName = code.getLang() + UUID.randomUUID(); + String dockerCommand = String.format("echo \"%s\" > main.go && go build main.go && timeout -s SIGKILL 15 ./main ; exit", code.getCode().replace("\"", "\\\"")); + ProcessBuilder pb = new ProcessBuilder() + .command("docker", "run", "--rm", "--name", containerName, "--network", "none", "--memory", "300m", "cc-go:dev", "sh", "-c", dockerCommand) + .redirectErrorStream(true); + ExecResult result = new ExecResult(); + return ContainerManager.initContainer(pb, containerName, result); + } +} diff --git a/src/main/java/com/debxrshi/collabcode/service/strategy/JavaExecutor.java b/src/main/java/com/debxrshi/collabcode/service/strategy/JavaExecutor.java new file mode 100644 index 0000000..97ee449 --- /dev/null +++ b/src/main/java/com/debxrshi/collabcode/service/strategy/JavaExecutor.java @@ -0,0 +1,26 @@ +package com.debxrshi.collabcode.service.strategy; + +import com.debxrshi.collabcode.model.Code; +import com.debxrshi.collabcode.model.ExecResult; +import com.debxrshi.collabcode.service.util.ContainerManager; +import org.springframework.stereotype.Component; + +import java.util.UUID; + +@Component("java") +public class JavaExecutor implements CodeExecutionStrategy { + + @Override + public ExecResult execCode(Code code) { + + String containerName = code.getLang() + UUID.randomUUID(); + String dockerCommand = String.format("echo \"%s\" > Main.java && javac Main.java && timeout -s SIGKILL 10 java Main ; exit", code.getCode().replace("\"", "\\\"")); + ProcessBuilder pb = new ProcessBuilder() + .command("docker", "run", "--rm", "--network", "none", + "--memory", "150m", "cc-java:dev", "sh", "-c", dockerCommand) + .redirectErrorStream(true); + ExecResult result = new ExecResult(); + return ContainerManager.initContainer(pb, containerName, result); + } +} + diff --git a/src/main/java/com/debxrshi/collabcode/service/strategy/JavaScriptExecutor.java b/src/main/java/com/debxrshi/collabcode/service/strategy/JavaScriptExecutor.java new file mode 100644 index 0000000..32467d3 --- /dev/null +++ b/src/main/java/com/debxrshi/collabcode/service/strategy/JavaScriptExecutor.java @@ -0,0 +1,25 @@ +package com.debxrshi.collabcode.service.strategy; + +import com.debxrshi.collabcode.model.Code; +import com.debxrshi.collabcode.model.ExecResult; +import com.debxrshi.collabcode.service.util.ContainerManager; +import org.springframework.stereotype.Component; + +import java.util.UUID; + + +@Component("javascript") +public class JavaScriptExecutor implements CodeExecutionStrategy { + + @Override + public ExecResult execCode(Code code) { + + String containerName = code.getLang() + UUID.randomUUID(); + String dockerCommand = String.format("echo \"%s\" > main.js && timeout -s SIGKILL 10 node main.js ; exit", code.getCode().replace("\"", "\\\"")); + ProcessBuilder pb = new ProcessBuilder() + .command("docker", "run", "--rm", "--name", containerName, "--network", "none", "--memory", "150m", "cc-node:dev", "sh", "-c", dockerCommand) + .redirectErrorStream(true); + ExecResult result = new ExecResult(); + return ContainerManager.initContainer(pb, containerName, result); + } +} diff --git a/src/main/java/com/debxrshi/collabcode/service/strategy/PythonExecutor.java b/src/main/java/com/debxrshi/collabcode/service/strategy/PythonExecutor.java new file mode 100644 index 0000000..e6cc235 --- /dev/null +++ b/src/main/java/com/debxrshi/collabcode/service/strategy/PythonExecutor.java @@ -0,0 +1,25 @@ +package com.debxrshi.collabcode.service.strategy; + +import com.debxrshi.collabcode.model.Code; +import com.debxrshi.collabcode.model.ExecResult; +import com.debxrshi.collabcode.service.util.ContainerManager; +import org.springframework.stereotype.Component; + +import java.util.UUID; + +@Component("python") +public class PythonExecutor implements CodeExecutionStrategy { + + @Override + public ExecResult execCode(Code code) { + String containerName = code.getLang() + UUID.randomUUID(); + String dockerCommand = String.format("echo \"%s\" > a.py && timeout -s SIGKILL 10 python3 a.py ; exit", code.getCode().replace("\"", "\\\"")); + ProcessBuilder pb = new ProcessBuilder() + .command("docker", "run", "--rm", "--name", containerName, "--network", "none", + "--memory", "150m", "cc-python:dev", "sh", "-c", dockerCommand) + .redirectErrorStream(true); + ExecResult result = new ExecResult(); + return ContainerManager.initContainer(pb, containerName, result); + } +} + diff --git a/src/main/java/com/debxrshi/collabcode/service/strategy/RustExecutor.java b/src/main/java/com/debxrshi/collabcode/service/strategy/RustExecutor.java new file mode 100644 index 0000000..622f3d5 --- /dev/null +++ b/src/main/java/com/debxrshi/collabcode/service/strategy/RustExecutor.java @@ -0,0 +1,25 @@ +package com.debxrshi.collabcode.service.strategy; + +import com.debxrshi.collabcode.model.Code; +import com.debxrshi.collabcode.model.ExecResult; +import com.debxrshi.collabcode.service.util.ContainerManager; +import org.springframework.stereotype.Component; + +import java.awt.*; +import java.util.UUID; + +@Component("rust") +public class RustExecutor implements CodeExecutionStrategy { + + @Override + public ExecResult execCode(Code code){ + + String containerName = code.getLang() + UUID.randomUUID(); + String dockerCommand = String.format("echo \"%s\" > main.rs && rustc main.rs && timeout -s SIGKILL 15 ./main ; exit", code.getCode().replace("\"", "\\\"")); + ProcessBuilder pb = new ProcessBuilder() + .command("docker", "run", "--rm", "--name", containerName, "--network", "none", "--memory", "300m", "cc-rust:dev", "sh", "-c", dockerCommand) + .redirectErrorStream(true); + ExecResult result = new ExecResult(); + return ContainerManager.initContainer(pb, containerName, result); + } +} diff --git a/src/main/java/com/debxrshi/collabcode/service/util/ContainerManager.java b/src/main/java/com/debxrshi/collabcode/service/util/ContainerManager.java new file mode 100644 index 0000000..69ee8cc --- /dev/null +++ b/src/main/java/com/debxrshi/collabcode/service/util/ContainerManager.java @@ -0,0 +1,70 @@ +package com.debxrshi.collabcode.service.util; + +import com.debxrshi.collabcode.model.ExecResult; + +import java.io.BufferedReader; +import java.io.InputStreamReader; + +public class ContainerManager { + + public static String readOut(Process process, String containerName) { + + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + StringBuilder sb = new StringBuilder(); + String line; + int maxOutputSize = 100000; + while ((line = reader.readLine()) != null) { + if (sb.length() + line.length() > maxOutputSize) { + terminateContainer(containerName); + sb.append("\nYour code generates output longer than allowed limit."); + break; + } + sb.append(line); + sb.append("\n"); + } + terminateContainer(containerName); + return sb.toString(); + } catch (Exception e) { + e.printStackTrace(); + return "Error encountered during exection"; + } + } + + public static void terminateContainer(String containerName) { + + try { + ProcessBuilder pb = new ProcessBuilder() + .command("docker", "rm", "-f", containerName); + Process p = pb.start(); + p.destroy(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static ExecResult initContainer(ProcessBuilder pb, String containerName, ExecResult result) { + + try { + Process p = pb.start(); + long startTime = System.currentTimeMillis(); + String output = readOut(p, containerName); + long endTime = System.currentTimeMillis(); + float time = (float) (endTime - startTime) / 1000; + if (output.contains("Killed")) { + result.setOut("Your code took too long to execute!"); + result.setTte(time); + } else { + result.setOut(output.trim()); + result.setTte(time); + } + return result; + } catch (Exception e) { + e.printStackTrace(); + result.setOut("Error encountered during execution"); + result.setTte(0.00F); + return result; + } + } + +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..77de3a9 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,5 @@ +spring.application.name=CollabCode +spring.datasource.url=jdbc:mysql://localhost:3306/collabcode +spring.datasource.username=root +spring.datasource.password=root +spring.jpa.hibernate.ddl-auto=update diff --git a/src/test/java/com/debxrshi/collabcode/CollabCodeApplicationTests.java b/src/test/java/com/debxrshi/collabcode/CollabCodeApplicationTests.java new file mode 100644 index 0000000..533e8f2 --- /dev/null +++ b/src/test/java/com/debxrshi/collabcode/CollabCodeApplicationTests.java @@ -0,0 +1,13 @@ +package com.debxrshi.collabcode; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class CollabCodeApplicationTests { + + @Test + void contextLoads() { + } + +}
Collaborative coding platform
Or