Update project files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
135
backend/app/routers/databases.py
Normal file
135
backend/app/routers/databases.py
Normal file
@@ -0,0 +1,135 @@
|
||||
"""Database switching endpoints — list, switch, create, upload, and download."""
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File
|
||||
from fastapi.responses import FileResponse
|
||||
from pydantic import BaseModel
|
||||
|
||||
from ..auth import verify_token
|
||||
from .. import database as db_module
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/api/databases",
|
||||
tags=["databases"],
|
||||
dependencies=[Depends(verify_token)],
|
||||
)
|
||||
|
||||
|
||||
class DatabaseInfo(BaseModel):
|
||||
name: str
|
||||
path: str
|
||||
size_bytes: int
|
||||
active: bool
|
||||
|
||||
|
||||
class SwitchRequest(BaseModel):
|
||||
path: str
|
||||
|
||||
|
||||
class CreateRequest(BaseModel):
|
||||
name: str
|
||||
|
||||
|
||||
class CopyRequest(BaseModel):
|
||||
name: str
|
||||
|
||||
|
||||
def _db_info(path: Path, active: bool) -> DatabaseInfo:
|
||||
return DatabaseInfo(
|
||||
name=path.stem,
|
||||
path=str(path),
|
||||
size_bytes=path.stat().st_size,
|
||||
active=active,
|
||||
)
|
||||
|
||||
|
||||
@router.get("", response_model=list[DatabaseInfo])
|
||||
def list_databases():
|
||||
"""List all .db files in the data directory."""
|
||||
current = Path(db_module.DB_PATH).resolve()
|
||||
dbs = []
|
||||
for f in sorted(db_module.DATA_DIR.glob("*.db")):
|
||||
dbs.append(_db_info(f, f.resolve() == current))
|
||||
return dbs
|
||||
|
||||
|
||||
@router.post("/switch", response_model=DatabaseInfo)
|
||||
def switch_db(req: SwitchRequest):
|
||||
"""Switch to a different database file. Path must be an existing .db file."""
|
||||
db_path = Path(req.path)
|
||||
if not db_path.exists():
|
||||
raise HTTPException(404, f"Database not found: {req.path}")
|
||||
if db_path.suffix != ".db":
|
||||
raise HTTPException(400, "Path must point to a .db file")
|
||||
db_module.switch_database(db_path)
|
||||
return _db_info(db_path, True)
|
||||
|
||||
|
||||
@router.post("/create", response_model=DatabaseInfo)
|
||||
def create_db(req: CreateRequest):
|
||||
"""Create a new empty database and switch to it."""
|
||||
name = req.name.strip()
|
||||
if not name:
|
||||
raise HTTPException(400, "Name is required")
|
||||
if not name.endswith(".db"):
|
||||
name += ".db"
|
||||
db_path = db_module.DATA_DIR / name
|
||||
if db_path.exists():
|
||||
raise HTTPException(409, f"Database already exists: {name}")
|
||||
db_module.switch_database(db_path)
|
||||
return _db_info(db_path, True)
|
||||
|
||||
|
||||
@router.post("/copy", response_model=DatabaseInfo)
|
||||
def copy_current_db(req: CopyRequest):
|
||||
"""Copy the current active database to a new file (snapshot/backup)."""
|
||||
name = req.name.strip()
|
||||
if not name:
|
||||
raise HTTPException(400, "Name is required")
|
||||
if not name.endswith(".db"):
|
||||
name += ".db"
|
||||
dest = db_module.DATA_DIR / name
|
||||
if dest.exists():
|
||||
raise HTTPException(409, f"Database already exists: {name}")
|
||||
shutil.copy2(db_module.DB_PATH, dest)
|
||||
return _db_info(dest, False)
|
||||
|
||||
|
||||
@router.get("/active", response_model=DatabaseInfo)
|
||||
def get_active():
|
||||
"""Return info about the currently active database."""
|
||||
return _db_info(Path(db_module.DB_PATH), True)
|
||||
|
||||
|
||||
@router.post("/upload", response_model=DatabaseInfo)
|
||||
def upload_db(file: UploadFile = File(...), switch: bool = True):
|
||||
"""Upload a .db file from the client and optionally switch to it."""
|
||||
if not file.filename or not file.filename.endswith(".db"):
|
||||
raise HTTPException(400, "File must be a .db file")
|
||||
# Sanitize filename
|
||||
safe_name = Path(file.filename).name
|
||||
dest = db_module.DATA_DIR / safe_name
|
||||
# If it already exists, add a suffix
|
||||
counter = 1
|
||||
while dest.exists():
|
||||
dest = db_module.DATA_DIR / f"{Path(safe_name).stem}_{counter}.db"
|
||||
counter += 1
|
||||
with open(dest, "wb") as f:
|
||||
shutil.copyfileobj(file.file, f)
|
||||
if switch:
|
||||
db_module.switch_database(dest)
|
||||
return _db_info(dest, switch)
|
||||
|
||||
|
||||
@router.get("/download")
|
||||
def download_db():
|
||||
"""Download the currently active database file."""
|
||||
p = Path(db_module.DB_PATH)
|
||||
if not p.exists():
|
||||
raise HTTPException(404, "Active database not found")
|
||||
return FileResponse(
|
||||
path=str(p),
|
||||
filename=p.name,
|
||||
media_type="application/octet-stream",
|
||||
)
|
||||
Reference in New Issue
Block a user