258 lines
8.5 KiB
Python
258 lines
8.5 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from typing import Any, Dict, List, Optional
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
from pydantic import BaseModel, Field
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from ..database import get_db
|
|
from ..models import AuthUserORM
|
|
from ..models.orm import SourceProductAssetORM
|
|
from ..services.asset_inventory_service import asset_inventory_service
|
|
from ..services.job_queue_service import job_queue_service
|
|
from ..services.task_service import task_service
|
|
from .dependencies import _get_current_user, _require_admin
|
|
|
|
|
|
router = APIRouter(prefix="/assets", tags=["assets"])
|
|
|
|
|
|
class AssetScanRequest(BaseModel):
|
|
inventory_types: List[str] = Field(default_factory=list)
|
|
root_ids: List[int] = Field(default_factory=list)
|
|
bind_orbits: bool = True
|
|
|
|
|
|
class S1UnpackRequest(BaseModel):
|
|
target_root: Optional[str] = None
|
|
overwrite: bool = False
|
|
min_disk_space_gb: Optional[float] = Field(default=None, ge=0)
|
|
delete_archive: Optional[bool] = None
|
|
|
|
|
|
class S1BatchUnpackRequest(BaseModel):
|
|
target_root: Optional[str] = None
|
|
overwrite: bool = False
|
|
min_disk_space_gb: Optional[float] = Field(default=None, ge=0)
|
|
delete_archive: Optional[bool] = None
|
|
scan_before_unpack: bool = True
|
|
|
|
|
|
class SourceMaterializeRequest(BaseModel):
|
|
target_root: Optional[str] = None
|
|
overwrite: bool = False
|
|
|
|
|
|
@router.get("/inventory/status")
|
|
async def get_asset_inventory_status(
|
|
current_user: AuthUserORM = Depends(_get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
_ = current_user
|
|
return await asset_inventory_service.get_status(db)
|
|
|
|
|
|
@router.post("/inventory/scan", status_code=202)
|
|
async def run_asset_inventory_scan(
|
|
request: Optional[AssetScanRequest] = None,
|
|
admin_user: AuthUserORM = Depends(_require_admin),
|
|
):
|
|
_ = admin_user
|
|
payload = (request or AssetScanRequest()).model_dump()
|
|
try:
|
|
task_id = await task_service.create_task(
|
|
"SCAN_ASSET_INVENTORY",
|
|
"Source/orbit asset inventory scan",
|
|
params=payload,
|
|
)
|
|
job_id = await job_queue_service.create_job(
|
|
"SCAN_ASSET_INVENTORY",
|
|
payload=payload,
|
|
task_id=task_id,
|
|
)
|
|
return {"message": "Asset inventory scan queued", "task_id": task_id, "job_id": job_id}
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=409, detail=str(exc)) from exc
|
|
|
|
|
|
@router.post("/inventory/scan-now")
|
|
async def run_asset_inventory_scan_now(
|
|
request: Optional[AssetScanRequest] = None,
|
|
admin_user: AuthUserORM = Depends(_require_admin),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
_ = admin_user
|
|
payload = (request or AssetScanRequest()).model_dump()
|
|
return await asset_inventory_service.scan_configured_roots(
|
|
db,
|
|
inventory_types=payload.get("inventory_types") or None,
|
|
root_ids=payload.get("root_ids") or None,
|
|
bind_orbits=bool(payload.get("bind_orbits", True)),
|
|
)
|
|
|
|
|
|
@router.get("/sources")
|
|
async def list_source_product_assets(
|
|
satellite_family: Optional[str] = None,
|
|
satellite: Optional[str] = None,
|
|
source_format: Optional[str] = None,
|
|
parse_status: Optional[str] = None,
|
|
include_inactive: bool = False,
|
|
limit: int = Query(200, ge=1, le=1000),
|
|
offset: int = Query(0, ge=0),
|
|
current_user: AuthUserORM = Depends(_get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
_ = current_user
|
|
return await asset_inventory_service.list_source_products(
|
|
db,
|
|
satellite_family=satellite_family,
|
|
satellite=satellite,
|
|
source_format=source_format,
|
|
parse_status=parse_status,
|
|
include_inactive=include_inactive,
|
|
limit=limit,
|
|
offset=offset,
|
|
)
|
|
|
|
|
|
@router.get("/orbits")
|
|
async def list_orbit_assets(
|
|
satellite_family: Optional[str] = None,
|
|
satellite: Optional[str] = None,
|
|
orbit_type: Optional[str] = None,
|
|
parse_status: Optional[str] = None,
|
|
include_inactive: bool = False,
|
|
limit: int = Query(200, ge=1, le=1000),
|
|
offset: int = Query(0, ge=0),
|
|
current_user: AuthUserORM = Depends(_get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
_ = current_user
|
|
return await asset_inventory_service.list_orbits(
|
|
db,
|
|
satellite_family=satellite_family,
|
|
satellite=satellite,
|
|
orbit_type=orbit_type,
|
|
parse_status=parse_status,
|
|
include_inactive=include_inactive,
|
|
limit=limit,
|
|
offset=offset,
|
|
)
|
|
|
|
|
|
@router.get("/issues")
|
|
async def list_asset_inventory_issues(
|
|
status: str = "OPEN",
|
|
severity: Optional[str] = None,
|
|
issue_code: Optional[str] = None,
|
|
limit: int = Query(200, ge=1, le=1000),
|
|
offset: int = Query(0, ge=0),
|
|
current_user: AuthUserORM = Depends(_get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
_ = current_user
|
|
return await asset_inventory_service.list_issues(
|
|
db,
|
|
status=status,
|
|
severity=severity,
|
|
issue_code=issue_code,
|
|
limit=limit,
|
|
offset=offset,
|
|
)
|
|
|
|
|
|
@router.post("/sources/{asset_id}/unpack-sentinel1")
|
|
async def unpack_sentinel1_source_asset(
|
|
asset_id: int,
|
|
request: Optional[S1UnpackRequest] = None,
|
|
admin_user: AuthUserORM = Depends(_require_admin),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
_ = admin_user
|
|
asset = await db.get(SourceProductAssetORM, asset_id)
|
|
if asset is None:
|
|
raise HTTPException(status_code=404, detail="Source product asset not found.")
|
|
if asset.source_format != "S1_ZIP":
|
|
raise HTTPException(status_code=400, detail="Only Sentinel-1 ZIP assets can be unpacked by this endpoint.")
|
|
|
|
request_data = request or S1UnpackRequest()
|
|
payload = {
|
|
"asset_id": asset_id,
|
|
"target_root": request_data.target_root,
|
|
"overwrite": bool(request_data.overwrite),
|
|
}
|
|
if request_data.min_disk_space_gb is not None:
|
|
payload["min_disk_space_gb"] = request_data.min_disk_space_gb
|
|
if request_data.delete_archive is not None:
|
|
payload["delete_archive"] = request_data.delete_archive
|
|
try:
|
|
task_id = await task_service.create_task(
|
|
"UNPACK_SENTINEL1",
|
|
"Sentinel-1 unpack",
|
|
params=payload,
|
|
)
|
|
job_id = await job_queue_service.create_job(
|
|
"UNPACK_SENTINEL1",
|
|
payload=payload,
|
|
task_id=task_id,
|
|
)
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=409, detail=str(exc)) from exc
|
|
await db.commit()
|
|
return {"message": "Sentinel-1 unpack task queued", "task_id": task_id, "job_id": job_id}
|
|
|
|
|
|
@router.post("/sources/{asset_id}/materialize")
|
|
async def materialize_source_asset(
|
|
asset_id: int,
|
|
request: Optional[SourceMaterializeRequest] = None,
|
|
admin_user: AuthUserORM = Depends(_require_admin),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
_ = admin_user
|
|
asset = await db.get(SourceProductAssetORM, asset_id)
|
|
if asset is None:
|
|
raise HTTPException(status_code=404, detail="Source product asset not found.")
|
|
request_data = request or SourceMaterializeRequest()
|
|
try:
|
|
result = asset_inventory_service.materialize_source_asset(
|
|
asset,
|
|
target_root=request_data.target_root,
|
|
overwrite=bool(request_data.overwrite),
|
|
)
|
|
except Exception as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
metadata = dict(asset.metadata_json or {})
|
|
metadata["last_materialized_dir"] = result.get("safe_dir") or result.get("target_dir")
|
|
metadata["last_materialized_at"] = datetime.utcnow().isoformat()
|
|
metadata["last_materialized_status"] = result.get("status")
|
|
asset.metadata_json = metadata
|
|
await db.commit()
|
|
return {"message": "Source asset materialized", "result": result}
|
|
|
|
|
|
@router.post("/inventory/unpack-sentinel1", status_code=202)
|
|
async def run_sentinel1_unpack_batch(
|
|
request: Optional[S1BatchUnpackRequest] = None,
|
|
admin_user: AuthUserORM = Depends(_require_admin),
|
|
):
|
|
_ = admin_user
|
|
payload = (request or S1BatchUnpackRequest()).model_dump()
|
|
try:
|
|
task_id = await task_service.create_task(
|
|
"UNPACK_SENTINEL1",
|
|
"Sentinel-1 batch unpack",
|
|
params=payload,
|
|
)
|
|
job_id = await job_queue_service.create_job(
|
|
"UNPACK_SENTINEL1",
|
|
payload=payload,
|
|
task_id=task_id,
|
|
)
|
|
return {"message": "Sentinel-1 batch unpack task queued", "task_id": task_id, "job_id": job_id}
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=409, detail=str(exc)) from exc
|