feat: move report artifacts to OSS storage
This commit is contained in:
@@ -29,6 +29,8 @@ class ArtifactDescriptor:
|
||||
sha256: str
|
||||
byte_size: int
|
||||
mime_type: str
|
||||
storage_provider: str = "local"
|
||||
bucket_alias: str = "arr-project-root"
|
||||
|
||||
def validate(self) -> None:
|
||||
path = PurePosixPath(self.storage_key)
|
||||
@@ -48,6 +50,12 @@ class ArtifactDescriptor:
|
||||
or any(character not in "0123456789abcdef" for character in self.sha256)
|
||||
or not 0 < self.byte_size <= MAX_DOWNLOAD_BYTES
|
||||
or not self.mime_type
|
||||
or not isinstance(self.storage_provider, str)
|
||||
or self.storage_provider not in {"local", "local_fixture", "oss", "s3"}
|
||||
or not isinstance(self.bucket_alias, str)
|
||||
or not self.bucket_alias
|
||||
or "/" in self.bucket_alias
|
||||
or "\\" in self.bucket_alias
|
||||
):
|
||||
raise PortalError("DOWNLOAD_REFERENCE_INVALID", "文件身份无效", 500)
|
||||
|
||||
@@ -63,14 +71,21 @@ class UnavailableArtifactReader:
|
||||
|
||||
|
||||
class ManagedObjectArtifactReader:
|
||||
"""Read a committed daily XLSX from ARR's immutable object store."""
|
||||
"""Read a committed report artifact from ARR's immutable object store."""
|
||||
|
||||
_EXPECTED_ROLES = {
|
||||
"daily_xlsx": "daily_report",
|
||||
"monthly_xlsx": "monthly_report",
|
||||
"company_ten_day_xlsx": "company_report",
|
||||
}
|
||||
|
||||
def __init__(self, object_store: ManagedObjectStore) -> None:
|
||||
self._object_store = object_store
|
||||
|
||||
def read(self, descriptor: ArtifactDescriptor) -> bytes:
|
||||
descriptor.validate()
|
||||
if descriptor.file_kind != "daily_xlsx":
|
||||
expected_role = self._EXPECTED_ROLES.get(descriptor.file_kind)
|
||||
if expected_role is None:
|
||||
raise PortalError("DOWNLOAD_REFERENCE_INVALID", "文件身份无效", 500)
|
||||
try:
|
||||
stored = self._object_store.inspect_committed(
|
||||
@@ -78,7 +93,7 @@ class ManagedObjectArtifactReader:
|
||||
descriptor.original_filename,
|
||||
)
|
||||
if (
|
||||
stored.role != "daily_report"
|
||||
stored.role != expected_role
|
||||
or stored.sha256 != descriptor.sha256
|
||||
or stored.byte_size != descriptor.byte_size
|
||||
or stored.mime_type != descriptor.mime_type
|
||||
@@ -87,7 +102,7 @@ class ManagedObjectArtifactReader:
|
||||
"DOWNLOAD_IDENTITY_MISMATCH", "文件完整性校验失败", 503
|
||||
)
|
||||
with tempfile.TemporaryDirectory(prefix="arr-download-") as temporary:
|
||||
destination = Path(temporary) / "daily.xlsx"
|
||||
destination = Path(temporary) / "artifact.bin"
|
||||
self._object_store.materialize(
|
||||
descriptor.storage_key,
|
||||
destination,
|
||||
@@ -111,16 +126,18 @@ class ManagedObjectArtifactReader:
|
||||
|
||||
|
||||
class RoutedArtifactReader:
|
||||
"""Route OSS daily artifacts and controlled local report artifacts safely."""
|
||||
"""Route managed OSS artifacts and legacy controlled-local artifacts safely."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
daily_reader: Optional[ArtifactReader],
|
||||
local_reader: ArtifactReader,
|
||||
oss_reader: Optional[ArtifactReader] = None,
|
||||
) -> None:
|
||||
self._daily_reader = daily_reader
|
||||
self._local_reader = local_reader
|
||||
self._oss_reader = oss_reader
|
||||
|
||||
def read(self, descriptor: ArtifactDescriptor) -> bytes:
|
||||
descriptor.validate()
|
||||
@@ -130,6 +147,12 @@ class RoutedArtifactReader:
|
||||
"DOWNLOAD_UNAVAILABLE", "文件读取服务暂不可用", 503
|
||||
)
|
||||
return self._daily_reader.read(descriptor)
|
||||
if descriptor.storage_provider in {"oss", "s3"}:
|
||||
if self._oss_reader is None:
|
||||
raise PortalError(
|
||||
"DOWNLOAD_UNAVAILABLE", "文件读取服务暂不可用", 503
|
||||
)
|
||||
return self._oss_reader.read(descriptor)
|
||||
return self._local_reader.read(descriptor)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user