mirror of
https://github.com/Eugeny/tabby-web.git
synced 2026-08-17 06:46:05 +01:00
fixup
This commit is contained in:
22
README.md
Normal file
22
README.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# Using Docker images
|
||||||
|
|
||||||
|
Tabby Web consists of two Docker images - `backend` and `frontend`. See an example set up in `docker-compose.yml`
|
||||||
|
|
||||||
|
## Environment variables
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
|
||||||
|
* `BACKEND_URL`
|
||||||
|
* `WEB_CONCURRENCY`
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
|
||||||
|
* `DATABASE_URL` (required).
|
||||||
|
* `FRONTEND_URL`
|
||||||
|
* `APP_DIST_STORAGE`: a `file://`, `s3://`, or `gcs://` URL to store app distros in.
|
||||||
|
* `SOCIAL_AUTH_*_KEY` & `SOCIAL_AUTH_*_SECRET`: social login credentials, supported providers are `GITHUB`, `GITLAB`, `MICROSOFT_GRAPH` and `GOOGLE_OAUTH2`.
|
||||||
|
* `ENABLE_HOMEPAGE`: set to `False` to disable the homepage and always redirect to the app.
|
||||||
|
|
||||||
|
## Installing Tabby app versions
|
||||||
|
|
||||||
|
* `docker-compose run backend ./manage.py add_version 1.0.156-nightly.1`
|
||||||
0
app-dist/.gitkeep
Normal file
0
app-dist/.gitkeep
Normal file
@@ -81,6 +81,7 @@ class AppVersionViewSet(ListModelMixin, GenericViewSet):
|
|||||||
return [
|
return [
|
||||||
self._get_version(x['name'])
|
self._get_version(x['name'])
|
||||||
for x in fs.listdir(settings.APP_DIST_STORAGE)
|
for x in fs.listdir(settings.APP_DIST_STORAGE)
|
||||||
|
if x['type'] == 'directory'
|
||||||
]
|
]
|
||||||
|
|
||||||
def _get_version(self, dir):
|
def _get_version(self, dir):
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import fsspec
|
import fsspec
|
||||||
|
import os
|
||||||
|
from fsspec.implementations.local import LocalFileSystem
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.http.response import HttpResponseRedirect
|
from django.http.response import FileResponse, HttpResponseNotFound, 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
|
from urllib.parse import urlparse
|
||||||
@@ -24,4 +26,9 @@ class AppDistView(APIView):
|
|||||||
def get(self, request, version=None, path=None, format=None):
|
def get(self, request, version=None, path=None, format=None):
|
||||||
fs = fsspec.filesystem(urlparse(settings.APP_DIST_STORAGE).scheme)
|
fs = fsspec.filesystem(urlparse(settings.APP_DIST_STORAGE).scheme)
|
||||||
url = f'{settings.APP_DIST_STORAGE}/{version}/{path}'
|
url = f'{settings.APP_DIST_STORAGE}/{version}/{path}'
|
||||||
return HttpResponseRedirect(fs.url(url))
|
if isinstance(fs, LocalFileSystem):
|
||||||
|
if not fs.exists(url):
|
||||||
|
return HttpResponseNotFound()
|
||||||
|
return FileResponse(fs.open(url), filename=os.path.basename(url))
|
||||||
|
else:
|
||||||
|
return HttpResponseRedirect(fs.url(url))
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
from django.views.static import serve
|
from django.views.static import serve
|
||||||
from .app.urls import urlpatterns as app_urlpatterns
|
from .app.urls import urlpatterns as app_urlpatterns
|
||||||
@@ -8,7 +9,7 @@ urlpatterns = [
|
|||||||
path('', include(app_urlpatterns)),
|
path('', include(app_urlpatterns)),
|
||||||
path('api/1/auth/social/', include('social_django.urls', namespace='social')),
|
path('api/1/auth/social/', include('social_django.urls', namespace='social')),
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
path(f'{settings.STATIC_URL.strip("/")}/<path:path>', serve, kwargs={
|
# path(f'{settings.STATIC_URL.strip("/")}/<path:path>', serve, kwargs={
|
||||||
'document_root': settings.STATIC_ROOT,
|
# 'document_root': settings.STATIC_ROOT,
|
||||||
}),
|
# }),
|
||||||
]
|
] + staticfiles_urlpatterns(settings.STATIC_URL)
|
||||||
|
|||||||
21
docker-compose.yml
Normal file
21
docker-compose.yml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
services:
|
||||||
|
frontend:
|
||||||
|
build: frontend
|
||||||
|
ports:
|
||||||
|
- 9090:80
|
||||||
|
environment:
|
||||||
|
- PORT=80
|
||||||
|
- BACKEND_URL=http://localhost:9091
|
||||||
|
backend:
|
||||||
|
build: backend
|
||||||
|
ports:
|
||||||
|
- 9091:80
|
||||||
|
volumes:
|
||||||
|
- ./app-dist:/app-dist
|
||||||
|
environment:
|
||||||
|
- DATABASE_URL
|
||||||
|
- PORT=80
|
||||||
|
- FRONTEND_URL=http://localhost:9090
|
||||||
|
- ENABLE_HOMEPAGE=False
|
||||||
|
- DEBUG=False
|
||||||
|
- APP_DIST_STORAGE=file:///app-dist
|
||||||
@@ -7,8 +7,6 @@ steps:
|
|||||||
- '${_DOCKER_TAG}'
|
- '${_DOCKER_TAG}'
|
||||||
- '--cache-from'
|
- '--cache-from'
|
||||||
- '${_DOCKER_TAG}'
|
- '${_DOCKER_TAG}'
|
||||||
- '--build-arg'
|
|
||||||
- 'BACKEND_URL=${_BACKEND_URL}'
|
|
||||||
- '.'
|
- '.'
|
||||||
|
|
||||||
images: ['${_DOCKER_TAG}']
|
images: ['${_DOCKER_TAG}']
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
<meta property="twitter:title" content="Tabby - a terminal for a more modern age">
|
<meta property="twitter:title" content="Tabby - a terminal for a more modern age">
|
||||||
<meta property="twitter:description" content="Tabby is a free and open source SSH, local and Telnet terminal with everything you'll ever need.">
|
<meta property="twitter:description" content="Tabby is a free and open source SSH, local and Telnet terminal with everything you'll ever need.">
|
||||||
<meta property="twitter:image" content="https://user-images.githubusercontent.com/161476/126016449-a053012a-e322-48ed-a2ab-3ed4f3281465.png">
|
<meta property="twitter:image" content="https://user-images.githubusercontent.com/161476/126016449-a053012a-e322-48ed-a2ab-3ed4f3281465.png">
|
||||||
|
|
||||||
|
<meta property="x-tabby-web-backend-url" content="{{backendURL}}">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<app></app>
|
<app></app>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { enableProdMode } from '@angular/core'
|
|||||||
import { ngExpressEngine } from '@nguniversal/express-engine'
|
import { ngExpressEngine } from '@nguniversal/express-engine'
|
||||||
|
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
|
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
|
|
||||||
|
|
||||||
@@ -42,7 +43,19 @@ function start () {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
app.get(['/', '/app', '/login'], (req, res) => {
|
app.get(['/', '/app', '/login'], (req, res) => {
|
||||||
res.render('index', { req })
|
res.render(
|
||||||
|
'index',
|
||||||
|
{
|
||||||
|
req,
|
||||||
|
providers: [
|
||||||
|
{ provide: 'BACKEND_URL', useValue: process.env.BACKEND_URL ?? '' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
(err: Error, html: string) => {
|
||||||
|
html = html.replace('{{backendURL}}', process.env.BACKEND_URL ?? '')
|
||||||
|
res.status(err ? 500 : 200).send(html || err.message)
|
||||||
|
},
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
app.get(['/terminal'], (req, res) => {
|
app.get(['/terminal'], (req, res) => {
|
||||||
|
|||||||
@@ -1,12 +1,23 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Inject, Injectable, Optional } from '@angular/core'
|
||||||
|
|
||||||
declare const BACKEND_URL: any
|
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class CommonService {
|
export class CommonService {
|
||||||
backendURL: string = BACKEND_URL
|
backendURL: string
|
||||||
|
|
||||||
constructor () {
|
constructor (@Inject('BACKEND_URL') @Optional() ssrBackendURL: string) {
|
||||||
|
const tag = (document.querySelector('meta[property=x-tabby-web-backend-url]') as HTMLMetaElement)
|
||||||
|
if (ssrBackendURL) {
|
||||||
|
this.backendURL = ssrBackendURL
|
||||||
|
tag.content = ssrBackendURL
|
||||||
|
} else {
|
||||||
|
if (tag.content && !tag.content.startsWith('{{')) {
|
||||||
|
this.backendURL = tag.content
|
||||||
|
} else {
|
||||||
|
this.backendURL = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(this.backendURL)
|
||||||
if (this.backendURL.endsWith('/')) {
|
if (this.backendURL.endsWith('/')) {
|
||||||
this.backendURL = this.backendURL.slice(0, -1)
|
this.backendURL = this.backendURL.slice(0, -1)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import './terminal-styles.scss'
|
import './terminal-styles.scss'
|
||||||
|
|
||||||
async function start () {
|
async function start () {
|
||||||
|
window['__filename'] = ''
|
||||||
|
|
||||||
await new Promise<void>(resolve => {
|
await new Promise<void>(resolve => {
|
||||||
window.addEventListener('message', event => {
|
window.addEventListener('message', event => {
|
||||||
if (event.data === 'connector-ready') {
|
if (event.data === 'connector-ready') {
|
||||||
|
|||||||
@@ -953,6 +953,11 @@ async-foreach@^0.1.3:
|
|||||||
resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542"
|
resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542"
|
||||||
integrity sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=
|
integrity sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=
|
||||||
|
|
||||||
|
async@~3.2.0:
|
||||||
|
version "3.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/async/-/async-3.2.1.tgz#d3274ec66d107a47476a4c49136aacdb00665fc8"
|
||||||
|
integrity sha512-XdD5lRO/87udXCMC9meWdYiR+Nq6ZjUfXidViUZGu2F1MO4T3XwZ1et0hb2++BgLfhyJwy44BGB/yx80ABx8hg==
|
||||||
|
|
||||||
asynckit@^0.4.0:
|
asynckit@^0.4.0:
|
||||||
version "0.4.0"
|
version "0.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||||
@@ -2898,6 +2903,13 @@ lru-cache@^6.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
yallist "^4.0.0"
|
yallist "^4.0.0"
|
||||||
|
|
||||||
|
lru-cache@~5.1.1:
|
||||||
|
version "5.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
|
||||||
|
integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
|
||||||
|
dependencies:
|
||||||
|
yallist "^3.0.2"
|
||||||
|
|
||||||
magic-string@^0.25.0:
|
magic-string@^0.25.0:
|
||||||
version "0.25.7"
|
version "0.25.7"
|
||||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"
|
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"
|
||||||
@@ -3064,6 +3076,20 @@ ms@2.1.2:
|
|||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||||
|
|
||||||
|
mustache-express@^1.3.1:
|
||||||
|
version "1.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/mustache-express/-/mustache-express-1.3.1.tgz#b5144513ab79a503c87aa8cb16fe019d2f61d9f9"
|
||||||
|
integrity sha512-RSSzrvM+CVAk9217dkWSNYyl6c2JnesNn6zaZ8+FvZSn8aLxY9l4kTnYqIoiE8GxdLyVQL2ak7XlMZS6t/l8YA==
|
||||||
|
dependencies:
|
||||||
|
async "~3.2.0"
|
||||||
|
lru-cache "~5.1.1"
|
||||||
|
mustache "^4.2.0"
|
||||||
|
|
||||||
|
mustache@^4.2.0:
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64"
|
||||||
|
integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==
|
||||||
|
|
||||||
nan@^2.13.2:
|
nan@^2.13.2:
|
||||||
version "2.14.2"
|
version "2.14.2"
|
||||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
|
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
|
||||||
@@ -4950,6 +4976,11 @@ y18n@^5.0.5:
|
|||||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
|
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
|
||||||
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
|
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
|
||||||
|
|
||||||
|
yallist@^3.0.2:
|
||||||
|
version "3.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
|
||||||
|
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
|
||||||
|
|
||||||
yallist@^4.0.0:
|
yallist@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
|
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
|
||||||
|
|||||||
Reference in New Issue
Block a user