from fastapi import FastAPI, UploadFile, File, HTTPException import os, subprocess, re from datetime import datetime ZIM_DIR = "/zims" app = FastAPI() def safe(name): return re.sub(r'[^\w\-. ]', '_', name)[:200] @app.post("/api/upload") async def upload(file: UploadFile = File(...)): if not file.filename.lower().endswith(".zim"): raise HTTPException(400, "ZIMs only") fname = safe(file.filename) path = os.path.join(ZIM_DIR, fname) with open(path, "wb") as f: while True: chunk = await file.read(8 * 1024 * 1024) if not chunk: break f.write(chunk) return {"ok": True, "filename": fname, "size": os.path.getsize(path)} @app.delete("/api/delete/{filename}") def delete(filename: str): fn = safe(filename) path = os.path.join(ZIM_DIR, fn) if not os.path.exists(path): # Try to find a file that starts with the base name (handles date-stripped catalog names) base = fn[:-4] if fn.endswith('.zim') else fn matches = [f for f in os.listdir(ZIM_DIR) if f.startswith(base) and f.endswith('.zim')] if not matches: raise HTTPException(404, f"No file matching {fn} or {base}*.zim") if len(matches) > 1: raise HTTPException(409, f"Multiple matches: {matches}") path = os.path.join(ZIM_DIR, matches[0]) os.remove(path) return {"ok": True, "deleted": os.path.basename(path)} @app.post("/api/reload") def reload_kiwix(): try: subprocess.run(["docker", "restart", "kiwix"], check=True, capture_output=True, timeout=60) return {"ok": True} except Exception as e: raise HTTPException(500, str(e))