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)