This commit is contained in:
tao.chen
2026-07-30 20:52:46 +08:00
parent b6a029fe7e
commit 91767461d6
12 changed files with 380 additions and 236 deletions
-4
View File
@@ -39,10 +39,6 @@ async def lifespan(app: Any) -> AsyncIterator[None]:
"RUSTFS_INTERNAL_ENDPOINT",
"http://rustfs:9000",
),
public_endpoint=os.getenv(
"RUSTFS_PUBLIC_ENDPOINT",
"http://localhost:9000",
),
access_key=os.environ["RUSTFS_ACCESS_KEY"],
secret_key=os.environ["RUSTFS_SECRET_KEY"],
)
-1
View File
@@ -78,7 +78,6 @@ async def create_resource_upload(
"expected_size_bytes": payload.expected_size_bytes,
"expected_hash": payload.expected_hash,
"idempotency_key": idempotency_key,
"url_scope": "public",
}
)
return {"request_id": context.request_id, "data": data, "meta": {}}
+21 -14
View File
@@ -1,14 +1,15 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
import httpx
from common.clients.base import BaseInternalClient, InternalClientError
class RuntimeClientError(InternalClientError):
"""Backward-compatible alias for the runtime error type."""
@dataclass(frozen=True)
class RuntimeClientError(Exception):
status_code: int
detail: Any
_RUNTIME_TRANSPORT_ERROR = RuntimeClientError(
@@ -22,11 +23,9 @@ _RUNTIME_TRANSPORT_ERROR = RuntimeClientError(
)
class RuntimeClient(BaseInternalClient):
error_class = RuntimeClientError
class RuntimeClient:
def __init__(self, client: httpx.AsyncClient) -> None:
super().__init__(client)
self.client = client
async def _request(
self,
@@ -34,12 +33,17 @@ class RuntimeClient(BaseInternalClient):
path: str,
payload: dict[str, Any],
) -> dict[str, Any]:
return await super()._request(
method,
path,
payload=payload,
on_transport_error=_RUNTIME_TRANSPORT_ERROR,
)
try:
response = await self.client.request(method, path, json=payload)
except httpx.RequestError as exc:
raise _RUNTIME_TRANSPORT_ERROR from exc
if response.is_error:
try:
detail = response.json().get("detail", response.text)
except ValueError:
detail = response.text
raise RuntimeClientError(response.status_code, detail)
return response.json()
async def get_workspace(
self,
@@ -72,3 +76,6 @@ class RuntimeClient(BaseInternalClient):
"/api/v1/jupyter",
{"action": "start", "workspace_id": workspace_id},
)
__all__ = ["RuntimeClient", "RuntimeClientError"]
+34 -9
View File
@@ -90,9 +90,6 @@ async def lifespan(app: Any) -> AsyncIterator[None]:
internal_endpoint=os.getenv(
"RUSTFS_INTERNAL_ENDPOINT",
"http://rustfs:9000"),
public_endpoint=os.getenv(
"RUSTFS_PUBLIC_ENDPOINT",
"http://localhost:9000"),
access_key=os.environ["RUSTFS_ACCESS_KEY"],
secret_key=os.environ["RUSTFS_SECRET_KEY"])
app.state.default_bucket = os.getenv(
@@ -218,18 +215,43 @@ async def create_upload_record(
object_key=upload.object_key,
content_type=upload.content_type or "application/octet-stream",
expected_hash=upload.expected_hash,
expires_seconds=900,
public=payload.url_scope == "public")
expires_seconds=900)
presigned_url = request.app.state.object_store.rewrite_to_public_path(
url,
public_base_url=_public_base_url(request),
)
return {
"upload_id": upload.upload_id,
"status": upload.upload_status,
"method": "PUT",
"presigned_url": url,
"presigned_url": presigned_url,
"required_headers": headers,
"expires_at": upload.expires_at.isoformat(),
}
def _public_base_url(request: Request) -> str:
"""Return the public base URL the client should use.
Falls back to the inbound request's ``Host`` header and the scheme
Nginx forwards via ``X-Forwarded-Proto`` so the resulting
presigned URL always points at the public edge rather than the
in-cluster RustFS endpoint.
"""
forwarded_proto = request.headers.get("x-forwarded-proto", "").strip()
scheme = forwarded_proto or request.url.scheme or "http"
host = (
request.headers.get("x-forwarded-host", "").strip()
or request.headers.get("host", "").strip()
)
if not host:
raise HTTPException(
status.HTTP_400_BAD_REQUEST,
"cannot determine public host for presigned URL",
)
return f"{scheme}://{host}"
async def complete_upload_record(
upload_id: str,
payload: CompleteUploadRequest,
@@ -408,8 +430,7 @@ async def create_server_object(
content_type=payload.content_type,
expected_size_bytes=len(content),
expected_hash=content_hash,
idempotency_key=payload.idempotency_key,
url_scope="internal"),
idempotency_key=payload.idempotency_key),
session,
request)
if upload_result.get("status") == "completed":
@@ -544,10 +565,14 @@ async def create_download_url(
object_key=item.object_key,
file_name=item.file_name,
expires_seconds=payload.expires_seconds)
presigned_url = request.app.state.object_store.rewrite_to_public_path(
url,
public_base_url=_public_base_url(request),
)
return {
"data": {
"storage_object_id": item.storage_object_id,
"presigned_url": url,
"presigned_url": presigned_url,
"method": "GET",
"expires_in_seconds": payload.expires_seconds,
}