134 lines
4.5 KiB
Rust
134 lines
4.5 KiB
Rust
use axum::http::{HeaderValue, StatusCode};
|
|
use platform_auth::{AuthPlatformErrorKind, WechatProviderError};
|
|
use platform_llm::{LlmError, LlmErrorKind};
|
|
use platform_oss::{OssError, OssErrorKind};
|
|
use serde_json::json;
|
|
|
|
use crate::http_error::AppError;
|
|
|
|
// API 层统一消费 platform 的稳定错误分类,避免各 route 重复 match 具体 provider 分支。
|
|
pub fn map_llm_error(error: LlmError) -> AppError {
|
|
let message = llm_error_message(&error);
|
|
let status = match error.kind() {
|
|
LlmErrorKind::InvalidRequest => StatusCode::BAD_REQUEST,
|
|
LlmErrorKind::InvalidConfig => StatusCode::SERVICE_UNAVAILABLE,
|
|
LlmErrorKind::Upstream
|
|
if matches!(
|
|
error,
|
|
LlmError::Upstream {
|
|
status_code: 429,
|
|
..
|
|
}
|
|
) =>
|
|
{
|
|
StatusCode::TOO_MANY_REQUESTS
|
|
}
|
|
LlmErrorKind::Timeout
|
|
| LlmErrorKind::Connectivity
|
|
| LlmErrorKind::Upstream
|
|
| LlmErrorKind::StreamUnavailable
|
|
| LlmErrorKind::EmptyResponse
|
|
| LlmErrorKind::Transport
|
|
| LlmErrorKind::Deserialize => StatusCode::BAD_GATEWAY,
|
|
};
|
|
|
|
AppError::from_status(status).with_message(message)
|
|
}
|
|
|
|
pub fn map_oss_error(error: OssError, provider: &'static str) -> AppError {
|
|
let status = oss_error_status(error.kind());
|
|
AppError::from_status(status).with_details(json!({
|
|
"provider": provider,
|
|
"message": error.to_string(),
|
|
}))
|
|
}
|
|
|
|
pub fn map_phone_auth_platform_store_error(message: String) -> AppError {
|
|
AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_message(message)
|
|
}
|
|
|
|
pub fn map_wechat_provider_error(error: WechatProviderError) -> AppError {
|
|
let status = match error.kind() {
|
|
AuthPlatformErrorKind::Disabled
|
|
| AuthPlatformErrorKind::MissingCode
|
|
| AuthPlatformErrorKind::InvalidCallback => StatusCode::BAD_REQUEST,
|
|
AuthPlatformErrorKind::InvalidConfig => StatusCode::SERVICE_UNAVAILABLE,
|
|
AuthPlatformErrorKind::RequestFailed
|
|
| AuthPlatformErrorKind::DeserializeFailed
|
|
| AuthPlatformErrorKind::MissingProfile
|
|
| AuthPlatformErrorKind::Upstream => StatusCode::BAD_GATEWAY,
|
|
AuthPlatformErrorKind::InvalidClaims
|
|
| AuthPlatformErrorKind::SignFailed
|
|
| AuthPlatformErrorKind::VerifyFailed
|
|
| AuthPlatformErrorKind::CookieConfig
|
|
| AuthPlatformErrorKind::HashFailed
|
|
| AuthPlatformErrorKind::InvalidVerifyCode => StatusCode::INTERNAL_SERVER_ERROR,
|
|
};
|
|
|
|
AppError::from_status(status).with_message(error.to_string())
|
|
}
|
|
|
|
pub fn attach_retry_after(error: AppError, retry_after_seconds: u64) -> AppError {
|
|
match HeaderValue::from_str(&retry_after_seconds.to_string()) {
|
|
Ok(value) => error.with_header("retry-after", value),
|
|
Err(_) => error,
|
|
}
|
|
}
|
|
|
|
fn oss_error_status(kind: OssErrorKind) -> StatusCode {
|
|
match kind {
|
|
OssErrorKind::InvalidConfig | OssErrorKind::InvalidRequest => StatusCode::BAD_REQUEST,
|
|
OssErrorKind::ObjectNotFound => StatusCode::NOT_FOUND,
|
|
OssErrorKind::Request | OssErrorKind::SerializePolicy | OssErrorKind::Sign => {
|
|
StatusCode::BAD_GATEWAY
|
|
}
|
|
}
|
|
}
|
|
|
|
fn llm_error_message(error: &LlmError) -> String {
|
|
match error {
|
|
LlmError::InvalidConfig(message)
|
|
| LlmError::InvalidRequest(message)
|
|
| LlmError::Transport(message)
|
|
| LlmError::Deserialize(message) => message.clone(),
|
|
LlmError::Timeout { .. }
|
|
| LlmError::Connectivity { .. }
|
|
| LlmError::Upstream { .. }
|
|
| LlmError::StreamUnavailable
|
|
| LlmError::EmptyResponse => error.to_string(),
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn map_oss_error_uses_stable_kind_for_not_found() {
|
|
let error = map_oss_error(
|
|
OssError::ObjectNotFound("missing object".to_string()),
|
|
"oss",
|
|
);
|
|
|
|
assert_eq!(error.status_code(), StatusCode::NOT_FOUND);
|
|
}
|
|
|
|
#[test]
|
|
fn map_llm_error_preserves_upstream_rate_limit() {
|
|
let error = map_llm_error(LlmError::Upstream {
|
|
status_code: 429,
|
|
message: "too many requests".to_string(),
|
|
});
|
|
|
|
assert_eq!(error.status_code(), StatusCode::TOO_MANY_REQUESTS);
|
|
}
|
|
|
|
#[test]
|
|
fn map_wechat_provider_error_keeps_provider_boundary() {
|
|
let error = map_wechat_provider_error(WechatProviderError::MissingCode);
|
|
|
|
assert_eq!(error.status_code(), StatusCode::BAD_REQUEST);
|
|
assert_eq!(error.message(), "缺少微信授权 code");
|
|
}
|
|
}
|