"""File storage utilities for persistence, retrieval, and checksum calculation.""" import hashlib import uuid from datetime import UTC, datetime from pathlib import Path from app.core.config import get_settings settings = get_settings() def ensure_storage() -> None: """Ensures required storage directories exist at service startup.""" for relative in ["originals", "derived/previews", "tmp"]: (settings.storage_root / relative).mkdir(parents=True, exist_ok=True) def compute_sha256(data: bytes) -> str: """Computes a SHA-256 hex digest for raw file bytes.""" return hashlib.sha256(data).hexdigest() def store_bytes(filename: str, data: bytes) -> str: """Stores file content under a unique path and returns its storage-relative location.""" stamp = datetime.now(UTC).strftime("%Y/%m/%d") safe_ext = Path(filename).suffix.lower() target_dir = settings.storage_root / "originals" / stamp target_dir.mkdir(parents=True, exist_ok=True) target_name = f"{uuid.uuid4()}{safe_ext}" target_path = target_dir / target_name target_path.write_bytes(data) return str(target_path.relative_to(settings.storage_root)) def read_bytes(relative_path: str) -> bytes: """Reads and returns bytes from a storage-relative path.""" return (settings.storage_root / relative_path).read_bytes() def absolute_path(relative_path: str) -> Path: """Returns the absolute filesystem path for a storage-relative location.""" return settings.storage_root / relative_path def write_preview(document_id: str, data: bytes, suffix: str = ".jpg") -> str: """Writes preview bytes and returns the preview path relative to storage root.""" target_dir = settings.storage_root / "derived" / "previews" target_dir.mkdir(parents=True, exist_ok=True) target_path = target_dir / f"{document_id}{suffix}" target_path.write_bytes(data) return str(target_path.relative_to(settings.storage_root))