From cbd8a93909e98bcc9608201f4265e8857359a65a Mon Sep 17 00:00:00 2001 From: Eugene Pankov Date: Mon, 26 Jul 2021 17:56:35 +0200 Subject: [PATCH] wip --- backend/.flake8 => .flake8 | 0 backend/Dockerfile | 5 +- backend/cloudbuild.yaml | 2 + backend/gunicorn.conf.py | 3 +- backend/poetry.lock | 279 ++---------------- backend/pyproject.toml | 3 +- backend/tabby/app/api.py | 24 +- .../tabby/app/{consumers.py => gateway.py} | 33 --- .../app/management/commands/add_version.py | 18 +- backend/tabby/app/urls.py | 9 +- backend/tabby/app/views.py | 17 +- backend/tabby/asgi.py | 8 +- backend/tabby/settings.py | 6 +- frontend/src/server.ts | 2 +- frontend/webpack.config.js | 2 +- 15 files changed, 84 insertions(+), 327 deletions(-) rename backend/.flake8 => .flake8 (100%) rename backend/tabby/app/{consumers.py => gateway.py} (75%) diff --git a/backend/.flake8 b/.flake8 similarity index 100% rename from backend/.flake8 rename to .flake8 diff --git a/backend/Dockerfile b/backend/Dockerfile index 9fd41f6..ea0d866 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,15 +1,16 @@ # syntax=docker/dockerfile:1 FROM python:3.7-alpine AS build +ARG EXTRA_DEPS RUN apk add build-base musl-dev libffi-dev openssl-dev mariadb-dev WORKDIR /app -RUN pip install -U setuptools 'cryptography>=3.0,<3.1' poetry==1.1.7 +RUN pip install -U setuptools 'cryptography>=3.0,<3.1' poetry==1.1.7 ${EXTRA_DEPS} COPY pyproject.toml poetry.lock ./ RUN poetry config virtualenvs.create false RUN poetry install --no-dev --no-ansi --no-interaction FROM node:12-alpine AS package WORKDIR /app -COPY --from=0 /usr /usr +COPY --from=0 /usr/local /usr/local COPY start.sh manage.py gunicorn.conf.py ./ COPY tabby tabby ENTRYPOINT ./start.sh diff --git a/backend/cloudbuild.yaml b/backend/cloudbuild.yaml index ff28f7e..9d90fee 100644 --- a/backend/cloudbuild.yaml +++ b/backend/cloudbuild.yaml @@ -7,6 +7,8 @@ steps: - '${_DOCKER_TAG}' - '--cache-from' - '${_DOCKER_TAG}' + - '--build-arg' + - 'EXTRA_DEPS=${_EXTRA_DEPS}' - '.' images: ['${_DOCKER_TAG}'] diff --git a/backend/gunicorn.conf.py b/backend/gunicorn.conf.py index f5871b8..6de1954 100644 --- a/backend/gunicorn.conf.py +++ b/backend/gunicorn.conf.py @@ -1,6 +1,5 @@ -wsgi_app = "tabby.asgi:application" +wsgi_app = "tabby.wsgi:application" workers = 4 -worker_class = 'uvicorn.workers.UvicornWorker' preload_app = True sendfile = True diff --git a/backend/poetry.lock b/backend/poetry.lock index c0e96fb..1c907b8 100644 --- a/backend/poetry.lock +++ b/backend/poetry.lock @@ -26,31 +26,6 @@ docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] -[[package]] -name = "autobahn" -version = "21.2.1" -description = "WebSocket client & server library, WAMP real-time framework" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -cryptography = ">=2.9.2" -hyperlink = ">=20.0.1" -txaio = ">=20.4.1" - -[package.extras] -accelerate = ["wsaccel (>=0.6.2)"] -all = ["zope.interface (>=3.6.0)", "twisted (>=20.3.0)", "attrs (>=19.2.0)", "wsaccel (>=0.6.2)", "python-snappy (>=0.5)", "msgpack (>=0.6.1)", "ujson (>=1.35)", "cbor2 (>=5.0.1)", "cbor (>=1.0.0)", "py-ubjson (>=0.8.4)", "flatbuffers (>=1.10)", "pyopenssl (>=16.2.0)", "service-identity (>=18.1.0)", "pynacl (>=1.0.1)", "pytrie (>=0.2)", "pyqrcode (>=1.1)", "cffi (>=1.11.5)", "argon2-cffi (>=18.1.0)", "passlib (>=1.7.1)", "xbr (>=20.1.1)", "cbor2 (>=5.1.0)", "zlmdb (>=20.4.1)", "web3 (>=4.8.1)", "jinja2 (>=2.11.2)", "rlp (>=2.0.1)", "py-eth-sig-utils (>=0.4.0)", "py-ecc (>=1.7.1)", "eth-abi (>=1.3.0)", "mnemonic (>=0.13)", "base58 (>=1.0.2,<2.0)", "ecdsa (>=0.13)", "py-multihash (>=0.2.3)"] -compress = ["python-snappy (>=0.5)"] -dev = ["pep8-naming (>=0.3.3)", "flake8 (>=2.5.1)", "pyflakes (>=1.0.0)", "pytest (>=2.8.6,<3.3.0)", "twine (>=1.6.5)", "sphinx (>=1.2.3)", "sphinxcontrib-images (>=0.9.2)", "pyenchant (>=1.6.6)", "sphinxcontrib-spelling (>=2.1.2)", "sphinx-rtd-theme (>=0.1.9)", "awscli", "qualname", "passlib", "wheel", "pytest-asyncio (<0.6)", "pytest-aiohttp"] -encryption = ["pyopenssl (>=16.2.0)", "service-identity (>=18.1.0)", "pynacl (>=1.0.1)", "pytrie (>=0.2)", "pyqrcode (>=1.1)"] -nvx = ["cffi (>=1.11.5)"] -scram = ["cffi (>=1.11.5)", "argon2-cffi (>=18.1.0)", "passlib (>=1.7.1)"] -serialization = ["msgpack (>=0.6.1)", "ujson (>=1.35)", "cbor2 (>=5.0.1)", "cbor (>=1.0.0)", "py-ubjson (>=0.8.4)", "flatbuffers (>=1.10)"] -twisted = ["zope.interface (>=3.6.0)", "twisted (>=20.3.0)", "attrs (>=19.2.0)"] -xbr = ["xbr (>=20.1.1)", "cbor2 (>=5.1.0)", "zlmdb (>=20.4.1)", "twisted (>=20.3.0)", "web3 (>=4.8.1)", "jinja2 (>=2.11.2)", "rlp (>=2.0.1)", "py-eth-sig-utils (>=0.4.0)", "py-ecc (>=1.7.1)", "eth-abi (>=1.3.0)", "mnemonic (>=0.13)", "base58 (>=1.0.2,<2.0)", "ecdsa (>=0.13)", "py-multihash (>=0.2.3)"] - [[package]] name = "automat" version = "20.2.0" @@ -85,22 +60,6 @@ python-versions = "*" [package.dependencies] pycparser = "*" -[[package]] -name = "channels" -version = "3.0.3" -description = "Brings async, event-driven capabilities to Django. Django 2.2 and up only." -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -asgiref = ">=3.2.10,<4" -daphne = ">=3.0,<4" -Django = ">=2.2" - -[package.extras] -tests = ["pytest", "pytest-django", "pytest-asyncio", "async-generator", "async-timeout", "coverage (>=4.5,<5.0)"] - [[package]] name = "chardet" version = "4.0.0" @@ -109,26 +68,6 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -[[package]] -name = "click" -version = "8.0.1" -description = "Composable command line interface toolkit" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} - -[[package]] -name = "colorama" -version = "0.4.4" -description = "Cross-platform colored terminal text." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - [[package]] name = "constantly" version = "15.1.0" @@ -157,22 +96,6 @@ pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] ssh = ["bcrypt (>=3.1.5)"] test = ["pytest (>=3.6.0,!=3.9.0,!=3.9.1,!=3.9.2)", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] -[[package]] -name = "daphne" -version = "3.0.2" -description = "Django ASGI (HTTP/WebSocket) server" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -asgiref = ">=3.2.10,<4" -autobahn = ">=0.18" -twisted = {version = ">=18.7", extras = ["tls"]} - -[package.extras] -tests = ["hypothesis (==4.23)", "pytest (>=3.10,<4.0)", "pytest-asyncio (>=0.8,<1.0)"] - [[package]] name = "defusedxml" version = "0.7.1" @@ -266,6 +189,31 @@ mccabe = ">=0.6.0,<0.7.0" pycodestyle = ">=2.7.0,<2.8.0" pyflakes = ">=2.3.0,<2.4.0" +[[package]] +name = "fsspec" +version = "2021.7.0" +description = "File-system specification" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +abfs = ["adlfs"] +adl = ["adlfs"] +dask = ["dask", "distributed"] +dropbox = ["dropboxdrivefs", "requests", "dropbox"] +entrypoints = ["importlib-metadata"] +gcs = ["gcsfs"] +git = ["pygit2"] +github = ["requests"] +gs = ["gcsfs"] +hdfs = ["pyarrow (>=1)"] +http = ["requests", "aiohttp"] +s3 = ["s3fs"] +sftp = ["paramiko"] +smb = ["smbprotocol"] +ssh = ["paramiko"] + [[package]] name = "gql" version = "2.0.0" @@ -315,14 +263,6 @@ gevent = ["gevent (>=1.4.0)"] setproctitle = ["setproctitle"] tornado = ["tornado (>=0.2)"] -[[package]] -name = "h11" -version = "0.12.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -category = "main" -optional = false -python-versions = ">=3.6" - [[package]] name = "hyperlink" version = "21.0.0" @@ -346,7 +286,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" name = "importlib-metadata" version = "4.4.0" description = "Read metadata from Python packages" -category = "main" +category = "dev" optional = false python-versions = ">=3.6" @@ -412,25 +352,6 @@ six = "*" [package.extras] test = ["pytest (>=2.7.3)", "pytest-cov", "coveralls", "futures", "pytest-benchmark", "mock"] -[[package]] -name = "pyasn1" -version = "0.4.8" -description = "ASN.1 types and codecs" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "pyasn1-modules" -version = "0.2.8" -description = "A collection of ASN.1-based protocols modules." -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -pyasn1 = ">=0.4.6,<0.5.0" - [[package]] name = "pycodestyle" version = "2.7.0" @@ -488,22 +409,6 @@ dev = ["sphinx", "sphinx-rtd-theme", "zope.interface", "cryptography (>=3.3.1,<4 docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] tests = ["pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)"] -[[package]] -name = "pyopenssl" -version = "19.1.0" -description = "Python wrapper module around the OpenSSL library" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -cryptography = ">=2.8" -six = ">=1.5.2" - -[package.extras] -docs = ["sphinx", "sphinx-rtd-theme"] -test = ["flaky", "pretend", "pytest (>=3.0.1)"] - [[package]] name = "python-dotenv" version = "0.17.1" @@ -587,27 +492,6 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -[[package]] -name = "service-identity" -version = "21.1.0" -description = "Service identity verification for pyOpenSSL & cryptography." -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -attrs = ">=19.1.0" -cryptography = "*" -pyasn1 = "*" -pyasn1-modules = "*" -six = "*" - -[package.extras] -dev = ["coverage[toml] (>=5.0.2)", "pytest", "sphinx", "furo", "idna", "pyopenssl"] -docs = ["sphinx", "furo"] -idna = ["idna"] -tests = ["coverage[toml] (>=5.0.2)", "pytest"] - [[package]] name = "six" version = "1.16.0" @@ -673,11 +557,8 @@ attrs = ">=19.2.0" Automat = ">=0.3.0" constantly = ">=15.1" hyperlink = ">=17.1.1" -idna = {version = ">=0.6,<2.3 || >2.3", optional = true, markers = "extra == \"tls\""} incremental = ">=16.10.1" PyHamcrest = ">=1.9.0,<1.10.0 || >1.10.0" -pyopenssl = {version = ">=16.0.0", optional = true, markers = "extra == \"tls\""} -service_identity = {version = ">=18.1.0", optional = true, markers = "extra == \"tls\""} "zope.interface" = ">=4.4.2" [package.extras] @@ -692,19 +573,6 @@ soap = ["soappy"] tls = ["pyopenssl (>=16.0.0)", "service_identity (>=18.1.0)", "idna (>=0.6,!=2.3)"] windows_platform = ["pywin32 (!=226)", "pyopenssl (>=16.0.0)", "service_identity (>=18.1.0)", "idna (>=0.6,!=2.3)", "pyasn1", "cryptography (>=2.5)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "soappy", "pyserial (>=3.0)", "h2 (>=3.0,<4.0)", "priority (>=1.1.0,<2.0)", "pywin32 (!=226)"] -[[package]] -name = "txaio" -version = "21.2.1" -description = "Compatibility API between asyncio/Twisted/Trollius" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.extras] -all = ["zope.interface (>=5.2.0)", "twisted (>=20.3.0)"] -dev = ["wheel", "pytest (>=2.6.4)", "pytest-cov (>=1.8.1)", "pep8 (>=1.6.2)", "sphinx (>=1.2.3)", "pyenchant (>=1.6.6)", "sphinxcontrib-spelling (>=2.1.2)", "sphinx-rtd-theme (>=0.1.9)", "tox (>=2.1.1)", "mock (==1.3.0)", "twine (>=1.6.5)", "tox-gh-actions (>=2.2.0)"] -twisted = ["zope.interface (>=5.2.0)", "twisted (>=20.3.0)"] - [[package]] name = "typing-extensions" version = "3.10.0.0" @@ -726,23 +594,6 @@ brotli = ["brotlipy (>=0.6.0)"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] -[[package]] -name = "uvicorn" -version = "0.14.0" -description = "The lightning-fast ASGI server." -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -asgiref = ">=3.3.4" -click = ">=7" -h11 = ">=0.8" -typing-extensions = {version = "*", markers = "python_version < \"3.8\""} - -[package.extras] -standard = ["websockets (>=9.1)", "httptools (>=0.2.0,<0.3.0)", "watchgod (>=0.6)", "python-dotenv (>=0.13)", "PyYAML (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "colorama (>=0.4)"] - [[package]] name = "websockets" version = "9.1" @@ -755,7 +606,7 @@ python-versions = ">=3.6.1" name = "zipp" version = "3.4.1" description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" +category = "dev" optional = false python-versions = ">=3.6" @@ -779,7 +630,7 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "84c68730a52d039da15d782202c5490079398992ede936c33a696cb97c1bfa5d" +content-hash = "a855859b9c8eb70c722c6e1ce320e3b4a8b554b8eeb12e42cfaea4dce857855d" [metadata.files] asgiref = [ @@ -790,10 +641,6 @@ attrs = [ {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, ] -autobahn = [ - {file = "autobahn-21.2.1-py2.py3-none-any.whl", hash = "sha256:41a3a3f89cde48643baf4e105d9491c566295f9abee951379e59121784044b8b"}, - {file = "autobahn-21.2.1.tar.gz", hash = "sha256:7e6b1bf95196b733978bab2d54a7ab8899c16ce11be369dc58422c07b7eea726"}, -] automat = [ {file = "Automat-20.2.0-py2.py3-none-any.whl", hash = "sha256:b6feb6455337df834f6c9962d6ccf771515b7d939bca142b29c20c2376bc6111"}, {file = "Automat-20.2.0.tar.gz", hash = "sha256:7979803c74610e11ef0c0d68a2942b152df52da55336e0c9d58daf1831cbdf33"}, @@ -853,22 +700,10 @@ cffi = [ {file = "cffi-1.14.5-cp39-cp39-win_amd64.whl", hash = "sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d"}, {file = "cffi-1.14.5.tar.gz", hash = "sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c"}, ] -channels = [ - {file = "channels-3.0.3-py3-none-any.whl", hash = "sha256:3f15bdd2138bb4796e76ea588a0a344b12a7964ea9b2e456f992fddb988a4317"}, - {file = "channels-3.0.3.tar.gz", hash = "sha256:056b72e51080a517a0f33a0a30003e03833b551d75394d6636c885d4edb8188f"}, -] chardet = [ {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, ] -click = [ - {file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"}, - {file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"}, -] -colorama = [ - {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, - {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, -] constantly = [ {file = "constantly-15.1.0-py2.py3-none-any.whl", hash = "sha256:dd2fa9d6b1a51a83f0d7dd76293d734046aa176e384bf6e33b7e44880eb37c5d"}, {file = "constantly-15.1.0.tar.gz", hash = "sha256:586372eb92059873e29eba4f9dec8381541b4d3834660707faf8ba59146dfc35"}, @@ -894,10 +729,6 @@ cryptography = [ {file = "cryptography-3.0-cp38-cp38-win_amd64.whl", hash = "sha256:bea0b0468f89cdea625bb3f692cd7a4222d80a6bdafd6fb923963f2b9da0e15f"}, {file = "cryptography-3.0.tar.gz", hash = "sha256:8e924dbc025206e97756e8903039662aa58aa9ba357d8e1d8fc29e3092322053"}, ] -daphne = [ - {file = "daphne-3.0.2-py3-none-any.whl", hash = "sha256:a9af943c79717bc52fe64a3c236ae5d3adccc8b5be19c881b442d2c3db233393"}, - {file = "daphne-3.0.2.tar.gz", hash = "sha256:76ffae916ba3aa66b46996c14fa713e46004788167a4873d647544e750e0e99f"}, -] defusedxml = [ {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, @@ -929,6 +760,10 @@ flake8 = [ {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, ] +fsspec = [ + {file = "fsspec-2021.7.0-py3-none-any.whl", hash = "sha256:86822ccf367da99957f49db64f7d5fd3d8d21444fac4dfdc8ebc38ee93d478c6"}, + {file = "fsspec-2021.7.0.tar.gz", hash = "sha256:792ebd3b54de0b30f1ce73f0ba0a8bcc864724f2d9f248cb8d0ece47db0cbde8"}, +] gql = [ {file = "gql-2.0.0-py2.py3-none-any.whl", hash = "sha256:35032ddd4bfe6b8f3169f806b022168932385d751eacc5c5f7122e0b3f4d6b88"}, {file = "gql-2.0.0.tar.gz", hash = "sha256:fe8d3a08047f77362ddfcfddba7cae377da2dd66f5e61c59820419c9283d4fb5"}, @@ -941,10 +776,6 @@ gunicorn = [ {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, {file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"}, ] -h11 = [ - {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"}, - {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"}, -] hyperlink = [ {file = "hyperlink-21.0.0-py2.py3-none-any.whl", hash = "sha256:e6b14c37ecb73e89c77d78cdb4c2cc8f3fb59a885c5b3f819ff4ed80f25af1b4"}, {file = "hyperlink-21.0.0.tar.gz", hash = "sha256:427af957daa58bc909471c6c40f74c5450fa123dd093fc53efd2e91d2705a56b"}, @@ -979,36 +810,6 @@ oauthlib = [ promise = [ {file = "promise-2.3.tar.gz", hash = "sha256:dfd18337c523ba4b6a58801c164c1904a9d4d1b1747c7d5dbf45b693a49d93d0"}, ] -pyasn1 = [ - {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, - {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"}, - {file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"}, - {file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"}, - {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, - {file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"}, - {file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"}, - {file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"}, - {file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"}, - {file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"}, - {file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"}, - {file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"}, - {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, -] -pyasn1-modules = [ - {file = "pyasn1-modules-0.2.8.tar.gz", hash = "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e"}, - {file = "pyasn1_modules-0.2.8-py2.4.egg", hash = "sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199"}, - {file = "pyasn1_modules-0.2.8-py2.5.egg", hash = "sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405"}, - {file = "pyasn1_modules-0.2.8-py2.6.egg", hash = "sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb"}, - {file = "pyasn1_modules-0.2.8-py2.7.egg", hash = "sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8"}, - {file = "pyasn1_modules-0.2.8-py2.py3-none-any.whl", hash = "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74"}, - {file = "pyasn1_modules-0.2.8-py3.1.egg", hash = "sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d"}, - {file = "pyasn1_modules-0.2.8-py3.2.egg", hash = "sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45"}, - {file = "pyasn1_modules-0.2.8-py3.3.egg", hash = "sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4"}, - {file = "pyasn1_modules-0.2.8-py3.4.egg", hash = "sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811"}, - {file = "pyasn1_modules-0.2.8-py3.5.egg", hash = "sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed"}, - {file = "pyasn1_modules-0.2.8-py3.6.egg", hash = "sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0"}, - {file = "pyasn1_modules-0.2.8-py3.7.egg", hash = "sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd"}, -] pycodestyle = [ {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, @@ -1033,10 +834,6 @@ pyjwt = [ {file = "PyJWT-2.1.0-py3-none-any.whl", hash = "sha256:934d73fbba91b0483d3857d1aff50e96b2a892384ee2c17417ed3203f173fca1"}, {file = "PyJWT-2.1.0.tar.gz", hash = "sha256:fba44e7898bbca160a2b2b501f492824fc8382485d3a6f11ba5d0c1937ce6130"}, ] -pyopenssl = [ - {file = "pyOpenSSL-19.1.0-py2.py3-none-any.whl", hash = "sha256:621880965a720b8ece2f1b2f54ea2071966ab00e2970ad2ce11d596102063504"}, - {file = "pyOpenSSL-19.1.0.tar.gz", hash = "sha256:9a24494b2602aaf402be5c9e30a0b82d4a5c67528fe8fb475e3f3bc00dd69507"}, -] python-dotenv = [ {file = "python-dotenv-0.17.1.tar.gz", hash = "sha256:b1ae5e9643d5ed987fc57cc2583021e38db531946518130777734f9589b3141f"}, {file = "python_dotenv-0.17.1-py2.py3-none-any.whl", hash = "sha256:00aa34e92d992e9f8383730816359647f358f4a3be1ba45e5a5cefd27ee91544"}, @@ -1066,10 +863,6 @@ semver = [ {file = "semver-2.13.0-py2.py3-none-any.whl", hash = "sha256:ced8b23dceb22134307c1b8abfa523da14198793d9787ac838e70e29e77458d4"}, {file = "semver-2.13.0.tar.gz", hash = "sha256:fa0fe2722ee1c3f57eac478820c3a5ae2f624af8264cbdf9000c980ff7f75e3f"}, ] -service-identity = [ - {file = "service-identity-21.1.0.tar.gz", hash = "sha256:6e6c6086ca271dc11b033d17c3a8bea9f24ebff920c587da090afc9519419d34"}, - {file = "service_identity-21.1.0-py2.py3-none-any.whl", hash = "sha256:f0b0caac3d40627c3c04d7a51b6e06721857a0e10a8775f2d1d7e72901b3a7db"}, -] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, @@ -1112,10 +905,6 @@ twisted = [ {file = "Twisted-20.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d95803193561a243cb0401b0567c6b7987d3f2a67046770e1dccd1c9e49a9780"}, {file = "Twisted-20.3.0.tar.bz2", hash = "sha256:d72c55b5d56e176563b91d11952d13b01af8725c623e498db5507b6614fc1e10"}, ] -txaio = [ - {file = "txaio-21.2.1-py2.py3-none-any.whl", hash = "sha256:c16b55f9a67b2419cfdf8846576e2ec9ba94fe6978a83080c352a80db31c93fb"}, - {file = "txaio-21.2.1.tar.gz", hash = "sha256:7d6f89745680233f1c4db9ddb748df5e88d2a7a37962be174c0fd04c8dba1dc8"}, -] typing-extensions = [ {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"}, {file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"}, @@ -1125,10 +914,6 @@ urllib3 = [ {file = "urllib3-1.26.6-py2.py3-none-any.whl", hash = "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4"}, {file = "urllib3-1.26.6.tar.gz", hash = "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"}, ] -uvicorn = [ - {file = "uvicorn-0.14.0-py3-none-any.whl", hash = "sha256:2a76bb359171a504b3d1c853409af3adbfa5cef374a4a59e5881945a97a93eae"}, - {file = "uvicorn-0.14.0.tar.gz", hash = "sha256:45ad7dfaaa7d55cab4cd1e85e03f27e9d60bc067ddc59db52a2b0aeca8870292"}, -] websockets = [ {file = "websockets-9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d144b350045c53c8ff09aa1cfa955012dd32f00c7e0862c199edcabb1a8b32da"}, {file = "websockets-9.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:b4ad84b156cf50529b8ac5cc1638c2cf8680490e3fccb6121316c8c02620a2e4"}, diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 08a76c9..e9eaf37 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -8,7 +8,6 @@ authors = ["Your Name "] python = "^3.7" Django = "^3.2.3" django-rest-framework = "^0.1.0" -channels = "^3.0.3" djangorestframework-dataclasses = "^0.9" social-auth-app-django = "^4.0.0" python-dotenv = "^0.17.1" @@ -23,7 +22,7 @@ requests = "^2.25.1" pyga = "^2.6.2" django-cors-headers = "^3.7.0" cryptography = "3.0" -uvicorn = "^0.14.0" +fsspec = "^2021.7.0" [tool.poetry.dev-dependencies] flake8 = "^3.9.2" diff --git a/backend/tabby/app/api.py b/backend/tabby/app/api.py index e8b25d2..ffa2b2e 100644 --- a/backend/tabby/app/api.py +++ b/backend/tabby/app/api.py @@ -1,3 +1,5 @@ +import fsspec +import os import asyncio import random from django.conf import settings @@ -15,8 +17,9 @@ from rest_framework.serializers import ModelSerializer, Serializer from rest_framework_dataclasses.serializers import DataclassSerializer from social_django.models import UserSocialAuth from typing import List +from urllib.parse import urlparse -from .consumers import GatewayAdminConnection +from .gateway import GatewayAdminConnection from .sponsors import check_is_sponsor, check_is_sponsor_cached from .models import Config, Gateway, User @@ -74,19 +77,26 @@ class AppVersionViewSet(ListModelMixin, GenericViewSet): queryset = '' def _get_versions(self): - return [self._get_version(x) for x in settings.APP_DIST_PATH.iterdir()] + fs = fsspec.filesystem(urlparse(settings.APP_DIST_STORAGE).scheme) + return [ + self._get_version(x['name']) + for x in fs.listdir(settings.APP_DIST_STORAGE) + ] - def _get_version(self, dir: Path): + def _get_version(self, dir): + fs = fsspec.filesystem(urlparse(settings.APP_DIST_STORAGE).scheme) plugins = [ - x.name for x in dir.iterdir() - if x.is_dir() and x.name not in [ + os.path.basename(x['name']) + for x in fs.listdir(dir) + if x['type'] == 'directory' and os.path.basename(x['name']) + not in [ 'tabby-web-container', 'tabby-web-demo', ] ] return AppVersion( - version=dir.name, + version=os.path.basename(dir), plugins=plugins, ) @@ -166,7 +176,7 @@ class InstanceInfoViewSet(RetrieveModelMixin, GenericViewSet): class NoGatewaysError(APIException): status_code = status.HTTP_503_SERVICE_UNAVAILABLE - default_detail ='No connection gateways available.' + default_detail = 'No connection gateways available.' default_code = 'no_gateways' diff --git a/backend/tabby/app/consumers.py b/backend/tabby/app/gateway.py similarity index 75% rename from backend/tabby/app/consumers.py rename to backend/tabby/app/gateway.py index 2aafb0b..60e680f 100644 --- a/backend/tabby/app/consumers.py +++ b/backend/tabby/app/gateway.py @@ -4,7 +4,6 @@ import os import secrets import ssl import websockets -from channels.generic.websocket import AsyncWebsocketConsumer from django.conf import settings from urllib.parse import quote @@ -91,35 +90,3 @@ class GatewayAdminConnection: async def close(self): await self.socket.close() await self.context.__aexit__(None, None, None) - - -class TCPConsumer(AsyncWebsocketConsumer): - async def connect(self): - self.closed = False - self.conn = GatewayConnection( - self.scope['url_route']['kwargs']['host'], - int(self.scope['url_route']['kwargs']['port']), - ) - await self.conn.connect() - await self.accept() - self.reader = asyncio.get_event_loop().create_task(self.socket_reader()) - - async def disconnect(self, close_code): - self.closed = True - await self.conn.close() - - async def receive(self, bytes_data): - await self.conn.send(bytes_data) - - async def socket_reader(self): - while True: - if self.closed: - return - try: - data = await self.conn.recv(timeout=10) - except asyncio.TimeoutError: - continue - except websockets.exceptions.ConnectionClosed: - await self.close() - return - await self.send(bytes_data=data) diff --git a/backend/tabby/app/management/commands/add_version.py b/backend/tabby/app/management/commands/add_version.py index f8bd0eb..4d064bf 100644 --- a/backend/tabby/app/management/commands/add_version.py +++ b/backend/tabby/app/management/commands/add_version.py @@ -1,3 +1,4 @@ +import fsspec import logging import requests import shutil @@ -6,6 +7,7 @@ import tempfile from django.core.management.base import BaseCommand from django.conf import settings from pathlib import Path +from urllib.parse import urlparse class Command(BaseCommand): @@ -16,7 +18,9 @@ class Command(BaseCommand): def handle(self, *args, **options): version = options['version'] - target: Path = settings.APP_DIST_PATH / version + target = f'{settings.APP_DIST_STORAGE}/{version}' + + fs = fsspec.filesystem(urlparse(settings.APP_DIST_STORAGE).scheme) plugin_list = [ 'tabby-web-container', @@ -51,9 +55,11 @@ class Command(BaseCommand): subprocess.check_call( ['tar', '-xzf', f.name, '-C', str(extraction_tmp)] ) - shutil.move(Path(extraction_tmp) / 'package', plugin_final_target) + shutil.move( + Path(extraction_tmp) / 'package', plugin_final_target + ) - if target.exists(): - shutil.rmtree(target) - shutil.copytree(tempdir, target) - target.chmod(0o755) + if fs.exists(target): + fs.rm(target, recursive=True) + fs.mkdir(target) + fs.put(str(tempdir), target, recursive=True) diff --git a/backend/tabby/app/urls.py b/backend/tabby/app/urls.py index 8c6e866..bb224df 100644 --- a/backend/tabby/app/urls.py +++ b/backend/tabby/app/urls.py @@ -2,7 +2,6 @@ from django.urls import path, re_path, include from rest_framework import routers from . import api -from . import consumers from . import views @@ -16,13 +15,9 @@ urlpatterns = [ path('api/1/instance-info', api.InstanceInfoViewSet.as_view({'get': 'retrieve'})), path('api/1/gateways/choose', api.ChooseGatewayViewSet.as_view({'post': 'retrieve'})), - # re_path('^(|login|app)$', views.IndexView.as_view()), + re_path('^(|login|app)$', views.IndexView.as_view()), + path('terminal', views.TerminalView.as_view()), - # path('terminal', views.TerminalView.as_view()), path('app-dist//', views.AppDistView.as_view()), path('', include(router.urls)), ] - -websocket_urlpatterns = [ - re_path(r'^api/1/gateway/tcp/(?P[^/]+):(?P\d+)$', consumers.TCPConsumer.as_asgi()), -] diff --git a/backend/tabby/app/views.py b/backend/tabby/app/views.py index 75dc01b..9e5a4f8 100644 --- a/backend/tabby/app/views.py +++ b/backend/tabby/app/views.py @@ -1,26 +1,25 @@ -import os +import fsspec from django.conf import settings +from django.http.response import HttpResponseRedirect from django.views import static from rest_framework.views import APIView +from urllib.parse import urlparse class IndexView(APIView): def get(self, request, format=None): - return static.serve(request, 'index.html', document_root=str(settings.BASE_DIR / 'build')) + return static.serve(request, 'index.html', document_root=str(settings.FRONTEND_BUILD_DIR)) class TerminalView(APIView): def get(self, request, format=None): - response = static.serve(request, 'terminal.html', document_root=str(settings.BASE_DIR / 'build')) + response = static.serve(request, 'terminal.html', document_root=str(settings.FRONTEND_BUILD_DIR)) response['X-Frame-Options'] = 'SAMEORIGIN' return response class AppDistView(APIView): def get(self, request, version=None, path=None, format=None): - return static.serve(request, os.path.join(version, path), document_root=str(settings.APP_DIST_PATH)) - - -# class BuildView(APIView): -# def get(self, request, path=None, format=None): -# return static.serve(request, path, document_root=str(settings.BASE_DIR / 'build')) + fs = fsspec.filesystem(urlparse(settings.APP_DIST_STORAGE).scheme) + url = f'{settings.APP_DIST_STORAGE}/{version}/{path}' + return HttpResponseRedirect(fs.url(url)) diff --git a/backend/tabby/asgi.py b/backend/tabby/asgi.py index f6ee157..d6fc204 100644 --- a/backend/tabby/asgi.py +++ b/backend/tabby/asgi.py @@ -4,15 +4,9 @@ import os os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tabby.settings') django.setup() -from channels.auth import AuthMiddlewareStack -from channels.routing import ProtocolTypeRouter, URLRouter +from channels.routing import ProtocolTypeRouter from django.core.asgi import get_asgi_application -from .app.urls import websocket_urlpatterns - application = ProtocolTypeRouter({ 'http': get_asgi_application(), - 'websocket': AuthMiddlewareStack( - URLRouter(websocket_urlpatterns), - ), }) diff --git a/backend/tabby/settings.py b/backend/tabby/settings.py index db4caa2..b554e01 100644 --- a/backend/tabby/settings.py +++ b/backend/tabby/settings.py @@ -8,6 +8,7 @@ load_dotenv() # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent +FRONTEND_BUILD_DIR = BASE_DIR / '../frontend/build' # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ @@ -32,7 +33,6 @@ INSTALLED_APPS = [ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', - 'channels', 'rest_framework', 'social_django', 'corsheaders', @@ -70,7 +70,6 @@ TEMPLATES = [ }, ] -ASGI_APPLICATION = 'tabby.asgi.application' WSGI_APPLICATION = 'tabby.wsgi.application' DATABASES = { @@ -138,6 +137,7 @@ LOGGING = { } STATIC_URL = '/static/' +STATICFILES_DIRS = [FRONTEND_BUILD_DIR] DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' @@ -169,7 +169,7 @@ SOCIAL_AUTH_PIPELINE = ( 'social_core.pipeline.user.user_details', ) -APP_DIST_PATH = Path(os.getenv('APP_DIST_PATH', BASE_DIR / 'app-dist')) +APP_DIST_STORAGE = os.getenv('APP_DIST_STORAGE', 'file://' + str(BASE_DIR / 'app-dist')) NPM_REGISTRY = os.getenv('NPM_REGISTRY', 'https://registry.npmjs.org').rstrip('/') FRONTEND_URL = None diff --git a/frontend/src/server.ts b/frontend/src/server.ts index 4b906ba..58577c9 100644 --- a/frontend/src/server.ts +++ b/frontend/src/server.ts @@ -31,7 +31,7 @@ function start () { app.set('view engine', 'html') app.set('views', DIST_FOLDER) - app.get('*.*', express.static(DIST_FOLDER, { + app.get('*.*', express.static(join(DIST_FOLDER, 'static'), { maxAge: '1y', })) diff --git a/frontend/webpack.config.js b/frontend/webpack.config.js index 09edd75..f761ec1 100644 --- a/frontend/webpack.config.js +++ b/frontend/webpack.config.js @@ -43,7 +43,7 @@ module.exports = { output: { path: outputPath, pathinfo: true, - publicPath: '/', + publicPath: '/static/', filename: '[name].js', chunkFilename: '[name].bundle.js', },