Restructurated backend

This commit is contained in:
DomySh
2022-07-21 01:02:46 +02:00
parent d4cc2f566c
commit 0ed8bb635e
15 changed files with 523 additions and 409 deletions

57
backend/utils/__init__.py Executable file
View File

@@ -0,0 +1,57 @@
import asyncio
from ipaddress import ip_interface
import os, socket, secrets, psutil
import sys
from fastapi_socketio import SocketManager
LOCALHOST_IP = socket.gethostbyname(os.getenv("LOCALHOST_IP","127.0.0.1"))
socketio:SocketManager = None
ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
ROUTERS_DIR = os.path.join(ROOT_DIR,"routers")
ON_DOCKER = len(sys.argv) > 1 and sys.argv[1] == "DOCKER"
DEBUG = len(sys.argv) > 1 and sys.argv[1] == "DEBUG"
FIREGEX_PORT = int(os.getenv("PORT","4444"))
JWT_ALGORITHM: str = "HS256"
API_VERSION = "2.0.0"
async def run_func(func, *args, **kwargs):
if asyncio.iscoroutinefunction(func):
return await func(*args, **kwargs)
else:
return func(*args, **kwargs)
async def refresh_frontend():
await socketio.emit("update","Refresh")
def refactor_name(name:str):
name = name.strip()
while " " in name: name = name.replace(" "," ")
return name
def gen_service_id(db):
while True:
res = secrets.token_hex(8)
if len(db.query('SELECT 1 FROM services WHERE service_id = ?;', res)) == 0:
break
return res
def list_files(mypath):
from os import listdir
from os.path import isfile, join
return [f for f in listdir(mypath) if isfile(join(mypath, f))]
def ip_parse(ip:str):
return str(ip_interface(ip).network)
def ip_family(ip:str):
return "ip6" if ip_interface(ip).version == 6 else "ip"
def get_interfaces():
def _get_interfaces():
for int_name, interfs in psutil.net_if_addrs().items():
for interf in interfs:
if interf.family in [socket.AF_INET, socket.AF_INET6]:
yield {"name": int_name, "addr":interf.address}
return list(_get_interfaces())

106
backend/utils/loader.py Normal file
View File

@@ -0,0 +1,106 @@
import os, httpx, websockets
from sys import prefix
from typing import Callable, List, Union
from fastapi import APIRouter, WebSocket
import asyncio
from starlette.responses import StreamingResponse
from fastapi.responses import FileResponse
from utils import DEBUG, ON_DOCKER, ROUTERS_DIR, list_files, run_func
from utils.models import ResetRequest
REACT_BUILD_DIR: str = "../frontend/build/" if not ON_DOCKER else "frontend/"
REACT_HTML_PATH: str = os.path.join(REACT_BUILD_DIR,"index.html")
async def frontend_debug_proxy(path):
httpc = httpx.AsyncClient()
req = httpc.build_request("GET",f"http://127.0.0.1:{os.getenv('F_PORT','3000')}/"+path)
resp = await httpc.send(req, stream=True)
return StreamingResponse(resp.aiter_bytes(),status_code=resp.status_code)
async def react_deploy(path):
file_request = os.path.join(REACT_BUILD_DIR, path)
if not os.path.isfile(file_request):
return FileResponse(REACT_HTML_PATH, media_type='text/html')
else:
return FileResponse(file_request)
def frontend_deploy(app):
if DEBUG:
async def forward_websocket(ws_a, ws_b):
while True:
data = await ws_a.receive_bytes()
await ws_b.send(data)
async def reverse_websocket(ws_a, ws_b):
while True:
data = await ws_b.recv()
await ws_a.send_text(data)
@app.websocket("/ws")
async def websocket_debug_proxy(ws: WebSocket):
await ws.accept()
async with websockets.connect(f"ws://127.0.0.1:{os.getenv('F_PORT','3000')}/ws") as ws_b_client:
fwd_task = asyncio.create_task(forward_websocket(ws, ws_b_client))
rev_task = asyncio.create_task(reverse_websocket(ws, ws_b_client))
await asyncio.gather(fwd_task, rev_task)
@app.get("/{full_path:path}", include_in_schema=False)
async def catch_all(full_path:str):
if DEBUG:
try:
return await frontend_debug_proxy(full_path)
except Exception:
return {"details":"Frontend not started at "+f"http://127.0.0.1:{os.getenv('F_PORT','3000')}"}
else: return await react_deploy(full_path)
def list_routers():
return [ele[:-3] for ele in list_files(ROUTERS_DIR) if ele != "__init__.py" and " " not in ele and ele.endswith(".py")]
class RouterModule():
router: Union[None, APIRouter]
reset: Union[None, Callable]
startup: Union[None, Callable]
shutdown: Union[None, Callable]
name: str
def __init__(self, router: APIRouter, reset: Callable, startup: Callable, shutdown: Callable, name:str):
self.router = router
self.reset = reset
self.startup = startup
self.shutdown = shutdown
self.name = name
def __repr__(self):
return f"RouterModule(router={self.router}, reset={self.reset}, startup={self.startup}, shutdown={self.shutdown})"
def get_router_modules():
res: List[RouterModule] = []
for route in list_routers():
module = getattr(__import__(f"routers.{route}"), route, None)
if module:
res.append(RouterModule(
router=getattr(module, "app", None),
reset=getattr(module, "reset", None),
startup=getattr(module, "startup", None),
shutdown=getattr(module, "shutdown", None),
name=route
))
return res
def load_routers(app):
resets, startups, shutdowns = [], [], []
for router in get_router_modules():
if router.router:
app.include_router(router.router, prefix=f"/{router.name}")
if router.reset:
resets.append(router.reset)
if router.startup:
startups.append(router.startup)
if router.shutdown:
shutdowns.append(router.shutdown)
async def reset(reset_option:ResetRequest):
for func in resets: await run_func(func, reset_option)
async def startup():
for func in startups: await run_func(func)
async def shutdown():
for func in shutdowns: await run_func(func)
return reset, startup, shutdown

28
backend/utils/models.py Normal file
View File

@@ -0,0 +1,28 @@
from typing import Union
from pydantic import BaseModel
class StatusMessageModel(BaseModel):
status:str
class StatusModel(BaseModel):
status: str
loggined: bool
version: str
class PasswordForm(BaseModel):
password: str
class PasswordChangeForm(BaseModel):
password: str
expire: bool
class ChangePasswordModel(BaseModel):
status: str
access_token: Union[str,None]
class IpInterface(BaseModel):
addr: str
name: str
class ResetRequest(BaseModel):
delete:bool