diff --git a/src/services/curseforge.rs b/src/services/curseforge.rs index a9aa728..f19c613 100644 --- a/src/services/curseforge.rs +++ b/src/services/curseforge.rs @@ -306,21 +306,11 @@ impl CurseforgeService { mods.push(doc); } - // empty 则直接返回 { "data": [] } - if mods.is_empty() { - return Err(ServiceError::NotFound { - resource: String::from("Mods"), - detail: Some(format!( - "No mods found for the provided modIds: {:?}", - mod_ids - )), - }); - } - // 检查是否有未找到的 mod_id let found_mod_ids: Vec = mods.iter().map(|m| m.id).collect(); let not_found_mod_ids: Vec = mod_ids - .into_iter() + .iter() + .copied() .filter(|id| !found_mod_ids.contains(id)) .collect(); if !not_found_mod_ids.is_empty() { @@ -333,6 +323,38 @@ impl CurseforgeService { log::trace!("All Mods have been found in the database."); } + /* + curl -i -X 'POST' \ + 'https://api.curseforge.com/v1/mods' \ + -H 'accept: application/json' \ + -H 'Content-Type: application/json' \ + -H 'x-api-key: $API_KEY' \ + -d '{ + "filterPcOnly": true, + "modIds": [ + 238222112 + ] + }' + HTTP/2 404 + content-length: 0 + date: Sat, 12 Sep 2026 07:06:03 GMT + x-cache: Error from cloudfront + via: 1.1 715cc2fe99ad4f311de46e74c166c5a2.cloudfront.net (CloudFront) + x-amz-cf-pop: LOS50-P4 + x-amz-cf-id: EGanUHlrFZiDD8YG6hWQpedqhiWQ30pKnB7DX8CBBNyTsyixGDDjNQ== + */ + // 不会返回 Body,可以任意填写 + // 但是必须返回 404,不能返回 200 + if mods.is_empty() { + return Err(ServiceError::NotFound { + resource: String::from("Mods"), + detail: Some(format!( + "No mods found for the provided modIds: {:?}", + mod_ids + )), + }); + } + let response_mods = mods.into_iter().map(|m| m.into()).collect(); Ok(ModsResponse { data: response_mods, diff --git a/src/services/modrinth.rs b/src/services/modrinth.rs index ef5450d..608d8c9 100644 --- a/src/services/modrinth.rs +++ b/src/services/modrinth.rs @@ -368,6 +368,7 @@ impl ModrinthService { params.push(("index", i.to_string())); } + // 在此处要打断 4xx/5xx 错误,返回 ServiceError::ExternalServiceError,以免被缓存 let response = client .get(api_url) .query(¶ms) @@ -376,9 +377,26 @@ impl ModrinthService { .map_err(|e| ServiceError::ExternalServiceError { service: String::from("Modrinth API"), message: format!("Failed to send request: {}", e), + })? + .error_for_status() + .map_err(|e| ServiceError::ExternalServiceError { + service: String::from("Modrinth API"), + message: format!("Upstream HTTP error: {}", e), })?; - let status = response.status(); + // 在此处要打断 4xx/5xx 错误,返回 ServiceError::ExternalServiceError,以免被缓存 + if let Err(e) = response.error_for_status_ref() { + let error_body = response + .text() + .await + .unwrap_or_else(|_| String::from("")); + return Err(ServiceError::ExternalServiceError { + service: String::from("Modrinth API"), + message: format!("Upstream HTTP error: {}, body: {}", e, error_body), + }); + } + + let status: reqwest::StatusCode = response.status(); let bytes = response .bytes() .await @@ -386,6 +404,7 @@ impl ModrinthService { service: String::from("Modrinth API"), message: format!("Failed to read response body: {}", e), })?; + let search_result = serde_json::from_slice(&bytes).map_err(|e| { ServiceError::UnexpectedError(format!( "Failed to parse JSON: {}, text: {}", @@ -925,14 +944,32 @@ impl ModrinthService { // 创建哈希值到版本的映射 let mut result: HashMap = HashMap::new(); + // 用 HashSet 来优化查找速度,避免 Vec contains O(n) 查找 + let requested_hashes_set: std::collections::HashSet<&str> = + hashes.iter().map(|s| s.as_str()).collect(); + for version in versions { - if let Some(first_file) = version.files.first() { + // 此处导致只有是第一个文件的哈希值会被映射到版本,其他文件的哈希值不会被映射 + // if let Some(first_file) = version.files.first() { + // let hash_value = match algorithm.as_str() { + // "sha1" => &first_file.hashes.sha1, + // "sha512" => &first_file.hashes.sha512, + // _ => continue, + // }; + // result.insert(hash_value.clone(), version); + // } + + // 遍历版本的所有文件,找到匹配的哈希值 + for file in &version.files { let hash_value = match algorithm.as_str() { - "sha1" => &first_file.hashes.sha1, - "sha512" => &first_file.hashes.sha512, + "sha1" => &file.hashes.sha1, + "sha512" => &file.hashes.sha512, _ => continue, }; - result.insert(hash_value.clone(), version); + // 只有客户端查询列表中包含的 hash,才插入到结果中 + if requested_hashes_set.contains(hash_value.as_str()) { + result.insert(hash_value.clone(), version.clone()); + } } }