本机IO失败去掉detail载荷
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m20s
Project CI / Backend tests (pull_request) Failing after 21s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m10s
Project CI / Frontend tests (pull_request) Successful in 2m27s
Project CI / Repository checks (pull_request) Failing after 15s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m50s
Project CI / Native shell tests (pull_request) Successful in 6m25s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m20s
Project CI / Backend tests (pull_request) Failing after 21s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m10s
Project CI / Frontend tests (pull_request) Successful in 2m27s
Project CI / Repository checks (pull_request) Failing after 15s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m50s
Project CI / Native shell tests (pull_request) Successful in 6m25s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
- Rust 错误枚举:clientSessionPersistFailed / runtimeSessionInstallFailed 改无字段变体,删除会带 AppData 路径的 payload 结构体 - auth_session:本机凭据/运行时会话 IO 失败改成写本地日志后返回无字段变体,不再把原始 error 放进载荷 - 前端:登录检查/发码/登录三处 catch 去掉 as 取 detail,改用本操作的固定文案 - ts-rs 重新生成 ClientAuthError 联合,删除两个已不存在的载荷绑定文件 - 补充 auth_error 用例:两个变体序列化为仅含 type 的无字段形状
This commit is contained in:
@@ -7,8 +7,9 @@
|
||||
//! 响应契约的 5 种破损各自只占一个变体:前端 `switch (failure.type)` 选到类别后,再用
|
||||
//! `switch (payload.reason)` 在**类型化**的细分上分流,仍然不碰文案。
|
||||
//!
|
||||
//! Rust **不预拼用户可见文案**:载荷只装原始事实(服务端 400 的原文、HTTP 状态码、本机 IO 的
|
||||
//! `detail`),服务端没给原文就是 `None`;前缀与句式由前端调用方在自己的 catch 分支按当前操作拼接。
|
||||
//! Rust **不预拼用户可见文案**:载荷只装原始事实(服务端 400 的原文、HTTP 状态码),服务端没给原文
|
||||
//! 就是 `None`;前缀与句式由前端调用方在自己的 catch 分支按当前操作拼接。本机 IO 的原始错误只写本地
|
||||
//! 日志,不进载荷(避免把 AppData 路径带进登录页与上报包),对应变体无字段、前端给固定文案。
|
||||
|
||||
use serde::Serialize;
|
||||
use ts_rs::TS;
|
||||
@@ -55,10 +56,10 @@ pub(crate) enum ClientAuthError {
|
||||
UnexpectedRejection(UnexpectedRejection),
|
||||
/// 登录服务响应的契约破损,细分原因见 [`AuthResponseInvalidReason`]。
|
||||
AuthResponseInvalid(AuthResponseInvalid),
|
||||
/// 本机登录凭据文件读写失败。
|
||||
ClientSessionPersistFailed(ClientSessionPersistFailed),
|
||||
/// 本机运行时会话安装 / 清理失败。
|
||||
RuntimeSessionInstallFailed(RuntimeSessionInstallFailed),
|
||||
/// 本机登录凭据文件读写失败;原始错误只写本地日志。
|
||||
ClientSessionPersistFailed,
|
||||
/// 本机运行时会话安装 / 清理失败;原始错误只写本地日志。
|
||||
RuntimeSessionInstallFailed,
|
||||
/// 认证网络客户端构建失败。
|
||||
AuthClientInitFailed,
|
||||
}
|
||||
@@ -186,22 +187,6 @@ pub(crate) struct AuthResponseInvalid {
|
||||
pub(crate) server_message: Option<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 ClientSessionPersistFailed {
|
||||
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 RuntimeSessionInstallFailed {
|
||||
pub(crate) detail: String,
|
||||
}
|
||||
|
||||
impl ClientAuthError {
|
||||
/// 会话路由的 401/403 是「登录态失效」:调用方据此清会话、按未登录处理,
|
||||
/// 既不给用户报错,也不进错误报告池。
|
||||
@@ -277,15 +262,11 @@ mod tests {
|
||||
"authResponseInvalid",
|
||||
),
|
||||
(
|
||||
ClientAuthError::ClientSessionPersistFailed(ClientSessionPersistFailed {
|
||||
detail: "x".to_string(),
|
||||
}),
|
||||
ClientAuthError::ClientSessionPersistFailed,
|
||||
"clientSessionPersistFailed",
|
||||
),
|
||||
(
|
||||
ClientAuthError::RuntimeSessionInstallFailed(RuntimeSessionInstallFailed {
|
||||
detail: "x".to_string(),
|
||||
}),
|
||||
ClientAuthError::RuntimeSessionInstallFailed,
|
||||
"runtimeSessionInstallFailed",
|
||||
),
|
||||
(
|
||||
@@ -304,9 +285,20 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn unit_variants_serialize_without_payload_fields() {
|
||||
let value =
|
||||
serde_json::to_value(ClientAuthError::LoginCodeMissing).expect("serialize auth error");
|
||||
assert_eq!(value, serde_json::json!({ "type": "loginCodeMissing" }));
|
||||
for (error, expected_type) in [
|
||||
(ClientAuthError::LoginCodeMissing, "loginCodeMissing"),
|
||||
(
|
||||
ClientAuthError::ClientSessionPersistFailed,
|
||||
"clientSessionPersistFailed",
|
||||
),
|
||||
(
|
||||
ClientAuthError::RuntimeSessionInstallFailed,
|
||||
"runtimeSessionInstallFailed",
|
||||
),
|
||||
] {
|
||||
let value = serde_json::to_value(error).expect("serialize auth error");
|
||||
assert_eq!(value, serde_json::json!({ "type": expected_type }));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -354,14 +346,6 @@ mod tests {
|
||||
.expect("serialize auth error");
|
||||
assert_eq!(invalid["reason"], "serverRejected");
|
||||
assert_eq!(invalid["serverMessage"], "登录服务请求失败");
|
||||
|
||||
let persist = serde_json::to_value(ClientAuthError::ClientSessionPersistFailed(
|
||||
ClientSessionPersistFailed {
|
||||
detail: "磁盘只读".to_string(),
|
||||
},
|
||||
))
|
||||
.expect("serialize auth error");
|
||||
assert_eq!(persist["detail"], "磁盘只读");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
|
||||
use crate::auth_error::{
|
||||
AuthNetworkFailure, AuthNetworkReason, AuthResponseInvalid, AuthResponseInvalidReason,
|
||||
AuthServiceUnavailable, ClientAuthError, ClientSessionPersistFailed, PasswordLoginRejected,
|
||||
PhoneCodeLoginRejected, RuntimeSessionInstallFailed, SendCodeRejected, ServerAddressReason,
|
||||
ServerAddressRejected, UnexpectedRejection,
|
||||
AuthServiceUnavailable, ClientAuthError, PasswordLoginRejected, PhoneCodeLoginRejected,
|
||||
SendCodeRejected, ServerAddressReason, ServerAddressRejected, UnexpectedRejection,
|
||||
};
|
||||
use crate::http_client::agc_main_site_client_builder;
|
||||
use crate::platform_session::{current_platform_session, PlatformSessionSnapshot};
|
||||
@@ -222,24 +221,21 @@ fn session_file_path(app: &tauri::AppHandle) -> Result<PathBuf, ClientAuthError>
|
||||
.app_data_dir()
|
||||
.map(|root| root.join(SESSION_FILE_NAME))
|
||||
.map_err(|error| {
|
||||
ClientAuthError::ClientSessionPersistFailed(ClientSessionPersistFailed {
|
||||
detail: format!("无法读取 AGC 应用数据目录:{error}"),
|
||||
})
|
||||
app_log!("无法读取 AGC 应用数据目录:{error}");
|
||||
ClientAuthError::ClientSessionPersistFailed
|
||||
})
|
||||
}
|
||||
|
||||
/// 凭据文件相关的字符串错误统一收口成凭据落盘失败变体。
|
||||
fn session_persist_error(message: impl Into<String>) -> ClientAuthError {
|
||||
ClientAuthError::ClientSessionPersistFailed(ClientSessionPersistFailed {
|
||||
detail: message.into(),
|
||||
})
|
||||
app_log!("{}", message.into());
|
||||
ClientAuthError::ClientSessionPersistFailed
|
||||
}
|
||||
|
||||
/// 本机运行时安装 / 清理失败的字符串错误统一收口。
|
||||
fn runtime_session_error(message: impl Into<String>) -> ClientAuthError {
|
||||
ClientAuthError::RuntimeSessionInstallFailed(RuntimeSessionInstallFailed {
|
||||
detail: message.into(),
|
||||
})
|
||||
app_log!("{}", message.into());
|
||||
ClientAuthError::RuntimeSessionInstallFailed
|
||||
}
|
||||
|
||||
/// 服务地址校验失败统一收口成带类型化原因的变体。
|
||||
|
||||
@@ -38,10 +38,8 @@ import {
|
||||
import type { AuthNetworkFailure } from '../services/generated/AuthNetworkFailure';
|
||||
import type { AuthResponseInvalid } from '../services/generated/AuthResponseInvalid';
|
||||
import type { AuthServiceUnavailable } from '../services/generated/AuthServiceUnavailable';
|
||||
import type { ClientSessionPersistFailed } from '../services/generated/ClientSessionPersistFailed';
|
||||
import type { PasswordLoginRejected } from '../services/generated/PasswordLoginRejected';
|
||||
import type { PhoneCodeLoginRejected } from '../services/generated/PhoneCodeLoginRejected';
|
||||
import type { RuntimeSessionInstallFailed } from '../services/generated/RuntimeSessionInstallFailed';
|
||||
import type { SendCodeRejected } from '../services/generated/SendCodeRejected';
|
||||
import type { ServerAddressRejected } from '../services/generated/ServerAddressRejected';
|
||||
import type { UnexpectedRejection } from '../services/generated/UnexpectedRejection';
|
||||
@@ -438,15 +436,13 @@ export function AuthenticatedClient({
|
||||
throw error;
|
||||
}
|
||||
case 'clientSessionPersistFailed': {
|
||||
const payload = failure as ClientSessionPersistFailed;
|
||||
const text = `本机登录凭据读写失败:${payload.detail}`;
|
||||
const text = '本机登录凭据读写失败';
|
||||
setAuthCheckError(text);
|
||||
setLoginStatus(text);
|
||||
throw error;
|
||||
}
|
||||
case 'runtimeSessionInstallFailed': {
|
||||
const payload = failure as RuntimeSessionInstallFailed;
|
||||
const text = `本机运行时会话安装失败:${payload.detail}`;
|
||||
const text = '本机运行时会话安装失败';
|
||||
setAuthCheckError(text);
|
||||
setLoginStatus(text);
|
||||
throw error;
|
||||
@@ -708,17 +704,11 @@ export function AuthenticatedClient({
|
||||
throw error;
|
||||
}
|
||||
case 'clientSessionPersistFailed': {
|
||||
const payload = failure as ClientSessionPersistFailed;
|
||||
setLoginStatus(
|
||||
`发送验证码失败:本机登录凭据读写失败:${payload.detail}`,
|
||||
);
|
||||
setLoginStatus('发送验证码失败:本机登录凭据读写失败');
|
||||
throw error;
|
||||
}
|
||||
case 'runtimeSessionInstallFailed': {
|
||||
const payload = failure as RuntimeSessionInstallFailed;
|
||||
setLoginStatus(
|
||||
`发送验证码失败:本机运行时会话安装失败:${payload.detail}`,
|
||||
);
|
||||
setLoginStatus('发送验证码失败:本机运行时会话安装失败');
|
||||
throw error;
|
||||
}
|
||||
case 'authClientInitFailed': {
|
||||
@@ -945,13 +935,11 @@ export function AuthenticatedClient({
|
||||
throw error;
|
||||
}
|
||||
case 'clientSessionPersistFailed': {
|
||||
const payload = failure as ClientSessionPersistFailed;
|
||||
setLoginStatus(`登录失败:本机登录凭据读写失败:${payload.detail}`);
|
||||
setLoginStatus('登录失败:本机登录凭据读写失败');
|
||||
throw error;
|
||||
}
|
||||
case 'runtimeSessionInstallFailed': {
|
||||
const payload = failure as RuntimeSessionInstallFailed;
|
||||
setLoginStatus(`登录失败:本机运行时会话安装失败:${payload.detail}`);
|
||||
setLoginStatus('登录失败:本机运行时会话安装失败');
|
||||
throw error;
|
||||
}
|
||||
case 'authClientInitFailed': {
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
import type { AuthNetworkFailure } from './AuthNetworkFailure';
|
||||
import type { AuthResponseInvalid } from './AuthResponseInvalid';
|
||||
import type { AuthServiceUnavailable } from './AuthServiceUnavailable';
|
||||
import type { ClientSessionPersistFailed } from './ClientSessionPersistFailed';
|
||||
import type { PasswordLoginRejected } from './PasswordLoginRejected';
|
||||
import type { PhoneCodeLoginRejected } from './PhoneCodeLoginRejected';
|
||||
import type { RuntimeSessionInstallFailed } from './RuntimeSessionInstallFailed';
|
||||
import type { SendCodeRejected } from './SendCodeRejected';
|
||||
import type { ServerAddressRejected } from './ServerAddressRejected';
|
||||
import type { UnexpectedRejection } from './UnexpectedRejection';
|
||||
@@ -30,6 +28,6 @@ export type ClientAuthError =
|
||||
| ({ type: 'authServiceUnavailable' } & AuthServiceUnavailable)
|
||||
| ({ type: 'unexpectedRejection' } & UnexpectedRejection)
|
||||
| ({ type: 'authResponseInvalid' } & AuthResponseInvalid)
|
||||
| ({ type: 'clientSessionPersistFailed' } & ClientSessionPersistFailed)
|
||||
| ({ type: 'runtimeSessionInstallFailed' } & RuntimeSessionInstallFailed)
|
||||
| { type: 'clientSessionPersistFailed' }
|
||||
| { type: 'runtimeSessionInstallFailed' }
|
||||
| { type: 'authClientInitFailed' };
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* 本机凭据文件读写的原始错误明细。
|
||||
*/
|
||||
export type ClientSessionPersistFailed = { detail: string };
|
||||
@@ -1,6 +0,0 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* 本机运行时会话安装 / 清理的原始错误明细。
|
||||
*/
|
||||
export type RuntimeSessionInstallFailed = { detail: string };
|
||||
@@ -16,10 +16,8 @@ import type { AuthNetworkFailure } from '../src/services/generated/AuthNetworkFa
|
||||
import type { AuthResponseInvalid } from '../src/services/generated/AuthResponseInvalid';
|
||||
import type { AuthServiceUnavailable } from '../src/services/generated/AuthServiceUnavailable';
|
||||
import type { ClientAuthError } from '../src/services/generated/ClientAuthError';
|
||||
import type { ClientSessionPersistFailed } from '../src/services/generated/ClientSessionPersistFailed';
|
||||
import type { PasswordLoginRejected } from '../src/services/generated/PasswordLoginRejected';
|
||||
import type { PhoneCodeLoginRejected } from '../src/services/generated/PhoneCodeLoginRejected';
|
||||
import type { RuntimeSessionInstallFailed } from '../src/services/generated/RuntimeSessionInstallFailed';
|
||||
import type { SendCodeRejected } from '../src/services/generated/SendCodeRejected';
|
||||
import type { ServerAddressRejected } from '../src/services/generated/ServerAddressRejected';
|
||||
import type { UnexpectedRejection } from '../src/services/generated/UnexpectedRejection';
|
||||
@@ -115,9 +113,9 @@ function variantMessage(error: ClientAuthError): string {
|
||||
}
|
||||
}
|
||||
case 'clientSessionPersistFailed':
|
||||
return (error as ClientSessionPersistFailed).detail;
|
||||
return '本机登录凭据读写失败';
|
||||
case 'runtimeSessionInstallFailed':
|
||||
return (error as RuntimeSessionInstallFailed).detail;
|
||||
return '本机运行时会话安装失败';
|
||||
case 'authClientInitFailed':
|
||||
return '网络客户端构建失败';
|
||||
default: {
|
||||
|
||||
Reference in New Issue
Block a user