1
This commit is contained in:
@@ -23,8 +23,8 @@ use shared_contracts::assets::{
|
||||
use spacetime_client::SpacetimeClientError;
|
||||
|
||||
use crate::{
|
||||
api_response::json_success_body, http_error::AppError, request_context::RequestContext,
|
||||
state::AppState,
|
||||
api_response::json_success_body, auth::AuthenticatedAccessToken, http_error::AppError,
|
||||
request_context::RequestContext, state::AppState,
|
||||
};
|
||||
|
||||
// 历史素材类型需要与 SpacetimeDB 侧白名单保持同一口径,避免新增素材类型时 HTTP 门面漏同步。
|
||||
@@ -119,6 +119,7 @@ pub async fn get_asset_read_url(
|
||||
pub async fn get_asset_history(
|
||||
State(state): State<AppState>,
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
Extension(authenticated): Extension<AuthenticatedAccessToken>,
|
||||
Query(query): Query<AssetHistoryQuery>,
|
||||
) -> Result<Json<Value>, AppError> {
|
||||
let asset_kind = query.kind.trim().to_string();
|
||||
@@ -133,18 +134,23 @@ pub async fn get_asset_history(
|
||||
|
||||
let entries = state
|
||||
.spacetime_client()
|
||||
.list_asset_history(module_assets::AssetHistoryListInput {
|
||||
asset_kind,
|
||||
limit: query.limit.unwrap_or(120).clamp(1, 120),
|
||||
})
|
||||
.list_asset_history(build_asset_history_list_input(asset_kind, query.limit))
|
||||
.await
|
||||
.map_err(map_confirm_asset_object_error)?;
|
||||
let owner_user_id = authenticated.claims().user_id().to_string();
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
AssetHistoryListResponse {
|
||||
assets: entries
|
||||
.into_iter()
|
||||
// 中文注释:Maincloud 旧 wasm 的历史素材 procedure 仍按类型返回,HTTP 门面必须兜底做账号隔离。
|
||||
.filter(|entry| {
|
||||
is_asset_history_owned_by(
|
||||
entry.owner_user_id.as_deref(),
|
||||
owner_user_id.as_str(),
|
||||
)
|
||||
})
|
||||
.map(|entry| AssetHistoryEntryPayload {
|
||||
owner_label: format_asset_owner_label(entry.owner_user_id.as_deref()),
|
||||
asset_object_id: entry.asset_object_id,
|
||||
@@ -296,6 +302,25 @@ fn is_supported_asset_history_kind(asset_kind: &str) -> bool {
|
||||
SUPPORTED_ASSET_HISTORY_KINDS.contains(&asset_kind)
|
||||
}
|
||||
|
||||
fn is_asset_history_owned_by(entry_owner_user_id: Option<&str>, owner_user_id: &str) -> bool {
|
||||
let owner_user_id = owner_user_id.trim();
|
||||
!owner_user_id.is_empty()
|
||||
&& entry_owner_user_id
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
== Some(owner_user_id)
|
||||
}
|
||||
|
||||
fn build_asset_history_list_input(
|
||||
asset_kind: String,
|
||||
limit: Option<u32>,
|
||||
) -> module_assets::AssetHistoryListInput {
|
||||
module_assets::AssetHistoryListInput {
|
||||
asset_kind,
|
||||
limit: limit.unwrap_or(120).clamp(1, 120),
|
||||
}
|
||||
}
|
||||
|
||||
fn supported_asset_history_kind_message() -> String {
|
||||
format!(
|
||||
"历史素材类型只支持 {}",
|
||||
@@ -490,6 +515,29 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn asset_history_owner_filter_keeps_only_authenticated_owner_assets() {
|
||||
assert!(super::is_asset_history_owned_by(
|
||||
Some("user-current"),
|
||||
"user-current"
|
||||
));
|
||||
assert!(!super::is_asset_history_owned_by(
|
||||
Some("user-other"),
|
||||
"user-current"
|
||||
));
|
||||
assert!(!super::is_asset_history_owned_by(None, "user-current"));
|
||||
assert!(!super::is_asset_history_owned_by(Some("user-current"), ""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn asset_history_input_clamps_limit_for_spacetime_query() {
|
||||
let input =
|
||||
super::build_asset_history_list_input("puzzle_cover_image".to_string(), Some(240));
|
||||
|
||||
assert_eq!(input.asset_kind, "puzzle_cover_image");
|
||||
assert_eq!(input.limit, 120);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn direct_upload_ticket_returns_service_unavailable_when_oss_missing() {
|
||||
let app = build_router(AppState::new(AppConfig::default()).expect("state should build"));
|
||||
|
||||
Reference in New Issue
Block a user