from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession from typing import Optional import os import string import uuid from pathlib import Path from pydantic import BaseModel from app.core.database import get_db from app.api.deps import require_admin from app.models.models import User from app.schemas.schemas import SystemSettingsResponse, SystemSettingsUpdate from app.services.settings_service import get_all_settings, update_settings_service router = APIRouter(prefix="/settings", tags=["System Settings"]) class TestShareRequest(BaseModel): path: str username: str password: str @router.get("", response_model=SystemSettingsResponse) async def get_settings( db: AsyncSession = Depends(get_db), admin_user: User = Depends(require_admin) ): """Retrieve global system settings.""" settings_dict = await get_all_settings(db) return { "storage_root": settings_dict.get("storage_root", ""), "global_quota_gb": int(settings_dict.get("global_quota_gb", 1000)), "default_client_quota_gb": int(settings_dict.get("default_client_quota_gb", 100)), "default_keep_daily": int(settings_dict.get("default_keep_daily", 7)), "default_keep_weekly": int(settings_dict.get("default_keep_weekly", 4)), "default_keep_monthly": int(settings_dict.get("default_keep_monthly", 12)), "auth_mode": settings_dict.get("auth_mode", "local"), "radius_host": settings_dict.get("radius_host", ""), "radius_port": int(settings_dict.get("radius_port", 1812)) if settings_dict.get("radius_port") else 1812, "radius_secret": settings_dict.get("radius_secret", ""), "storage_network_enabled": settings_dict.get("storage_network_enabled", "false").lower() == "true", "storage_network_user": settings_dict.get("storage_network_user", ""), "storage_network_pass": settings_dict.get("storage_network_pass", ""), } @router.put("", response_model=SystemSettingsResponse) async def update_settings( payload: SystemSettingsUpdate, db: AsyncSession = Depends(get_db), admin_user: User = Depends(require_admin) ): """Update global system settings.""" # Convert Pydantic model to a dict of values (filtering out None) updates = {k: v for k, v in payload.model_dump().items() if v is not None} try: settings_dict = await update_settings_service(db, updates) except ValueError as e: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=str(e) ) return { "storage_root": settings_dict.get("storage_root", ""), "global_quota_gb": int(settings_dict.get("global_quota_gb", 1000)), "default_client_quota_gb": int(settings_dict.get("default_client_quota_gb", 100)), "default_keep_daily": int(settings_dict.get("default_keep_daily", 7)), "default_keep_weekly": int(settings_dict.get("default_keep_weekly", 4)), "default_keep_monthly": int(settings_dict.get("default_keep_monthly", 12)), "auth_mode": settings_dict.get("auth_mode", "local"), "radius_host": settings_dict.get("radius_host", ""), "radius_port": int(settings_dict.get("radius_port", 1812)) if settings_dict.get("radius_port") else 1812, "radius_secret": settings_dict.get("radius_secret", ""), "storage_network_enabled": settings_dict.get("storage_network_enabled", "false").lower() == "true", "storage_network_user": settings_dict.get("storage_network_user", ""), "storage_network_pass": settings_dict.get("storage_network_pass", ""), } @router.get("/explore-dir") async def explore_directory( path: Optional[str] = None, admin_user: User = Depends(require_admin) ): """ Explore directories on the host filesystem. """ import platform if not path or path.strip() == "": if platform.system().lower() == "windows": drives = [] for letter in string.ascii_uppercase: drive_path = f"{letter}:\\" if os.path.exists(drive_path): drives.append({"name": drive_path, "path": drive_path}) return {"current_path": "", "folders": drives} else: return {"current_path": "/", "folders": [{"name": "/", "path": "/"}]} # Resolve path target = Path(path).resolve() if not target.exists() or not target.is_dir(): raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"The path '{path}' does not exist or is not a directory." ) try: folders = [] for entry in os.scandir(target): try: if entry.is_dir(): folders.append({ "name": entry.name, "path": str(Path(entry.path).resolve()) }) except Exception: continue # Skip inaccessible folders.sort(key=lambda x: x["name"].lower()) return { "current_path": str(target), "parent_path": str(target.parent) if target.parent != target else None, "folders": folders } except Exception as e: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f"Failed to read directory contents: {str(e)}" ) @router.post("/test-network-share") async def test_network_share( payload: TestShareRequest, admin_user: User = Depends(require_admin) ): """ Attempts to connect to a UNC network share and tests write access. """ from app.services.settings_service import connect_network_share ok = connect_network_share(payload.path, payload.username, payload.password) if not ok: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Failed to connect to the network share. Check the server path, username, and password." ) try: target_path = Path(payload.path) os.makedirs(target_path, exist_ok=True) test_file = target_path / f".write_test_{uuid.uuid4().hex[:6]}" test_file.touch() test_file.unlink() return {"message": "Network share connected and write access verified successfully!"} except Exception as e: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f"Network share connected, but write access test failed: {str(e)}" )