|
|
import os
|
|
|
import io
|
|
|
|
|
|
import sqlite3
|
|
|
import zipfile
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
from fastapi import APIRouter, UploadFile, File, Query, HTTPException
|
|
|
from fastapi.responses import JSONResponse, StreamingResponse
|
|
|
|
|
|
from storage.common import validate_token
|
|
|
|
|
|
router = APIRouter(prefix="/db", tags=["Database Manager"])
|
|
|
HF_TOKEN = os.getenv("HF_TOKEN")
|
|
|
DB_PATH = Path("/data/db")
|
|
|
|
|
|
@router.get("/download_all_db_files", tags=["Database Manager"])
|
|
|
async def download_all_db_files(
|
|
|
token: str = Query(..., description="Token required for authorization")
|
|
|
):
|
|
|
"""
|
|
|
Download all .db files from /data/db as a ZIP archive.
|
|
|
|
|
|
Steps:
|
|
|
- Validate the token.
|
|
|
- Verify that /data/db exists and contains .db files.
|
|
|
- Create an in-memory ZIP containing all .db files.
|
|
|
- Return the ZIP as a downloadable file.
|
|
|
"""
|
|
|
validate_token(token)
|
|
|
|
|
|
if not DB_PATH.exists():
|
|
|
raise HTTPException(status_code=404, detail="DB folder does not exist")
|
|
|
|
|
|
db_files = list(DB_PATH.glob("*.db"))
|
|
|
if not db_files:
|
|
|
raise HTTPException(status_code=404, detail="No .db files found")
|
|
|
|
|
|
|
|
|
zip_buffer = io.BytesIO()
|
|
|
|
|
|
with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zipf:
|
|
|
for file_path in db_files:
|
|
|
zipf.write(file_path, arcname=file_path.name)
|
|
|
|
|
|
zip_buffer.seek(0)
|
|
|
|
|
|
return StreamingResponse(
|
|
|
zip_buffer,
|
|
|
media_type="application/zip",
|
|
|
headers={
|
|
|
"Content-Disposition": 'attachment; filename="db_files.zip"'
|
|
|
}
|
|
|
)
|
|
|
|
|
|
|
|
|
@router.post("/upload_db_file", tags=["Database Manager"])
|
|
|
async def upload_db_file(
|
|
|
file: UploadFile = File(...),
|
|
|
token: str = Query(..., description="Token required for authorization")
|
|
|
):
|
|
|
"""
|
|
|
Upload a file to the /data/db folder.
|
|
|
|
|
|
Steps:
|
|
|
- Validate the token.
|
|
|
- Ensure /data/db exists (create it if missing).
|
|
|
- Save the uploaded file inside /data/db using its original filename.
|
|
|
- Return a JSON response confirming the upload.
|
|
|
"""
|
|
|
validate_token(token)
|
|
|
|
|
|
|
|
|
DB_PATH.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
final_path = DB_PATH / file.filename
|
|
|
|
|
|
try:
|
|
|
|
|
|
file_bytes = await file.read()
|
|
|
with open(final_path, "wb") as f:
|
|
|
f.write(file_bytes)
|
|
|
except Exception as exc:
|
|
|
raise HTTPException(status_code=500, detail=f"Failed to save file: {exc}")
|
|
|
|
|
|
return JSONResponse(
|
|
|
status_code=200,
|
|
|
content={
|
|
|
"status": "ok",
|
|
|
"saved_to": str(final_path)
|
|
|
}
|
|
|
)
|
|
|
|
|
|
@router.post("/execute_query", tags=["Database Manager"])
|
|
|
async def execute_query(
|
|
|
db_filename: str = Query(..., description="SQLite .db file name"),
|
|
|
query: str = Query(..., description="SQL query to execute"),
|
|
|
token: str = Query(..., description="Token required for authorization")
|
|
|
):
|
|
|
"""
|
|
|
Execute a SQL query against a specified SQLite database file in /data/db.
|
|
|
|
|
|
Steps:
|
|
|
- Validate the token.
|
|
|
- Ensure the requested .db file exists inside /data/db.
|
|
|
- Connect to the database using sqlite3.
|
|
|
- Execute the provided query.
|
|
|
- Return the results as a list of dictionaries (column: value).
|
|
|
- Capture and return errors if query execution fails.
|
|
|
"""
|
|
|
validate_token(token)
|
|
|
|
|
|
db_file_path = DB_PATH / db_filename
|
|
|
|
|
|
if not db_file_path.exists() or not db_file_path.is_file():
|
|
|
raise HTTPException(status_code=404, detail=f"Database file {db_filename} not found")
|
|
|
|
|
|
try:
|
|
|
conn = sqlite3.connect(db_file_path)
|
|
|
conn.row_factory = sqlite3.Row
|
|
|
cur = conn.cursor()
|
|
|
|
|
|
cur.execute(query)
|
|
|
rows = cur.fetchall()
|
|
|
|
|
|
|
|
|
results = [dict(row) for row in rows]
|
|
|
|
|
|
conn.commit()
|
|
|
conn.close()
|
|
|
except sqlite3.Error as e:
|
|
|
raise HTTPException(status_code=400, detail=f"SQL error: {e}")
|
|
|
except Exception as e:
|
|
|
raise HTTPException(status_code=500, detail=f"Unexpected error: {e}")
|
|
|
|
|
|
return JSONResponse(content={"results": results}) |