diff --git a/src/backend/server/main.py b/src/backend/server/main.py index 81b145c..94069ea 100644 --- a/src/backend/server/main.py +++ b/src/backend/server/main.py @@ -1,14 +1,16 @@ -from typing import Any +from typing import Any, Optional import datetime +import inspect import logging -from fastapi import FastAPI, Request +from fastapi import BackgroundTasks, FastAPI, Request, WebSocket from fastapi.responses import FileResponse, ORJSONResponse, PlainTextResponse from fastapi.staticfiles import StaticFiles from api import expose from db import users +from plugins import downloader, handler from server import build from utils import config, const, motd, settings, status @@ -60,7 +62,8 @@ async def rebuild(request: Request) -> PlainTextResponse: # Login @app.get('/', response_class=FileResponse) -async def login_page() -> FileResponse: +async def login_page(bg_tasks: BackgroundTasks) -> FileResponse: + bg_tasks.add_task(handler.load_all) return FileResponse('../public/login.html') @@ -128,10 +131,32 @@ async def update_setting(request: Request, id_: str) -> PlainTextResponse: # Plugins -@app.get('/plugins/{plugin}/{endpoint:path}') -@app.put('/plugins/{plugin}/{endpoint:path}') -@app.post('/plugins/{plugin}/{endpoint:path}') -@app.delete('/plugins/{plugin}/{endpoint:path}') +@app.get('/plugins', response_class=FileResponse) +async def plugins_page(request: Request) -> FileResponse: + if database.uuid_exists(request.cookies.get('auth_cookie')): + return FileResponse('../public/plugins.html') + return FileResponse('../public/blocked.html') + + +@app.post('/plugins') +async def install_plugin(request: Request) -> PlainTextResponse: + response = PlainTextResponse() + if not database.uuid_exists(request.cookies.get('auth_cookie')): + response.status_code = 401 + return response + + data = await request.json() + if data.get('url'): + if not downloader.from_url(data.get('url')): + response.status_code = 501 + + return response + + +@app.get('/plugins/api/{plugin}/{endpoint:path}') +@app.put('/plugins/api/{plugin}/{endpoint:path}') +@app.post('/plugins/api/{plugin}/{endpoint:path}') +@app.delete('/plugins/api/{plugin}/{endpoint:path}') async def plugins(request: Request, plugin: str, endpoint: str) -> Any: response = PlainTextResponse() if not database.uuid_exists(request.cookies.get('auth_cookie')): @@ -140,7 +165,46 @@ async def plugins(request: Request, plugin: str, endpoint: str) -> Any: # Remove sensitive cookie(s) request.cookies['auth_cookie'] = '' + + # Run callback in async callback = expose.fetch_callback(plugin, endpoint, request.method) - if callback: - return callback(endpoint, request) + if callback is None: + logging.error(f'Callback for "{plugin}/{endpoint}" does not exist') + response.status_code = 503 + return response + return_value = None + if inspect.iscoroutinefunction(callback): + return_value = await callback(endpoint, request) + else: + # Sync function detected + logging.error(f'Plugin "{plugin}" uses synchronous functions, request blocked') + response.status_code = 503 + if return_value is not None: + return return_value return response + + +@app.websocket('/plugins/wsapi/{plugin}/{endpoint}') +async def ws_plugins(websocket: WebSocket, plugin: str, endpoint: str) -> Any: + response = PlainTextResponse() + if not database.uuid_exists(websocket.cookies.get('auth_cookie')): + response.status_code = 401 + return response + + # Remove sensitive cookie(s) + websocket.cookies['auth_cookie'] = '' + + # Run callback in async + callback = expose.fetch_callback(plugin, endpoint, 'WEBSOCKET') + if callback is None: + logging.error(f'Callback for "{plugin}/{endpoint}" does not exist') + response.status_code = 503 + return response + + if inspect.iscoroutinefunction(callback): + await callback(endpoint, websocket) + else: + # Sync function detected + logging.error(f'Plugin "{plugin}" uses synchronous functions, request blocked') + response.status_code = 503 + return response