feat: Add detailed metadata to node files listing

Enhance the node files API to include comprehensive file metadata:
- File size in bytes
- File creation time (ISO 8601 format)
- File modification time (ISO 8601 format)
- File extension

Create new NodeFile schema to support these additional fields
while keeping the existing ProjectFile schema for backward compatibility.

This provides users with better information to manage and identify
files in the Web UI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
YueGuobin 2026-05-08 22:57:19 +08:00
parent 5b75d633aa
commit 51a4c73e21
No known key found for this signature in database
4 changed files with 50 additions and 13 deletions

View File

@ -192,14 +192,14 @@ async def get_compute_project_files(project: Project = Depends(dep_project)) ->
return await project.list_files()
@router.get("/projects/{project_id}/nodes/{node_type}/{node_id}/files", response_model=List[schemas.ProjectFile])
@router.get("/projects/{project_id}/nodes/{node_type}/{node_id}/files", response_model=List[schemas.NodeFile])
async def get_compute_node_files(
node_type: str,
node_id: str,
project: Project = Depends(dep_project)
) -> List[schemas.ProjectFile]:
) -> List[schemas.NodeFile]:
"""
Return files belonging to a specific node.
Return files belonging to a specific node with detailed metadata.
"""
node_path = f"project-files/{node_type}/{node_id}"

View File

@ -522,10 +522,10 @@ async def delete_disk_image(
await node.delete(f"/disk_image/{disk_name}")
@router.get("/{node_id}/files", response_model=List[schemas.ProjectFile], dependencies=[Depends(has_privilege("Node.Audit"))])
async def list_node_files(node: Node = Depends(dep_node)) -> List[schemas.ProjectFile]:
@router.get("/{node_id}/files", response_model=List[schemas.NodeFile], dependencies=[Depends(has_privilege("Node.Audit"))])
async def list_node_files(node: Node = Depends(dep_node)) -> List[schemas.NodeFile]:
"""
List files in a node directory.
List files in a node directory with detailed metadata.
Required privilege: Node.Audit
"""

View File

@ -18,6 +18,7 @@ import os
import shutil
import asyncio
import hashlib
import datetime
from uuid import UUID, uuid4
from fastapi import HTTPException
@ -420,10 +421,10 @@ class Project:
async def list_node_files(self, node_path: str):
"""
List files in a specific node directory.
List files in a specific node directory with detailed metadata.
:param node_path: Relative path to node directory (e.g., "project-files/qemu/node-id")
:returns: Array of files in the node directory
:returns: Array of files in the node directory with metadata
"""
node_full_path = os.path.normpath(os.path.join(self.path, node_path))
@ -442,14 +443,37 @@ class Project:
for filename in os.listdir(node_full_path):
file_path = os.path.join(node_full_path, filename)
if os.path.isfile(file_path) and not filename.endswith(".ghost"):
file_info = {"path": filename}
try:
file_info["md5sum"] = await wait_run_in_executor(
self._hash_file, file_path
# Get file stat information
stat_info = await wait_run_in_executor(os.stat, file_path)
# Get file extension
_, extension = os.path.splitext(filename)
extension = extension.lstrip('.')
# Format timestamps as ISO 8601
created_at = await wait_run_in_executor(
lambda: datetime.datetime.fromtimestamp(stat_info.st_ctime).isoformat()
)
except OSError:
modified_at = await wait_run_in_executor(
lambda: datetime.datetime.fromtimestamp(stat_info.st_mtime).isoformat()
)
# Get MD5 checksum
md5sum = await wait_run_in_executor(self._hash_file, file_path)
file_info = {
"path": filename,
"md5sum": md5sum,
"size": stat_info.st_size,
"created_at": created_at,
"modified_at": modified_at,
"extension": extension
}
files.append(file_info)
except OSError as e:
log.warning(f"Error getting metadata for file '{filename}': {e}")
continue
files.append(file_info)
except OSError as e:
log.error(f"Error listing node files: {e}")

View File

@ -105,6 +105,19 @@ class ProjectFile(BaseModel):
md5sum: str = Field(..., description="File checksum")
class NodeFile(BaseModel):
"""
Detailed file information for node files.
"""
path: str = Field(..., description="File name")
md5sum: str = Field(..., description="File checksum")
size: int = Field(..., description="File size in bytes")
created_at: str = Field(..., description="File creation time (ISO 8601)")
modified_at: str = Field(..., description="File modification time (ISO 8601)")
extension: str = Field(..., description="File extension")
class ProjectCompression(str, Enum):
"""
Supported project compression.