1
0
mirror of https://github.com/Eugeny/tabby-web.git synced 2025-06-25 22:00:11 +00:00
This commit is contained in:
Eugene Pankov 2021-07-26 17:56:35 +02:00
parent 151a2fab60
commit cbd8a93909
No known key found for this signature in database
GPG Key ID: 5896FCBBDD1CF4F4
15 changed files with 84 additions and 327 deletions

@ -1,15 +1,16 @@
# syntax=docker/dockerfile:1 # syntax=docker/dockerfile:1
FROM python:3.7-alpine AS build FROM python:3.7-alpine AS build
ARG EXTRA_DEPS
RUN apk add build-base musl-dev libffi-dev openssl-dev mariadb-dev RUN apk add build-base musl-dev libffi-dev openssl-dev mariadb-dev
WORKDIR /app 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 ./ COPY pyproject.toml poetry.lock ./
RUN poetry config virtualenvs.create false RUN poetry config virtualenvs.create false
RUN poetry install --no-dev --no-ansi --no-interaction RUN poetry install --no-dev --no-ansi --no-interaction
FROM node:12-alpine AS package FROM node:12-alpine AS package
WORKDIR /app WORKDIR /app
COPY --from=0 /usr /usr COPY --from=0 /usr/local /usr/local
COPY start.sh manage.py gunicorn.conf.py ./ COPY start.sh manage.py gunicorn.conf.py ./
COPY tabby tabby COPY tabby tabby
ENTRYPOINT ./start.sh ENTRYPOINT ./start.sh

@ -7,6 +7,8 @@ steps:
- '${_DOCKER_TAG}' - '${_DOCKER_TAG}'
- '--cache-from' - '--cache-from'
- '${_DOCKER_TAG}' - '${_DOCKER_TAG}'
- '--build-arg'
- 'EXTRA_DEPS=${_EXTRA_DEPS}'
- '.' - '.'
images: ['${_DOCKER_TAG}'] images: ['${_DOCKER_TAG}']

@ -1,6 +1,5 @@
wsgi_app = "tabby.asgi:application" wsgi_app = "tabby.wsgi:application"
workers = 4 workers = 4
worker_class = 'uvicorn.workers.UvicornWorker'
preload_app = True preload_app = True
sendfile = True sendfile = True

279
backend/poetry.lock generated

@ -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 = ["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"] 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]] [[package]]
name = "automat" name = "automat"
version = "20.2.0" version = "20.2.0"
@ -85,22 +60,6 @@ python-versions = "*"
[package.dependencies] [package.dependencies]
pycparser = "*" 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]] [[package]]
name = "chardet" name = "chardet"
version = "4.0.0" version = "4.0.0"
@ -109,26 +68,6 @@ category = "main"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 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]] [[package]]
name = "constantly" name = "constantly"
version = "15.1.0" version = "15.1.0"
@ -157,22 +96,6 @@ pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"]
ssh = ["bcrypt (>=3.1.5)"] 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)"] 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]] [[package]]
name = "defusedxml" name = "defusedxml"
version = "0.7.1" version = "0.7.1"
@ -266,6 +189,31 @@ mccabe = ">=0.6.0,<0.7.0"
pycodestyle = ">=2.7.0,<2.8.0" pycodestyle = ">=2.7.0,<2.8.0"
pyflakes = ">=2.3.0,<2.4.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]] [[package]]
name = "gql" name = "gql"
version = "2.0.0" version = "2.0.0"
@ -315,14 +263,6 @@ gevent = ["gevent (>=1.4.0)"]
setproctitle = ["setproctitle"] setproctitle = ["setproctitle"]
tornado = ["tornado (>=0.2)"] 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]] [[package]]
name = "hyperlink" name = "hyperlink"
version = "21.0.0" version = "21.0.0"
@ -346,7 +286,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
name = "importlib-metadata" name = "importlib-metadata"
version = "4.4.0" version = "4.4.0"
description = "Read metadata from Python packages" description = "Read metadata from Python packages"
category = "main" category = "dev"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
@ -412,25 +352,6 @@ six = "*"
[package.extras] [package.extras]
test = ["pytest (>=2.7.3)", "pytest-cov", "coveralls", "futures", "pytest-benchmark", "mock"] 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]] [[package]]
name = "pycodestyle" name = "pycodestyle"
version = "2.7.0" 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"] docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"]
tests = ["pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)"] 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]] [[package]]
name = "python-dotenv" name = "python-dotenv"
version = "0.17.1" version = "0.17.1"
@ -587,27 +492,6 @@ category = "main"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 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]] [[package]]
name = "six" name = "six"
version = "1.16.0" version = "1.16.0"
@ -673,11 +557,8 @@ attrs = ">=19.2.0"
Automat = ">=0.3.0" Automat = ">=0.3.0"
constantly = ">=15.1" constantly = ">=15.1"
hyperlink = ">=17.1.1" hyperlink = ">=17.1.1"
idna = {version = ">=0.6,<2.3 || >2.3", optional = true, markers = "extra == \"tls\""}
incremental = ">=16.10.1" incremental = ">=16.10.1"
PyHamcrest = ">=1.9.0,<1.10.0 || >1.10.0" 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" "zope.interface" = ">=4.4.2"
[package.extras] [package.extras]
@ -692,19 +573,6 @@ soap = ["soappy"]
tls = ["pyopenssl (>=16.0.0)", "service_identity (>=18.1.0)", "idna (>=0.6,!=2.3)"] 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)"] 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]] [[package]]
name = "typing-extensions" name = "typing-extensions"
version = "3.10.0.0" 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"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] 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]] [[package]]
name = "websockets" name = "websockets"
version = "9.1" version = "9.1"
@ -755,7 +606,7 @@ python-versions = ">=3.6.1"
name = "zipp" name = "zipp"
version = "3.4.1" version = "3.4.1"
description = "Backport of pathlib-compatible object wrapper for zip files" description = "Backport of pathlib-compatible object wrapper for zip files"
category = "main" category = "dev"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
@ -779,7 +630,7 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"]
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.7" python-versions = "^3.7"
content-hash = "84c68730a52d039da15d782202c5490079398992ede936c33a696cb97c1bfa5d" content-hash = "a855859b9c8eb70c722c6e1ce320e3b4a8b554b8eeb12e42cfaea4dce857855d"
[metadata.files] [metadata.files]
asgiref = [ asgiref = [
@ -790,10 +641,6 @@ attrs = [
{file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
{file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, {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 = [ automat = [
{file = "Automat-20.2.0-py2.py3-none-any.whl", hash = "sha256:b6feb6455337df834f6c9962d6ccf771515b7d939bca142b29c20c2376bc6111"}, {file = "Automat-20.2.0-py2.py3-none-any.whl", hash = "sha256:b6feb6455337df834f6c9962d6ccf771515b7d939bca142b29c20c2376bc6111"},
{file = "Automat-20.2.0.tar.gz", hash = "sha256:7979803c74610e11ef0c0d68a2942b152df52da55336e0c9d58daf1831cbdf33"}, {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-cp39-cp39-win_amd64.whl", hash = "sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d"},
{file = "cffi-1.14.5.tar.gz", hash = "sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c"}, {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 = [ chardet = [
{file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"},
{file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, {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 = [ constantly = [
{file = "constantly-15.1.0-py2.py3-none-any.whl", hash = "sha256:dd2fa9d6b1a51a83f0d7dd76293d734046aa176e384bf6e33b7e44880eb37c5d"}, {file = "constantly-15.1.0-py2.py3-none-any.whl", hash = "sha256:dd2fa9d6b1a51a83f0d7dd76293d734046aa176e384bf6e33b7e44880eb37c5d"},
{file = "constantly-15.1.0.tar.gz", hash = "sha256:586372eb92059873e29eba4f9dec8381541b4d3834660707faf8ba59146dfc35"}, {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-cp38-cp38-win_amd64.whl", hash = "sha256:bea0b0468f89cdea625bb3f692cd7a4222d80a6bdafd6fb923963f2b9da0e15f"},
{file = "cryptography-3.0.tar.gz", hash = "sha256:8e924dbc025206e97756e8903039662aa58aa9ba357d8e1d8fc29e3092322053"}, {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 = [ defusedxml = [
{file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"},
{file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, {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-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"},
{file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, {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 = [ gql = [
{file = "gql-2.0.0-py2.py3-none-any.whl", hash = "sha256:35032ddd4bfe6b8f3169f806b022168932385d751eacc5c5f7122e0b3f4d6b88"}, {file = "gql-2.0.0-py2.py3-none-any.whl", hash = "sha256:35032ddd4bfe6b8f3169f806b022168932385d751eacc5c5f7122e0b3f4d6b88"},
{file = "gql-2.0.0.tar.gz", hash = "sha256:fe8d3a08047f77362ddfcfddba7cae377da2dd66f5e61c59820419c9283d4fb5"}, {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-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"},
{file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"}, {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 = [ hyperlink = [
{file = "hyperlink-21.0.0-py2.py3-none-any.whl", hash = "sha256:e6b14c37ecb73e89c77d78cdb4c2cc8f3fb59a885c5b3f819ff4ed80f25af1b4"}, {file = "hyperlink-21.0.0-py2.py3-none-any.whl", hash = "sha256:e6b14c37ecb73e89c77d78cdb4c2cc8f3fb59a885c5b3f819ff4ed80f25af1b4"},
{file = "hyperlink-21.0.0.tar.gz", hash = "sha256:427af957daa58bc909471c6c40f74c5450fa123dd093fc53efd2e91d2705a56b"}, {file = "hyperlink-21.0.0.tar.gz", hash = "sha256:427af957daa58bc909471c6c40f74c5450fa123dd093fc53efd2e91d2705a56b"},
@ -979,36 +810,6 @@ oauthlib = [
promise = [ promise = [
{file = "promise-2.3.tar.gz", hash = "sha256:dfd18337c523ba4b6a58801c164c1904a9d4d1b1747c7d5dbf45b693a49d93d0"}, {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 = [ pycodestyle = [
{file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"},
{file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, {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-py3-none-any.whl", hash = "sha256:934d73fbba91b0483d3857d1aff50e96b2a892384ee2c17417ed3203f173fca1"},
{file = "PyJWT-2.1.0.tar.gz", hash = "sha256:fba44e7898bbca160a2b2b501f492824fc8382485d3a6f11ba5d0c1937ce6130"}, {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 = [ python-dotenv = [
{file = "python-dotenv-0.17.1.tar.gz", hash = "sha256:b1ae5e9643d5ed987fc57cc2583021e38db531946518130777734f9589b3141f"}, {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"}, {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-py2.py3-none-any.whl", hash = "sha256:ced8b23dceb22134307c1b8abfa523da14198793d9787ac838e70e29e77458d4"},
{file = "semver-2.13.0.tar.gz", hash = "sha256:fa0fe2722ee1c3f57eac478820c3a5ae2f624af8264cbdf9000c980ff7f75e3f"}, {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 = [ six = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, {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-cp37-cp37m-win_amd64.whl", hash = "sha256:d95803193561a243cb0401b0567c6b7987d3f2a67046770e1dccd1c9e49a9780"},
{file = "Twisted-20.3.0.tar.bz2", hash = "sha256:d72c55b5d56e176563b91d11952d13b01af8725c623e498db5507b6614fc1e10"}, {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 = [ typing-extensions = [
{file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"}, {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"}, {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-py2.py3-none-any.whl", hash = "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4"},
{file = "urllib3-1.26.6.tar.gz", hash = "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"}, {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 = [ websockets = [
{file = "websockets-9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d144b350045c53c8ff09aa1cfa955012dd32f00c7e0862c199edcabb1a8b32da"}, {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"}, {file = "websockets-9.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:b4ad84b156cf50529b8ac5cc1638c2cf8680490e3fccb6121316c8c02620a2e4"},

@ -8,7 +8,6 @@ authors = ["Your Name <you@example.com>"]
python = "^3.7" python = "^3.7"
Django = "^3.2.3" Django = "^3.2.3"
django-rest-framework = "^0.1.0" django-rest-framework = "^0.1.0"
channels = "^3.0.3"
djangorestframework-dataclasses = "^0.9" djangorestframework-dataclasses = "^0.9"
social-auth-app-django = "^4.0.0" social-auth-app-django = "^4.0.0"
python-dotenv = "^0.17.1" python-dotenv = "^0.17.1"
@ -23,7 +22,7 @@ requests = "^2.25.1"
pyga = "^2.6.2" pyga = "^2.6.2"
django-cors-headers = "^3.7.0" django-cors-headers = "^3.7.0"
cryptography = "3.0" cryptography = "3.0"
uvicorn = "^0.14.0" fsspec = "^2021.7.0"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
flake8 = "^3.9.2" flake8 = "^3.9.2"

@ -1,3 +1,5 @@
import fsspec
import os
import asyncio import asyncio
import random import random
from django.conf import settings from django.conf import settings
@ -15,8 +17,9 @@ from rest_framework.serializers import ModelSerializer, Serializer
from rest_framework_dataclasses.serializers import DataclassSerializer from rest_framework_dataclasses.serializers import DataclassSerializer
from social_django.models import UserSocialAuth from social_django.models import UserSocialAuth
from typing import List 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 .sponsors import check_is_sponsor, check_is_sponsor_cached
from .models import Config, Gateway, User from .models import Config, Gateway, User
@ -74,19 +77,26 @@ class AppVersionViewSet(ListModelMixin, GenericViewSet):
queryset = '' queryset = ''
def _get_versions(self): 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 = [ plugins = [
x.name for x in dir.iterdir() os.path.basename(x['name'])
if x.is_dir() and x.name not in [ for x in fs.listdir(dir)
if x['type'] == 'directory' and os.path.basename(x['name'])
not in [
'tabby-web-container', 'tabby-web-container',
'tabby-web-demo', 'tabby-web-demo',
] ]
] ]
return AppVersion( return AppVersion(
version=dir.name, version=os.path.basename(dir),
plugins=plugins, plugins=plugins,
) )
@ -166,7 +176,7 @@ class InstanceInfoViewSet(RetrieveModelMixin, GenericViewSet):
class NoGatewaysError(APIException): class NoGatewaysError(APIException):
status_code = status.HTTP_503_SERVICE_UNAVAILABLE status_code = status.HTTP_503_SERVICE_UNAVAILABLE
default_detail ='No connection gateways available.' default_detail = 'No connection gateways available.'
default_code = 'no_gateways' default_code = 'no_gateways'

@ -4,7 +4,6 @@ import os
import secrets import secrets
import ssl import ssl
import websockets import websockets
from channels.generic.websocket import AsyncWebsocketConsumer
from django.conf import settings from django.conf import settings
from urllib.parse import quote from urllib.parse import quote
@ -91,35 +90,3 @@ class GatewayAdminConnection:
async def close(self): async def close(self):
await self.socket.close() await self.socket.close()
await self.context.__aexit__(None, None, None) 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)

@ -1,3 +1,4 @@
import fsspec
import logging import logging
import requests import requests
import shutil import shutil
@ -6,6 +7,7 @@ import tempfile
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.conf import settings from django.conf import settings
from pathlib import Path from pathlib import Path
from urllib.parse import urlparse
class Command(BaseCommand): class Command(BaseCommand):
@ -16,7 +18,9 @@ class Command(BaseCommand):
def handle(self, *args, **options): def handle(self, *args, **options):
version = options['version'] 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 = [ plugin_list = [
'tabby-web-container', 'tabby-web-container',
@ -51,9 +55,11 @@ class Command(BaseCommand):
subprocess.check_call( subprocess.check_call(
['tar', '-xzf', f.name, '-C', str(extraction_tmp)] ['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(): if fs.exists(target):
shutil.rmtree(target) fs.rm(target, recursive=True)
shutil.copytree(tempdir, target) fs.mkdir(target)
target.chmod(0o755) fs.put(str(tempdir), target, recursive=True)

@ -2,7 +2,6 @@ from django.urls import path, re_path, include
from rest_framework import routers from rest_framework import routers
from . import api from . import api
from . import consumers
from . import views from . import views
@ -16,13 +15,9 @@ urlpatterns = [
path('api/1/instance-info', api.InstanceInfoViewSet.as_view({'get': 'retrieve'})), path('api/1/instance-info', api.InstanceInfoViewSet.as_view({'get': 'retrieve'})),
path('api/1/gateways/choose', api.ChooseGatewayViewSet.as_view({'post': '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/<version>/<path:path>', views.AppDistView.as_view()), path('app-dist/<version>/<path:path>', views.AppDistView.as_view()),
path('', include(router.urls)), path('', include(router.urls)),
] ]
websocket_urlpatterns = [
re_path(r'^api/1/gateway/tcp/(?P<host>[^/]+):(?P<port>\d+)$', consumers.TCPConsumer.as_asgi()),
]

@ -1,26 +1,25 @@
import os import fsspec
from django.conf import settings from django.conf import settings
from django.http.response import HttpResponseRedirect
from django.views import static from django.views import static
from rest_framework.views import APIView from rest_framework.views import APIView
from urllib.parse import urlparse
class IndexView(APIView): class IndexView(APIView):
def get(self, request, format=None): 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): class TerminalView(APIView):
def get(self, request, format=None): 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' response['X-Frame-Options'] = 'SAMEORIGIN'
return response return response
class AppDistView(APIView): class AppDistView(APIView):
def get(self, request, version=None, path=None, format=None): 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)) fs = fsspec.filesystem(urlparse(settings.APP_DIST_STORAGE).scheme)
url = f'{settings.APP_DIST_STORAGE}/{version}/{path}'
return HttpResponseRedirect(fs.url(url))
# class BuildView(APIView):
# def get(self, request, path=None, format=None):
# return static.serve(request, path, document_root=str(settings.BASE_DIR / 'build'))

@ -4,15 +4,9 @@ import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tabby.settings') os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tabby.settings')
django.setup() django.setup()
from channels.auth import AuthMiddlewareStack from channels.routing import ProtocolTypeRouter
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application from django.core.asgi import get_asgi_application
from .app.urls import websocket_urlpatterns
application = ProtocolTypeRouter({ application = ProtocolTypeRouter({
'http': get_asgi_application(), 'http': get_asgi_application(),
'websocket': AuthMiddlewareStack(
URLRouter(websocket_urlpatterns),
),
}) })

@ -8,6 +8,7 @@ load_dotenv()
# Build paths inside the project like this: BASE_DIR / 'subdir'. # Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent BASE_DIR = Path(__file__).resolve().parent.parent
FRONTEND_BUILD_DIR = BASE_DIR / '../frontend/build'
# Quick-start development settings - unsuitable for production # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
@ -32,7 +33,6 @@ INSTALLED_APPS = [
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'channels',
'rest_framework', 'rest_framework',
'social_django', 'social_django',
'corsheaders', 'corsheaders',
@ -70,7 +70,6 @@ TEMPLATES = [
}, },
] ]
ASGI_APPLICATION = 'tabby.asgi.application'
WSGI_APPLICATION = 'tabby.wsgi.application' WSGI_APPLICATION = 'tabby.wsgi.application'
DATABASES = { DATABASES = {
@ -138,6 +137,7 @@ LOGGING = {
} }
STATIC_URL = '/static/' STATIC_URL = '/static/'
STATICFILES_DIRS = [FRONTEND_BUILD_DIR]
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
@ -169,7 +169,7 @@ SOCIAL_AUTH_PIPELINE = (
'social_core.pipeline.user.user_details', '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('/') NPM_REGISTRY = os.getenv('NPM_REGISTRY', 'https://registry.npmjs.org').rstrip('/')
FRONTEND_URL = None FRONTEND_URL = None

@ -31,7 +31,7 @@ function start () {
app.set('view engine', 'html') app.set('view engine', 'html')
app.set('views', DIST_FOLDER) app.set('views', DIST_FOLDER)
app.get('*.*', express.static(DIST_FOLDER, { app.get('*.*', express.static(join(DIST_FOLDER, 'static'), {
maxAge: '1y', maxAge: '1y',
})) }))

@ -43,7 +43,7 @@ module.exports = {
output: { output: {
path: outputPath, path: outputPath,
pathinfo: true, pathinfo: true,
publicPath: '/', publicPath: '/static/',
filename: '[name].js', filename: '[name].js',
chunkFilename: '[name].bundle.js', chunkFilename: '[name].bundle.js',
}, },