feat(URL协议): 增强URL协议注册健壮性

- 新增check_url_protocol_status命令检测注册状态
- 新增unregister_url_protocol命令取消注册
- 改进register_url_protocol命令增加验证逻辑
- 前端增加状态检测、自动重试和操作日志功能
- 更新多语言翻译支持新功能
This commit is contained in:
Yukino_fox
2026-05-04 12:11:18 +08:00
parent 3e85c32c9c
commit a2ec923788
28 changed files with 3246 additions and 429 deletions
+2
View File
@@ -4221,6 +4221,7 @@ dependencies = [
"chrono",
"dirs",
"hex",
"libc",
"once_cell",
"parking_lot",
"rand 0.8.5",
@@ -4242,6 +4243,7 @@ dependencies = [
"tracing-subscriber",
"urlencoding",
"uuid",
"windows-sys 0.59.0",
"winreg 0.52.0",
]
+4
View File
@@ -44,6 +44,10 @@ urlencoding = "2.1"
[target.'cfg(windows)'.dependencies]
winreg = "0.52"
windows-sys = { version = "0.59", features = ["Win32_UI_Shell", "Win32_UI_WindowsAndMessaging"] }
[target.'cfg(unix)'.dependencies]
libc = "0.2"
[profile.release]
panic = "abort"
+683 -22
View File
@@ -13,13 +13,474 @@ pub struct RegisterUrlProtocolResult {
pub registered: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UrlProtocolStatus {
pub registered: bool,
pub protocol: String,
pub platform: String,
pub details: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ElevationStatus {
pub is_elevated: bool,
pub platform: String,
pub can_request_elevation: bool,
}
#[tauri::command]
pub async fn check_url_protocol_status(
_state: tauri::State<'_, Arc<RwLock<AppState>>>,
) -> Result<IpcResponse<UrlProtocolStatus>, String> {
let protocol = "secscore";
eprintln!("[URL Protocol] Checking status for protocol: {}", protocol);
#[cfg(target_os = "windows")]
{
use std::process::Command;
let ps_check = format!(
r#"
$ErrorActionPreference = 'SilentlyContinue'
$key = Get-Item 'HKCU:\Software\Classes\{protocol}'
if (-not $key) {{
Write-Output 'NOT_FOUND'
exit 0
}}
$urlProtocol = Get-ItemProperty -Path 'HKCU:\Software\Classes\{protocol}' -Name 'URL Protocol'
$command = Get-ItemProperty -Path 'HKCU:\Software\Classes\{protocol}\shell\open\command' -Name '(Default)'
if ($urlProtocol -and $command) {{
Write-Output 'REGISTERED'
Write-Output ("CMD:" + $command.'(Default)')
}} else {{
Write-Output 'PARTIAL'
Write-Output ("URL_PROTOCOL:" + [string]($null -ne $urlProtocol))
Write-Output ("COMMAND:" + [string]($null -ne $command))
}}
"#,
protocol = protocol
);
let output = Command::new("powershell")
.args(["-NoProfile", "-NonInteractive", "-Command", &ps_check])
.output();
let (registered, details) = match output {
Ok(o) => {
let stdout = String::from_utf8_lossy(&o.stdout).to_string();
eprintln!("[URL Protocol] Windows PowerShell check output: {}", stdout.trim());
if stdout.contains("REGISTERED") {
(true, "URL protocol fully registered in Windows Registry".to_string())
} else if stdout.contains("PARTIAL") {
(false, format!("Partial registration: {}", stdout.trim()))
} else {
(false, "Registry key HKCU\\Software\\Classes\\secscore not found".to_string())
}
}
Err(e) => {
eprintln!("[URL Protocol] Windows PowerShell check error: {}", e);
(false, format!("Failed to check registry: {}", e))
}
};
eprintln!("[URL Protocol] Windows check result: registered={}, {}", registered, details);
Ok(IpcResponse::success(UrlProtocolStatus {
registered,
protocol: protocol.to_string(),
platform: "windows".to_string(),
details,
}))
}
#[cfg(target_os = "macos")]
{
let mut registered = false;
let mut details = String::new();
let exe_path = std::env::current_exe().unwrap_or_default();
let app_bundle = find_app_bundle(&exe_path);
if app_bundle.is_some() {
let lsregister = "/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister";
let output = std::process::Command::new(lsregister)
.args(["-dump"])
.output();
match output {
Ok(o) => {
let stdout = String::from_utf8_lossy(&o.stdout);
if stdout.contains("secscore") {
registered = true;
details = "secscore:// registered via Launch Services (app bundle)".to_string();
} else {
details = "secscore:// not found in Launch Services dump".to_string();
}
}
Err(e) => {
details = format!("Failed to query Launch Services: {}", e);
}
}
}
if !registered {
let home = dirs::home_dir().unwrap_or_default();
let plist_path = home.join("Library/LaunchAgents/com.secscore.url-handler.plist");
if plist_path.exists() {
registered = true;
details = "URL handler plist found at ~/Library/LaunchAgents".to_string();
} else if details.is_empty() {
details = "No app bundle or plist found for URL protocol".to_string();
}
}
eprintln!("[URL Protocol] macOS check result: registered={}, {}", registered, details);
Ok(IpcResponse::success(UrlProtocolStatus {
registered,
protocol: protocol.to_string(),
platform: "macos".to_string(),
details,
}))
}
#[cfg(target_os = "linux")]
{
let home = dirs::home_dir().unwrap_or_default();
let desktop_path = home.join(".local/share/applications/secscore.desktop");
let mut registered = false;
let mut details = String::new();
if desktop_path.exists() {
match std::fs::read_to_string(&desktop_path) {
Ok(content) => {
if content.contains("x-scheme-handler/secscore") {
let xdg_output = std::process::Command::new("xdg-settings")
.args(["get", "default-url-scheme-handler", "secscore"])
.output();
match xdg_output {
Ok(o) if o.status.success() => {
let handler = String::from_utf8_lossy(&o.stdout).trim().to_string();
if handler == "secscore.desktop" {
registered = true;
details = "secscore.desktop is default URL scheme handler".to_string();
} else {
registered = true;
details = format!(
"Desktop file exists with MimeType, but default handler is: {}",
handler
);
}
}
_ => {
registered = true;
details = "Desktop file exists with MimeType (xdg-settings query failed)".to_string();
}
}
} else {
details = "Desktop file exists but missing x-scheme-handler/secscore MimeType".to_string();
}
}
Err(e) => {
details = format!("Failed to read desktop file: {}", e);
}
}
} else {
details = "secscore.desktop not found in ~/.local/share/applications".to_string();
}
eprintln!("[URL Protocol] Linux check result: registered={}, {}", registered, details);
Ok(IpcResponse::success(UrlProtocolStatus {
registered,
protocol: protocol.to_string(),
platform: "linux".to_string(),
details,
}))
}
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
{
Ok(IpcResponse::success(UrlProtocolStatus {
registered: false,
protocol: "secscore".to_string(),
platform: "unknown".to_string(),
details: "URL protocol check not supported on this platform".to_string(),
}))
}
}
#[tauri::command]
pub async fn unregister_url_protocol(
_state: tauri::State<'_, Arc<RwLock<AppState>>>,
) -> Result<IpcResponse<RegisterUrlProtocolResult>, String> {
let protocol = "secscore";
eprintln!("[URL Protocol] Unregistering protocol: {}", protocol);
#[cfg(target_os = "windows")]
{
use std::process::Command;
let ps_script = format!(
r#"
$ErrorActionPreference = 'SilentlyContinue'
Remove-Item -Path 'HKCU:\Software\Classes\{protocol}' -Recurse -Force
if (-not (Test-Path 'HKCU:\Software\Classes\{protocol}')) {{
Write-Output 'SUCCESS'
}} else {{
Write-Output 'FAILED'
}}
"#,
protocol = protocol
);
let output = Command::new("powershell")
.args(["-NoProfile", "-NonInteractive", "-Command", &ps_script])
.output();
let success = match output {
Ok(o) => {
let stdout = String::from_utf8_lossy(&o.stdout);
let ok = o.status.success() && stdout.contains("SUCCESS");
eprintln!("[URL Protocol] Windows PowerShell unregister result: {}", ok);
ok
}
Err(e) => {
eprintln!("[URL Protocol] Windows PowerShell unregister error: {}", e);
false
}
};
return Ok(IpcResponse::success(RegisterUrlProtocolResult {
registered: Some(!success),
}));
}
#[cfg(target_os = "macos")]
{
let exe_path = std::env::current_exe().unwrap_or_default();
let mut unregistered = true;
if let Some(bundle_path) = find_app_bundle(&exe_path) {
let lsregister = "/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister";
let output = std::process::Command::new(lsregister)
.args(["-u", &bundle_path.to_string_lossy()])
.output();
match output {
Ok(o) => {
eprintln!("[URL Protocol] macOS lsregister -u result: {}", o.status.success());
}
Err(e) => {
eprintln!("[URL Protocol] macOS lsregister -u error: {}", e);
unregistered = false;
}
}
}
let home = dirs::home_dir().unwrap_or_default();
let plist_path = home.join("Library/LaunchAgents/com.secscore.url-handler.plist");
if plist_path.exists() {
match std::fs::remove_file(&plist_path) {
Ok(_) => {
eprintln!("[URL Protocol] macOS plist removed: {:?}", plist_path);
}
Err(e) => {
eprintln!("[URL Protocol] macOS plist remove error: {}", e);
unregistered = false;
}
}
}
return Ok(IpcResponse::success(RegisterUrlProtocolResult {
registered: Some(!unregistered),
}));
}
#[cfg(target_os = "linux")]
{
use std::fs;
let home = dirs::home_dir().unwrap_or_default();
let desktop_path = home.join(".local/share/applications/secscore.desktop");
let mut unregistered = true;
if desktop_path.exists() {
match fs::remove_file(&desktop_path) {
Ok(_) => {
eprintln!("[URL Protocol] Linux desktop file removed: {:?}", desktop_path);
let apps_dir = home.join(".local/share/applications");
let _ = std::process::Command::new("update-desktop-database")
.arg(apps_dir.to_string_lossy().to_string())
.status();
}
Err(e) => {
eprintln!("[URL Protocol] Linux desktop file remove error: {}", e);
unregistered = false;
}
}
}
return Ok(IpcResponse::success(RegisterUrlProtocolResult {
registered: Some(!unregistered),
}));
}
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
{
Ok(IpcResponse::error(
"URL protocol unregistration is not supported on this platform",
))
}
}
#[tauri::command]
pub async fn check_elevation(
_state: tauri::State<'_, Arc<RwLock<AppState>>>,
) -> Result<IpcResponse<ElevationStatus>, String> {
#[cfg(target_os = "windows")]
{
let is_elevated = check_windows_elevation();
return Ok(IpcResponse::success(ElevationStatus {
is_elevated,
platform: "windows".to_string(),
can_request_elevation: true,
}));
}
#[cfg(target_os = "macos")]
{
let is_elevated = unsafe { libc::geteuid() == 0 };
return Ok(IpcResponse::success(ElevationStatus {
is_elevated,
platform: "macos".to_string(),
can_request_elevation: true,
}));
}
#[cfg(target_os = "linux")]
{
let is_elevated = unsafe { libc::geteuid() == 0 };
return Ok(IpcResponse::success(ElevationStatus {
is_elevated,
platform: "linux".to_string(),
can_request_elevation: true,
}));
}
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
{
Ok(IpcResponse::success(ElevationStatus {
is_elevated: false,
platform: "unknown".to_string(),
can_request_elevation: false,
}))
}
}
#[tauri::command]
pub async fn request_elevation(
app: AppHandle,
_state: tauri::State<'_, Arc<RwLock<AppState>>>,
) -> Result<IpcResponse<()>, String> {
#[cfg(target_os = "windows")]
{
let exe_path =
std::env::current_exe().map_err(|e| format!("Failed to get exe path: {}", e))?;
let exe_str = exe_path.to_string_lossy().to_string();
use std::ffi::OsStr;
use std::os::windows::ffi::OsStrExt;
use windows_sys::Win32::UI::Shell::ShellExecuteW;
use windows_sys::Win32::UI::WindowsAndMessaging::SW_SHOW;
let verb: Vec<u16> = OsStr::new("runas\0").encode_wide().collect();
let file: Vec<u16> = OsStr::new(&format!("{}\0", exe_str)).encode_wide().collect();
let params: Vec<u16> = OsStr::new("\0").encode_wide().collect();
let result = unsafe {
ShellExecuteW(
0 as _,
verb.as_ptr(),
file.as_ptr(),
params.as_ptr(),
std::ptr::null(),
SW_SHOW,
)
};
if result as usize > 32 {
app.exit(0);
Ok(IpcResponse::success(()))
} else {
Err(format!(
"Failed to request elevation (ShellExecuteW returned {})",
result as usize
))
}
}
#[cfg(target_os = "macos")]
{
let exe_path =
std::env::current_exe().map_err(|e| format!("Failed to get exe path: {}", e))?;
let exe_str = exe_path.to_string_lossy().to_string();
let script = format!(
"do shell script \"\\\"{}\\\"\" with administrator privileges",
exe_str
);
let status = std::process::Command::new("osascript")
.args(["-e", &script])
.status();
match status {
Ok(s) if s.success() => {
app.exit(0);
Ok(IpcResponse::success(()))
}
Ok(s) => Err(format!("Elevation cancelled or failed (exit code: {:?})", s.code())),
Err(e) => Err(format!("Failed to request elevation: {}", e)),
}
}
#[cfg(target_os = "linux")]
{
let exe_path =
std::env::current_exe().map_err(|e| format!("Failed to get exe path: {}", e))?;
let exe_str = exe_path.to_string_lossy().to_string();
let status = std::process::Command::new("pkexec")
.arg(&exe_str)
.status();
match status {
Ok(s) if s.success() => {
app.exit(0);
Ok(IpcResponse::success(()))
}
Ok(s) => Err(format!("Elevation cancelled or failed (exit code: {:?})", s.code())),
Err(e) => Err(format!("Failed to request elevation: {}", e)),
}
}
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
{
let _ = app;
Err("Elevation is not supported on this platform".to_string())
}
}
#[tauri::command]
pub async fn register_url_protocol(
app: AppHandle,
_state: tauri::State<'_, Arc<RwLock<AppState>>>,
) -> Result<IpcResponse<RegisterUrlProtocolResult>, String> {
#[cfg(target_os = "windows")]
let protocol = "secscore";
eprintln!("[URL Protocol] Starting registration for protocol: {}", protocol);
#[cfg(target_os = "windows")]
{
@@ -27,50 +488,222 @@ pub async fn register_url_protocol(
let exe_path =
std::env::current_exe().map_err(|e| format!("Failed to get executable path: {}", e))?;
let exe_path_str = exe_path.to_string_lossy();
let reg_command = format!(
r#"reg add "HKCU\Software\Classes\{}" /ve /d "URL:SecScore Protocol" /f"#,
protocol
eprintln!("[URL Protocol] Windows: Exe path = {}", exe_path_str);
let ps_script = format!(
r#"
$ErrorActionPreference = 'Stop'
try {{
New-Item -Path 'HKCU:\Software\Classes\{protocol}' -Force | Out-Null
Set-ItemProperty -Path 'HKCU:\Software\Classes\{protocol}' -Name '(Default)' -Value 'URL:SecScore Protocol' -Force
Set-ItemProperty -Path 'HKCU:\Software\Classes\{protocol}' -Name 'URL Protocol' -Value '' -Force
New-Item -Path 'HKCU:\Software\Classes\{protocol}\DefaultIcon' -Force | Out-Null
Set-ItemProperty -Path 'HKCU:\Software\Classes\{protocol}\DefaultIcon' -Name '(Default)' -Value '{exe},1' -Force
New-Item -Path 'HKCU:\Software\Classes\{protocol}\shell' -Force | Out-Null
New-Item -Path 'HKCU:\Software\Classes\{protocol}\shell\open' -Force | Out-Null
New-Item -Path 'HKCU:\Software\Classes\{protocol}\shell\open\command' -Force | Out-Null
Set-ItemProperty -Path 'HKCU:\Software\Classes\{protocol}\shell\open\command' -Name '(Default)' -Value '"{exe}" "%1"' -Force
Write-Output 'SUCCESS'
}} catch {{
Write-Error $_.Exception.Message
exit 1
}}
"#,
protocol = protocol,
exe = exe_path_str
);
let reg_command2 = format!(
r#"reg add "HKCU\Software\Classes\{}\DefaultIcon" /ve /d "{},1" /f"#,
protocol, exe_path_str
);
let output = Command::new("powershell")
.args(["-NoProfile", "-NonInteractive", "-Command", &ps_script])
.output();
let reg_command3 = format!(
r#"reg add "HKCU\Software\Classes\{}\shell\open\command" /ve /d "\"{}\" \"%%1\"" /f"#,
protocol, exe_path_str
);
let mut registered = false;
match output {
Ok(o) => {
let stdout = String::from_utf8_lossy(&o.stdout);
let stderr = String::from_utf8_lossy(&o.stderr);
let success = o.status.success() && stdout.contains("SUCCESS");
eprintln!("[URL Protocol] Windows PowerShell result: success={}, stdout={}, stderr={}", success, stdout.trim(), stderr.trim());
if success {
let query_cmd = format!(
r#"reg query "HKCU\Software\Classes\{}\shell\open\command" /ve"#,
protocol
);
let verify = Command::new("cmd").args(["/C", &query_cmd]).output();
registered = verify.map(|v| v.status.success()).unwrap_or(false);
}
}
Err(e) => {
eprintln!("[URL Protocol] Windows PowerShell error: {}", e);
}
}
let _ = Command::new("cmd").args(["/C", &reg_command]).output();
let _ = Command::new("cmd").args(["/C", &reg_command2]).output();
let _ = Command::new("cmd").args(["/C", &reg_command3]).output();
eprintln!("[URL Protocol] Windows registration verification: {}", registered);
let _ = app;
return Ok(IpcResponse::success(RegisterUrlProtocolResult {
registered: Some(true),
registered: Some(registered),
}));
}
#[cfg(target_os = "macos")]
{
use std::fs;
use std::process::Command;
let exe_path =
std::env::current_exe().map_err(|e| format!("Failed to get executable path: {}", e))?;
let app_bundle = find_app_bundle(&exe_path);
let mut registered = false;
if let Some(bundle_path) = app_bundle {
eprintln!("[URL Protocol] macOS: Found app bundle at {:?}", bundle_path);
let output = Command::new(
"/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister",
)
.args(["-f", &bundle_path.to_string_lossy()])
.output();
match output {
Ok(o) if o.status.success() => {
eprintln!("[URL Protocol] macOS: lsregister -f succeeded");
registered = true;
}
Ok(o) => {
eprintln!("[URL Protocol] macOS: lsregister -f failed (exit: {:?}), trying -R -f", o.status.code());
let _ = Command::new("/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister")
.args(["-R", "-f", &bundle_path.to_string_lossy()])
.output();
registered = true;
}
Err(e) => {
eprintln!("[URL Protocol] macOS: lsregister error: {}", e);
}
}
} else {
eprintln!("[URL Protocol] macOS: No app bundle found, using plist fallback");
let home = dirs::home_dir().unwrap_or_default();
let plist_dir = home.join("Library/LaunchAgents");
let _ = fs::create_dir_all(&plist_dir);
let plist_path = plist_dir.join("com.secscore.url-handler.plist");
let plist_content = format!(
r#"<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.secscore.url-handler</string>
<key>ProgramArguments</key>
<array>
<string>{}</string>
</array>
<key>Sockets</key>
<dict>
<key>secscore</key>
<dict>
<key>SockServiceName</key>
<string>secscore</string>
</dict>
</dict>
</dict>
</plist>"#,
exe_path.to_string_lossy()
);
match fs::write(&plist_path, plist_content) {
Ok(_) => {
eprintln!("[URL Protocol] macOS: Plist written to {:?}", plist_path);
registered = plist_path.exists();
}
Err(e) => {
eprintln!("[URL Protocol] macOS: Plist write error: {}", e);
}
}
}
let _ = app;
return Ok(IpcResponse::success(RegisterUrlProtocolResult {
registered: Some(false),
registered: Some(registered),
}));
}
#[cfg(target_os = "linux")]
{
use std::fs;
let exe_path =
std::env::current_exe().map_err(|e| format!("Failed to get executable path: {}", e))?;
let exe_str = exe_path.to_string_lossy();
let home = dirs::home_dir().unwrap_or_default();
let apps_dir = home.join(".local/share/applications");
let _ = fs::create_dir_all(&apps_dir);
let desktop_path = apps_dir.join("secscore.desktop");
let desktop_content = format!(
r#"[Desktop Entry]
Name=SecScore
Comment=Security Score Application
Exec={} %u
Icon=secscore
Type=Application
Terminal=false
Categories=Utility;
MimeType=x-scheme-handler/secscore;
"#,
exe_str
);
match fs::write(&desktop_path, &desktop_content) {
Ok(_) => {
eprintln!("[URL Protocol] Linux: Desktop file written to {:?}", desktop_path);
}
Err(e) => {
eprintln!("[URL Protocol] Linux: Desktop file write error: {}", e);
let _ = app;
return Ok(IpcResponse::success(RegisterUrlProtocolResult {
registered: Some(false),
}));
}
}
let update_result = std::process::Command::new("update-desktop-database")
.arg(apps_dir.to_string_lossy().to_string())
.status();
eprintln!("[URL Protocol] Linux: update-desktop-database result: {:?}", update_result);
let xdg_set_result = std::process::Command::new("xdg-settings")
.args([
"set",
"default-url-scheme-handler",
"secscore",
"secscore.desktop",
])
.status();
eprintln!("[URL Protocol] Linux: xdg-settings set result: {:?}", xdg_set_result);
let xdg_mime_result = std::process::Command::new("xdg-mime")
.args([
"default",
"secscore.desktop",
"x-scheme-handler/secscore",
])
.status();
eprintln!("[URL Protocol] Linux: xdg-mime default result: {:?}", xdg_mime_result);
let verify_exists = desktop_path.exists();
let verify_content = fs::read_to_string(&desktop_path).unwrap_or_default();
let actually_registered = verify_exists && verify_content.contains("x-scheme-handler/secscore");
eprintln!("[URL Protocol] Linux registration verification: {}", actually_registered);
let _ = app;
return Ok(IpcResponse::success(RegisterUrlProtocolResult {
registered: Some(false),
registered: Some(actually_registered),
}));
}
@@ -99,3 +732,31 @@ pub async fn app_restart(
) -> Result<(), String> {
app.restart();
}
#[cfg(target_os = "windows")]
fn check_windows_elevation() -> bool {
use std::process::Command;
Command::new("net")
.args(["session"])
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.map(|s| s.success())
.unwrap_or(false)
}
#[cfg(target_os = "macos")]
fn find_app_bundle(exe_path: &std::path::Path) -> Option<std::path::PathBuf> {
let mut current = exe_path.parent()?;
loop {
if current.extension().map_or(false, |e| e == "app") {
return Some(current.to_path_buf());
}
if !current.parent().map_or(false, |p| p != current) {
break;
}
current = current.parent()?;
}
None
}
+1 -14
View File
@@ -108,7 +108,6 @@ pub struct SetPasswordsResponse {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OAuthConfig {
pub platform_id: String,
pub platform_secret: String,
pub callback_url: String,
}
@@ -463,7 +462,6 @@ pub async fn oauth_get_authorization_url(
pub async fn oauth_exchange_code(
code: String,
platform_id: String,
platform_secret: String,
callback_url: String,
code_verifier: String,
state: State<'_, Arc<RwLock<AppState>>>,
@@ -473,19 +471,16 @@ pub async fn oauth_exchange_code(
code, platform_id, callback_url
);
// 获取设备 UUID 和 IP 地址
let device_uuid = get_or_create_device_uuid();
let ip_address = get_local_ip().await.unwrap_or_else(|_| "127.0.0.1".to_string());
println!("[OAuth] 设备 UUID: {}, IP: {}", device_uuid, ip_address);
let state_guard = state.read();
let client = &state_guard.http_client;
let payload = serde_json::json!({
"grant_type": "authorization_code",
"code": &code,
"client_id": &platform_id,
"client_secret": &platform_secret,
"redirect_uri": &callback_url,
"code_verifier": &code_verifier,
"device_uuid": &device_uuid,
@@ -537,7 +532,6 @@ pub async fn oauth_revoke_token(
token: String,
token_type_hint: Option<String>,
platform_id: String,
platform_secret: String,
state: State<'_, Arc<RwLock<AppState>>>,
) -> Result<IpcResponse<()>, String> {
let state_guard = state.read();
@@ -546,7 +540,6 @@ pub async fn oauth_revoke_token(
let mut payload = serde_json::json!({
"token": token,
"client_id": platform_id,
"client_secret": platform_secret
});
if let Some(hint) = token_type_hint {
@@ -575,7 +568,6 @@ pub async fn oauth_revoke_token(
pub async fn oauth_introspect_token(
token: String,
platform_id: String,
platform_secret: String,
state: State<'_, Arc<RwLock<AppState>>>,
) -> Result<IpcResponse<OAuthIntrospectResponse>, String> {
let state_guard = state.read();
@@ -586,7 +578,6 @@ pub async fn oauth_introspect_token(
.json(&serde_json::json!({
"token": token,
"client_id": platform_id,
"client_secret": platform_secret
}))
.send()
.await
@@ -667,7 +658,6 @@ pub async fn oauth_get_user_info(
pub async fn oauth_refresh_token(
refresh_token: String,
platform_id: String,
platform_secret: String,
state: State<'_, Arc<RwLock<AppState>>>,
) -> Result<IpcResponse<OAuthTokenResponse>, String> {
let state_guard = state.read();
@@ -679,7 +669,6 @@ pub async fn oauth_refresh_token(
"grant_type": "refresh_token",
"refresh_token": refresh_token,
"client_id": platform_id,
"client_secret": platform_secret
}))
.send()
.await
@@ -901,7 +890,6 @@ pub async fn oauth_clear_login_state() -> Result<IpcResponse<()>, String> {
pub async fn oauth_refresh_access_token(
refresh_token: String,
platform_id: String,
platform_secret: String,
state: State<'_, Arc<RwLock<AppState>>>,
) -> Result<IpcResponse<OAuthTokenResponse>, String> {
println!("[OAuth] 刷新 access_token");
@@ -916,7 +904,6 @@ pub async fn oauth_refresh_access_token(
"grant_type": "refresh_token",
"refresh_token": refresh_token,
"client_id": platform_id,
"client_secret": platform_secret,
"device_uuid": device_uuid,
"ip_address": ip_address,
});
+4
View File
@@ -153,6 +153,10 @@ pub fn run() {
mcp_server_stop,
mcp_server_status,
register_url_protocol,
check_url_protocol_status,
unregister_url_protocol,
check_elevation,
request_elevation,
app_quit,
app_restart,
])