mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-07-22 08:26:44 +08:00
feat: improve bat daemon cli output
This commit is contained in:
@@ -109,13 +109,11 @@ impl OfficialLauncherBootstrapService {
|
||||
/// Fetches latest official PC game config.
|
||||
pub fn fetch_game_config(&self) -> Result<YostarJpLauncherGameConfig, String> {
|
||||
let envelope = self.fetch_launcher_envelope("/api/launcher/game/config")?;
|
||||
let config: YostarJpLauncherGameConfig =
|
||||
serde_json::from_value(envelope.data).map_err(|error| {
|
||||
format!("Failed to parse official launcher game config data: {error}")
|
||||
})?;
|
||||
let config: YostarJpLauncherGameConfig = serde_json::from_value(envelope.data)
|
||||
.map_err(|error| format!("解析官方启动器 game config 数据失败:{error}"))?;
|
||||
|
||||
if config.game_latest_version.is_empty() || config.game_latest_file_path.is_empty() {
|
||||
return Err("Official launcher game config is missing latest version or basis".into());
|
||||
return Err("官方启动器 game config 缺少最新版本或基准路径".into());
|
||||
}
|
||||
|
||||
Ok(config)
|
||||
@@ -125,7 +123,7 @@ impl OfficialLauncherBootstrapService {
|
||||
pub fn fetch_cdn_config(&self) -> Result<YostarJpLauncherCdnConfig, String> {
|
||||
let envelope = self.fetch_launcher_envelope("/api/launcher/advanced/game/download/cdn")?;
|
||||
serde_json::from_value(envelope.data)
|
||||
.map_err(|error| format!("Failed to parse official launcher CDN config data: {error}"))
|
||||
.map_err(|error| format!("解析官方启动器 CDN config 数据失败:{error}"))
|
||||
}
|
||||
|
||||
/// Fetches the official remote manifest URL for a PC client version and
|
||||
@@ -136,7 +134,7 @@ impl OfficialLauncherBootstrapService {
|
||||
file_path: &str,
|
||||
) -> Result<YostarJpLauncherManifestUrl, String> {
|
||||
if version.is_empty() || file_path.is_empty() {
|
||||
return Err("Launcher manifest version and file path must not be empty".into());
|
||||
return Err("启动器 manifest 版本和文件路径不能为空".into());
|
||||
}
|
||||
|
||||
let path = format!(
|
||||
@@ -146,13 +144,11 @@ impl OfficialLauncherBootstrapService {
|
||||
);
|
||||
let envelope = self.fetch_launcher_envelope(&path)?;
|
||||
let manifest_url: YostarJpLauncherManifestUrl = serde_json::from_value(envelope.data)
|
||||
.map_err(|error| {
|
||||
format!("Failed to parse official launcher manifest URL data: {error}")
|
||||
})?;
|
||||
.map_err(|error| format!("解析官方启动器 manifest URL 数据失败:{error}"))?;
|
||||
|
||||
if !is_official_launcher_package_url(&manifest_url.url) {
|
||||
return Err(format!(
|
||||
"Official launcher API returned non-official manifest URL: {}",
|
||||
"官方启动器 API 返回了非官方 manifest URL:{}",
|
||||
manifest_url.url
|
||||
));
|
||||
}
|
||||
@@ -166,9 +162,7 @@ impl OfficialLauncherBootstrapService {
|
||||
manifest_url: &str,
|
||||
) -> Result<YostarJpLauncherRemoteManifest, String> {
|
||||
if !is_official_launcher_package_url(manifest_url) {
|
||||
return Err(format!(
|
||||
"Refusing to fetch non-official launcher manifest URL: {manifest_url}"
|
||||
));
|
||||
return Err(format!("拒绝拉取非官方启动器 manifest URL:{manifest_url}"));
|
||||
}
|
||||
|
||||
let output = self.run_curl_with_retry(
|
||||
@@ -186,16 +180,16 @@ impl OfficialLauncherBootstrapService {
|
||||
|output| {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
format!(
|
||||
"curl failed for launcher manifest {manifest_url}: {}",
|
||||
"curl 拉取启动器 manifest 失败 {manifest_url}:{}",
|
||||
stderr.trim()
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
let manifest: YostarJpLauncherRemoteManifest = serde_json::from_slice(&output.stdout)
|
||||
.map_err(|error| format!("Failed to parse official launcher manifest: {error}"))?;
|
||||
.map_err(|error| format!("解析官方启动器 manifest 失败:{error}"))?;
|
||||
if manifest.files.is_empty() {
|
||||
return Err("Official launcher remote manifest has no files".into());
|
||||
return Err("官方启动器远端 manifest 没有文件条目".into());
|
||||
}
|
||||
|
||||
Ok(manifest)
|
||||
@@ -246,22 +240,22 @@ impl OfficialLauncherBootstrapService {
|
||||
},
|
||||
|output| {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
format!("curl failed for {url}: {}", stderr.trim())
|
||||
format!("curl 拉取失败 {url}:{}", stderr.trim())
|
||||
},
|
||||
)?;
|
||||
|
||||
let envelope: LauncherEnvelope = serde_json::from_slice(&output.stdout)
|
||||
.map_err(|error| format!("Failed to parse official launcher API response: {error}"))?;
|
||||
.map_err(|error| format!("解析官方启动器 API 响应失败:{error}"))?;
|
||||
|
||||
if envelope.code != 200 {
|
||||
return Err(format!(
|
||||
"Official launcher API returned code {}: {}",
|
||||
"官方启动器 API 返回错误码 {}:{}",
|
||||
envelope.code,
|
||||
envelope
|
||||
.message
|
||||
.as_deref()
|
||||
.or(envelope.msg.as_deref())
|
||||
.unwrap_or("<no message>")
|
||||
.unwrap_or("<无消息>")
|
||||
));
|
||||
}
|
||||
|
||||
@@ -280,20 +274,20 @@ impl OfficialLauncherBootstrapService {
|
||||
Ok(output) if output.status.success() => return Ok(output),
|
||||
Ok(output) => {
|
||||
last_error = Some(format!(
|
||||
"attempt {attempt}/{attempts}: {}",
|
||||
"第 {attempt}/{attempts} 次尝试失败:{}",
|
||||
failure_message(&output)
|
||||
));
|
||||
}
|
||||
Err(error) => {
|
||||
last_error = Some(format!(
|
||||
"attempt {attempt}/{attempts}: Failed to launch curl command {}: {error}",
|
||||
"第 {attempt}/{attempts} 次尝试失败:启动 curl 命令失败 {}:{error}",
|
||||
self.curl_command
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(last_error.unwrap_or_else(|| format!("curl command {} did not run", self.curl_command)))
|
||||
Err(last_error.unwrap_or_else(|| format!("curl 命令未执行:{}", self.curl_command)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,7 +309,7 @@ pub struct LauncherEnvelope {
|
||||
/// Builds an official launcher API URL from a path.
|
||||
pub fn launcher_api_url(path: &str) -> Result<String, String> {
|
||||
if !path.starts_with('/') || path.contains("..") || path.contains('\\') {
|
||||
return Err(format!("Invalid official launcher API path: {path}"));
|
||||
return Err(format!("官方启动器 API 路径无效:{path}"));
|
||||
}
|
||||
|
||||
Ok(format!("{YOSTAR_JP_LAUNCHER_API_ROOT}{path}"))
|
||||
@@ -337,7 +331,7 @@ pub fn is_official_launcher_package_url(url: &str) -> bool {
|
||||
/// relative package path.
|
||||
pub fn launcher_package_url(cdn_root: &str, file_path: &str) -> Result<String, String> {
|
||||
if !is_official_launcher_package_url(cdn_root) {
|
||||
return Err(format!("Launcher CDN root is not official: {cdn_root}"));
|
||||
return Err(format!("启动器 CDN 根地址不是官方地址:{cdn_root}"));
|
||||
}
|
||||
|
||||
validate_launcher_package_path(file_path)?;
|
||||
@@ -368,15 +362,15 @@ fn normalize_relative_path(path: &str) -> String {
|
||||
|
||||
fn validate_launcher_package_path(path: &str) -> Result<(), String> {
|
||||
if path.is_empty() || path.starts_with('/') || path.starts_with('\\') {
|
||||
return Err(format!("Invalid launcher package path: {path}"));
|
||||
return Err(format!("启动器包路径无效:{path}"));
|
||||
}
|
||||
|
||||
for segment in path.split('/') {
|
||||
if segment.is_empty() || segment == "." || segment == ".." {
|
||||
return Err(format!("Invalid launcher package path: {path}"));
|
||||
return Err(format!("启动器包路径无效:{path}"));
|
||||
}
|
||||
if segment.contains('\\') || segment.contains('?') || segment.contains('#') {
|
||||
return Err(format!("Invalid launcher package path: {path}"));
|
||||
return Err(format!("启动器包路径无效:{path}"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,14 +388,14 @@ pub fn launcher_authorization_header(
|
||||
timestamp: Option<u64>,
|
||||
) -> Result<String, String> {
|
||||
if launcher_version.is_empty() {
|
||||
return Err("Launcher version must not be empty".into());
|
||||
return Err("启动器版本不能为空".into());
|
||||
}
|
||||
|
||||
let timestamp = match timestamp {
|
||||
Some(timestamp) => timestamp,
|
||||
None => SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map_err(|error| format!("System time is before Unix epoch: {error}"))?
|
||||
.map_err(|error| format!("系统时间早于 Unix epoch:{error}"))?
|
||||
.as_secs(),
|
||||
};
|
||||
|
||||
@@ -411,7 +405,7 @@ pub fn launcher_authorization_header(
|
||||
"version": launcher_version,
|
||||
});
|
||||
let head_json = serde_json::to_string(&head)
|
||||
.map_err(|error| format!("Failed to serialize launcher auth head: {error}"))?;
|
||||
.map_err(|error| format!("序列化启动器认证 head 失败:{error}"))?;
|
||||
let mut hasher = Md5::new();
|
||||
hasher.update(head_json.as_bytes());
|
||||
hasher.update(body.as_bytes());
|
||||
@@ -422,7 +416,7 @@ pub fn launcher_authorization_header(
|
||||
"head": head,
|
||||
"sign": sign,
|
||||
}))
|
||||
.map_err(|error| format!("Failed to serialize launcher auth header: {error}"))
|
||||
.map_err(|error| format!("序列化启动器认证 header 失败:{error}"))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
Reference in New Issue
Block a user