engine / storage /data_routers.py
VeuReu's picture
Upload 7 files
46cf14b verified
raw
history blame
5.79 kB
import os
import io
import json
import shutil
import sqlite3
from pathlib import Path
from fastapi import APIRouter, UploadFile, File, Query, HTTPException
from fastapi.responses import FileResponse, JSONResponse
from storage.files.file_manager import FileManager
from storage.common import validate_token
router = APIRouter(prefix="/data", tags=["Data Manager"])
DATA_ROOT = Path("/data")
HF_TOKEN = os.getenv("HF_TOKEN")
@router.get("/data_tree", tags=["Data Manager"])
def get_data_tree(
token: str = Query(..., description="Token required for authorization")
):
"""
Return a formatted tree of folders and files under /data.
Behavior:
- Validate token.
- Walk the /data directory and build a recursive tree.
- Each entry includes: name, type (file/directory), and children if directory.
"""
validate_token(token)
def build_tree(path: Path):
"""
Build a tree representation of directories and files.
Prevents errors by checking if the path is a directory before iterating.
"""
if not path.exists():
return {"name": path.name, "type": "missing"}
# Si es un fichero → devolvemos un nodo simple
if path.is_file():
return {
"name": path.name,
"type": "file"
}
# Si es un directorio → construimos sus hijos
children = []
try:
entries = sorted(path.iterdir(), key=lambda p: p.name.lower())
except Exception:
# Si por cualquier razón no podemos listar, lo tratamos como file
return {
"name": path.name,
"type": "file"
}
for child in entries:
children.append(build_tree(child))
return {
"name": path.name,
"type": "directory",
"children": children
}
if not DATA_ROOT.exists():
return {"error": "/data does not exist"}
return build_tree(DATA_ROOT)
@router.post("/create_data_item", tags=["Data Manager"])
def create_data_item(
path: str = Query(..., description="Full path starting with /data/"),
item_type: str = Query(..., description="directory or file"),
token: str = Query(..., description="Token required for authorization")
):
"""
Create a directory or file under /data.
Restrictions:
- Path must start with /data/.
- Writing to /data/db or /data/media (or any of their subpaths) is forbidden.
- item_type must be 'directory' or 'file'.
Behavior:
- Validate token.
- Check path validity and protection.
- Create directory or empty file.
- Raise error if path already exists.
"""
validate_token(token)
target = Path(path)
# Validación básica
if not path.startswith("/data/"):
raise HTTPException(status_code=400, detail="Path must start with /data/")
if item_type not in ("directory", "file"):
raise HTTPException(status_code=400, detail="item_type must be 'directory' or 'file'")
# Protección de carpetas sensibles
protected = ["/data/db", "/data/media"]
for p in protected:
if path == p or path.startswith(p + "/"):
raise HTTPException(
status_code=403,
detail=f"Access to protected path '{p}' is not allowed"
)
# No permitir sobrescritura
if target.exists():
raise HTTPException(status_code=409, detail="Path already exists")
try:
if item_type == "directory":
target.mkdir(parents=True, exist_ok=False)
else:
target.parent.mkdir(parents=True, exist_ok=True)
with open(target, "wb") as f:
f.write(b"")
except Exception as exc:
raise HTTPException(status_code=500, detail=f"Failed to create item: {exc}")
return {
"status": "ok",
"created": str(target),
"type": item_type
}
@router.delete("/delete_path", tags=["Data Manager"])
def delete_path(
target: str = Query(..., description="Ruta absoluta dentro de /data a borrar"),
token: str = Query(..., description="Token requerido para autorización")
):
"""
Delete a file or directory recursively inside /data, except protected folders.
Behavior:
- Validate token.
- Validate path starts with /data/.
- Deny deletion of /data/db and /data/media (and anything inside them).
- If target is a file → delete file.
- If target is a directory → delete recursively.
"""
validate_token(token)
# Normalizar ruta
path = Path(target).resolve()
# Debe estar dentro de /data/
if not str(path).startswith(str(DATA_ROOT)):
raise HTTPException(status_code=400, detail="Path must be inside /data/")
# Carpetas protegidas
protected = [
DATA_ROOT / "db",
DATA_ROOT / "media",
]
for p in protected:
if path == p or str(path).startswith(str(p) + "/"):
raise HTTPException(
status_code=403,
detail=f"Deletion forbidden: {p} is protected"
)
# Verificar existencia
if not path.exists():
raise HTTPException(status_code=404, detail="Target path does not exist")
try:
if path.is_file():
path.unlink()
else:
shutil.rmtree(path)
except Exception as exc:
raise HTTPException(status_code=500, detail=f"Failed to delete: {exc}")
return {
"status": "ok",
"deleted": str(path)
}