giga_tcg/routes/routes.py

207 lines
6.7 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, BackgroundTasks
from fastapi.responses import StreamingResponse
from typing import Optional
import logging
from schemas.file import (
FileSchema,
CreateFileRequest,
CreateFileResponse,
GetFileResponse,
DeleteFileResponse,
GetFileQueryParams
)
from schemas.box import (
CreateBoxResponse,
CreateBoxRequest,
BoxSchema,
UpdateBoxRequest,
CreateOpenBoxRequest,
CreateOpenBoxResponse,
OpenBoxSchema
)
from services.file import FileService
from services.box import BoxService
from services.task import TaskService
from dependencies import (
get_file_service,
get_box_service,
get_task_service,
get_create_file_metadata,
get_box_data,
get_box_update_data,
get_open_box_data
)
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api", tags=["cards"])
MAX_FILE_SIZE = 100 * 1024 * 1024 # 100 MB
async def validate_file_upload(file: UploadFile) -> bytes:
"""Validate uploaded file and return its contents."""
if not file.filename:
raise HTTPException(status_code=400, detail="No filename provided")
content = await file.read()
if len(content) > MAX_FILE_SIZE:
raise HTTPException(status_code=413, detail="File too large")
return content
@router.post("/files", response_model=CreateFileResponse, status_code=201)
async def create_file(
background_tasks: BackgroundTasks,
file: UploadFile = File(...),
metadata: CreateFileRequest = Depends(get_create_file_metadata),
file_service: FileService = Depends(get_file_service),
task_service: TaskService = Depends(get_task_service)
) -> CreateFileResponse:
"""Create a new file entry with the uploaded file."""
try:
content = await validate_file_upload(file)
logger.debug(f"File received: {file.filename}")
logger.debug(f"Metadata: {metadata}")
metadata.filename = metadata.filename or file.filename
if not file_service.validate_file(content, metadata):
raise HTTPException(status_code=400, detail="Invalid file content")
created_file = file_service.create_file(content, metadata)
if metadata.source == 'manabox':
background_tasks.add_task(task_service.process_manabox_file, created_file)
return CreateFileResponse(
status_code=201,
success=True,
files=[FileSchema.from_orm(created_file)]
)
except HTTPException as http_ex:
raise http_ex
except Exception as e:
logger.error(f"File upload failed: {str(e)}")
raise HTTPException(
status_code=500,
detail="Internal server error occurred during file upload"
)
finally:
await file.close()
@router.get("/files/{file_id:path}", response_model=GetFileResponse)
@router.get("/files", response_model=GetFileResponse)
async def get_file(
file_id: Optional[str] = None,
query: GetFileQueryParams = Depends(),
file_service: FileService = Depends(get_file_service)
) -> GetFileResponse:
"""
Get file(s) by optional ID and/or status.
If file_id is provided, returns that specific file.
If status is provided, returns all files with that status.
If neither is provided, returns all files.
"""
try:
if file_id:
file = file_service.get_file(file_id)
files = [file]
else:
files = file_service.get_files(status=query.status)
return GetFileResponse(
status_code=200,
success=True,
files=[FileSchema.from_orm(f) for f in files]
)
except Exception as e:
logger.error(f"Get file(s) failed: {str(e)}")
raise HTTPException(status_code=400, detail=str(e))
@router.delete("/files/{file_id}", response_model=DeleteFileResponse)
async def delete_file(
file_id: str,
file_service: FileService = Depends(get_file_service)
) -> DeleteFileResponse:
"""Delete a file by ID."""
try:
file = file_service.delete_file(file_id)
return DeleteFileResponse(
status_code=200,
success=True,
files=[FileSchema.from_orm(file)]
)
except Exception as e:
logger.error(f"Delete file failed: {str(e)}")
raise HTTPException(status_code=400, detail=str(e))
@router.post("/boxes", response_model=CreateBoxResponse, status_code=201)
async def create_box(
box_data: CreateBoxRequest = Depends(get_box_data),
box_service: BoxService = Depends(get_box_service)
) -> CreateBoxResponse:
"""Create a new box."""
try:
result = box_service.create_box(box_data)
return CreateBoxResponse(
status_code=201,
success=True,
box=[BoxSchema.from_orm(result)]
)
except Exception as e:
logger.error(f"Create box failed: {str(e)}")
raise HTTPException(status_code=400, detail=str(e))
@router.put("/boxes/{box_id}", response_model=CreateBoxResponse)
async def update_box(
box_id: str,
box_data: UpdateBoxRequest = Depends(get_box_update_data),
box_service: BoxService = Depends(get_box_service)
) -> CreateBoxResponse:
"""Update an existing box."""
try:
result = box_service.update_box(box_id, box_data)
return CreateBoxResponse(
status_code=200,
success=True,
box=[BoxSchema.from_orm(result)]
)
except Exception as e:
logger.error(f"Update box failed: {str(e)}")
raise HTTPException(status_code=400, detail=str(e))
@router.delete("/boxes/{box_id}", response_model=CreateBoxResponse)
async def delete_box(
box_id: str,
box_service: BoxService = Depends(get_box_service)
) -> CreateBoxResponse:
"""Delete a box by ID."""
try:
result = box_service.delete_box(box_id)
return CreateBoxResponse(
status_code=200,
success=True,
box=[BoxSchema.from_orm(result)]
)
except Exception as e:
logger.error(f"Delete box failed: {str(e)}")
raise HTTPException(status_code=400, detail=str(e))
@router.post("/boxes/{box_id}/open", response_model=CreateOpenBoxResponse, status_code=201)
async def open_box(
box_id: str,
box_data: CreateOpenBoxRequest = Depends(get_open_box_data),
box_service: BoxService = Depends(get_box_service)
) -> CreateOpenBoxResponse:
"""Open a box by ID."""
try:
result = box_service.open_box(box_id, box_data)
return CreateOpenBoxResponse(
status_code=201,
success=True,
open_box=[OpenBoxSchema.from_orm(result)]
)
except Exception as e:
logger.error(f"Open box failed: {str(e)}")
raise HTTPException(status_code=400, detail=str(e))