|
|
@@ -1,32 +1,34 @@
|
|
|
import os
|
|
|
import shutil
|
|
|
+import logging
|
|
|
+import tempfile
|
|
|
from fastapi import FastAPI, Request, File, UploadFile, Form
|
|
|
-from fastapi.responses import HTMLResponse, RedirectResponse
|
|
|
+from fastapi.responses import HTMLResponse, RedirectResponse, FileResponse
|
|
|
from fastapi.templating import Jinja2Templates
|
|
|
from fastapi.staticfiles import StaticFiles
|
|
|
|
|
|
-app = FastAPI()
|
|
|
+# Setup logging
|
|
|
+logging.basicConfig(level=logging.INFO)
|
|
|
+logger = logging.getLogger("overlay-manager")
|
|
|
+
|
|
|
+# root_path ensures FastAPI knows it lives at /patch-manager/
|
|
|
+app = FastAPI(root_path="/patch-manager")
|
|
|
|
|
|
-# 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 = []
|
|
|
- dirs = set([""]) # Always include the root directory as an option
|
|
|
+ dirs = set([""])
|
|
|
if os.path.exists(OVERLAY_DIR):
|
|
|
for root, dirnames, filenames in os.walk(OVERLAY_DIR):
|
|
|
- # Calculate the relative directory path
|
|
|
rel_root = os.path.relpath(root, OVERLAY_DIR)
|
|
|
if rel_root != ".":
|
|
|
dirs.add(rel_root)
|
|
|
-
|
|
|
for filename in filenames:
|
|
|
rel_path = os.path.relpath(os.path.join(root, filename), OVERLAY_DIR)
|
|
|
files.append(rel_path)
|
|
|
@@ -34,9 +36,21 @@ async def index(request: Request):
|
|
|
return templates.TemplateResponse("index.html", {
|
|
|
"request": request,
|
|
|
"files": sorted(files),
|
|
|
- "dirs": sorted(list(dirs)) # Pass the sorted directories to the template dropdown
|
|
|
+ "dirs": sorted(list(dirs))
|
|
|
})
|
|
|
|
|
|
+@app.get("/download-all")
|
|
|
+async def download_all():
|
|
|
+ """Zips the entire /srv directory and serves it as a backup."""
|
|
|
+ with tempfile.TemporaryDirectory() as tmpdir:
|
|
|
+ zip_base = os.path.join(tmpdir, "backup")
|
|
|
+ shutil.make_archive(zip_base, 'zip', OVERLAY_DIR)
|
|
|
+ return FileResponse(
|
|
|
+ path=f"{zip_base}.zip",
|
|
|
+ filename="ardupilot_overlays_backup.zip",
|
|
|
+ media_type='application/zip'
|
|
|
+ )
|
|
|
+
|
|
|
@app.post("/upload")
|
|
|
async def upload_file(file: UploadFile = File(...), target_path: str = Form("")):
|
|
|
save_dir = os.path.join(OVERLAY_DIR, target_path.strip("/"))
|
|
|
@@ -44,38 +58,49 @@ async def upload_file(file: UploadFile = File(...), target_path: str = Form(""))
|
|
|
file_location = os.path.join(save_dir, file.filename)
|
|
|
with open(file_location, "wb+") as file_object:
|
|
|
shutil.copyfileobj(file.file, file_object)
|
|
|
-
|
|
|
- return RedirectResponse(url="/patch-manager/", status_code=303)
|
|
|
+ return RedirectResponse(url="./", status_code=303)
|
|
|
|
|
|
@app.post("/create_folder")
|
|
|
async def create_folder(folder_path: str = Form(...)):
|
|
|
save_dir = os.path.join(OVERLAY_DIR, folder_path.strip("/"))
|
|
|
os.makedirs(save_dir, exist_ok=True)
|
|
|
- return RedirectResponse(url="/patch-manager/", status_code=303)
|
|
|
+ return RedirectResponse(url="./", status_code=303)
|
|
|
|
|
|
@app.post("/delete")
|
|
|
async def delete_file(filepath: str = Form(...)):
|
|
|
- target = os.path.join(OVERLAY_DIR, filepath)
|
|
|
+ target = os.path.join(OVERLAY_DIR, filepath.lstrip("/"))
|
|
|
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)
|
|
|
+ if os.path.isdir(target):
|
|
|
+ shutil.rmtree(target)
|
|
|
+ else:
|
|
|
+ os.remove(target)
|
|
|
+ return RedirectResponse(url="./", 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):
|
|
|
+ clean_path = filepath.lstrip("/")
|
|
|
+ target = os.path.join(OVERLAY_DIR, clean_path)
|
|
|
+ logger.info(f"Attempting to edit: {target}")
|
|
|
+
|
|
|
+ if not os.path.exists(target):
|
|
|
+ return HTMLResponse(content=f"<h1>404 Not Found</h1><p>File {target} missing.</p><a href='./'>Back</a>", status_code=404)
|
|
|
+
|
|
|
+ try:
|
|
|
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})
|
|
|
+ except Exception as e:
|
|
|
+ return HTMLResponse(content=f"<h1>Error</h1><p>{str(e)}</p>", status_code=500)
|
|
|
+
|
|
|
+ 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)
|
|
|
+ target = os.path.join(OVERLAY_DIR, filepath.lstrip("/"))
|
|
|
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)
|
|
|
+ return RedirectResponse(url="./", status_code=303)
|