AuthClientInitFailed带构建失败原始错误

- 错误枚举:authClientInitFailed 从 unit 变体改成带 detail 的载荷变体,导出 AuthClientInitFailed 绑定
- auth_session:build_client 失败时把 reqwest 原始错误放进 detail,不再丢弃
- carrier 用例按带载荷变体 as 取 detail
- 前端维持固定文案,不把 detail 贴到界面上
This commit is contained in:
2026-10-02 12:12:50 +08:00
parent 7128e865c0
commit e1e8725dab
5 changed files with 41 additions and 10 deletions
@@ -61,8 +61,8 @@ pub(crate) enum ClientAuthError {
ClientSessionPersistFailed(ClientSessionPersistFailed),
/// 本机运行时会话安装 / 清理失败;`detail` 同上。
RuntimeSessionInstallFailed(RuntimeSessionInstallFailed),
/// 认证网络客户端构建失败。
AuthClientInitFailed,
/// 认证网络客户端构建失败;`detail` 是原始错误。
AuthClientInitFailed(AuthClientInitFailed),
}
// ---- 可枚举的细分原因:类型化字段,不是字符串 ----
@@ -204,6 +204,14 @@ pub(crate) struct RuntimeSessionInstallFailed {
pub(crate) detail: String,
}
/// 认证网络客户端构建失败的原始错误明细。
#[derive(Clone, Debug, PartialEq, Eq, Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export, export_to = concat!(env!("CARGO_MANIFEST_DIR"), "/../src/services/generated/"))]
pub(crate) struct AuthClientInitFailed {
pub(crate) detail: String,
}
impl ClientAuthError {
/// 会话路由的 401/403 是「登录态失效」:调用方据此清会话、按未登录处理,
/// 既不给用户报错,也不进错误报告池。
@@ -291,7 +299,9 @@ mod tests {
"runtimeSessionInstallFailed",
),
(
ClientAuthError::AuthClientInitFailed,
ClientAuthError::AuthClientInitFailed(AuthClientInitFailed {
detail: "x".to_string(),
}),
"authClientInitFailed",
),
];
@@ -364,6 +374,15 @@ mod tests {
))
.expect("serialize auth error");
assert_eq!(persist["detail"], "磁盘只读");
let init = serde_json::to_value(ClientAuthError::AuthClientInitFailed(
AuthClientInitFailed {
detail: "tls handshake failed".to_string(),
},
))
.expect("serialize auth error");
assert_eq!(init["type"], "authClientInitFailed");
assert_eq!(init["detail"], "tls handshake failed");
}
#[test]
@@ -5,10 +5,10 @@
//! origin 变化都会让旧身份的在途请求失败关闭;同一身份的凭据轮换不改变身份代次。
use crate::auth_error::{
AuthNetworkFailure, AuthNetworkReason, AuthResponseInvalid, AuthResponseInvalidReason,
AuthServiceUnavailable, ClientAuthError, ClientSessionPersistFailed, PasswordLoginRejected,
PhoneCodeLoginRejected, RuntimeSessionInstallFailed, SendCodeRejected, ServerAddressReason,
ServerAddressRejected, UnexpectedRejection,
AuthClientInitFailed, AuthNetworkFailure, AuthNetworkReason, AuthResponseInvalid,
AuthResponseInvalidReason, AuthServiceUnavailable, ClientAuthError, ClientSessionPersistFailed,
PasswordLoginRejected, PhoneCodeLoginRejected, RuntimeSessionInstallFailed, SendCodeRejected,
ServerAddressReason, ServerAddressRejected, UnexpectedRejection,
};
use crate::http_client::agc_main_site_client_builder;
use crate::platform_session::{current_platform_session, PlatformSessionSnapshot};
@@ -443,7 +443,11 @@ fn build_client() -> Result<reqwest::Client, ClientAuthError> {
.connect_timeout(Duration::from_secs(10))
.timeout(HTTP_TIMEOUT)
.build()
.map_err(|_| ClientAuthError::AuthClientInitFailed)
.map_err(|error| {
ClientAuthError::AuthClientInitFailed(AuthClientInitFailed {
detail: error.to_string(),
})
})
}
fn network_error(error: &reqwest::Error) -> ClientAuthError {
@@ -0,0 +1,6 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* 认证网络客户端构建失败的原始错误明细。
*/
export type AuthClientInitFailed = { detail: string };
@@ -1,4 +1,5 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { AuthClientInitFailed } from './AuthClientInitFailed';
import type { AuthNetworkFailure } from './AuthNetworkFailure';
import type { AuthResponseInvalid } from './AuthResponseInvalid';
import type { AuthServiceUnavailable } from './AuthServiceUnavailable';
@@ -32,4 +33,4 @@ export type ClientAuthError =
| ({ type: 'authResponseInvalid' } & AuthResponseInvalid)
| ({ type: 'clientSessionPersistFailed' } & ClientSessionPersistFailed)
| ({ type: 'runtimeSessionInstallFailed' } & RuntimeSessionInstallFailed)
| { type: 'authClientInitFailed' };
| ({ type: 'authClientInitFailed' } & AuthClientInitFailed);
@@ -12,6 +12,7 @@ import { describe, expect, it, vi } from 'vitest';
import { loginClientWithPassword } from '../src/services/clientAuth';
import { ClientAuthErrorWrapper } from '../src/services/clientAuthErrorWrapper';
import type { AuthClientInitFailed } from '../src/services/generated/AuthClientInitFailed';
import type { AuthNetworkFailure } from '../src/services/generated/AuthNetworkFailure';
import type { AuthResponseInvalid } from '../src/services/generated/AuthResponseInvalid';
import type { AuthServiceUnavailable } from '../src/services/generated/AuthServiceUnavailable';
@@ -119,7 +120,7 @@ function variantMessage(error: ClientAuthError): string {
case 'runtimeSessionInstallFailed':
return (error as RuntimeSessionInstallFailed).detail;
case 'authClientInitFailed':
return '网络客户端构建失败';
return (error as AuthClientInitFailed).detail;
default: {
expectNever(error);
return '';