Files
svg-backend/backend/app/api/routes/admin.py

182 lines
6.3 KiB
Python

from fastapi import APIRouter, Depends, Query
from app.core.config import settings
from app.repositories.scheme_artifacts import artifact_exists, list_scheme_artifacts
from app.repositories.scheme_versions import get_current_scheme_version
from app.repositories.schemes import get_scheme_record_by_scheme_id, list_scheme_records
from app.repositories.uploads import get_upload_record_by_upload_id
from app.security.auth import require_api_key
from app.services.artifact_maintenance import (
cleanup_publish_preview_storage,
inspect_publish_preview_storage,
)
from app.services.display_regenerator import regenerate_display_artifact
from app.services.scheme_validation import build_scheme_validation_report
router = APIRouter()
@router.get(f"{settings.api_v1_prefix}/admin/schemes/{{scheme_id}}/current/artifacts")
async def list_current_scheme_artifacts(
scheme_id: str,
role: str = Depends(require_api_key),
):
scheme = await get_scheme_record_by_scheme_id(scheme_id)
version = await get_current_scheme_version(
scheme_id=scheme.scheme_id,
current_version_number=scheme.current_version_number,
)
rows = await list_scheme_artifacts(scheme_version_id=version.scheme_version_id)
return {
"scheme_id": scheme.scheme_id,
"scheme_version_id": version.scheme_version_id,
"items": [
{
"artifact_id": row.artifact_id,
"artifact_type": row.artifact_type,
"artifact_variant": row.artifact_variant,
"status": row.status,
"storage_path": row.storage_path,
"meta_json": row.meta_json,
"created_at": row.created_at.isoformat(),
}
for row in rows
],
"total": len(rows),
}
@router.get(f"{settings.api_v1_prefix}/admin/schemes/{{scheme_id}}/current/validation")
async def validate_current_scheme(
scheme_id: str,
role: str = Depends(require_api_key),
):
scheme = await get_scheme_record_by_scheme_id(scheme_id)
version = await get_current_scheme_version(
scheme_id=scheme.scheme_id,
current_version_number=scheme.current_version_number,
)
report = await build_scheme_validation_report(
scheme_id=scheme.scheme_id,
scheme_version_id=version.scheme_version_id,
)
return {
"scheme_id": scheme.scheme_id,
"scheme_version_id": version.scheme_version_id,
"report": report,
}
@router.post(f"{settings.api_v1_prefix}/admin/schemes/{{scheme_id}}/current/display/regenerate")
async def regenerate_current_display(
scheme_id: str,
mode: str = Query(default="passthrough"),
role: str = Depends(require_api_key),
):
scheme = await get_scheme_record_by_scheme_id(scheme_id)
version = await get_current_scheme_version(
scheme_id=scheme.scheme_id,
current_version_number=scheme.current_version_number,
)
upload = await get_upload_record_by_upload_id(scheme.source_upload_id)
return await regenerate_display_artifact(
scheme_id=scheme.scheme_id,
scheme_version_id=version.scheme_version_id,
upload_id=upload.upload_id,
original_filename=upload.original_filename,
sanitized_storage_path=upload.sanitized_storage_path,
mode=mode,
)
@router.post(f"{settings.api_v1_prefix}/admin/display/backfill")
async def bulk_backfill_display_artifacts(
mode: str = Query(default="passthrough"),
limit: int = Query(default=100, ge=1, le=1000),
only_missing: bool = Query(default=True),
role: str = Depends(require_api_key),
):
schemes = await list_scheme_records(limit=limit, offset=0)
processed: list[dict] = []
skipped: list[dict] = []
failed: list[dict] = []
for scheme in schemes:
try:
version = await get_current_scheme_version(
scheme_id=scheme.scheme_id,
current_version_number=scheme.current_version_number,
)
if only_missing:
exists = await artifact_exists(
scheme_version_id=version.scheme_version_id,
artifact_type="display_svg",
artifact_variant=mode,
)
if exists:
skipped.append(
{
"scheme_id": scheme.scheme_id,
"scheme_version_id": version.scheme_version_id,
"reason": "artifact already exists",
}
)
continue
upload = await get_upload_record_by_upload_id(scheme.source_upload_id)
result = await regenerate_display_artifact(
scheme_id=scheme.scheme_id,
scheme_version_id=version.scheme_version_id,
upload_id=upload.upload_id,
original_filename=upload.original_filename,
sanitized_storage_path=upload.sanitized_storage_path,
mode=mode,
)
processed.append(
{
"scheme_id": scheme.scheme_id,
"scheme_version_id": version.scheme_version_id,
"artifact_variant": result["artifact_variant"],
}
)
except Exception as exc:
failed.append(
{
"scheme_id": scheme.scheme_id,
"reason": f"{exc.__class__.__name__}: {exc}",
}
)
return {
"mode": mode,
"limit": limit,
"only_missing": only_missing,
"processed_count": len(processed),
"skipped_count": len(skipped),
"failed_count": len(failed),
"processed": processed,
"skipped": skipped,
"failed": failed,
}
@router.get(f"{settings.api_v1_prefix}/admin/artifacts/publish-preview/audit")
async def audit_publish_preview_storage(
role: str = Depends(require_api_key),
):
return await inspect_publish_preview_storage()
@router.post(f"{settings.api_v1_prefix}/admin/artifacts/publish-preview/cleanup")
async def cleanup_publish_preview_artifacts_endpoint(
dry_run: bool = Query(default=True),
role: str = Depends(require_api_key),
):
return await cleanup_publish_preview_storage(dry_run=dry_run)