Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ A Tauri 2 desktop tool for one-click switching between multiple ZCode accounts,
- **Claim promotions**: one-click claim for eligible promotions; "Auto claim" toggle (off by default) checks and claims periodically — manual actions take priority
- **Encrypted import / export**: `.zsb` bundle, PBKDF2(100k) + AES-256-GCM password encryption
- **Bilingual UI (中文 / English)**: one-click switch in Settings — main window, tray, error messages and CLI output all covered; first run follows your OS language
- **In-app updates**: the main window notices a new version; download and install from Settings
- **Tray / autostart / CLI automation**

## Security Design
Expand All @@ -24,6 +25,7 @@ A Tauri 2 desktop tool for one-click switching between multiple ZCode accounts,
- **Path traversal protection**: account id whitelist (`[A-Za-z0-9-]`); delete/read cannot escape the account store directory
- **Encrypted export**: PBKDF2-HMAC-SHA256 (100k iterations) + AES-256-GCM, random salt/nonce; wrong password simply fails, no plaintext traces
- **Credentials decrypted locally only**: decryption is used solely to display username/email; export files are password-encrypted
- **Update verification**: in-app updates download only from this repo's GitHub Releases and check the published SHA-256 before installing or opening the package

## CLI

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Tauri 2 桌面工具:在多个 ZCode 账号之间一键切换,自动显示
- **活动领取**:可领套餐一键领取;「自动领取」开关(默认关)定时自动检测并领取,手动操作优先
- **加密导入导出**:`.zsb` 捆绑包,PBKDF2(100k) + AES-256-GCM 口令加密
- **中英双语**:设置里一键切换 中文 / English,主窗、托盘、错误提示、CLI 输出全覆盖;首次运行按系统语言自动选择
- **应用内更新**:发现新版本时主窗口会提示,也可在设置里下载并安装
- **托盘 / 开机自启 / CLI 自动化**

## 安全设计
Expand All @@ -24,6 +25,7 @@ Tauri 2 桌面工具:在多个 ZCode 账号之间一键切换,自动显示
- **路径穿越防护**:账号 id 白名单(`[A-Za-z0-9-]`),删除/读取均不可逃出账号库目录
- **加密导出**:PBKDF2-HMAC-SHA256(100k 迭代)+ AES-256-GCM,随机 salt/nonce;错密码即失败,无明文痕迹
- **凭据只在本地解密**:凭据解密仅用于显示用户名/邮箱;导出文件凭口令加密
- **更新包校验**:应用内更新只下载本仓库 GitHub Releases 的安装包,核对随发布附带的 SHA-256 后才安装或打开

## CLI

Expand Down
30 changes: 30 additions & 0 deletions src-tauri/src/i18n.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,21 @@ const ZH: &[(&str, &str)] = &[
("cli.read_fail", "读取失败:{e}"),
("cli.json_fail", "JSON 解析失败:{e}"),
("err.lang.unknown", "未知语言:{lang}(支持 zh / en)"),
("err.update.rate", "GitHub 接口繁忙,请稍后再试"),
("err.update.http", "请求失败(HTTP {code})"),
("err.update.network", "网络请求失败:{e}"),
("err.update.parse", "无法解析发布信息"),
("err.update.bad_version", "无法识别当前版本号"),
("err.update.none", "没有可安装的更新"),
("err.update.no_asset", "没有适用于当前系统的安装包"),
("err.update.checksum_missing", "发布包缺少 SHA-256 校验和,已取消安装"),
("err.update.checksum_mismatch", "安装包校验失败,已取消安装"),
("err.update.too_big", "安装包过大,已取消下载"),
("err.update.download", "下载失败:{e}"),
("err.update.apply", "无法启动安装:{e}"),
("err.update.path", "安装路径异常,已取消安装"),
("err.update.busy", "正在安装更新"),
("err.update.write", "无法写入更新文件:{e}"),
];

const EN: &[(&str, &str)] = &[
Expand Down Expand Up @@ -371,6 +386,21 @@ const EN: &[(&str, &str)] = &[
("cli.read_fail", "Read failed: {e}"),
("cli.json_fail", "JSON parse failed: {e}"),
("err.lang.unknown", "Unknown language: {lang} (supported: zh / en)"),
("err.update.rate", "GitHub is rate-limiting requests — try again shortly"),
("err.update.http", "Request failed (HTTP {code})"),
("err.update.network", "Network request failed: {e}"),
("err.update.parse", "Could not parse the release feed"),
("err.update.bad_version", "The current version number is not recognized"),
("err.update.none", "No update to install"),
("err.update.no_asset", "This release has no package for the current system"),
("err.update.checksum_missing", "The release has no SHA-256 checksum — installation cancelled"),
("err.update.checksum_mismatch", "The package failed its checksum — installation cancelled"),
("err.update.too_big", "The package is too large — download cancelled"),
("err.update.download", "Download failed: {e}"),
("err.update.apply", "Could not start the installer: {e}"),
("err.update.path", "The install path is not usable — installation cancelled"),
("err.update.busy", "An update is already being installed"),
("err.update.write", "Could not write the update file: {e}"),
];

fn table_for(lang: Lang) -> &'static [(&'static str, &'static str)] {
Expand Down
23 changes: 23 additions & 0 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod oauth;
mod probe;
mod quota;
mod store;
mod updater;
mod zcrypto;

use serde_json::{json, Value};
Expand Down Expand Up @@ -1123,6 +1124,26 @@ async fn open_external(url: String) -> Result<(), String> {
store::open_url(&url)
}

#[tauri::command]
async fn check_app_update(app: AppHandle) -> Result<updater::UpdateCheck, String> {
let current = app.package_info().version.to_string();
let exe = std::env::current_exe().ok();
match tauri::async_runtime::spawn_blocking(move || updater::check(&current, exe.as_deref())).await {
Ok(r) => r,
Err(e) => Err(i18n::trf("err.update.apply", &[("e", &e.to_string())])),
}
}

#[tauri::command]
async fn install_app_update(app: AppHandle) -> Result<updater::UpdateApply, String> {
let current = app.package_info().version.to_string();
let exe = std::env::current_exe().map_err(|e| i18n::trf("err.update.apply", &[("e", &e.to_string())]))?;
match tauri::async_runtime::spawn_blocking(move || updater::install(app, &current, &exe)).await {
Ok(r) => r,
Err(e) => Err(i18n::trf("err.update.apply", &[("e", &e.to_string())])),
}
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
Expand Down Expand Up @@ -1168,6 +1189,8 @@ pub fn run() {
set_zcode_path,
launch_zcode,
open_external,
check_app_update,
install_app_update,
open_settings,
reveal_main,
])
Expand Down
Loading