| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980 |
- """OSS 转存:把帖子媒体直链转存到公网 CDN,返回 cdn_url(绕过防盗链 / 临时签名链时效)。
- 接口(实测 2026-06-26):
- POST settings.oss_upload_url body{src_url, src_type:"video"|"image"}
- → {status:0, oss_object:{cdn_url:"https://res.cybertogether.net/...", oss_object_key, ...}}
- 纯 parser(parse_oss_response)可离线测;upload_stream 负责 HTTP(失败抛 OssError);
- to_oss 是兜底包装——任何失败返回原 src_url、永不抛,供 decompose 降级不阻断。
- """
- from __future__ import annotations
- from typing import Any, Callable, Optional
- import httpx
- from core.config import Settings
- class OssError(RuntimeError):
- pass
- def parse_oss_response(response: Any) -> str:
- """{status:0, oss_object:{cdn_url}} → cdn_url。status!=0 或缺 cdn_url 抛 OssError。"""
- if not isinstance(response, dict):
- raise OssError("bad_response: not a dict")
- status = response.get("status")
- if status not in (0, "0"):
- raise OssError(f"business_error: status={status} msg={response.get('msg')}")
- obj = response.get("oss_object") or {}
- cdn = obj.get("cdn_url")
- if not cdn:
- raise OssError("empty_cdn_url: oss_object.cdn_url missing")
- return cdn
- def upload_stream(
- src_url: str,
- *,
- src_type: str,
- settings: Optional[Settings] = None,
- http_post: Callable[..., Any] = httpx.post,
- env_file: str = ".env",
- ) -> str:
- """转存一条直链到 OSS,返回 cdn_url;HTTP/解析失败抛 OssError。"""
- settings = settings or Settings.from_env(env_file)
- if src_type not in ("video", "image"):
- raise OssError(f"bad_src_type: {src_type}")
- try:
- resp = http_post(
- settings.oss_upload_url,
- json={"src_url": src_url, "src_type": src_type},
- headers={"Content-Type": "application/json"},
- timeout=settings.oss_upload_timeout_seconds,
- )
- resp.raise_for_status()
- data = resp.json()
- except httpx.HTTPError as exc:
- raise OssError(f"http_error: {exc}") from exc
- except ValueError as exc:
- raise OssError("bad_json") from exc
- return parse_oss_response(data)
- def to_oss(
- src_url: str,
- src_type: str,
- *,
- settings: Optional[Settings] = None,
- http_post: Callable[..., Any] = httpx.post,
- env_file: str = ".env",
- ) -> str:
- """兜底包装:成功返回 cdn_url,任何失败(含空入参)返回原 src_url,永不抛。"""
- if not src_url:
- return src_url
- try:
- return upload_stream(src_url, src_type=src_type, settings=settings,
- http_post=http_post, env_file=env_file)
- except Exception:
- return src_url
|