diff --git a/src-tauri/src/cmd.rs b/src-tauri/src/cmd.rs index 5c2cb38..e976d40 100644 --- a/src-tauri/src/cmd.rs +++ b/src-tauri/src/cmd.rs @@ -1,7 +1,10 @@ use crate::{ + config::{read_profiles, save_profiles, ProfileItem}, events::{emit::ClashInfoPayload, state::ClashInfoState}, - utils::{clash, import}, + utils::{app_home_dir, clash, fetch::fetch_profile}, }; +use std::fs::File; +use std::io::Write; use tauri::{api::process::kill_children, AppHandle, State}; #[tauri::command] @@ -14,14 +17,6 @@ pub fn restart_sidebar(app_handle: AppHandle, clash_info: State<'_, ClashInfoSta } } -#[tauri::command] -pub async fn import_profile(url: String) -> Result { - match import::import_profile(&url).await { - Ok(_) => Ok(String::from("success")), - Err(_) => Err(String::from("error")), - } -} - #[tauri::command] pub fn get_clash_info(clash_info: State<'_, ClashInfoState>) -> Option { match clash_info.0.lock() { @@ -29,3 +24,44 @@ pub fn get_clash_info(clash_info: State<'_, ClashInfoState>) -> Option None, } } + +/// Import the Profile from url and +/// save to the `profiles.yaml` file +#[tauri::command] +pub async fn import_profile(url: String) -> Result { + let result = match fetch_profile(&url).await { + Some(r) => r, + None => { + log::error!("failed to fetch profile from `{}`", url); + return Err(format!("failed")); + } + }; + + let path = app_home_dir().join("profiles").join(&result.file); + File::create(path) + .unwrap() + .write(result.data.as_bytes()) + .unwrap(); + + // update profiles.yaml + let mut profiles = read_profiles(); + let mut items = match profiles.items { + Some(p) => p, + None => vec![], + }; + + let profile = ProfileItem { + name: Some(result.name), + file: Some(result.file), + mode: Some(format!("rule")), + url: Some(url), + selected: Some(vec![]), // Todo: parse the selected list + extra: Some(result.extra), + }; + + items.push(profile); + profiles.items = Some(items); + save_profiles(&profiles); + + Ok(format!("success")) +} diff --git a/src-tauri/src/config/profiles.rs b/src-tauri/src/config/profiles.rs index 7a13f5a..65df3d1 100644 --- a/src-tauri/src/config/profiles.rs +++ b/src-tauri/src/config/profiles.rs @@ -39,3 +39,12 @@ pub struct ProfileExtra { pub total: u64, pub expire: u64, } + +#[derive(Default, Debug, Clone, Deserialize, Serialize)] +/// the result from url +pub struct ProfileResponse { + pub name: String, + pub file: String, + pub data: String, + pub extra: ProfileExtra, +} diff --git a/src-tauri/src/utils/import.rs b/src-tauri/src/utils/fetch.rs similarity index 50% rename from src-tauri/src/utils/import.rs rename to src-tauri/src/utils/fetch.rs index 19311f5..fc58e23 100644 --- a/src-tauri/src/utils/import.rs +++ b/src-tauri/src/utils/fetch.rs @@ -1,9 +1,4 @@ -extern crate reqwest; - -use crate::config::{read_profiles, save_profiles, ProfileExtra, ProfileItem}; -use crate::utils::app_home_dir; -use std::fs::File; -use std::io::Write; +use crate::config::{ProfileExtra, ProfileResponse}; use std::time::{SystemTime, UNIX_EPOCH}; /// parse the string @@ -21,12 +16,13 @@ fn parse_string<'a>(target: &'a str, key: &'a str) -> Option<&'a str> { } } -/// Todo: log -/// Import the Profile from url -/// save to the `verge.yaml` file -pub async fn import_profile(profile_url: &str) -> Result<(), reqwest::Error> { - let resp = reqwest::get(profile_url).await?; - let header = resp.headers().clone(); +/// fetch and parse the profile +pub async fn fetch_profile(url: &str) -> Option { + let resp = match reqwest::get(url).await { + Ok(res) => res, + Err(_) => return None, + }; + let header = resp.headers(); // parse the Subscription Userinfo let extra = { @@ -56,60 +52,34 @@ pub async fn import_profile(profile_url: &str) -> Result<(), reqwest::Error> { } }; - // parse the file name - let file_name = { - let file_name = header.get("Content-Disposition").unwrap().to_str().unwrap(); - let file_name = parse_string(file_name, "filename="); + // parse the `name` and `file` + let (name, file) = { + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs(); + let file = format!("{}.yaml", now); + let name = header.get("Content-Disposition").unwrap().to_str().unwrap(); + let name = parse_string(name, "filename="); - match file_name { - Some(f) => f.to_string(), - None => { - let cur_time = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs(); - format!("{}.yaml", cur_time) - } + match name { + Some(f) => (f.to_string(), file), + None => (file.clone(), file), } }; - // save file - let file_data = resp.text_with_charset("utf-8").await?; - let file_path = app_home_dir().join("profiles").join(&file_name); - File::create(file_path) - .unwrap() - .write(file_data.as_bytes()) - .unwrap(); - - // update profiles.yaml - let mut profiles = read_profiles(); - let mut items = match profiles.items { - Some(p) => p, - None => vec![], + // get the data + let data = match resp.text_with_charset("utf-8").await { + Ok(d) => d, + Err(_) => return None, }; - let profile = ProfileItem { - name: Some(file_name.clone()), - file: Some(file_name.clone()), - mode: Some(String::from("rule")), - url: Some(String::from(profile_url)), - selected: Some(vec![]), - extra: Some(extra), - }; - - let target_index = items - .iter() - .position(|x| x.name.is_some() && x.name.as_ref().unwrap().as_str() == file_name.as_str()); - - match target_index { - Some(idx) => items[idx] = profile, - None => items.push(profile), - }; - - profiles.items = Some(items); - save_profiles(&profiles); - - Ok(()) + Some(ProfileResponse { + file, + name, + data, + extra, + }) } #[test] diff --git a/src-tauri/src/utils/mod.rs b/src-tauri/src/utils/mod.rs index 064253f..047d7c9 100644 --- a/src-tauri/src/utils/mod.rs +++ b/src-tauri/src/utils/mod.rs @@ -2,6 +2,6 @@ mod dirs; pub use self::dirs::*; pub mod clash; -pub mod import; +pub mod fetch; pub mod init; pub mod sysopt;