"""Configured public-avatar storage boundary used by API routes and health.""" from __future__ import annotations from dataclasses import dataclass from pathlib import Path from typing import Any from starlette.exceptions import HTTPException as StarletteHTTPException from starlette.responses import Response from starlette.staticfiles import StaticFiles from .config import settings from .upload_storage import ( CurrentAvatarReferenceProof, PUBLIC_AVATAR_DIRECTORY, UploadManifestProof, UploadWriteFreezeGate, assert_path_without_reparse, assert_regular_file_without_reparse, connected_database_target_sha256, is_runtime_generated_public_avatar_relative_path, public_avatar_url_to_relative_path, public_avatar_relative_path_sha256, read_decodable_public_avatar_object, validate_current_avatar_references, validate_upload_manifest, ) def _configured_upload_root() -> Path: configured = Path(settings.user_upload_dir) if not configured.is_absolute(): if settings.user_upload_manifest_required: raise RuntimeError("public USER_UPLOAD_DIR must be absolute") configured = Path.cwd() / configured if settings.user_upload_manifest_required: try: assert_path_without_reparse(configured, "public USER_UPLOAD_DIR") except ValueError as exc: raise RuntimeError(str(exc)) from exc return configured.resolve(strict=False) upload_root = _configured_upload_root() upload_manifest_proof: UploadManifestProof | None = None if settings.user_upload_manifest_required: if not upload_root.is_dir(): raise RuntimeError("initialized public USER_UPLOAD_DIR does not exist") if not settings.user_upload_manifest_path: raise RuntimeError("USER_UPLOAD_MANIFEST_PATH is required") if len(settings.user_upload_manifest_sha256) != 64: raise RuntimeError("USER_UPLOAD_MANIFEST_SHA256 is required") if not settings.user_upload_write_freeze_path: raise RuntimeError("USER_UPLOAD_WRITE_FREEZE_PATH is required") freeze_path = Path(settings.user_upload_write_freeze_path) if not freeze_path.is_absolute(): raise RuntimeError("USER_UPLOAD_WRITE_FREEZE_PATH must be absolute") upload_manifest_proof = validate_upload_manifest( upload_root=upload_root, manifest_path=Path(settings.user_upload_manifest_path), expected_manifest_sha256=settings.user_upload_manifest_sha256, expected_write_freeze_path=freeze_path, expected_database_target_sha256=settings.public_runtime_db_target_sha256, verify_preserved_objects=True, reject_unbound_extras=False, ) else: upload_root.mkdir(parents=True, exist_ok=True) public_avatar_root = upload_root / PUBLIC_AVATAR_DIRECTORY if settings.user_upload_manifest_required: if not public_avatar_root.is_dir(): raise RuntimeError("initialized public avatar directory does not exist") try: assert_path_without_reparse( public_avatar_root, "initialized public avatar directory", ) except ValueError as exc: raise RuntimeError(str(exc)) from exc else: public_avatar_root.mkdir(parents=True, exist_ok=True) configured_freeze_path = ( Path(settings.user_upload_write_freeze_path) if settings.user_upload_write_freeze_path else None ) upload_write_freeze_gate = UploadWriteFreezeGate(configured_freeze_path) @dataclass(frozen=True, slots=True) class RuntimeUploadDatabaseProof: database_target_sha256: str current_references: CurrentAvatarReferenceProof async def validate_runtime_upload_database_state( pool: Any, ) -> RuntimeUploadDatabaseProof: """Bind readiness to one repeatable-read snapshot from this API's pool.""" if upload_manifest_proof is None: raise RuntimeError("public upload manifest proof is unavailable") async with pool.acquire() as connection: async with connection.transaction(isolation="repeatable_read", readonly=True): database_target = await connected_database_target_sha256(connection) if database_target != settings.public_runtime_db_target_sha256: raise RuntimeError("connected public runtime database target drift") await connection.execute( "SELECT set_config('app.ai_context', '', true), " "set_config('app.current_role', 'admin', true)" ) rows = await connection.fetch( """ SELECT avatar_url FROM app.app_user WHERE avatar_url IS NOT NULL AND btrim(avatar_url) LIKE '/uploads/%' ORDER BY avatar_url """ ) current_references = validate_current_avatar_references( upload_root=upload_root, avatar_urls=[str(row["avatar_url"]) for row in rows], initial_manifest=upload_manifest_proof, expected_database_target_sha256=database_target, ) return RuntimeUploadDatabaseProof( database_target_sha256=database_target, current_references=current_references, ) def assert_public_avatar_url_exists(value: str) -> None: if not value.startswith("/uploads/"): return relative_path = public_avatar_url_to_relative_path(value) path_sha256 = public_avatar_relative_path_sha256(relative_path) preserved_decode_by_path = dict( upload_manifest_proof.preserved_path_decode_records if upload_manifest_proof is not None else () ) decode_valid = preserved_decode_by_path.get(path_sha256) if decode_valid is False: raise ValueError("public avatar URL object is forensic-only") candidate = upload_root / Path(*relative_path.split("/")) if decode_valid is True: assert_regular_file_without_reparse( candidate, "public avatar URL object", ) return if not is_runtime_generated_public_avatar_relative_path(relative_path): raise ValueError("public avatar URL object is not authorized") read_decodable_public_avatar_object( relative_path=relative_path, path=candidate, label="public avatar URL object", ) class FlatPublicAvatarStaticFiles(StaticFiles): """Serve only one validated public avatar image, never nested/hidden extras.""" async def get_response(self, path: str, scope): method = str(scope.get("method", "GET")).upper() if method not in {"GET", "HEAD"}: raise StarletteHTTPException(status_code=405) try: normalized = public_avatar_url_to_relative_path( f"/uploads/profile-avatars/{path}" ) filename = normalized.split("/", 1)[1] path_sha256 = public_avatar_relative_path_sha256(normalized) preserved_decode_by_path = dict( upload_manifest_proof.preserved_path_decode_records if upload_manifest_proof is not None else () ) preserved_public_cache = { record[0]: (record[1], record[2], record[3], record[4]) for record in ( upload_manifest_proof.preserved_records if upload_manifest_proof is not None else () ) } decode_valid = preserved_decode_by_path.get(path_sha256) if decode_valid is False: raise ValueError("public avatar object is forensic-only") preserved = preserved_public_cache.get(path_sha256) if preserved is not None: content_sha256, size_bytes, media_type, content = preserved body = content if method == "GET" else b"" return Response( content=body, media_type=media_type, headers={ "Content-Length": str(size_bytes), "ETag": f'"{content_sha256}"', "Cache-Control": "public, max-age=3600", }, ) if decode_valid is True: raise ValueError("validated public avatar cache is unavailable") if not is_runtime_generated_public_avatar_relative_path(normalized): raise ValueError("public avatar object is not authorized") candidate = Path(self.directory) / filename runtime_object = read_decodable_public_avatar_object( relative_path=normalized, path=candidate, label="public avatar static object", ) except (OSError, ValueError) as exc: raise StarletteHTTPException(status_code=404) from exc return Response( content=runtime_object.content if method == "GET" else b"", media_type=runtime_object.media_type, headers={ "Content-Length": str(runtime_object.size_bytes), "ETag": f'"{runtime_object.content_sha256}"', "Cache-Control": "public, max-age=3600", }, )