96 lines
2.8 KiB
Python
96 lines
2.8 KiB
Python
from fastapi import FastAPI, UploadFile, File, HTTPException, Form
|
|
from fastapi.responses import JSONResponse, FileResponse
|
|
from fastapi.staticfiles import StaticFiles
|
|
import os, json, uuid, shutil, re
|
|
from datetime import datetime
|
|
|
|
PDF_DIR = "/files"
|
|
META = "/files/.metadata.json"
|
|
|
|
app = FastAPI()
|
|
|
|
def load_meta():
|
|
if os.path.exists(META):
|
|
with open(META) as f:
|
|
return json.load(f)
|
|
return {}
|
|
|
|
def save_meta(m):
|
|
with open(META, "w") as f:
|
|
json.dump(m, f, indent=2)
|
|
|
|
def safe_name(name):
|
|
return re.sub(r'[^\w\-. ]', '_', name)[:200]
|
|
|
|
@app.get("/api/list")
|
|
def list_pdfs():
|
|
meta = load_meta()
|
|
items = []
|
|
for fid, info in meta.items():
|
|
path = os.path.join(PDF_DIR, info["filename"])
|
|
if os.path.exists(path):
|
|
items.append({
|
|
"id": fid,
|
|
"title": info.get("title", info["filename"]),
|
|
"filename": info["filename"],
|
|
"tag": info.get("tag", ""),
|
|
"size": os.path.getsize(path),
|
|
"uploaded": info.get("uploaded", ""),
|
|
})
|
|
items.sort(key=lambda x: x["uploaded"], reverse=True)
|
|
return items
|
|
|
|
@app.post("/api/upload")
|
|
async def upload(file: UploadFile = File(...), title: str = Form(""), tag: str = Form("")):
|
|
if not file.filename.lower().endswith(".pdf"):
|
|
raise HTTPException(400, "PDFs only")
|
|
fid = uuid.uuid4().hex[:12]
|
|
fname = safe_name(file.filename)
|
|
path = os.path.join(PDF_DIR, f"{fid}_{fname}")
|
|
with open(path, "wb") as f:
|
|
shutil.copyfileobj(file.file, f)
|
|
meta = load_meta()
|
|
meta[fid] = {
|
|
"filename": f"{fid}_{fname}",
|
|
"title": title or fname.rsplit(".", 1)[0],
|
|
"tag": tag,
|
|
"uploaded": datetime.utcnow().isoformat(),
|
|
}
|
|
save_meta(meta)
|
|
return {"id": fid}
|
|
|
|
@app.delete("/api/delete/{fid}")
|
|
def delete(fid: str):
|
|
meta = load_meta()
|
|
if fid not in meta:
|
|
raise HTTPException(404)
|
|
path = os.path.join(PDF_DIR, meta[fid]["filename"])
|
|
if os.path.exists(path):
|
|
os.remove(path)
|
|
del meta[fid]
|
|
save_meta(meta)
|
|
return {"ok": True}
|
|
|
|
@app.get("/view/{fid}")
|
|
def view(fid: str):
|
|
meta = load_meta()
|
|
if fid not in meta:
|
|
raise HTTPException(404)
|
|
path = os.path.join(PDF_DIR, meta[fid]["filename"])
|
|
return FileResponse(path, media_type="application/pdf")
|
|
|
|
@app.patch("/api/update/{fid}")
|
|
async def update(fid: str, tag: str = Form(None), title: str = Form(None)):
|
|
meta = load_meta()
|
|
if fid not in meta:
|
|
raise HTTPException(404)
|
|
if tag is not None:
|
|
meta[fid]["tag"] = tag
|
|
if title is not None and title.strip():
|
|
meta[fid]["title"] = title.strip()
|
|
save_meta(meta)
|
|
return {"ok": True}
|
|
|
|
|
|
app.mount("/", StaticFiles(directory="/static", html=True), name="static")
|