This commit is contained in:
NanGua-QWQ
2026-03-30 22:01:51 +08:00
25 changed files with 6044 additions and 198 deletions
+98
View File
@@ -606,6 +606,26 @@ version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "const-random"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
dependencies = [
"const-random-macro",
]
[[package]]
name = "const-random-macro"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
dependencies = [
"getrandom 0.2.17",
"once_cell",
"tiny-keccak",
]
[[package]]
name = "convert_case"
version = "0.4.0"
@@ -728,6 +748,12 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crunchy"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
[[package]]
name = "crypto-common"
version = "0.1.7"
@@ -967,6 +993,15 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "dlv-list"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f"
dependencies = [
"const-random",
]
[[package]]
name = "dom_query"
version = "0.27.0"
@@ -2818,6 +2853,16 @@ dependencies = [
"num-traits",
]
[[package]]
name = "ordered-multimap"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79"
dependencies = [
"dlv-list",
"hashbrown 0.14.5",
]
[[package]]
name = "os_pipe"
version = "1.2.3"
@@ -3850,6 +3895,16 @@ dependencies = [
"zeroize",
]
[[package]]
name = "rust-ini"
version = "0.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7"
dependencies = [
"cfg-if",
"ordered-multimap",
]
[[package]]
name = "rust_decimal"
version = "1.40.0"
@@ -4178,12 +4233,14 @@ dependencies = [
"sqlx",
"tauri",
"tauri-build",
"tauri-plugin-deep-link",
"tauri-plugin-shell",
"tokio",
"tokio-util",
"tracing",
"tracing-appender",
"tracing-subscriber",
"urlencoding",
"uuid",
]
@@ -5215,6 +5272,27 @@ dependencies = [
"walkdir",
]
[[package]]
name = "tauri-plugin-deep-link"
version = "2.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94deb2e2e4641514ac496db2cddcfc850d6fc9d51ea17b82292a0490bd20ba5b"
dependencies = [
"dunce",
"plist",
"rust-ini",
"serde",
"serde_json",
"tauri",
"tauri-plugin",
"tauri-utils",
"thiserror 2.0.18",
"tracing",
"url",
"windows-registry",
"windows-result 0.3.4",
]
[[package]]
name = "tauri-plugin-shell"
version = "2.3.5"
@@ -5450,6 +5528,15 @@ dependencies = [
"time-core",
]
[[package]]
name = "tiny-keccak"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
dependencies = [
"crunchy",
]
[[package]]
name = "tinystr"
version = "0.8.2"
@@ -6440,6 +6527,17 @@ dependencies = [
"windows-link 0.1.3",
]
[[package]]
name = "windows-registry"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e"
dependencies = [
"windows-link 0.1.3",
"windows-result 0.3.4",
"windows-strings 0.4.2",
]
[[package]]
name = "windows-result"
version = "0.3.4"
+2
View File
@@ -16,6 +16,7 @@ tauri-build = { version = "2", features = [] }
[dependencies]
tauri = { version = "2", features = ["macos-private-api", "tray-icon", "image-png", "protocol-asset"] }
tauri-plugin-shell = "2"
tauri-plugin-deep-link = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["full"] }
@@ -39,6 +40,7 @@ once_cell = "1"
parking_lot = { version = "0.12", features = ["send_guard"] }
dirs = "6.0.0"
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
urlencoding = "2.1"
[profile.release]
panic = "abort"
Binary file not shown.
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
{"default":{"identifier":"default","description":"Default capabilities for SecScore application","local":true,"windows":["main"],"permissions":["core:default","core:window:default","core:window:allow-show","core:window:allow-hide","core:window:allow-close","core:window:allow-minimize","core:window:allow-maximize","core:window:allow-unmaximize","core:window:allow-start-dragging","core:webview:default","core:app:default","core:tray:default","core:tray:allow-new","core:tray:allow-set-icon","core:tray:allow-set-menu","core:menu:default","shell:allow-open"]}}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+137
View File
@@ -30,6 +30,30 @@ pub struct SetPasswordsResponse {
pub recovery_string: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OAuthConfig {
pub platform_id: String,
pub platform_secret: String,
pub callback_url: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OAuthTokenResponse {
pub access_token: String,
pub refresh_token: String,
pub token_type: String,
pub expires_in: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OAuthUserInfo {
pub user_id: String,
pub email: String,
pub name: String,
pub github_username: Option<String>,
pub permission: u32,
}
fn get_iv_hex() -> String {
SecurityService::generate_iv_hex()
}
@@ -271,3 +295,116 @@ pub async fn auth_clear_all(
Err(e) => Ok(IpcResponse::error(&e)),
}
}
#[tauri::command]
pub async fn oauth_get_authorization_url(
platform_id: String,
callback_url: String,
) -> Result<IpcResponse<String>, String> {
let url = format!(
"https://sectl.top/oauth/authorize?client_id={}&redirect_uri={}&response_type=code",
platform_id,
urlencoding::encode(&callback_url)
);
Ok(IpcResponse::success(url))
}
#[tauri::command]
pub async fn oauth_exchange_code(
code: String,
platform_id: String,
platform_secret: String,
callback_url: String,
) -> Result<IpcResponse<OAuthTokenResponse>, String> {
let client = reqwest::Client::new();
let response = client
.post("https://sectl.top/api/oauth/token")
.json(&serde_json::json!({
"grant_type": "authorization_code",
"code": code,
"client_id": platform_id,
"client_secret": platform_secret,
"redirect_uri": callback_url
}))
.send()
.await
.map_err(|e| format!("Request failed: {}", e))?;
if !response.status().is_success() {
let error_text = response
.text()
.await
.unwrap_or_else(|_| "Unknown error".to_string());
return Ok(IpcResponse::error(&error_text));
}
let token_response: OAuthTokenResponse = response
.json()
.await
.map_err(|e| format!("Failed to parse response: {}", e))?;
Ok(IpcResponse::success(token_response))
}
#[tauri::command]
pub async fn oauth_get_user_info(
access_token: String,
) -> Result<IpcResponse<OAuthUserInfo>, String> {
let client = reqwest::Client::new();
let response = client
.get("https://sectl.top/api/oauth/userinfo")
.header("Authorization", format!("Bearer {}", access_token))
.send()
.await
.map_err(|e| format!("Request failed: {}", e))?;
if !response.status().is_success() {
let error_text = response
.text()
.await
.unwrap_or_else(|_| "Unknown error".to_string());
return Ok(IpcResponse::error(&error_text));
}
let user_info: OAuthUserInfo = response
.json()
.await
.map_err(|e| format!("Failed to parse response: {}", e))?;
Ok(IpcResponse::success(user_info))
}
#[tauri::command]
pub async fn oauth_refresh_token(
refresh_token: String,
platform_id: String,
platform_secret: String,
) -> Result<IpcResponse<OAuthTokenResponse>, String> {
let client = reqwest::Client::new();
let response = client
.post("https://sectl.top/api/oauth/token")
.json(&serde_json::json!({
"grant_type": "refresh_token",
"refresh_token": refresh_token,
"client_id": platform_id,
"client_secret": platform_secret
}))
.send()
.await
.map_err(|e| format!("Request failed: {}", e))?;
if !response.status().is_success() {
let error_text = response
.text()
.await
.unwrap_or_else(|_| "Unknown error".to_string());
return Ok(IpcResponse::error(&error_text));
}
let token_response: OAuthTokenResponse = response
.json()
.await
.map_err(|e| format!("Failed to parse response: {}", e))?;
Ok(IpcResponse::success(token_response))
}
+26
View File
@@ -12,6 +12,7 @@ use crate::services::settings::{SettingsKey, SettingsValue};
use crate::{commands::*, state::AppState};
use parking_lot::RwLock;
use std::sync::Arc;
use tauri::Emitter;
#[cfg(desktop)]
use tauri::{
image::Image,
@@ -26,6 +27,7 @@ use tokio::time::{timeout, Duration};
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_deep_link::init())
.setup(|app| {
let state = AppState::new(app.handle().clone());
app.manage(Arc::new(RwLock::new(state)));
@@ -73,6 +75,10 @@ pub fn run() {
auth_generate_recovery,
auth_reset_by_recovery,
auth_clear_all,
oauth_get_authorization_url,
oauth_exchange_code,
oauth_get_user_info,
oauth_refresh_token,
theme_list,
theme_current,
theme_set,
@@ -136,6 +142,26 @@ pub fn setup_app(app: &mut App) -> Result<(), Box<dyn std::error::Error>> {
setup_window_events(app)?;
setup_deep_link(app)?;
Ok(())
}
fn setup_deep_link(app: &mut App) -> Result<(), Box<dyn std::error::Error>> {
let handle = app.handle().clone();
#[cfg(desktop)]
{
use tauri_plugin_deep_link::DeepLinkExt;
app.deep_link().on_open_url(move |event| {
let url = event.urls().first().map(|u| u.to_string()).unwrap_or_default();
if !url.is_empty() {
let _ = handle.emit("deep-link://new-url", url);
}
});
}
Ok(())
}
+8
View File
@@ -58,6 +58,14 @@
"plugins": {
"shell": {
"open": true
},
"deepLink": {
"domains": [
{
"host": "oauth",
"schemes": ["secscore"]
}
]
}
}
}