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

View File

@ -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

View File

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

View File

@ -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

279
backend/poetry.lock generated
View File

@ -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"},

View File

@ -8,7 +8,6 @@ authors = ["Your Name <you@example.com>"]
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"

View File

@ -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'

View File

@ -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)

View File

@ -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)

View File

@ -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/<version>/<path:path>', views.AppDistView.as_view()),
path('', include(router.urls)),
]
websocket_urlpatterns = [
re_path(r'^api/1/gateway/tcp/(?P<host>[^/]+):(?P<port>\d+)$', consumers.TCPConsumer.as_asgi()),
]

View File

@ -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))

View File

@ -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),
),
})

View File

@ -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

View File

@ -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',
}))

View File

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