This commit is contained in:
2026-05-08 22:07:05 +08:00
61 changed files with 4364 additions and 202 deletions

View File

@@ -34,6 +34,8 @@
1. 当前产品口径为服务器上传 AI 生成资源、Web 端只负责读取。
2. 因此 `STS` 不作为默认上传主链,`api-server` 只暴露禁用式 contract避免浏览器拿到 OSS 写权限。
3. 服务端生成资源应优先复用 `OssClient::put_object`,上传成功后再走对象确认链路写入 `asset_object`
4. 读签名和 `HEAD Object` 的入参必须直接传 object_key不要把 bucket 名拼进路径;例如 `generated-square-hole-assets/.../image.png` 才是正确入参,`xushi-dev/...` 这类前缀不属于 object_key。
5. OSS V4 `x-oss-date` 必须固定为 `yyyyMMdd'T'HHmmss'Z'`,不能依赖 `time::Time::to_string()`;后者在小时小于 10 时可能输出非补零时间,导致签名格式错误。
## 3. 边界约束

View File

@@ -1084,6 +1084,7 @@ fn build_v4_signature_scope(endpoint: &str, signed_at: OffsetDateTime) -> Result
}
fn build_v4_signature_date(signed_at: OffsetDateTime) -> Result<String, OssError> {
// 中文注释time::Time 的 Display 在小时小于 10 时不会稳定补零OSS V4 必须使用固定宽度 UTC 时间。
Ok(format!(
"{}T{:02}{:02}{:02}Z",
format_v4_signature_scope_date(signed_at),
@@ -1486,6 +1487,39 @@ mod tests {
assert!(response.signed_url.contains("&x-oss-signature="));
}
#[test]
fn sign_get_object_url_uses_square_hole_object_key_without_bucket_prefix() {
let client = OssClient::new(
OssConfig::new(
"xushi-dev".to_string(),
"oss-cn-shanghai.aliyuncs.com".to_string(),
"test-access-key-id".to_string(),
"test-access-key-secret".to_string(),
DEFAULT_READ_EXPIRE_SECONDS,
DEFAULT_POST_EXPIRE_SECONDS,
DEFAULT_POST_MAX_SIZE_BYTES,
DEFAULT_SUCCESS_ACTION_STATUS,
)
.expect("OSS config should be valid"),
);
let response = client
.sign_get_object_url(OssSignedGetObjectUrlRequest {
object_key: "generated-square-hole-assets/square-hole-session-546d881972684be2980a2a882cd0cc71/square-hole-profile-134411276ce1469cbe398f946a25d7f8/square-hole-shape-image/rabbit-option/asset-1777979289912039/image.png".to_string(),
expire_seconds: Some(300),
})
.expect("square hole object key should build signed url");
assert_eq!(response.bucket, "xushi-dev".to_string());
assert_eq!(
response.object_key,
"generated-square-hole-assets/square-hole-session-546d881972684be2980a2a882cd0cc71/square-hole-profile-134411276ce1469cbe398f946a25d7f8/square-hole-shape-image/rabbit-option/asset-1777979289912039/image.png".to_string()
);
assert!(response
.signed_url
.starts_with("https://xushi-dev.oss-cn-shanghai.aliyuncs.com/generated-square-hole-assets/square-hole-session-546d881972684be2980a2a882cd0cc71/square-hole-profile-134411276ce1469cbe398f946a25d7f8/square-hole-shape-image/rabbit-option/asset-1777979289912039/image.png?"));
}
#[test]
fn sign_get_object_url_rejects_unsupported_prefix() {
let client = build_client();