Files

163 lines
6.5 KiB
Python

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)}"
)