|
@@ -0,0 +1,69 @@
|
|
|
|
|
+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: ROOT PATH FOR NGINX
|
|
|
|
|
+# Why: When Nginx routes traffic from yourdomain.com/patch-manager/
|
|
|
|
|
+# to this sidecar, FastAPI needs to know its "base" URL is no longer "/".
|
|
|
|
|
+# If we don't set this, the app will try to load CSS and submit forms
|
|
|
|
|
+# to the wrong root URL, resulting in 404 errors.
|
|
|
|
|
+# =====================================================================
|
|
|
|
|
+app = FastAPI(root_path="/patch-manager")
|
|
|
|
|
+
|
|
|
|
|
+app.mount("/static", StaticFiles(directory="static"), name="static")
|
|
|
|
|
+templates = Jinja2Templates(directory="templates")
|
|
|
|
|
+
|
|
|
|
|
+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)
|
|
|
|
|
+
|
|
|
|
|
+ # Notice we redirect to "/" here. Because we set root_path="/patch-manager",
|
|
|
|
|
+ # FastAPI automatically translates this redirect to "/patch-manager/"!
|
|
|
|
|
+ return RedirectResponse(url="/", status_code=303)
|
|
|
|
|
+
|
|
|
|
|
+@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="/", 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="/", status_code=303)
|