fix(platform): govern geospatial analysis and raster handoffs

This commit is contained in:
Jens
2026-08-30 06:00:15 +02:00
parent 96f90373dc
commit 80a2d1654d
63 changed files with 2335 additions and 312 deletions
+99
View File
@@ -10,6 +10,8 @@ from app.core.errors import AppError
class StorageService:
UPLOAD_CHUNK_SIZE = 8 * 1024 * 1024
@staticmethod
def _base_dir() -> Path:
return Path(get_settings().storage_root).resolve()
@@ -143,6 +145,75 @@ class StorageService:
}
return metadata
@staticmethod
async def persist_upload_file(
*,
project_id: str,
dataset_id: str,
dataset_type: str,
original_filename: str,
upload: Any,
content_type: str | None,
max_bytes: int,
chunk_size: int | None = None,
) -> dict[str, Any]:
"""Stream an UploadFile to governed storage with a hard byte limit.
The reverse proxy limit is defense in depth. This backend boundary is
authoritative as direct/loopback requests can bypass that proxy.
"""
if max_bytes <= 0:
raise ValueError("max_bytes must be positive")
resolved_chunk_size = chunk_size or StorageService.UPLOAD_CHUNK_SIZE
if resolved_chunk_size <= 0:
raise ValueError("chunk_size must be positive")
normalized_type = StorageService.normalize_dataset_type(dataset_type)
file_path = Path(
StorageService.dataset_file_path(
project_id,
dataset_id,
normalized_type,
original_filename,
)
)
file_path.parent.mkdir(parents=True, exist_ok=True)
digest = hashlib.sha256()
size_bytes = 0
try:
with file_path.open("wb") as stream:
while True:
chunk = await upload.read(resolved_chunk_size)
if not chunk:
break
size_bytes += len(chunk)
if size_bytes > max_bytes:
raise AppError(
code="UPLOAD_TOO_LARGE",
message="Upload exceeds the configured backend size limit.",
details={
"max_bytes": max_bytes,
"max_upload_mb": max_bytes // (1024 * 1024),
},
status_code=413,
)
stream.write(chunk)
digest.update(chunk)
except Exception:
file_path.unlink(missing_ok=True)
parent = file_path.parent
if parent.exists() and parent.is_dir() and not any(parent.iterdir()):
parent.rmdir()
raise
return {
"original_filename": StorageService._safe_filename(original_filename),
"stored_filename": file_path.name,
"content_type": content_type or "application/octet-stream",
"size_bytes": size_bytes,
"checksum_sha256": digest.hexdigest(),
"storage_path": str(file_path),
}
@staticmethod
def persist_dataset_file_from_path(
project_id: str,
@@ -198,6 +269,34 @@ class StorageService:
}
return metadata
@staticmethod
def persist_file_from_path(
storage_path: str,
source_path: str | Path,
original_filename: str,
content_type: str | None,
) -> dict[str, Any]:
source = Path(source_path).resolve()
if not source.is_file():
raise FileNotFoundError(f"Source artifact does not exist: {source}")
target = Path(storage_path)
target.parent.mkdir(parents=True, exist_ok=True)
digest = hashlib.sha256()
size_bytes = 0
with source.open("rb") as input_stream, target.open("wb") as output_stream:
for chunk in iter(lambda: input_stream.read(StorageService.UPLOAD_CHUNK_SIZE), b""):
output_stream.write(chunk)
digest.update(chunk)
size_bytes += len(chunk)
return {
"original_filename": StorageService._safe_filename(original_filename),
"stored_filename": target.name,
"content_type": content_type or "application/octet-stream",
"size_bytes": size_bytes,
"checksum_sha256": digest.hexdigest(),
"storage_path": str(target),
}
@staticmethod
def remove_dataset_file(path: str) -> None:
target = Path(path)