diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..287a2f0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,162 @@ +### Python template +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/JetClient/state.xml b/.idea/JetClient/state.xml new file mode 100644 index 0000000..cf4802b --- /dev/null +++ b/.idea/JetClient/state.xml @@ -0,0 +1,38 @@ + + + + + + \ No newline at end of file diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..4450099 --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,12 @@ + + + + + redis + true + jdbc.RedisDriver + jdbc:redis://localhost:6379/0 + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..ae51011 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,36 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..c026b2b --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..89705fc --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/peetcode.iml b/.idea/peetcode.iml new file mode 100644 index 0000000..26d44c7 --- /dev/null +++ b/.idea/peetcode.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/client/src/Constants/Navbar/Navbar.jsx b/client/src/Constants/Navbar/Navbar.jsx index 28ecfd1..e42f70e 100644 --- a/client/src/Constants/Navbar/Navbar.jsx +++ b/client/src/Constants/Navbar/Navbar.jsx @@ -9,7 +9,7 @@ const Navbar = () => {
logo -

PeetCode

+

NeetCode

@@ -25,4 +25,4 @@ const Navbar = () => { ) } -export default Navbar +export default Navbar \ No newline at end of file diff --git a/client/src/constants.js b/client/src/constants.js index 2a84056..08f307f 100644 --- a/client/src/constants.js +++ b/client/src/constants.js @@ -1 +1 @@ -export const backendUrl = "https://api.peetcode.com"; +export const backendUrl = "https://localhost:3000"; diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..cae562a --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,55 @@ +version: "2.4" #pids_limit are not available in version 3 or higher + +services: + rabbitmq: + image: rabbitmq:3.11-management + restart: always +# not necessary to expose ports, because they are already exposed in the image + ports: + - "5672:5672" + - "15672:15672" + networks: + - common + healthcheck: + test: ["CMD", "rabbitmqctl", "status"] + interval: 5s + timeout: 30s + retries: 3 + + redis: + image: redis + restart: always + networks: + - common + ports: + - "6379:6379" + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 30s + retries: 3 + + workers: + build: + context: ./workers + dockerfile: Dockerfile + environment: + - REDIS_HOST=redis + - REDIS_PORT=6379 + - RABBITMQ_HOST=rabbitmq + depends_on: + rabbitmq: + condition: service_healthy + redis: + condition: service_healthy + networks: + - common + pids_limit: 100 + cpus: 0.5 + mem_limit: 100m + + +networks: + common: + driver: bridge + diff --git a/server/index.js b/server/index.js index 9691a83..80b9fca 100644 --- a/server/index.js +++ b/server/index.js @@ -213,4 +213,4 @@ app.post("/login", (req, res) => { app.listen(port, () => { console.log(`Example app listening on port ${port}`); -}); +}); \ No newline at end of file diff --git a/server/package-lock.json b/server/package-lock.json index 55cd30c..c23c4eb 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -9,10 +9,104 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "amqplib": "^0.10.3", "body-parser": "^1.20.2", "cors": "^2.8.5", "express": "^4.18.2", - "jsonwebtoken": "^9.0.0" + "jsonwebtoken": "^9.0.0", + "redis": "^4.6.6" + } + }, + "node_modules/@acuminous/bitsyntax": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@acuminous/bitsyntax/-/bitsyntax-0.1.2.tgz", + "integrity": "sha512-29lUK80d1muEQqiUsSo+3A0yP6CdspgC95EnKBMi22Xlwt79i/En4Vr67+cXhU+cZjbti3TgGGC5wy1stIywVQ==", + "dependencies": { + "buffer-more-ints": "~1.0.0", + "debug": "^4.3.4", + "safe-buffer": "~5.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/@acuminous/bitsyntax/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@acuminous/bitsyntax/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/@acuminous/bitsyntax/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/client": { + "version": "1.5.7", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.7.tgz", + "integrity": "sha512-gaOBOuJPjK5fGtxSseaKgSvjiZXQCdLlGg9WYQst+/GRUjmXaiB5kVkeQMRtPc7Q2t93XZcJfBMSwzs/XS9UZw==", + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@redis/graph": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.0.tgz", + "integrity": "sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/json": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.4.tgz", + "integrity": "sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/search": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.2.tgz", + "integrity": "sha512-/cMfstG/fOh/SsE+4/BQGeuH/JJloeWuH+qJzM8dbxuWvdWibWAOAHHCZTMPhV3xIlH4/cUEIA8OV5QnYpaVoA==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/time-series": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.4.tgz", + "integrity": "sha512-ThUIgo2U/g7cCuZavucQTQzA9g9JbDDY2f64u3AbAoz/8vE2lt2U37LamDUVChhaDA3IRT9R6VvJwqnUfTJzng==", + "peerDependencies": { + "@redis/client": "^1.0.0" } }, "node_modules/accepts": { @@ -27,6 +121,20 @@ "node": ">= 0.6" } }, + "node_modules/amqplib": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.10.3.tgz", + "integrity": "sha512-UHmuSa7n8vVW/a5HGh2nFPqAEr8+cD4dEZ6u9GjP91nHfr1a54RyAKyra7Sb5NH7NBKOUlyQSMXIp0qAixKexw==", + "dependencies": { + "@acuminous/bitsyntax": "^0.1.2", + "buffer-more-ints": "~1.0.0", + "readable-stream": "1.x >=1.1.9", + "url-parse": "~1.5.10" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -60,6 +168,11 @@ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" }, + "node_modules/buffer-more-ints": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz", + "integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==" + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -80,6 +193,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -112,6 +233,11 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -299,6 +425,14 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "engines": { + "node": ">= 4" + } + }, "node_modules/get-intrinsic": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", @@ -373,6 +507,11 @@ "node": ">= 0.10" } }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, "node_modules/jsonwebtoken": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", @@ -558,6 +697,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -580,6 +724,35 @@ "node": ">= 0.8" } }, + "node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/redis": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.6.tgz", + "integrity": "sha512-aLs2fuBFV/VJ28oLBqYykfnhGGkFxvx0HdCEBYdJ99FFbSEMZ7c1nVKwR6ZRv+7bb7JnC0mmCzaqu8frgOYhpA==", + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.5.7", + "@redis/graph": "1.1.0", + "@redis/json": "1.0.4", + "@redis/search": "1.1.2", + "@redis/time-series": "1.0.4" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -686,6 +859,11 @@ "node": ">= 0.8" } }, + "node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -714,6 +892,15 @@ "node": ">= 0.8" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -737,6 +924,76 @@ } }, "dependencies": { + "@acuminous/bitsyntax": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@acuminous/bitsyntax/-/bitsyntax-0.1.2.tgz", + "integrity": "sha512-29lUK80d1muEQqiUsSo+3A0yP6CdspgC95EnKBMi22Xlwt79i/En4Vr67+cXhU+cZjbti3TgGGC5wy1stIywVQ==", + "requires": { + "buffer-more-ints": "~1.0.0", + "debug": "^4.3.4", + "safe-buffer": "~5.1.2" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "requires": {} + }, + "@redis/client": { + "version": "1.5.7", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.7.tgz", + "integrity": "sha512-gaOBOuJPjK5fGtxSseaKgSvjiZXQCdLlGg9WYQst+/GRUjmXaiB5kVkeQMRtPc7Q2t93XZcJfBMSwzs/XS9UZw==", + "requires": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + } + }, + "@redis/graph": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.0.tgz", + "integrity": "sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==", + "requires": {} + }, + "@redis/json": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.4.tgz", + "integrity": "sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==", + "requires": {} + }, + "@redis/search": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.2.tgz", + "integrity": "sha512-/cMfstG/fOh/SsE+4/BQGeuH/JJloeWuH+qJzM8dbxuWvdWibWAOAHHCZTMPhV3xIlH4/cUEIA8OV5QnYpaVoA==", + "requires": {} + }, + "@redis/time-series": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.4.tgz", + "integrity": "sha512-ThUIgo2U/g7cCuZavucQTQzA9g9JbDDY2f64u3AbAoz/8vE2lt2U37LamDUVChhaDA3IRT9R6VvJwqnUfTJzng==", + "requires": {} + }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -746,6 +1003,17 @@ "negotiator": "0.6.3" } }, + "amqplib": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.10.3.tgz", + "integrity": "sha512-UHmuSa7n8vVW/a5HGh2nFPqAEr8+cD4dEZ6u9GjP91nHfr1a54RyAKyra7Sb5NH7NBKOUlyQSMXIp0qAixKexw==", + "requires": { + "@acuminous/bitsyntax": "^0.1.2", + "buffer-more-ints": "~1.0.0", + "readable-stream": "1.x >=1.1.9", + "url-parse": "~1.5.10" + } + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -775,6 +1043,11 @@ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" }, + "buffer-more-ints": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz", + "integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==" + }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -789,6 +1062,11 @@ "get-intrinsic": "^1.0.2" } }, + "cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==" + }, "content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -812,6 +1090,11 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, "cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -966,6 +1249,11 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==" + }, "get-intrinsic": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", @@ -1019,6 +1307,11 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, "jsonwebtoken": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", @@ -1157,6 +1450,11 @@ "side-channel": "^1.0.4" } }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -1173,6 +1471,35 @@ "unpipe": "1.0.0" } }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "redis": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.6.tgz", + "integrity": "sha512-aLs2fuBFV/VJ28oLBqYykfnhGGkFxvx0HdCEBYdJ99FFbSEMZ7c1nVKwR6ZRv+7bb7JnC0mmCzaqu8frgOYhpA==", + "requires": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.5.7", + "@redis/graph": "1.1.0", + "@redis/json": "1.0.4", + "@redis/search": "1.1.2", + "@redis/time-series": "1.0.4" + } + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -1249,6 +1576,11 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + }, "toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -1268,6 +1600,15 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", diff --git a/server/package.json b/server/package.json index 6c77591..ecf1aaf 100644 --- a/server/package.json +++ b/server/package.json @@ -9,9 +9,11 @@ "author": "", "license": "ISC", "dependencies": { + "amqplib": "^0.10.3", "body-parser": "^1.20.2", "cors": "^2.8.5", "express": "^4.18.2", - "jsonwebtoken": "^9.0.0" + "jsonwebtoken": "^9.0.0", + "redis": "^4.6.6" } } diff --git a/workers/Config/__init__.py b/workers/Config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workers/Config/rabbitmq.py b/workers/Config/rabbitmq.py new file mode 100644 index 0000000..0956ed3 --- /dev/null +++ b/workers/Config/rabbitmq.py @@ -0,0 +1,65 @@ +from json import JSONDecodeError + +import pika +from .redis import set +import json + + +class RabbitMQ: + connection = None + + def __init__(self, queue, callback, host='localhost', durable=True): + self.queue = queue + self.connection = pika.BlockingConnection(pika.ConnectionParameters(host=host)) + self.channel = self.connection.channel() + self.channel.queue_declare(queue=self.queue, durable=durable) + self.callback = callback + + if self.connection is None: + print("Connection not established") + + def __del__(self): + if self.connection is not None: + self.connection.close() + + def _callback(self, ch, method, properties, body): + if body is None: + return + try: + res = self.callback(body) + if res: + print(res) + self.ack(method) + except JSONDecodeError: + print("JSONDecodeError") + set('submission:' + str(body['user_id']) + ':' + str(body['problem_id']), json.dumps({"status": "Failed", "message": "Internal Error"})) + + except Exception as e: + print(e) + body = json.loads(body) + print(body) + set('submission:' + str(body['user_id']) + ':' + str(body['problem_id']), json.dumps({"status": "Failed", "message": "Internal Error"})) + self.ack(method) + + def send(self, message): + self.channel.basic_publish(exchange='', routing_key=self.queue, body=message) + print(" [x] Sent %r" % message) + + def consume(self): + self.channel.basic_consume(queue=self.queue, on_message_callback=self._callback) + print(' [*] Waiting for messages. To exit press CTRL+C') + self.channel.start_consuming() + + def receive_once(self): + method_frame, header_frame, body = self.channel.basic_get(queue=self.queue, auto_ack=True) + if method_frame: + print(method_frame, header_frame, body) + self.channel.basic_ack(method_frame.delivery_tag) + else: + print('No message returned') + + def ack(self, method_frame): + self.channel.basic_ack(delivery_tag=method_frame.delivery_tag) + + def close(self): + self.connection.close() diff --git a/workers/Config/redis.py b/workers/Config/redis.py new file mode 100644 index 0000000..459ed2d --- /dev/null +++ b/workers/Config/redis.py @@ -0,0 +1,35 @@ +import redis +import os + +REDIS_HOST = os.getenv('REDIS_HOST', 'localhost') +REDIS_PORT = os.getenv('REDIS_PORT', 6379) +REDIS_DB = os.getenv('REDIS_DB', 0) +REDIS_PASSWORD = os.getenv('REDIS_PASSWORD', None) + +r = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, db=REDIS_DB, password=REDIS_PASSWORD, decode_responses=True) +if not r.ping(): + raise Exception("Redis is not running") + + +def get(key): + return r.get(key) + + +def set(key, value): + return r.set(key, value) + + +def delete(key): + return r.delete(key) + + +def exists(key): + return r.exists(key) + + +def keys(pattern): + return r.keys(pattern) + + +def flushdb(): + return r.flushdb() diff --git a/workers/Dockerfile b/workers/Dockerfile new file mode 100644 index 0000000..abb64ba --- /dev/null +++ b/workers/Dockerfile @@ -0,0 +1,26 @@ +FROM ubuntu:latest + +WORKDIR /app + +RUN apt-get update && apt-get install -y \ + python3 \ + python3-pip + +RUN pip3 install --upgrade pip + +COPY requirements.txt /app/requirements.txt + +RUN pip3 install -r requirements.txt + +RUN apt-get install -y openjdk-18-jdk sudo + +COPY . /app + +RUN chmod 700 / + +RUN mkdir -p temp +RUN chmod 755 -R /app/temp +RUN adduser --disabled-password --gecos '' peetcode + + +CMD ["python3", "worker.py"] diff --git a/workers/Readme.md b/workers/Readme.md new file mode 100644 index 0000000..1a119a5 --- /dev/null +++ b/workers/Readme.md @@ -0,0 +1,39 @@ + # Code Execution service + +## Architecture +![img.png](img.png) + +## Description + +This implementation is very crude and done for the sake of learning. +C, C++, Java and python are supported +Requires to strictly follow queue schema which is + +```json +{ + "lang": "java", + "code": "import java.util.Scanner; public class Main {\n public static void main(String[] args) {\n int a,b; Scanner sc = new Scanner(System.in); a = sc.nextInt(); b = sc.nextInt(); System.out.println(a+b); }\n}", + "input": "1 2", + "expected_output": "3", + "problem_id": 1, + "user_id": 1 +} +``` + +A test Implementation can be found in `testProducer.py` + +I have not integrated this with the main project i.e. server and client due to my inexperienced with js, although I believe it should be easy to do so. + +## How to run + +```bash +docker-compose up --build +``` + +# How to access saved data + +all workers use userid and problemid passed in data to save in redis + +default key format is: `submission::` which can be accessed using redis library in any language + + diff --git a/workers/RunCode/__init__.py b/workers/RunCode/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workers/RunCode/fs.py b/workers/RunCode/fs.py new file mode 100644 index 0000000..fde7b5d --- /dev/null +++ b/workers/RunCode/fs.py @@ -0,0 +1,11 @@ +import os + +TEMP_DIR_PATH = os.getenv('TEMP_DIR_PATH', 'temp') + + +def init(): + os.makedirs(TEMP_DIR_PATH, exist_ok=True) + + +def cleanup(): + os.system('rm -rf ' + TEMP_DIR_PATH + '/*') diff --git a/workers/RunCode/run.py b/workers/RunCode/run.py new file mode 100644 index 0000000..20420a2 --- /dev/null +++ b/workers/RunCode/run.py @@ -0,0 +1,125 @@ +import os +import subprocess +import resource +import time + +USER = "sudo -u peetcode" + +ERROR_CODES = { + '200': 'Success', + 'compile_error': 'Compile Error', + 'runtime_error': 'Runtime Error', + 'time_limit_exceeded': 'Time Limit Exceeded', + 'memory_limit_exceeded': 'Memory Limit Exceeded', + 'output_limit_exceeded': 'Output Limit Exceeded', + 'wrong_answer': 'Wrong Answer', + 'unknown_error': 'Unknown Error', + 'language_not_supported': 'Language Not Supported', +} + +LANG_EXT_MAP = { + 'python': 'py', + 'c': 'c', + 'cpp': 'cpp', + 'java': 'java', + 'javascript': 'js', +} + +LANG_COMPILE_MAP = { + 'python': '200', + 'c': 'gcc', + 'cpp': 'g++', + 'java': 'javac', + 'javascript': '200', +} + +LANG_RUN_MAP = { + 'python': 'python3', + 'c': './a.out', + 'cpp': './a.out', + 'java': 'java', + 'javascript': 'node', +} + +cwd = os.environ.get('TEMP_DIR_PATH', 'temp') + + +def compile(lang, filename): + if lang == 'python' or lang == 'javascript': + return { + "status": "200", + "message": "Success" + } + + command = LANG_COMPILE_MAP[lang] + ' ' + filename + command = USER + ' ' + command + + stdout = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd) + if stdout.returncode != 0: + return { + "status": "compile_error", + "message": stdout.stderr.decode('utf-8') + } + + return { + "status": "200", + "message": stdout.stdout.decode('utf-8') + } + + +def run_with_timeout(command, input, time_limit): + process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + stdin=subprocess.PIPE, cwd=cwd) + start_time = time.time() + while process.poll() is None: + if time.time() - start_time > time_limit: + process.kill() + raise subprocess.TimeoutExpired(command, time_limit) + + stdout, stderr = process.communicate(input=input.encode('utf-8')) + + if process.returncode != 0: + error = stderr.decode('utf-8') + if "Killed" in error: + return { + "status": "memory_limit_exceeded", + "message": "Memory Limit Exceeded" + } + if "Resource temporarily unavailable" in error: + return { + "status": "output_limit_exceeded", + "message": "No Fork Bombs >:(" + } + return { + "status": "runtime_error", + "message": stderr.decode('utf-8') + } + + return { + "status": "200", + "message": stdout.decode('utf-8') + } + + +def run(lang, filename, input, time_limit): + command = LANG_RUN_MAP[lang] + ' ' + filename + command = USER + ' ' + command + input = input if input is not None else '' + + try: + stdout = run_with_timeout(command, input, time_limit) + except subprocess.TimeoutExpired: + return { + "status": "time_limit_exceeded", + "message": "Time Limit Exceeded" + } + + return stdout + + +def match_output(output, expected_output): + output = output.strip() + expected_output = expected_output.strip() + if output == expected_output: + return True + return False diff --git a/workers/img.png b/workers/img.png new file mode 100644 index 0000000..35cc7db Binary files /dev/null and b/workers/img.png differ diff --git a/workers/requirements.txt b/workers/requirements.txt new file mode 100644 index 0000000..0b803f0 --- /dev/null +++ b/workers/requirements.txt @@ -0,0 +1,5 @@ +pika==1.3.2 +pip==22.3.1 +setuptools==65.5.1 +wheel==0.38.4 +redis \ No newline at end of file diff --git a/workers/testProducer.py b/workers/testProducer.py new file mode 100644 index 0000000..f91be01 --- /dev/null +++ b/workers/testProducer.py @@ -0,0 +1,16 @@ +import pika +import json +connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) +channel = connection.channel() +# channel.queue_declare(queue='code_submission_queue') +message = { + "lang": "java", + "code": "import java.util.Scanner; public class Main {\n public static void main(String[] args) {\n int a,b; Scanner sc = new Scanner(System.in); a = sc.nextInt(); b = sc.nextInt(); System.out.println(a+b); }\n}", + "input": "1 2", + "expected_output": "3", + "problem_id": 1, + "user_id": 1 +} +channel.basic_publish(exchange='', routing_key='code_submission', body=json.dumps(message)) +connection.close() +print(" [x] Sent 'Hello World!'") \ No newline at end of file diff --git a/workers/worker.py b/workers/worker.py new file mode 100644 index 0000000..0061551 --- /dev/null +++ b/workers/worker.py @@ -0,0 +1,77 @@ +from Config.rabbitmq import RabbitMQ +from Config.redis import get, set, delete, exists, keys +from RunCode.run import compile, run, match_output, LANG_EXT_MAP +from RunCode.fs import init, cleanup, TEMP_DIR_PATH + +import json +import os + +TIMEOUT = 2 # seconds + +def callback(body): + body = json.loads(body) + required_fields = ['lang', 'code', 'problem_id', 'user_id'] + for field in required_fields: + if field not in body: + return { + "status": "400", + "message": "Bad Request" + } + + cleanup() + print(body) + lang = body['lang'] + code = body['code'] + input = body['input'] if 'input' in body else None + expected_output = body['expected_output'] if 'expected_output' in body else None + problem_id = body['problem_id'] + user_id = body['user_id'] + set('submission:' + str(user_id) + ':' + str(problem_id), json.dumps({"status": "running", "message": "Running"})) + + filename = 'Main.' + LANG_EXT_MAP[lang] + with open(TEMP_DIR_PATH + '/' + filename, 'w') as f: + f.write(code) + + compile_status = compile(lang, filename) + if compile_status['status'] != '200': + set('submission:' + str(user_id) + ':' + str(problem_id), json.dumps(compile_status)) + return compile_status + + run_status = run(lang, filename, input, TIMEOUT) + if run_status['status'] != '200': + set('submission:' + str(user_id) + ':' + str(problem_id), json.dumps(run_status)) + return run_status + + if expected_output is not None: + if not match_output(run_status['message'], expected_output): + status = { + "status": "wrong_answer", + "message": "Wrong Answer" + } + + set('submission:' + str(user_id) + ':' + str(problem_id), json.dumps(status)) + return status + + status = { + "status": "correct_answer", + "message": "Correct Answer" + } + set('submission:' + str(user_id) + ':' + str(problem_id), json.dumps(status)) + return status + + +def main(): + init() + host = os.environ.get('RABBITMQ_HOST', 'localhost') + print(host) + rabbitmq = RabbitMQ("code_submission", callback, host=host) + try: + rabbitmq.consume() + cleanup() + except KeyboardInterrupt: + cleanup() + rabbitmq.close() + + +if __name__ == '__main__': + main()