diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..3abe3d4
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,2 @@
+node_modules
+npm-debug.log
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b512c09
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+node_modules
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..4d98a89
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,23 @@
+FROM ubuntu
+WORKDIR /usr/src/app
+COPY package*.json ./
+COPY . .
+RUN apt-get update
+RUN apt-get install curl -y
+RUN curl -sL https://deb.nodesource.com/setup_10.x | bash
+RUN apt-get install nodejs -y
+RUN npm install -y
+RUN apt-get install \
+ apt-transport-https \
+ ca-certificates \
+ curl \
+ gnupg-agent \
+ software-properties-common -y
+RUN curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
+RUN add-apt-repository \
+ "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
+ $(lsb_release -cs) \
+ stable"
+RUN apt-get update
+RUN apt-get install docker-ce docker-ce-cli containerd.io -y
+CMD ["npm","run","dev"]
diff --git a/Dockerfiles/DockerCPP b/Dockerfiles/DockerCPP
new file mode 100644
index 0000000..8d8c820
--- /dev/null
+++ b/Dockerfiles/DockerCPP
@@ -0,0 +1,4 @@
+FROM ubuntu
+WORKDIR /usr/src/app
+RUN apt-get update
+RUN apt-get install build-essential -y
diff --git a/Dockerfiles/DockerJava b/Dockerfiles/DockerJava
new file mode 100644
index 0000000..e71e2e5
--- /dev/null
+++ b/Dockerfiles/DockerJava
@@ -0,0 +1,4 @@
+FROM ubuntu
+WORKDIR /usr/src/app
+RUN apt-get update
+RUN apt-get install openjdk-8-jdk -y
diff --git a/Dockerfiles/DockerPython b/Dockerfiles/DockerPython
new file mode 100644
index 0000000..825f062
--- /dev/null
+++ b/Dockerfiles/DockerPython
@@ -0,0 +1,7 @@
+FROM ubuntu
+WORKDIR /usr/src/app
+RUN apt-get update
+RUN apt-get install software-properties-common -y
+RUN add-apt-repository ppa:deadsnakes/ppa -y
+RUN apt-get update
+RUN apt-get install python3.8 -y
diff --git a/LICENSE b/LICENSE
index 72d2fc6..408991c 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,21 +1,21 @@
-MIT License
-
-Copyright (c) 2020 Abhigyan Abhikaushalam Students' Forum
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+MIT License
+
+Copyright (c) 2020 Rajat Maheshwari
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index c2de498..d01700a 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,182 @@
-# Remote Code Executor
-- Server Side Code for a Remote Code Executor
+
+
+
+
+
Remote Code Executor
+
+
+ Server-side code of a Remote Code Executor
+
+ Report Bug
+ ยท
+ Request Feature
+
+
+
+
+
+ Table of Contents
+
+ -
+ About The Project
+
+
+ -
+ Getting Started
+
+
+ - Usage
+ - Roadmap
+ - Contributing
+ - License
+ - Contact
+
+
+
+
+
+## About The Project
+
+This is the server-side code of a Remote Code Executor. This is a project assigned by the Coding forum of my college and is similar to the Online IDEs of websites like CodeChef and Leetcode.
+
+Salient Features:
+
+- Code Sanitisation
+- An individual Docker Container is created for every code posted on the API, so no code interferes with any other code
+- All Async code so that the server can handle multiple requests without error
+
+### Built With
+
+
+
+
+
+
+## Getting Started
+
+To get a local copy up and running follow these simple steps.
+
+### Prerequisites
+
+- npm
+ ```sh
+ npm install npm@latest -g
+ ```
+- docker
+ ```sh
+ curl -fsSL https://get.docker.com -o get-docker.sh
+ ```
+ ```sh
+ sudo sh get-docker.sh
+ ```
+
+### Installation
+#### If you are on a Linux(preferably Ubuntu) Machine
+1. Clone the repo
+ ```sh
+ git clone https://github.com/rajatmaheshwari2512/remote-code-exec
+ ```
+2. Install NPM packages
+ ```sh
+ npm install
+ ```
+3. To build the docker images
+ ```sh
+ cd Dockerfiles
+ ```
+ ```sh
+ docker build -t cpp:v1 -f DockerCPP .
+ ```
+ ```sh
+ docker build -t python:v1 -f DockerPython .
+ ```
+ ```sh
+ docker build -t java:v1 -f DockerJava .
+ ```
+#### If you are on any other Machine
+1. Clone the repo
+ ```sh
+ git clone https://github.com/rajatmaheshwari2512/remote-code-exec
+ ```
+2. Install NPM packages
+ ```sh
+ npm install
+ ```
+3. Build the Server's Dockerfile
+ ```sh
+ docker build -t rceserver:v1 .
+ ```
+4. Run the Docker Image
+ ```sh
+ docker run --privileged=true -v /var/run/docker.sock:/var/run/docker.sock -d -p 3000:3000 rceserver:v1
+ ```
+5. Create a shell to the created Docker Container
+ ```sh
+ docker ps
+ ```
+ ```sh
+ docker exec -it /bin/bash
+ ```
+6. Build the Images inside the Container
+ ```sh
+ cd Dockerfiles
+ ```
+ ```sh
+ docker build -t cpp:v1 -f DockerCPP .
+ ```
+ ```sh
+ docker build -t python:v1 -f DockerPython .
+ ```
+ ```sh
+ docker build -t java:v1 -f DockerJava .
+ ```
+
+
+## Usage
+
+1. To run the server in dev mode use
+ ```sh
+ npm run dev
+ ```
+2. To run the server in production mode
+ ```sh
+ npm start
+ ```
+3. Note that dev mode uses nodemon so that the server can be changed and restarted easily
+
+
+
+## Roadmap
+
+See the [open issues](https://github.com/rajatmaheshwari2512/remote-code-exec/issues) for a list of proposed features (and known issues).
+
+
+
+## Contributing
+
+Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**.
+
+1. Fork the Project
+2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
+3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
+4. Push to the Branch (`git push origin feature/AmazingFeature`)
+5. Open a Pull Request
+
+
+
+## License
+
+Distributed under the MIT License. See `LICENSE` for more information.
+
+
+
+## Contact
+
+Rajat Maheshwari - mrajat67@yahoo.com
+
+Project Link: [https://github.com/rajatmaheshwari2512/remote-code-exec](https://github.com/rajatmaheshwari2512/remote-code-exec)
\ No newline at end of file
diff --git a/app.js b/app.js
index fa7e6d6..07dc080 100644
--- a/app.js
+++ b/app.js
@@ -4,6 +4,8 @@ var cookieParser = require("cookie-parser");
var logger = require("morgan");
var codeRouter = require("./routes/codeUpload");
var cors = require("cors");
+var swaggerUi = require("swagger-ui-express"),
+ swaggerDocument = require("./swagger.json");
var app = express();
@@ -14,6 +16,7 @@ app.use(cookieParser());
app.use(cors());
app.use("/codeupload", codeRouter);
+app.use("/", swaggerUi.serve, swaggerUi.setup(swaggerDocument));
app.use(function (req, res, next) {
next(createError(404));
diff --git a/bin/www b/bin/www
index 91605aa..e1fac40 100644
--- a/bin/www
+++ b/bin/www
@@ -51,4 +51,4 @@ function onError(error) {
function onListening() {
console.log("Server running on localhost:" + port);
-}
+}
\ No newline at end of file
diff --git a/languages/cpp.js b/languages/cpp.js
index a66720b..7ee3f47 100644
--- a/languages/cpp.js
+++ b/languages/cpp.js
@@ -2,25 +2,34 @@ const util = require("util");
const exec = util.promisify(require("child_process").exec);
var fs = require("fs");
-const cpp = (input, res) => {
- fs.writeFile("input.txt", input, (err) => {
+const cpp = (input, res, name) => {
+ fs.writeFile(`${name}.txt`, input, (err) => {
if (err) res.json({ error: err });
- exec("g++ input.cpp && ./a.out {
- res.json(result);
- exec("rm input.cpp && rm a.out").then((resp) =>
- console.log("CPP File Deleted")
- );
- exec("rm input.txt").then((resp) => console.log("Input CPP Deleted"));
- })
- .catch((err) => {
- res.json(err);
- exec("rm input.cpp").then((resp) => console.log("CPP File Deleted"));
- exec("rm input.txt").then((resp) => console.log("Input CPP Deleted"));
- });
+ exec("docker run -d -it cpp:v1 /bin/bash").then((resp) => {
+ var id = resp.stdout.substring(0, 12);
+ exec(
+ `docker cp ${name}.cpp ${id}:/usr/src/app/test.cpp && docker cp ${name}.txt ${id}:/usr/src/app/input.txt && docker exec ${id} bash -c "g++ test.cpp && ./a.out {
+ res.json(resp);
+ exec(`rm ${name}.cpp && rm ${name}.txt`).then((resp) =>
+ console.log("Files removed")
+ );
+ exec(`docker kill ${id}`).then((resp) =>
+ console.log("Container Stopped")
+ );
+ })
+ .catch((err) => {
+ res.json(err);
+ exec(`rm ${name}.cpp && rm ${name}.txt`).then((resp) =>
+ console.log("Files removed")
+ );
+ exec(`docker kill ${id}`).then((resp) =>
+ console.log("Container Stopped")
+ );
+ });
+ });
});
};
-module.exports = cpp;
+module.exports = cpp;
\ No newline at end of file
diff --git a/languages/java.js b/languages/java.js
new file mode 100644
index 0000000..15ba44c
--- /dev/null
+++ b/languages/java.js
@@ -0,0 +1,35 @@
+const util = require("util");
+const exec = util.promisify(require("child_process").exec);
+var fs = require("fs");
+
+const java = (input, res, name) => {
+ fs.writeFile(`${name}.txt`, input, (err) => {
+ if (err) res.json({ error: err });
+ exec("docker run -d -it java:v1 /bin/bash").then((resp) => {
+ var id = resp.stdout.substring(0, 12);
+ exec(
+ `docker cp ${name}.java ${id}:/usr/src/app/test.java && docker cp ${name}.txt ${id}:/usr/src/app/input.txt && docker exec ${id} bash -c "javac test.java && java test {
+ res.json(resp);
+ exec(`rm ${name}.java && rm ${name}.txt`).then((resp) =>
+ console.log("Files removed")
+ );
+ exec(`docker kill ${id}`).then((resp) =>
+ console.log("Container Stopped")
+ );
+ })
+ .catch((err) => {
+ res.json(err);
+ exec(`rm ${name}.java && rm ${name}.txt`).then((resp) =>
+ console.log("Files removed")
+ );
+ exec(`docker kill ${id}`).then((resp) =>
+ console.log("Container Stopped")
+ );
+ });
+ });
+ });
+};
+module.exports = java;
diff --git a/languages/python.js b/languages/python.js
index ceb06bc..fef4e67 100644
--- a/languages/python.js
+++ b/languages/python.js
@@ -2,23 +2,34 @@ const util = require("util");
const exec = util.promisify(require("child_process").exec);
var fs = require("fs");
-const python = (input, res) => {
- fs.writeFile("input.txt", input, (err) => {
+const python = (input, res, name) => {
+ fs.writeFile(`${name}.txt`, input, (err) => {
if (err) res.json({ error: err });
- exec("python input.py {
- res.json(result);
- exec("rm input.py").then((resp) => console.log("PY File Deleted"));
- exec("rm input.txt").then((resp) => console.log("Input PY Deleted"));
- })
- .catch((err) => {
- res.json(err);
- exec("rm input.py").then((resp) => console.log("PY File Deleted"));
- exec("rm input.txt").then((resp) => console.log("Input PY Deleted"));
- });
+ exec("docker run -d -it python:v1 /bin/bash").then((resp) => {
+ var id = resp.stdout.substring(0, 12);
+ exec(
+ `docker cp ${name}.py ${id}:/usr/src/app/test.py && docker cp ${name}.txt ${id}:/usr/src/app/input.txt && docker exec ${id} bash -c "python3 test.py {
+ res.json(resp);
+ exec(`rm ${name}.py && rm ${name}.txt`).then((resp) =>
+ console.log("Files removed")
+ );
+ exec(`docker kill ${id}`).then((resp) =>
+ console.log("Container Stopped")
+ );
+ })
+ .catch((err) => {
+ res.json(err);
+ exec(`rm ${name}.py && rm ${name}.txt`).then((resp) =>
+ console.log("Files removed")
+ );
+ exec(`docker kill ${id}`).then((resp) =>
+ console.log("Container Stopped")
+ );
+ });
+ });
});
};
module.exports = python;
diff --git a/package-lock.json b/package-lock.json
index f5114fb..33e5c97 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -86,6 +86,11 @@
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
+ "array-uniq": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.2.tgz",
+ "integrity": "sha1-X8w3OSB3VyPP1k1lxkvvU7+eum0="
+ },
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
@@ -1037,6 +1042,14 @@
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
},
+ "randomstring": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/randomstring/-/randomstring-1.1.5.tgz",
+ "integrity": "sha1-bfBij3XL1ZMpMNn+OrTpVqGFGMM=",
+ "requires": {
+ "array-uniq": "1.0.2"
+ }
+ },
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@@ -1234,6 +1247,19 @@
"has-flag": "^3.0.0"
}
},
+ "swagger-ui-dist": {
+ "version": "3.38.0",
+ "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.38.0.tgz",
+ "integrity": "sha512-sselV8VY6f1BBauY9Sdmwz0jVaWTnGuHQWei7BaTpiUrLcoEUdmmK5bKefLXiwq+dx//es2S8mOvUS+tcXDsKg=="
+ },
+ "swagger-ui-express": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.1.6.tgz",
+ "integrity": "sha512-Xs2BGGudvDBtL7RXcYtNvHsFtP1DBFPMJFRxHe5ez/VG/rzVOEjazJOOSc/kSCyxreCTKfJrII6MJlL9a6t8vw==",
+ "requires": {
+ "swagger-ui-dist": "^3.18.1"
+ }
+ },
"term-size": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz",
@@ -1385,4 +1411,4 @@
"dev": true
}
}
-}
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index 3265b08..4d5b972 100644
--- a/package.json
+++ b/package.json
@@ -12,9 +12,11 @@
"debug": "~2.6.9",
"express": "~4.16.1",
"http-errors": "~1.6.3",
- "morgan": "~1.9.1"
+ "morgan": "~1.9.1",
+ "randomstring": "^1.1.5",
+ "swagger-ui-express": "^4.1.6"
},
"devDependencies": {
"nodemon": "^2.0.6"
}
-}
+}
\ No newline at end of file
diff --git a/routes/codeUpload.js b/routes/codeUpload.js
index 5cf9561..4815d1d 100644
--- a/routes/codeUpload.js
+++ b/routes/codeUpload.js
@@ -1,10 +1,17 @@
var express = require("express");
var fs = require("fs");
+const util = require("util");
+const exec = util.promisify(require("child_process").exec);
+var randomstring = require("randomstring");
var cppRun = require("../languages/cpp");
var pythonRun = require("../languages/python");
+var javaRun = require("../languages/java");
var { languageCode } = require("../shared/languageCode");
var { cppList } = require("../shared/blacklist");
+var { pythonList } = require("../shared/blacklist");
+var { javaList } = require("../shared/blacklist");
+var validate = require("../shared/validate");
var router = express.Router();
@@ -16,13 +23,38 @@ router
const code = req.body.code;
const langid = req.body.langid;
const input = req.body.input;
- fs.writeFile(`input.${languageCode[langid]}`, code, (err) => {
+ var name = randomstring.generate({
+ length: 7,
+ charset: "alphabetic",
+ });
+ fs.writeFile(`${name}.${languageCode[langid]}`, code, (err) => {
if (err) res.json({ error: err });
if (langid == 1) {
- cppRun(input, res);
+ if (validate(cppList, code)) {
+ cppRun(input, res, name);
+ } else {
+ res.json({ error: "invalid code" });
+ exec(`rm ${name}.cpp`).then((resp) =>
+ console.log("Input CPP Deleted")
+ );
+ }
} else if (langid == 2) {
- pythonRun(input, res);
+ if (validate(pythonList, code)) {
+ pythonRun(input, res, name);
+ } else {
+ res.json({ error: "invalid code" });
+ exec(`rm ${name}.py`).then((resp) => console.log("Input PY Deleted"));
+ }
+ } else if (langid == 3) {
+ if (validate(javaList, code)) {
+ javaRun(input, res, name);
+ } else {
+ res.json({ error: "invalid code" });
+ exec(`rm ${name}.java`).then((resp) =>
+ console.log("Input JAVA Deleted")
+ );
+ }
}
});
});
-module.exports = router;
+module.exports = router;
\ No newline at end of file
diff --git a/shared/blacklist.js b/shared/blacklist.js
index 2a6a466..b1da914 100644
--- a/shared/blacklist.js
+++ b/shared/blacklist.js
@@ -1,4 +1,12 @@
-var pythonList = ["os", "subprocess"];
-var cppList = ["bits/stdc++.h", "stdlib.h"];
+var pythonList = ["import os", "import subprocess"];
+var cppList = ["popen", "fork", "system(", "unistd.h", "exec"];
+var javaList = [
+ "Process",
+ "getRuntime()",
+ "exec(",
+ "ProcessBuilder",
+ "start()",
+];
exports.pythonList = pythonList;
exports.cppList = cppList;
+exports.javaList = javaList;
\ No newline at end of file
diff --git a/shared/languageCode.js b/shared/languageCode.js
index e475aec..16e0914 100644
--- a/shared/languageCode.js
+++ b/shared/languageCode.js
@@ -1,4 +1,5 @@
var languageCode = new Map();
languageCode[1] = "cpp";
languageCode[2] = "py";
-exports.languageCode = languageCode;
+languageCode[3] = "java";
+exports.languageCode = languageCode;
\ No newline at end of file
diff --git a/shared/validate.js b/shared/validate.js
new file mode 100644
index 0000000..073ffca
--- /dev/null
+++ b/shared/validate.js
@@ -0,0 +1,11 @@
+const fs = require("fs");
+
+const validate = (list, code) => {
+ for (let i = 0; i < list.length; i++) {
+ if (code.includes(list[i])) {
+ return false;
+ }
+ }
+ return true;
+};
+module.exports = validate;
diff --git a/swagger.json b/swagger.json
new file mode 100644
index 0000000..24447a3
--- /dev/null
+++ b/swagger.json
@@ -0,0 +1,99 @@
+{
+ "swagger":"2.0",
+ "info":{
+ "version":"1.0.0",
+ "title":"API Explorer for the Remote Code Executor",
+ "description":"API Sandbox for you to familiarize yourself with the API of this server",
+ "license":{
+ "name":"MIT",
+ "url":"https://opensource.org/licenses/MIT"
+ }
+ },
+ "host":"localhost:3000",
+ "basePath":"/",
+ "schemes":[
+ "http"
+ ],
+ "consumes":[
+ "application/json"
+ ],
+ "produces":[
+ "application/json"
+ ],
+ "paths":{
+ "/codeUpload":{
+ "get":{
+ "tags":[
+ "codeUpload"
+ ],
+ "summary":"Check Health of the API and working status of the server",
+ "responses":{
+ "200":{
+ "description":"OK",
+ "schema":{
+ "$ref":"#/definitions/Health"
+ }
+ }
+ }
+ },
+ "post":{
+ "tags":[
+ "codeUpload"
+ ],
+ "parameters":[
+ {
+ "name":"Input Body",
+ "in":"body",
+ "description":"The Body of the Request",
+ "schema":{
+ "$ref":"#/definitions/Code"
+ }
+ }
+ ],
+ "summary":"Post the Language, the Code and the Input at this endpoint",
+ "responses":{
+ "200":{
+ "description":"OK",
+ "schema":{
+ "$ref":"#/definitions/Response"
+ }
+ }
+ }
+ }
+ }
+ },
+ "definitions":{
+ "Response":{
+ "properties":{
+ "stdout":{
+ "type":"string"
+ },
+ "stderr":{
+ "type":"string"
+ }
+ }
+ },
+ "Health":{
+ "properties":{
+ "APIWorking":{
+ "type":"boolean",
+ "default":true
+ }
+ }
+ },
+ "Code":{
+ "properties":{
+ "langid":{
+ "type":"integer",
+ "default":2
+ },
+ "code":{
+ "type":"string"
+ },
+ "input":{
+ "type":"string"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file