Update app.py
Browse files
app.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
| 1 |
from fastapi import FastAPI, UploadFile, Request
|
| 2 |
from fastapi.responses import FileResponse, HTMLResponse
|
| 3 |
-
from fastapi.templating import Jinja2Templates
|
| 4 |
from fastapi.middleware.cors import CORSMiddleware
|
| 5 |
import os
|
| 6 |
import uuid
|
|
@@ -10,83 +9,105 @@ import threading
|
|
| 10 |
app = FastAPI()
|
| 11 |
|
| 12 |
# CORS
|
| 13 |
-
|
| 14 |
app.add_middleware(
|
| 15 |
-
CORSMiddleware,
|
| 16 |
-
allow_origins=["*"],
|
| 17 |
-
allow_credentials=True,
|
| 18 |
-
allow_methods=["*"],
|
| 19 |
-
allow_headers=["*"],
|
| 20 |
)
|
| 21 |
|
| 22 |
-
templates = Jinja2Templates(directory="templates")
|
| 23 |
-
|
| 24 |
UPLOAD_DIR = "uploads"
|
| 25 |
os.makedirs(UPLOAD_DIR, exist_ok=True)
|
| 26 |
|
| 27 |
-
# Store file
|
| 28 |
-
|
| 29 |
file_times = {}
|
|
|
|
| 30 |
|
| 31 |
-
# Serve frontend
|
| 32 |
-
|
| 33 |
@app.get("/", response_class=HTMLResponse)
|
| 34 |
-
def home(
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
# Upload
|
| 38 |
|
|
|
|
| 39 |
@app.post("/upload")
|
| 40 |
async def upload(file: UploadFile):
|
| 41 |
-
file_id = str(uuid.uuid4())[:8]
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
with open(file_path, "wb") as f:
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
# Save upload time
|
| 50 |
-
file_times[file_path] = time.time()
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
def cleanup_files():
|
| 71 |
-
while True:
|
| 72 |
-
now = time.time()
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
time.sleep(60) # check every 1 minute
|
| 82 |
-
```
|
| 83 |
-
|
| 84 |
-
# Start cleanup thread
|
| 85 |
|
| 86 |
threading.Thread(target=cleanup_files, daemon=True).start()
|
| 87 |
|
| 88 |
-
# Run
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
uvicorn.run("app:app", host="0.0.0.0", port=7860, reload=True)
|
|
|
|
| 1 |
from fastapi import FastAPI, UploadFile, Request
|
| 2 |
from fastapi.responses import FileResponse, HTMLResponse
|
|
|
|
| 3 |
from fastapi.middleware.cors import CORSMiddleware
|
| 4 |
import os
|
| 5 |
import uuid
|
|
|
|
| 9 |
app = FastAPI()
|
| 10 |
|
| 11 |
# CORS
|
|
|
|
| 12 |
app.add_middleware(
|
| 13 |
+
CORSMiddleware,
|
| 14 |
+
allow_origins=["*"],
|
| 15 |
+
allow_credentials=True,
|
| 16 |
+
allow_methods=["*"],
|
| 17 |
+
allow_headers=["*"],
|
| 18 |
)
|
| 19 |
|
|
|
|
|
|
|
| 20 |
UPLOAD_DIR = "uploads"
|
| 21 |
os.makedirs(UPLOAD_DIR, exist_ok=True)
|
| 22 |
|
| 23 |
+
# Store file metadata: path -> {time, original_name}
|
|
|
|
| 24 |
file_times = {}
|
| 25 |
+
file_meta = {}
|
| 26 |
|
| 27 |
+
# ββ Serve frontend ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 28 |
@app.get("/", response_class=HTMLResponse)
|
| 29 |
+
def home():
|
| 30 |
+
with open("templates/index.html", "r") as f:
|
| 31 |
+
return HTMLResponse(content=f.read())
|
|
|
|
| 32 |
|
| 33 |
+
# ββ Upload ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 34 |
@app.post("/upload")
|
| 35 |
async def upload(file: UploadFile):
|
| 36 |
+
file_id = str(uuid.uuid4())[:8]
|
| 37 |
+
# Sanitize filename β replace spaces with underscores, keep extension safe
|
| 38 |
+
safe_name = file.filename.replace(" ", "_")
|
| 39 |
+
file_path = os.path.join(UPLOAD_DIR, f"{file_id}_{safe_name}")
|
| 40 |
+
|
| 41 |
+
with open(file_path, "wb") as f:
|
| 42 |
+
f.write(await file.read())
|
| 43 |
+
|
| 44 |
+
# Save upload time and original name
|
| 45 |
+
file_times[file_path] = time.time()
|
| 46 |
+
file_meta[file_path] = file.filename
|
| 47 |
+
|
| 48 |
+
download_id = f"{file_id}_{safe_name}"
|
| 49 |
+
return {
|
| 50 |
+
"link": f"/file/{download_id}",
|
| 51 |
+
"file_id": download_id,
|
| 52 |
+
"original_name": file.filename,
|
| 53 |
+
"expires_in": 300,
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
# ββ Download ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 57 |
+
@app.get("/file/{file_id:path}")
|
| 58 |
+
def get_file(file_id: str):
|
| 59 |
+
file_path = os.path.join(UPLOAD_DIR, file_id)
|
| 60 |
+
|
| 61 |
+
if not os.path.exists(file_path):
|
| 62 |
+
from fastapi.responses import JSONResponse
|
| 63 |
+
return JSONResponse(status_code=404, content={"error": "File expired or not found"})
|
| 64 |
+
|
| 65 |
+
# Check if expired (extra safety check)
|
| 66 |
+
if file_path in file_times:
|
| 67 |
+
if time.time() - file_times[file_path] > 300:
|
| 68 |
+
if os.path.exists(file_path):
|
| 69 |
+
os.remove(file_path)
|
| 70 |
+
file_times.pop(file_path, None)
|
| 71 |
+
file_meta.pop(file_path, None)
|
| 72 |
+
from fastapi.responses import JSONResponse
|
| 73 |
+
return JSONResponse(status_code=410, content={"error": "File has expired"})
|
| 74 |
+
|
| 75 |
+
original_name = file_meta.get(file_path, os.path.basename(file_path))
|
| 76 |
+
return FileResponse(
|
| 77 |
+
file_path,
|
| 78 |
+
filename=original_name,
|
| 79 |
+
headers={"Content-Disposition": f'attachment; filename="{original_name}"'},
|
| 80 |
+
)
|
| 81 |
+
|
| 82 |
+
# ββ File status endpoint (for countdown UI) βββββββββββββββββββββββββββββββββββ
|
| 83 |
+
@app.get("/status/{file_id:path}")
|
| 84 |
+
def file_status(file_id: str):
|
| 85 |
+
file_path = os.path.join(UPLOAD_DIR, file_id)
|
| 86 |
+
if file_path not in file_times:
|
| 87 |
+
return {"exists": False}
|
| 88 |
+
elapsed = time.time() - file_times[file_path]
|
| 89 |
+
remaining = max(0, 300 - elapsed)
|
| 90 |
+
return {
|
| 91 |
+
"exists": os.path.exists(file_path),
|
| 92 |
+
"remaining_seconds": int(remaining),
|
| 93 |
+
"original_name": file_meta.get(file_path, ""),
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
# ββ Auto-delete thread ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 97 |
def cleanup_files():
|
| 98 |
+
while True:
|
| 99 |
+
now = time.time()
|
| 100 |
+
for path in list(file_times.keys()):
|
| 101 |
+
if now - file_times[path] > 300: # 5 minutes
|
| 102 |
+
if os.path.exists(path):
|
| 103 |
+
os.remove(path)
|
| 104 |
+
file_times.pop(path, None)
|
| 105 |
+
file_meta.pop(path, None)
|
| 106 |
+
time.sleep(60) # check every 1 minute
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
|
| 108 |
threading.Thread(target=cleanup_files, daemon=True).start()
|
| 109 |
|
| 110 |
+
# ββ Run βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 111 |
+
if __name__ == "__main__":
|
| 112 |
+
import uvicorn
|
| 113 |
+
uvicorn.run("app:app", host="0.0.0.0", port=7860, reload=True)
|
|
|