| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- // Prevents additional console window on Windows in release, DO NOT REMOVE!!
- #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
- mod commands;
- mod steam_auth;
- mod browser_automation;
- mod api_client;
- mod config;
- mod key_storage;
- mod machine;
- mod storage;
- mod updater;
- mod auto_updater;
- mod error;
- mod security;
- mod heartbeat;
- mod app_logger;
- use commands::*;
- use browser_automation::*;
- use api_client::{ApiConfig as BackendApiConfig, initialize_api_client};
- use config::*;
- use tauri::Manager;
- // 在最开头初始化日志系统(任何 panic 都要捕获)
- fn init_logging() {
- // 初始化应用日志系统(每日一个日志文件,写在 exe 同目录下)
- if let Err(e) = app_logger::init_app_logger() {
- eprintln!("Failed to init app logger: {}", e);
- }
- // 设置 panic hook + Windows 顶层异常处理器,确保程序崩溃时把堆栈信息写入日志文件
- app_logger::setup_all_crash_handlers();
- }
- fn main() {
- // 最优先初始化日志(panic hook 必须在任何可能 panic 之前设置)
- init_logging();
- app_logger::info("应用主程序开始启动...");
- // --- 单实例检测:防止同一台电脑启动多个窗口 ---
- #[cfg(target_os = "windows")]
- {
- use std::ffi::OsStr;
- use std::os::windows::ffi::OsStrExt;
- use std::ptr;
- const MUTEX_NAME: &str = "SteamSH_SingleInstance_Mutex_v1";
- let mutex_name_wide: Vec<u16> = OsStr::new(MUTEX_NAME)
- .encode_wide()
- .chain(std::iter::once(0))
- .collect();
- unsafe {
- let handle = winapi::um::synchapi::CreateMutexW(
- ptr::null_mut(),
- 0, // bInitialOwner = false,新实例不会获得锁的拥有权
- mutex_name_wide.as_ptr(),
- );
- if !handle.is_null() {
- // 检查是否已有互斥量(即已有实例运行)
- let already_exists = winapi::um::errhandlingapi::GetLastError()
- == winapi::shared::winerror::ERROR_ALREADY_EXISTS;
- if already_exists {
- // 已有实例运行,尝试激活它的主窗口,然后退出当前进程
- app_logger::warn("检测到已有实例正在运行,激活已有窗口并退出当前进程");
- // 尝试通过 FindWindow 找到并激活已有窗口
- let class_name: Vec<u16> = OsStr::new("SteamSH")
- .encode_wide()
- .chain(std::iter::once(0))
- .collect();
- let title: Vec<u16> = OsStr::new("steam扫货(磨损版)")
- .encode_wide()
- .chain(std::iter::once(0))
- .collect();
- // 先尝试通过窗口类名找
- let hwnd = winapi::um::winuser::FindWindowW(class_name.as_ptr(), ptr::null());
- let hwnd = if hwnd.is_null() {
- // 再尝试通过窗口标题找
- winapi::um::winuser::FindWindowW(ptr::null(), title.as_ptr())
- } else {
- hwnd
- };
- if !hwnd.is_null() {
- // 激活窗口并置于前台
- winapi::um::winuser::SetForegroundWindow(hwnd);
- winapi::um::winuser::ShowWindow(hwnd, winapi::um::winuser::SW_RESTORE);
- winapi::um::winuser::BringWindowToTop(hwnd);
- }
- // 关闭 mutex 句柄并退出
- winapi::um::handleapi::CloseHandle(handle);
- std::process::exit(0);
- }
- // 记录 mutex 句柄,在程序结束时关闭(通过 drop 或 at_exit)
- // 为确保程序结束时释放 mutex,这里用 Box::leak 存起来,
- // 让它在进程结束时由 OS 自动释放(进程退出时内核自动关闭所有句柄)
- Box::leak(Box::new(handle));
- }
- }
- }
- // --- 单实例检测结束 ---
- // 阶段二:安全检查
- if let Err(e) = security::perform_all_security_checks() {
- app_logger::error(&format!("安全检查失败: {}", e));
- // 在生产环境中,安全检查失败应该显示错误信息后退出
- if !cfg!(debug_assertions) {
- // 显示友好的错误对话框
- #[cfg(target_os = "windows")]
- {
- use std::ffi::OsStr;
- use std::os::windows::ffi::OsStrExt;
- let msg: Vec<u16> = OsStr::new(&format!("安全检查失败:\n{}\n\n请检查系统环境后重试。\n\n详细信息请查看日志文件。", e))
- .encode_wide()
- .chain(Some(0))
- .collect();
- let title: Vec<u16> = OsStr::new("SteamSH 启动失败")
- .encode_wide()
- .chain(Some(0))
- .collect();
- unsafe {
- extern "system" {
- fn MessageBoxW(hwnd: *mut std::ffi::c_void, text: *const u16, caption: *const u16, utype: u32) -> i32;
- }
- const MB_ICONERROR: u32 = 0x10;
- const MB_OK: u32 = 0x0;
- const MB_TOPMOST: u32 = 0x40000;
- MessageBoxW(std::ptr::null_mut(), msg.as_ptr(), title.as_ptr(), MB_ICONERROR | MB_OK | MB_TOPMOST);
- }
- }
- std::process::exit(1);
- }
- }
- app_logger::info("安全检查通过");
- // 初始化存储系统
- if let Err(e) = storage::init_storage() {
- app_logger::error(&format!("Failed to initialize storage: {}", e));
- // 存储初始化失败不应该阻止程序启动
- }
- // 初始化应用配置和API客户端
- let app_config = AppConfig::load().unwrap_or_else(|e| {
- app_logger::error(&format!("Failed to load config: {}, using defaults", e));
- AppConfig::default()
- });
- // 初始化API客户端
- let api_config = BackendApiConfig {
- base_url: app_config.api.base_url.clone(),
- timeout: app_config.api.timeout,
- software_code: app_config.api.software_code.clone(),
- };
- if let Err(e) = initialize_api_client(api_config) {
- app_logger::error(&format!("Failed to initialize API client: {}", e));
- // API客户端初始化失败也不阻止程序启动,用户可以在运行时重新配置
- }
- tauri::Builder::default()
- .invoke_handler(tauri::generate_handler![
- // Steam本地功能
- generate_steam_code,
- fetch_confirmations,
- accept_confirmation,
- deny_confirmation,
- save_steam_account,
- load_steam_accounts,
- launch_browser_session,
- execute_automated_shopping,
- // project-local steam-msg commands
- save_steam_guard_file,
- save_steam_account_to_project,
- list_project_steam_accounts,
- delete_project_steam_account,
- update_account_is_us_account,
- export_mafile_to_path,
- get_steam_wallet_balance,
- read_proxy_config,
- write_proxy_config,
- test_proxy_configuration,
- invalidate_proxy,
- get_proxy_pool_status,
- refill_proxy_pool,
- verify_steam_login,
- // 后端API调用
- initialize_backend_api,
- user_register,
- user_login,
- user_unbind,
- user_recharge,
- user_heartbeat,
- secure_heartbeat,
- user_logout,
- activate_card,
- get_steam_account_limit,
- get_steam_accounts,
- create_steam_account,
- bind_steam_account,
- update_steam_account,
- delete_steam_account,
- get_announcements,
- health_check,
- get_public_key,
- get_client_configs,
- // 配置管理
- get_app_config,
- update_api_url,
- get_api_base_url,
- // 机器码和存储
- get_machine_code,
- store_user_info,
- get_stored_user_info,
- get_current_username,
- get_user_profile,
- get_system_username,
- clear_stored_user_info,
- minimize_window,
- close_window,
- stop_all_processes,
- rsa_encrypt_aes_key,
- secure_get_announcements,
- secure_user_login,
- secure_user_register,
- forgot_password_request,
- secure_user_unbind,
- secure_user_change_password,
- secure_user_recharge,
- secure_client_version_check,
- store_rsa_public_key,
- has_stored_rsa_key,
- clear_stored_rsa_key,
- rsa_encrypt_aes_key_by_stored_key,
- get_rsa_key_version,
- // 安全通信模块命令
- initialize_security_client,
- check_security_initialized,
- get_security_status,
- refresh_security_keys,
- reset_security_client,
- sync_hmac_key,
- secure_buff_filter_options,
- secure_buff_goods_attr_list,
- secure_buff_selection_config,
- secure_buff_selection_start,
- secure_buff_selection_log,
- secure_buff_selection_result,
- secure_buff_selection_item_delete,
- secure_buff_selection_stop,
- batch_import_buff_selection_items,
- secure_buff_sweep_bound_steam_usernames,
- secure_buff_sweep_start,
- spawn_buff_sweep_local_runner,
- secure_buff_sweep_stop,
- secure_buff_sweep_log,
- secure_buff_sweep_result,
- secure_buff_sweep_append_success_item,
- // 本地扫货成功数据存储
- read_local_sweep_logs,
- read_sweep_worker_log,
- read_sweep_worker_backup_log,
- get_sweep_worker_log_files,
- read_local_sweep_success_items,
- read_steam_account_sweep_items,
- add_local_sweep_success_item,
- add_demo_sweep_success_item,
- secure_keys_hmac,
- // 密钥管理命令
- get_key_manager_status,
- get_hmac_key_status,
- // 心跳管理命令
- get_heartbeat_state_command,
- stop_heartbeat_command,
- get_heartbeat_config_command,
- read_text_file,
- // 更新相关
- updater::check_for_updates,
- updater::manual_update_check,
- updater::start_manual_update,
- // 自动更新相关(自定义实现)
- auto_updater::get_current_exe_path,
- auto_updater::get_exe_directory,
- auto_updater::download_update,
- auto_updater::verify_exe,
- auto_updater::execute_update_and_restart,
- auto_updater::check_pending_update,
- auto_updater::cleanup_update_files,
- // 日志相关
- get_app_log_path
- ])
- .setup(|app| {
- heartbeat::set_app_handle(app.handle().clone());
- // 开发者工具在发布版本中禁用
- // 如需调试,可通过环境变量 TAURI_DEVTOOLS=1 在 release 中启用
- Ok(())
- })
- .run(tauri::generate_context!())
- .unwrap_or_else(|e| {
- let err_msg = format!("Tauri application error: {}", e);
- app_logger::error(&err_msg);
- // 显示友好的错误对话框
- #[cfg(target_os = "windows")]
- {
- use std::ffi::OsStr;
- use std::os::windows::ffi::OsStrExt;
- let msg: Vec<u16> = OsStr::new(&format!("应用启动失败:\n{}\n\n详细信息请查看日志文件。", e))
- .encode_wide()
- .chain(Some(0))
- .collect();
- let title: Vec<u16> = OsStr::new("SteamSH 启动失败")
- .encode_wide()
- .chain(Some(0))
- .collect();
- unsafe {
- extern "system" {
- fn MessageBoxW(hwnd: *mut std::ffi::c_void, text: *const u16, caption: *const u16, utype: u32) -> i32;
- }
- const MB_ICONERROR: u32 = 0x10;
- const MB_OK: u32 = 0x0;
- const MB_TOPMOST: u32 = 0x40000;
- MessageBoxW(std::ptr::null_mut(), msg.as_ptr(), title.as_ptr(), MB_ICONERROR | MB_OK | MB_TOPMOST);
- }
- }
- std::process::exit(1);
- });
- }
|