restrict ops endpoints to admin-only access block operator and viewer keys from admin maintenance routes cover destructive pricing cleanup in smoke execution, not only preview extend orchestration without regressing existing smoke stages
182 lines
6.3 KiB
Python
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_admin_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_admin_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_admin_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_admin_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_admin_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_admin_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_admin_api_key),
|
|
):
|
|
return await cleanup_publish_preview_storage(dry_run=dry_run)
|