Files
Nexus/server/api/files.py
T
2026-06-02 05:59:55 +08:00

78 lines
2.4 KiB
Python

"""RESTful file browser API (GET + ETag). Legacy mutations remain on /api/sync/*."""
from __future__ import annotations
from fastapi import APIRouter, Depends, Header, HTTPException, Request, Response
from fastapi.responses import JSONResponse
from server.api.auth_jwt import get_current_admin
from server.application.services.files_browse_service import (
etag_for_payload,
get_cached_browse,
list_remote_directory,
set_cached_browse,
)
from server.domain.models import Admin
router = APIRouter(prefix="/api/files", tags=["files"])
async def _audit_browse(
request: Request,
server_id: int,
path: str,
admin_username: str,
server_name: str,
) -> None:
from server.api.sync_v2 import _audit_sync
await _audit_sync(
"browse_directory",
"server",
server_id,
f"Browse {server_name}:{path}",
admin_username,
request,
)
@router.get("/browse")
async def browse_directory_get(
request: Request,
server_id: int,
path: str = "/",
admin: Admin = Depends(get_current_admin),
if_none_match: str | None = Header(default=None, alias="If-None-Match"),
):
"""Browse remote directory via SSH. Supports ETag / If-None-Match to skip SSH when unchanged."""
from server.infrastructure.database.server_repo import ServerRepositoryImpl
from server.utils.posix_paths import PosixPathError, normalize_remote_abs_path
session = request.state.db
repo = ServerRepositoryImpl(session)
server = await repo.get_by_id(server_id)
if not server:
raise HTTPException(status_code=404, detail="Server not found")
try:
norm_path = normalize_remote_abs_path(path)
except PosixPathError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
client_etag = (if_none_match or "").strip()
if client_etag:
cached = get_cached_browse(server_id, norm_path)
if cached and cached[0] == client_etag:
return Response(status_code=304, headers={"ETag": client_etag})
payload = await list_remote_directory(session, server_id, norm_path)
etag = etag_for_payload(payload)
set_cached_browse(server_id, norm_path, etag, payload)
await _audit_browse(request, server_id, norm_path, admin.username, server.name)
if client_etag and client_etag == etag:
return Response(status_code=304, headers={"ETag": etag})
return JSONResponse(content=payload, headers={"ETag": etag})