0.9.9
parent
75318d5b28
commit
e796eceaf8
|
|
@ -51,6 +51,9 @@ class CelestialBodyUpdate(BaseModel):
|
|||
is_active: Optional[bool] = None
|
||||
extra_data: Optional[Dict[str, Any]] = None
|
||||
|
||||
class ResourceUpdate(BaseModel):
|
||||
extra_data: Optional[Dict[str, Any]] = None
|
||||
|
||||
|
||||
@router.post("/", status_code=status.HTTP_201_CREATED)
|
||||
async def create_celestial_body(
|
||||
|
|
@ -782,13 +785,17 @@ async def get_static_data(
|
|||
|
||||
@router.post("/resources/upload")
|
||||
async def upload_resource(
|
||||
body_id: Optional[str] = None,
|
||||
body_id: str = Query(..., description="Celestial body ID"),
|
||||
resource_type: str = Query(..., description="Type: texture, model, icon, thumbnail, data"),
|
||||
file: UploadFile = File(...),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
Upload a resource file (texture, model, icon, etc.)
|
||||
|
||||
Upload directory logic:
|
||||
- Probes (type='probe'): upload to 'model' directory
|
||||
- Others (planet, satellite, etc.): upload to 'texture' directory
|
||||
"""
|
||||
import os
|
||||
import aiofiles
|
||||
|
|
@ -802,15 +809,37 @@ async def upload_resource(
|
|||
detail=f"Invalid resource_type. Must be one of: {valid_types}"
|
||||
)
|
||||
|
||||
# Get celestial body to determine upload directory
|
||||
body = await celestial_body_service.get_body_by_id(body_id, db)
|
||||
if not body:
|
||||
raise HTTPException(status_code=404, detail=f"Celestial body {body_id} not found")
|
||||
|
||||
# Determine upload directory based on body type
|
||||
# Probes -> model directory, Others -> texture directory
|
||||
if body.type == 'probe' and resource_type in ['model', 'texture']:
|
||||
upload_subdir = 'model'
|
||||
elif resource_type in ['model', 'texture']:
|
||||
upload_subdir = 'texture'
|
||||
else:
|
||||
# For icon, thumbnail, data, use resource_type as directory
|
||||
upload_subdir = resource_type
|
||||
|
||||
# Create upload directory structure
|
||||
upload_dir = Path("upload") / resource_type
|
||||
upload_dir = Path("upload") / upload_subdir
|
||||
upload_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Generate unique filename
|
||||
import uuid
|
||||
file_ext = os.path.splitext(file.filename)[1]
|
||||
unique_filename = f"{uuid.uuid4()}{file_ext}"
|
||||
file_path = upload_dir / unique_filename
|
||||
# Use original filename
|
||||
original_filename = file.filename
|
||||
file_path = upload_dir / original_filename
|
||||
|
||||
# If file already exists, append timestamp to make it unique
|
||||
if file_path.exists():
|
||||
from datetime import datetime
|
||||
name_without_ext = os.path.splitext(original_filename)[0]
|
||||
file_ext = os.path.splitext(original_filename)[1]
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
original_filename = f"{name_without_ext}_{timestamp}{file_ext}"
|
||||
file_path = upload_dir / original_filename
|
||||
|
||||
# Save file
|
||||
try:
|
||||
|
|
@ -821,29 +850,42 @@ async def upload_resource(
|
|||
# Get file size
|
||||
file_size = os.path.getsize(file_path)
|
||||
|
||||
# Store relative path (from upload directory)
|
||||
relative_path = f"{upload_subdir}/{original_filename}"
|
||||
|
||||
# Determine MIME type
|
||||
mime_type = file.content_type
|
||||
|
||||
# Create resource record
|
||||
resource = await resource_service.create_resource(
|
||||
{
|
||||
"body_id": body_id,
|
||||
"resource_type": resource_type,
|
||||
"file_path": str(file_path),
|
||||
"file_path": relative_path,
|
||||
"file_size": file_size,
|
||||
"mime_type": file.content_type,
|
||||
"mime_type": mime_type,
|
||||
},
|
||||
db
|
||||
)
|
||||
|
||||
logger.info(f"Uploaded resource: {file_path} ({file_size} bytes)")
|
||||
# Commit the transaction
|
||||
await db.commit()
|
||||
await db.refresh(resource)
|
||||
|
||||
logger.info(f"Uploaded resource for {body.name} ({body.type}): {relative_path} ({file_size} bytes)")
|
||||
|
||||
return {
|
||||
"id": resource.id,
|
||||
"resource_type": resource.resource_type,
|
||||
"file_path": resource.file_path,
|
||||
"file_size": resource.file_size,
|
||||
"message": "File uploaded successfully"
|
||||
"upload_directory": upload_subdir,
|
||||
"message": f"File uploaded successfully to {upload_subdir} directory"
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
# Rollback transaction
|
||||
await db.rollback()
|
||||
# Clean up file if database operation fails
|
||||
if file_path.exists():
|
||||
os.remove(file_path)
|
||||
|
|
@ -871,6 +913,7 @@ async def get_body_resources(
|
|||
"file_size": resource.file_size,
|
||||
"mime_type": resource.mime_type,
|
||||
"created_at": resource.created_at.isoformat(),
|
||||
"extra_data": resource.extra_data,
|
||||
})
|
||||
|
||||
return {"body_id": body_id, "resources": result}
|
||||
|
|
@ -914,6 +957,47 @@ async def delete_resource(
|
|||
raise HTTPException(status_code=500, detail="Failed to delete resource")
|
||||
|
||||
|
||||
@router.put("/resources/{resource_id}")
|
||||
async def update_resource(
|
||||
resource_id: int,
|
||||
update_data: ResourceUpdate,
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
Update resource metadata (e.g., scale parameter for models)
|
||||
"""
|
||||
from sqlalchemy import select, update
|
||||
|
||||
# Get resource record
|
||||
result = await db.execute(
|
||||
select(Resource).where(Resource.id == resource_id)
|
||||
)
|
||||
resource = result.scalar_one_or_none()
|
||||
|
||||
if not resource:
|
||||
raise HTTPException(status_code=404, detail="Resource not found")
|
||||
|
||||
# Update extra_data
|
||||
await db.execute(
|
||||
update(Resource)
|
||||
.where(Resource.id == resource_id)
|
||||
.values(extra_data=update_data.extra_data)
|
||||
)
|
||||
await db.commit()
|
||||
|
||||
# Get updated resource
|
||||
result = await db.execute(
|
||||
select(Resource).where(Resource.id == resource_id)
|
||||
)
|
||||
updated_resource = result.scalar_one_or_none()
|
||||
|
||||
return {
|
||||
"id": updated_resource.id,
|
||||
"extra_data": updated_resource.extra_data,
|
||||
"message": "Resource updated successfully"
|
||||
}
|
||||
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Orbit Management APIs
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class CelestialBody(BaseModel):
|
|||
id: str = Field(..., description="JPL Horizons ID")
|
||||
name: str = Field(..., description="Display name")
|
||||
name_zh: str | None = Field(None, description="Chinese name")
|
||||
type: Literal["planet", "probe", "star", "dwarf_planet", "satellite"] = Field(..., description="Body type")
|
||||
type: Literal["planet", "probe", "star", "dwarf_planet", "satellite", "comet"] = Field(..., description="Body type")
|
||||
positions: list[Position] = Field(
|
||||
default_factory=list, description="Position history"
|
||||
)
|
||||
|
|
@ -43,7 +43,7 @@ class BodyInfo(BaseModel):
|
|||
|
||||
id: str
|
||||
name: str
|
||||
type: Literal["planet", "probe", "star", "dwarf_planet", "satellite"]
|
||||
type: Literal["planet", "probe", "star", "dwarf_planet", "satellite", "comet"]
|
||||
description: str
|
||||
launch_date: str | None = None
|
||||
status: str | None = None
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 433 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 461 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 432 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 432 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 333 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 134 KiB |
Loading…
Reference in New Issue