完善fastapi接口

This commit is contained in:
puke
2025-11-05 19:46:47 +08:00
parent eee604d8e9
commit 15899afb6f
11 changed files with 595 additions and 56 deletions

View File

@@ -1,7 +1,7 @@
"""
File service endpoints
Provides access to generated files (videos, images, audio).
Provides access to generated files (videos, images, audio) and resource files.
"""
from pathlib import Path
@@ -17,15 +17,49 @@ async def get_file(file_path: str):
"""
Get file by path
Serves files from the output directory only.
Serves files from allowed directories:
- output/ - Generated files (videos, images, audio)
- workflows/ - ComfyUI workflow files
- templates/ - HTML templates
- bgm/ - Background music
- data/bgm/ - Custom background music
- data/templates/ - Custom templates
- resources/ - Other resources (images, fonts, etc.)
- **file_path**: File name or path (e.g., "abc123.mp4" or "subfolder/abc123.mp4")
- **file_path**: File path relative to allowed directories
Examples:
- "abc123.mp4" → output/abc123.mp4
- "workflows/runninghub/image_flux.json" → workflows/runninghub/image_flux.json
- "templates/1080x1920/default.html" → templates/1080x1920/default.html
- "bgm/default.mp3" → bgm/default.mp3
- "resources/example.png" → resources/example.png
Returns file for download or preview.
"""
try:
# Automatically prepend "output/" to the path
full_path = f"output/{file_path}"
# Define allowed directories (in priority order)
allowed_prefixes = [
"output/",
"workflows/",
"templates/",
"bgm/",
"data/bgm/",
"data/templates/",
"resources/",
]
# Check if path starts with allowed prefix, otherwise try output/
full_path = None
for prefix in allowed_prefixes:
if file_path.startswith(prefix):
full_path = file_path
break
# If no prefix matched, assume it's in output/ (backward compatibility)
if full_path is None:
full_path = f"output/{file_path}"
abs_path = Path.cwd() / full_path
if not abs_path.exists():
@@ -34,11 +68,19 @@ async def get_file(file_path: str):
if not abs_path.is_file():
raise HTTPException(status_code=400, detail=f"Path is not a file: {file_path}")
# Security: only allow access to output directory
# Security: only allow access to specified directories
try:
rel_path = abs_path.relative_to(Path.cwd())
if not str(rel_path).startswith("output"):
raise HTTPException(status_code=403, detail="Access denied: only output directory is accessible")
rel_path_str = str(rel_path)
# Check if path starts with any allowed prefix
is_allowed = any(rel_path_str.startswith(prefix.rstrip('/')) for prefix in allowed_prefixes)
if not is_allowed:
raise HTTPException(
status_code=403,
detail=f"Access denied: only {', '.join(p.rstrip('/') for p in allowed_prefixes)} directories are accessible"
)
except ValueError:
raise HTTPException(status_code=403, detail="Access denied")
@@ -52,6 +94,8 @@ async def get_file(file_path: str):
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.gif': 'image/gif',
'.html': 'text/html',
'.json': 'application/json',
}
media_type = media_types.get(suffix, 'application/octet-stream')