Compare commits
18 Commits
759bab3ff3
...
master
Author | SHA1 | Date | |
---|---|---|---|
0c2af63888 | |||
18ef42023f | |||
49a49f7ea6 | |||
d8b8c0f801 | |||
9cdc0ad923 | |||
af0954b6ff | |||
6df31460e8 | |||
b00e0faedc | |||
02097426ba | |||
bd6afffd77 | |||
f2e4478edc | |||
3a5716a2f9 | |||
26490887ee | |||
9a254575f2 | |||
db218cb8c2 | |||
fd5fc0a85d | |||
7307abf7d8 | |||
b6f41f4d0a |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -4,7 +4,8 @@ __pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
requirements.txt
|
||||
|
||||
users.json
|
||||
app/users.json
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
|
14
Dockerfile
14
Dockerfile
@@ -1,6 +1,14 @@
|
||||
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9-slim
|
||||
ENV DOCKER=1
|
||||
|
||||
COPY requirements.txt /app/requirements.txt
|
||||
RUN pip install -r /app/requirements.txt
|
||||
COPY ./app /app/app
|
||||
RUN pip install \
|
||||
python-jose \
|
||||
passlib \
|
||||
python-multipart \
|
||||
docker \
|
||||
aiodocker \
|
||||
sse-starlette \
|
||||
anyio
|
||||
COPY ./app /app/app
|
||||
|
||||
CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"]
|
||||
|
3
Pipfile
3
Pipfile
@@ -12,6 +12,7 @@ passlib = "*"
|
||||
python-multipart = "*"
|
||||
docker = "*"
|
||||
aiodocker = "*"
|
||||
sse-starlette = "*"
|
||||
|
||||
[dev-packages]
|
||||
pytest = "*"
|
||||
@@ -24,5 +25,5 @@ mypy = "*"
|
||||
python_version = "3.9"
|
||||
|
||||
[scripts]
|
||||
test = "pytest app/test/test_main.py -s"
|
||||
test = "pytest app/test/test_auth.py app/test/test_server.py -W ignore::DeprecationWarning -s"
|
||||
dev = "uvicorn app.main:app --reload"
|
||||
|
16
app/main.py
16
app/main.py
@@ -1,8 +1,20 @@
|
||||
from fastapi import FastAPI, Depends
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from app import auth, user, server
|
||||
from os import getenv
|
||||
app = FastAPI(docs_url="/")
|
||||
|
||||
app = FastAPI()
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
dependencies = list()
|
||||
if not getenv('DISABLE_AUTH'):
|
||||
dependencies.append(Depends(auth.authorise))
|
||||
app.include_router(auth.router)
|
||||
app.include_router(user.router)
|
||||
app.include_router(server.router, dependencies=[Depends(auth.authorise)])
|
||||
app.include_router(server.router, dependencies=dependencies)
|
||||
|
@@ -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 docker
|
||||
import aiodocker
|
||||
from asyncio import sleep
|
||||
|
||||
router = APIRouter()
|
||||
client = docker.from_env()
|
||||
@@ -42,24 +44,33 @@ async def commnd(server, command):
|
||||
return f"{server} {base64.urlsafe_b64decode(command).decode('utf_8')}"
|
||||
|
||||
|
||||
@router.websocket("/server/{server}/logs")
|
||||
async def logs(websocket: WebSocket, server: str):
|
||||
await websocket.accept()
|
||||
@router.get("/server/{server}/logs")
|
||||
async def logs(server: str, request: Request, follow: bool = True, tail: int = 1000):
|
||||
container = await getContainer(server)
|
||||
async for line in container.log(stdout=True, follow=True, tail=5000):
|
||||
#print(line)
|
||||
await websocket.send_bytes(line)
|
||||
print("Closed")
|
||||
return server
|
||||
#event_generator = logGenerator(request, server)
|
||||
if follow:
|
||||
return EventSourceResponse(logStream(container, tail))
|
||||
else:
|
||||
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")
|
||||
async def stats(websocket: WebSocket, server: str):
|
||||
await websocket.accept()
|
||||
async def stats(server: str, request: Request, stream: bool = True, delay: int = 2):
|
||||
container = await getContainer(server)
|
||||
async for line in container.log():
|
||||
await websocket.send_bytes(line)
|
||||
return server
|
||||
if stream:
|
||||
return EventSourceResponse(statStream(request, container, delay))
|
||||
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):
|
||||
return await docker.containers.get(server)
|
21
app/test/test_server.py
Normal file
21
app/test/test_server.py
Normal file
@@ -0,0 +1,21 @@
|
||||
#curl -i -X POST http://localhost:8000/token -H "Content-Type: application/x-www-form-urlencoded" -d "username=johndoe&password=secret"
|
||||
|
||||
# curl -X 'GET' \
|
||||
# 'http://localhost:8000/users/me/' \
|
||||
# -H 'accept: application/json' \
|
||||
# -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJqb2huZG9lIiwiZXhwIjoxNjMxNDQ4MjQ1fQ.DrM92jgRiry0uXBXn-61rRehATW4zDhHUWoGR6lv6Us'
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
import docker
|
||||
|
||||
from server import *
|
||||
|
||||
app = FastAPI()
|
||||
app.include_router(router)
|
||||
testclient = TestClient(app)
|
||||
|
||||
def test_start():
|
||||
#response = testclient.post("/server/minecraft/start")
|
||||
#assert response.status_code == 200
|
||||
pass
|
||||
|
@@ -9,6 +9,3 @@ async def read_users_me(current_user: User = Depends(get_current_active_user)):
|
||||
return current_user
|
||||
|
||||
|
||||
@router.get("/users/me/items/")
|
||||
async def read_own_items(current_user: User = Depends(get_current_active_user)):
|
||||
return [{"item_id": "Foo", "owner": current_user.username}]
|
||||
|
18
app/useradd.py
Normal file
18
app/useradd.py
Normal 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
10
app/users.json
Normal file → Executable 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"]
|
||||
}
|
||||
}
|
@@ -1,14 +1,14 @@
|
||||
version: '3.7'
|
||||
|
||||
services:
|
||||
app:
|
||||
api:
|
||||
build: .
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
- 8000:8000
|
||||
tty: true
|
||||
stdin_open: true
|
||||
volumes:
|
||||
- ./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
|
@@ -5,10 +5,7 @@ services:
|
||||
build: .
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
- 8000:80
|
||||
tty: true
|
||||
stdin_open: true
|
||||
volumes:
|
||||
- ./app:/app/app
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
command: pytest app/test/test_main.py -s
|
@@ -1,13 +1,33 @@
|
||||
version: '3.7'
|
||||
|
||||
services:
|
||||
app:
|
||||
build: .
|
||||
services:
|
||||
api:
|
||||
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
|
||||
ports:
|
||||
- 8000:80
|
||||
tty: true
|
||||
stdin_open: true
|
||||
|
||||
- .env
|
||||
|
||||
caddy:
|
||||
image: lucaslorentz/caddy-docker-proxy:ci-alpine
|
||||
ports:
|
||||
- 80:80
|
||||
- 443:443
|
||||
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
10
users.json.sample
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"test": {
|
||||
"username": "test",
|
||||
"hashed_password": "$2b$12$VS1k1fdA4x2EeF1a/LMIyex.evEQGF5EsviIcG22S55YO8uUQCT7q",
|
||||
"disabled": false,
|
||||
"servers": [
|
||||
"test"
|
||||
]
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user