from pathlib import Path from typing import Optional from fastapi import APIRouter from fastapi.exceptions import HTTPException from starlette.responses import FileResponse router_ui = APIRouter() @router_ui.get("/favicon.ico", include_in_schema=False) async def favicon(): return FileResponse(str(Path(__file__).parent / "ui/favicon.ico")) @router_ui.get("/fallback_file.html", include_in_schema=False) async def fallback(): return FileResponse(str(Path(__file__).parent / "ui/fallback_file.html")) @router_ui.get("/ui_version", include_in_schema=False) async def ui_version(): from freqtrade.commands.deploy_commands import read_ui_version uibase = Path(__file__).parent / "ui/installed/" version = read_ui_version(uibase) return { "version": version if version else "not_installed", } @router_ui.get("/{rest_of_path:path}", include_in_schema=False) async def index_html(rest_of_path: str): """ Emulate path fallback to index.html. """ if rest_of_path.startswith("api") or rest_of_path.startswith("."): raise HTTPException(status_code=404, detail="Not Found") uibase = Path(__file__).parent / "ui/installed/" filename = uibase / rest_of_path # It's security relevant to check "relative_to". # Without this, Directory-traversal is possible. media_type: Optional[str] = None if filename.suffix == ".js": # Force text/javascript for .js files - Circumvent faulty system configuration media_type = "application/javascript" if filename.is_file() and filename.is_relative_to(uibase): return FileResponse(str(filename), media_type=media_type) index_file = uibase / "index.html" if not index_file.is_file(): return FileResponse(str(uibase.parent / "fallback_file.html")) # Fall back to index.html, as indicated by vue router docs return FileResponse(str(index_file))