Procházet zdrojové kódy

s3.fmode.cn 公网链接铁律: public_url 统一走 CDN 域名

liuyuyang před 3 týdny
rodič
revize
54728a9793
2 změnil soubory, kde provedl 14 přidání a 6 odebrání
  1. 4 2
      references/s3-upload.md
  2. 10 4
      scripts/common.py

+ 4 - 2
references/s3-upload.md

@@ -4,8 +4,10 @@
 
 ```
 桶:   storage-s3-nkkj (华为云 OBS, cn-north-4)
-域名: 公网直接URL https://storage-s3-nkkj.obs.cn-north-4.myhuaweicloud.com/<key>
-      (s3.fmode.cn CNAME 生效后可换 https://s3.fmode.cn/<key>, 相对路径报告不受影响)
+域名: 【铁律 2026-08-29】公开链接一律 https://s3.fmode.cn/<key> (全小写 Host)
+      链路: s3.fmode.cn(CDN) -> 回源 storage-s3-nkkj.obs-website.cn-north-4
+            (OBS 静态网站托管) + CDN 响应头 Content-Disposition: inline
+      桶本身不渲染网页, 必须经 CDN 才能静态渲染; OBS 原生域名只用于签名上传/内部校验
 铁律: 只能写 user/<我的userid>/ 前缀; 他人前缀 = 越权
 ```
 

+ 10 - 4
scripts/common.py

@@ -291,6 +291,11 @@ S3_BUCKET = os.environ.get("S3_BUCKET", "storage-s3-nkkj")
 S3_REGION = os.environ.get("S3_REGION", "cn-north-4")
 S3_HOST = os.environ.get("S3_ENDPOINT", "https://obs.cn-north-4.myhuaweicloud.com") \
     .replace("https://", "").rstrip("/")
+# 公网访问铁律(2026-08-29 用户定调): 对外链接必须 s3.fmode.cn(全小写)。
+# 该域名走华为云CDN回源OBS静态网站托管(Content-Disposition:inline), OBS桶本身不渲染网页,
+# 直接用OBS原生域名会破坏网页渲染与统一域名。仅签名上传仍走 S3_UPLOAD_HOST。
+S3_PUBLIC_HOST = os.environ.get("S3_PUBLIC_HOST", "s3.fmode.cn")
+S3_UPLOAD_HOST = f"{S3_BUCKET}.{S3_HOST}"
 
 
 def _sigv4(method: str, key: str, payload: bytes, cred: dict,
@@ -331,12 +336,12 @@ def _sigv4(method: str, key: str, payload: bytes, cred: dict,
 
 def s3_put_bytes(key: str, payload: bytes, content_type: str = "application/octet-stream",
                  public_read: bool = True) -> str:
-    """上传字节到 S3_BUCKET, 返回可公开访问的 URL。"""
+    """上传字节到 S3_BUCKET, 返回可公开访问的 URL(s3.fmode.cn, 见 S3_PUBLIC_HOST 铁律)。"""
     cred = get_storage_credentials(f"user/{get_userid()}/")
     hdrs = _sigv4("PUT", key, payload, cred, content_type)
     if not public_read:
         hdrs.pop("x-amz-acl", None)
-    url = f"https://{S3_BUCKET}.{S3_HOST}/{key}"
+    url = f"https://{S3_UPLOAD_HOST}/{key}"
     req = urllib.request.Request(url, data=payload, method="PUT", headers=hdrs)
     with urllib.request.urlopen(req, timeout=300) as r:
         if r.status not in (200, 201):
@@ -351,10 +356,11 @@ def s3_put_file(path: str, key: str, content_type: str = None) -> str:
 
 
 def public_url(key: str) -> str:
-    """公开访问 URL。s3.fmode.cn CNAME 生效前用 OBS 原生域名。"""
+    """公开访问 URL 铁律: 一律 https://s3.fmode.cn/<key>(CDN 静态渲染, 全小写 Host)。
+    S3_PUBLIC_HOST 可覆盖; 旧 S3_PUBLIC_BASE 环境变量保持兼容。"""
     if os.environ.get("S3_PUBLIC_BASE"):
         return f"{os.environ['S3_PUBLIC_BASE'].rstrip('/')}/{key}"
-    return f"https://{S3_BUCKET}.{S3_HOST}/{key}"
+    return f"https://{S3_PUBLIC_HOST}/{key}"
 
 
 def guess_content_type(path: str) -> str: