""" Dataset/Filesystem management endpoints """ from fastapi import APIRouter, Depends, HTTPException, status from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from typing import List, Optional from pydantic import BaseModel from services.zfs_runner import zfs_runner from services import auth as auth_service from models import Dataset, DatasetType router = APIRouter(prefix="/api/datasets", tags=["datasets"]) security = HTTPBearer() def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)): """Verify JWT token and return username""" username = auth_service.verify_token(credentials.credentials) if not username: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token" ) return username class CreateDatasetRequest(BaseModel): name: str properties: Optional[dict] = None class DatasetPropertiesRequest(BaseModel): compression: Optional[str] = None quota: Optional[int] = None reservation: Optional[int] = None @router.get("/", response_model=List[Dataset]) async def list_datasets( pool: str = "tank", current_user: str = Depends(get_current_user) ): """ List datasets in pool (default: tank) """ try: datasets = zfs_runner.list_datasets(pool) return [ Dataset( name=d["name"], type=DatasetType(d["type"]), used=d["used"], avail=d["avail"], refer=d["refer"], mountpoint=d["mountpoint"] if d["mountpoint"] != "-" else None ) for d in datasets ] except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.post("/", response_model=dict) async def create_dataset( request: CreateDatasetRequest, current_user: str = Depends(get_current_user) ): """ Create new dataset """ try: result = zfs_runner.create_dataset(request.name, request.properties) if result.get("status") == "error": raise HTTPException(status_code=400, detail=result.get("message")) return result except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.patch("/{dataset_name:path}") async def update_dataset_properties( dataset_name: str, request: DatasetPropertiesRequest, current_user: str = Depends(get_current_user) ): """ Update dataset properties (compression, quota, reservation) """ try: props: dict = {} if request.compression is not None: props["compression"] = request.compression if request.quota is not None: props["quota"] = str(request.quota) if request.quota > 0 else "none" if request.reservation is not None: props["reservation"] = str(request.reservation) if request.reservation > 0 else "none" if not props: return {"status": "ok", "message": "Nothing to update"} result = zfs_runner.set_dataset_properties(dataset_name, props) if result.get("status") == "error": raise HTTPException(status_code=400, detail=result.get("message")) return result except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.delete("/{dataset_name:path}") async def delete_dataset( dataset_name: str, recursive: bool = False, current_user: str = Depends(get_current_user) ): """ Delete dataset """ try: result = zfs_runner.destroy_dataset(dataset_name, recursive=recursive) if result.get("status") == "error": raise HTTPException(status_code=400, detail=result.get("message")) return result except Exception as e: raise HTTPException(status_code=500, detail=str(e))