import os import shutil from fastapi import FastAPI, Request, File, UploadFile, Form from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.templating import Jinja2Templates from fastapi.staticfiles import StaticFiles # ===================================================================== # MODIFICATION START: REMOVED ROOT_PATH # Reason: We removed the `root_path="/patch-manager"` argument. # Because Nginx already strips the prefix before passing traffic to # this container, setting it here was causing FastAPI to double-process # the route and throw internal 404s. Standard FastAPI initialization is best. # ===================================================================== app = FastAPI() # ===================================================================== # MODIFICATION END # ===================================================================== # Failsafe: Create the directory inside the container if it somehow gets missed os.makedirs("static", exist_ok=True) app.mount("/static", StaticFiles(directory="static"), name="static") templates = Jinja2Templates(directory="templates") # The persistent directory for your custom code OVERLAY_DIR = "/srv" @app.get("/", response_class=HTMLResponse) async def index(request: Request): files = [] if os.path.exists(OVERLAY_DIR): for root, dirs, filenames in os.walk(OVERLAY_DIR): for filename in filenames: rel_path = os.path.relpath(os.path.join(root, filename), OVERLAY_DIR) files.append(rel_path) return templates.TemplateResponse("index.html", {"request": request, "files": sorted(files)}) @app.post("/upload") async def upload_file(file: UploadFile = File(...), target_path: str = Form("")): save_dir = os.path.join(OVERLAY_DIR, target_path.strip("/")) os.makedirs(save_dir, exist_ok=True) file_location = os.path.join(save_dir, file.filename) with open(file_location, "wb+") as file_object: shutil.copyfileobj(file.file, file_object) # ===================================================================== # MODIFICATION START: EXPLICIT NGINX REDIRECTS # Reason: Since we removed root_path, we must explicitly tell the # user's browser to redirect back to the Nginx URL ("/patch-manager/") # after a successful action, otherwise it redirects to the internal "/" # and gets lost on the main ArduPilot page. # ===================================================================== return RedirectResponse(url="/patch-manager/", status_code=303) # ===================================================================== # MODIFICATION END # ===================================================================== @app.post("/delete") async def delete_file(filepath: str = Form(...)): target = os.path.join(OVERLAY_DIR, filepath) if os.path.exists(target): os.remove(target) target_dir = os.path.dirname(target) if not os.listdir(target_dir) and target_dir != OVERLAY_DIR: os.rmdir(target_dir) return RedirectResponse(url="/patch-manager/", status_code=303) @app.get("/edit", response_class=HTMLResponse) async def edit_file(request: Request, filepath: str): target = os.path.join(OVERLAY_DIR, filepath) content = "" if os.path.exists(target): with open(target, "r", encoding="utf-8", errors="ignore") as f: content = f.read() return templates.TemplateResponse("edit.html", {"request": request, "filepath": filepath, "content": content}) @app.post("/edit") async def save_file(filepath: str = Form(...), content: str = Form(...)): target = os.path.join(OVERLAY_DIR, filepath) if os.path.exists(target): with open(target, "w", encoding="utf-8") as f: f.write(content) return RedirectResponse(url="/patch-manager/", status_code=303)