Compare commits

...

13 Commits

Author SHA1 Message Date
0c2af63888 Implement logs and stats 2022-01-17 20:51:13 +13:00
18ef42023f Change test user 2022-01-17 20:50:32 +13:00
49a49f7ea6 Use server side events 2022-01-17 05:27:26 +00:00
d8b8c0f801 Add cors midleware 2022-01-17 05:26:59 +00:00
9cdc0ad923 Add dependencies 2022-01-17 05:26:27 +00:00
af0954b6ff Add dependencies and use uvicorn 2022-01-17 05:25:02 +00:00
6df31460e8 Add production file 2022-01-17 05:23:16 +00:00
b00e0faedc Remove ports, tty and stdin_open 2022-01-16 07:10:06 +00:00
02097426ba Add user creation script 2022-01-16 07:09:23 +00:00
bd6afffd77 Ignore users.json 2022-01-16 07:07:55 +00:00
f2e4478edc Move and rename 2022-01-16 07:06:45 +00:00
3a5716a2f9 Remove requirements.txt 2022-01-16 07:05:00 +00:00
26490887ee Add users, docker.sock 2022-01-16 07:04:39 +00:00
11 changed files with 105 additions and 45 deletions

3
.gitignore vendored
View File

@@ -4,7 +4,8 @@ __pycache__/
*.py[cod] *.py[cod]
*$py.class *$py.class
requirements.txt requirements.txt
users.json
app/users.json
# C extensions # C extensions
*.so *.so

View File

@@ -1,11 +1,14 @@
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9-slim FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9-slim
ENV DOCKER=1 ENV DOCKER=1
COPY requirements.txt /app/requirements.txt
RUN pip install \ RUN pip install \
python-jose \ python-jose \
passlib \ passlib \
python-multipart \ python-multipart \
docker \ docker \
aiodocker aiodocker \
sse-starlette \
anyio
COPY ./app /app/app COPY ./app /app/app
CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"]

View File

@@ -12,6 +12,7 @@ passlib = "*"
python-multipart = "*" python-multipart = "*"
docker = "*" docker = "*"
aiodocker = "*" aiodocker = "*"
sse-starlette = "*"
[dev-packages] [dev-packages]
pytest = "*" pytest = "*"

View File

@@ -1,7 +1,16 @@
from fastapi import FastAPI, Depends from fastapi import FastAPI, Depends
from fastapi.middleware.cors import CORSMiddleware
from app import auth, user, server from app import auth, user, server
from os import getenv from os import getenv
app = FastAPI() app = FastAPI(docs_url="/")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
dependencies = list() dependencies = list()
if not getenv('DISABLE_AUTH'): if not getenv('DISABLE_AUTH'):

View File

@@ -1,7 +1,9 @@
from fastapi import APIRouter, HTTPException, status, WebSocket from fastapi import APIRouter, HTTPException, status, WebSocket, Request
from sse_starlette.sse import EventSourceResponse
import base64 import base64
import docker import docker
import aiodocker import aiodocker
from asyncio import sleep
router = APIRouter() router = APIRouter()
client = docker.from_env() client = docker.from_env()
@@ -42,24 +44,33 @@ async def commnd(server, command):
return f"{server} {base64.urlsafe_b64decode(command).decode('utf_8')}" return f"{server} {base64.urlsafe_b64decode(command).decode('utf_8')}"
@router.websocket("/server/{server}/logs") @router.get("/server/{server}/logs")
async def logs(websocket: WebSocket, server: str): async def logs(server: str, request: Request, follow: bool = True, tail: int = 1000):
await websocket.accept()
container = await getContainer(server) container = await getContainer(server)
async for line in container.log(stdout=True, follow=True, tail=5000): #event_generator = logGenerator(request, server)
#print(line) if follow:
await websocket.send_bytes(line) return EventSourceResponse(logStream(container, tail))
print("Closed") else:
return server return await container.log(stdout=True, follow=False, tail=tail)
async def logStream(container, tail):
async for line in container.log(stdout=True, follow=True, tail=tail):
yield line
yield '\n'
@router.get("/server/{server}/stats") @router.get("/server/{server}/stats")
async def stats(websocket: WebSocket, server: str): async def stats(server: str, request: Request, stream: bool = True, delay: int = 2):
await websocket.accept()
container = await getContainer(server) container = await getContainer(server)
async for line in container.log(): if stream:
await websocket.send_bytes(line) return EventSourceResponse(statStream(request, container, delay))
return server return await container.stats(stream=False)
async def statStream(request, container, delay):
while True:
yield await container.stats(stream=False)
_delay = delay - 1 if delay else 0
print(_delay)
await sleep(_delay)
async def getContainer(server): async def getContainer(server):
return await docker.containers.get(server) return await docker.containers.get(server)

18
app/useradd.py Normal file
View File

@@ -0,0 +1,18 @@
from distutils.fancy_getopt import fancy_getopt
from site import USER_BASE
from passlib.context import CryptContext
from json import load, dump
from sys import argv
with open("app/users.json", 'r+') as f:
fake_users_db = load(f)
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
fake_users_db[argv[1]] = {"username": argv[1], "hashed_password": pwd_context.hash(argv[2]),
"disabled": False, "servers": argv[3:]}
f.seek(0)
dump(fake_users_db, f, indent=2)
print(fake_users_db)

10
app/users.json Normal file → Executable file
View File

@@ -1,10 +0,0 @@
{
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "johndoe@example.com",
"hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
"disabled": "False",
"servers": ["a", "b","minecraft"]
}
}

View File

@@ -1,14 +1,14 @@
version: '3.7' version: '3.7'
services: services:
app: api:
build: . build: .
env_file: env_file:
- .env - .env
ports: ports:
- 8000:8000 - 8000:8000
tty: true
stdin_open: true
volumes: volumes:
- ./app:/app/app - ./app:/app/app
- /var/run/docker.sock:/var/run/docker.sock
- ./users.json:/app/app/users.json
command: uvicorn app.main:app --host 0.0.0.0 --reload command: uvicorn app.main:app --host 0.0.0.0 --reload

View File

@@ -5,10 +5,7 @@ services:
build: . build: .
env_file: env_file:
- .env - .env
ports:
- 8000:80
tty: true
stdin_open: true
volumes: volumes:
- ./app:/app/app - ./app:/app/app
- /var/run/docker.sock:/var/run/docker.sock
command: pytest app/test/test_main.py -s command: pytest app/test/test_main.py -s

View File

@@ -1,13 +1,33 @@
version: '3.7' version: '3.7'
services: services:
app: api:
build: . build: ./
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./users.json:/app/app/users.json
restart: unless-stopped
networks:
- caddy
labels:
caddy: console
caddy.reverse_proxy: "{{upstreams 80}}"
#caddy.tls: "admin@localhost"
caddy.tls: "internal"
env_file: env_file:
- .env - .env
caddy:
image: lucaslorentz/caddy-docker-proxy:ci-alpine
ports: ports:
- 8000:80 - 80:80
tty: true - 443:443
stdin_open: true volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- caddy
restart: unless-stopped
networks:
caddy:
name: caddy

10
users.json.sample Normal file
View File

@@ -0,0 +1,10 @@
{
"test": {
"username": "test",
"hashed_password": "$2b$12$VS1k1fdA4x2EeF1a/LMIyex.evEQGF5EsviIcG22S55YO8uUQCT7q",
"disabled": false,
"servers": [
"test"
]
}
}