150 lines
4.3 KiB
Python
150 lines
4.3 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
from typing import Optional
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Request
|
|
from fastapi.responses import FileResponse
|
|
from pydantic import BaseModel, field_validator
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from ..database import get_db
|
|
from ..models import AuthUserORM
|
|
from ..services.job_queue_service import job_queue_service
|
|
from ..services.psinsar_catalog_service import (
|
|
JOB_TYPE_REBUILD_PSINSAR_CATALOG,
|
|
TASK_TYPE_REBUILD_PSINSAR_CATALOG,
|
|
psinsar_catalog_service,
|
|
)
|
|
from ..services.task_service import task_service
|
|
from .dependencies import (
|
|
_add_operation_audit_log,
|
|
_get_current_user,
|
|
_require_admin,
|
|
_validate_export_path,
|
|
)
|
|
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
class PsinsarCatalogRebuildRequest(BaseModel):
|
|
publish_root: Optional[str] = None
|
|
full_rebuild: bool = True
|
|
|
|
@field_validator("publish_root", mode="before")
|
|
@classmethod
|
|
def _validate_publish_root(cls, value):
|
|
if value is None:
|
|
return None
|
|
path = str(value).strip()
|
|
return path or None
|
|
|
|
|
|
@router.get("/ps-products/catalog-status")
|
|
async def get_psinsar_catalog_status(
|
|
current_user: AuthUserORM = Depends(_get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
_ = current_user
|
|
return await psinsar_catalog_service.get_catalog_status(db)
|
|
|
|
|
|
@router.post("/ps-products/rebuild", status_code=202)
|
|
async def queue_psinsar_catalog_rebuild(
|
|
request: PsinsarCatalogRebuildRequest,
|
|
http_request: Request,
|
|
db: AsyncSession = Depends(get_db),
|
|
admin_user: AuthUserORM = Depends(_require_admin),
|
|
):
|
|
_ = admin_user
|
|
publish_root = request.publish_root
|
|
if publish_root:
|
|
publish_root = _validate_export_path(publish_root, "publish_root")
|
|
|
|
task_id = await task_service.create_task(
|
|
TASK_TYPE_REBUILD_PSINSAR_CATALOG,
|
|
"PS-InSAR 结果目录重建",
|
|
params={
|
|
"publish_root": publish_root,
|
|
"full_rebuild": request.full_rebuild,
|
|
},
|
|
db=db,
|
|
)
|
|
await job_queue_service.create_job(
|
|
JOB_TYPE_REBUILD_PSINSAR_CATALOG,
|
|
payload={
|
|
"publish_root": publish_root,
|
|
"full_rebuild": request.full_rebuild,
|
|
},
|
|
task_id=task_id,
|
|
db=db,
|
|
)
|
|
await _add_operation_audit_log(
|
|
db,
|
|
request=http_request,
|
|
action="psinsar_catalog_rebuild_queued",
|
|
resource="ps-products/rebuild",
|
|
detail={
|
|
"task_id": task_id,
|
|
"full_rebuild": request.full_rebuild,
|
|
},
|
|
)
|
|
await db.commit()
|
|
return {
|
|
"message": "PS-InSAR 结果目录重建任务已入队",
|
|
"task_id": task_id,
|
|
}
|
|
|
|
|
|
@router.get("/ps-products")
|
|
async def list_psinsar_products(
|
|
limit: int = 100,
|
|
offset: int = 0,
|
|
status: Optional[str] = None,
|
|
query: Optional[str] = None,
|
|
current_user: AuthUserORM = Depends(_get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
_ = current_user
|
|
return await psinsar_catalog_service.list_products(
|
|
db,
|
|
limit=limit,
|
|
offset=offset,
|
|
status=status,
|
|
query=query,
|
|
)
|
|
|
|
|
|
@router.get("/ps-products/{product_db_id}")
|
|
async def get_psinsar_product_detail(
|
|
product_db_id: int,
|
|
current_user: AuthUserORM = Depends(_get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
_ = current_user
|
|
detail = await psinsar_catalog_service.get_product_detail(db, product_db_id=product_db_id)
|
|
if detail is None:
|
|
raise HTTPException(status_code=404, detail="PS-InSAR product not found")
|
|
return detail
|
|
|
|
|
|
@router.get("/ps-products/{product_db_id}/preview")
|
|
async def get_psinsar_product_preview(
|
|
product_db_id: int,
|
|
current_user: AuthUserORM = Depends(_get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
_ = current_user
|
|
detail = await psinsar_catalog_service.get_product_detail(db, product_db_id=product_db_id)
|
|
if detail is None:
|
|
raise HTTPException(status_code=404, detail="PS-InSAR product not found")
|
|
preview_path = str(detail.get("preview_path") or "").strip()
|
|
if not preview_path or not os.path.isfile(preview_path):
|
|
raise HTTPException(status_code=404, detail="Preview not found")
|
|
return FileResponse(
|
|
preview_path,
|
|
media_type="image/png",
|
|
headers={"Cache-Control": "public, max-age=31536000, immutable"},
|
|
)
|