Files
sfs/app/api.py
2025-09-04 11:47:29 +03:00

122 lines
3.7 KiB
Python

from fastapi import FastAPI, File, UploadFile, Depends, HTTPException, Security
from fastapi.responses import FileResponse, PlainTextResponse, StreamingResponse
from fastapi.security import APIKeyHeader
from sqlalchemy import exists
import hashlib
from ftplib import FTP
from io import BytesIO
from . import db
from dotenv import load_dotenv
import os
import hmac
load_dotenv()
FILES_DIR = os.getenv("FILES_DIR")
API_KEY_HASH = os.getenv("API_KEY_HASH")
api_key_header = APIKeyHeader(name="X-API-Key")
FTP_URL = os.getenv("FTP_URL")
FTP_LOGIN = os.getenv("FTP_LOGIN")
FTP_PASSWORD = os.getenv("FTP_PASSWORD")
CACHE_DIR = "cache"
def verify_api_key(api_key: str = Security(api_key_header)):
api_key_hashed = hashlib.sha256(api_key.encode()).hexdigest()
if not hmac.compare_digest(api_key_hashed, API_KEY_HASH):
raise HTTPException(status_code=403, detail="Forbidden. (╥﹏╥)")
return api_key
def compute_hash(data: bytes, algorithm="sha256") -> str:
h = hashlib.new(algorithm)
h.update(data)
return h.hexdigest()
app = FastAPI()
@app.get("/")
async def root():
return {"message": "hiii from sfs"}
@app.post("/file")
async def save_file(file: UploadFile = File(...), api_key: str = Depends(verify_api_key)):
contents = await file.read()
hash = compute_hash(contents)
existed_url = db.file_exists(file.size, hash)
if not existed_url:
file_url = db.add_file(file.filename, file.content_type, file.size, hash)
try:
ftp = FTP(FTP_URL)
ftp.login(FTP_LOGIN, FTP_PASSWORD)
buffer = BytesIO(contents)
try:
ftp.mkd(FILES_DIR)
except:
pass
ftp.storbinary(f"STOR {FILES_DIR}/{file_url}", buffer)
ftp.quit()
cached = os.path.join(CACHE_DIR, file_url)
if os.path.exists(cached):
os.remove(cached)
return {"status": "saved", "filename": file_url}
except Exception as e:
db.remove_file(file_url)
return {"status": "error", "message": f"Could not save file {file_url}: {e}"}
else:
return {"status": "file_exists", "filename": existed_url}
@app.get("/file/{filename}")
async def get_file(filename: str, raw: bool = False, api_key: str = Depends(verify_api_key)):
local_path = os.path.join(CACHE_DIR, filename)
if not os.path.exists(local_path):
try:
ftp = FTP(FTP_URL)
ftp.login(FTP_LOGIN, FTP_PASSWORD)
with open(local_path, "wb") as f:
ftp.retrbinary(f"RETR {FILES_DIR}/{filename}", f.write)
ftp.quit()
except Exception as e:
raise HTTPException(status_code=404, detail=f"File not found: {e}")
if raw:
with open(local_path, "r", encoding="utf-8", errors="ignore") as f:
return PlainTextResponse(f.read())
return FileResponse(local_path, filename=filename)
from ftplib import FTP
@app.delete("/file/{filename}")
async def delete_file(filename: str, api_key: str = Depends(verify_api_key)):
if not db.remove_file(filename):
return {"status": "error", "message": "no file like that"}
try:
ftp = FTP(FTP_URL)
ftp.login(FTP_LOGIN, FTP_PASSWORD)
ftp.delete(f"{FILES_DIR}/{filename}")
ftp.quit()
return {"status": "deleted"}
except Exception as e:
return {"status": "error", "message": f"Could not delete file {filename}: {e}"}
@app.get("/files/")
async def get_list_of_files(api_key: str = Depends(verify_api_key)):
return db.get_all_files()
@app.get("/healthchecker")
async def healthchecker():
return {"message": "Howdy, all is fine :3"}