Extract yaak-api crate with sysproxy for OS-level proxy detection

- New crates/yaak-api crate with no Tauri dependency
- Uses sysproxy to auto-detect OS proxy settings (macOS, Windows, Linux)
- Moved yaak_api_client from yaak-tauri-utils, now takes version string
- Removed api_client and error modules from yaak-tauri-utils
- Updated all callers in license, plugins, notifications, and URI scheme
This commit is contained in:
Gregory Schier
2026-02-12 08:20:08 -08:00
parent 83f6f9786b
commit 38153118bc
17 changed files with 360 additions and 200 deletions
+1
View File
@@ -57,6 +57,7 @@ url = "2"
tokio-util = { version = "0.7", features = ["codec"] }
ts-rs = { workspace = true }
uuid = "1.12.1"
yaak-api = { workspace = true }
yaak-common = { workspace = true }
yaak-tauri-utils = { workspace = true }
yaak-core = { workspace = true }
+1 -1
View File
@@ -36,7 +36,7 @@ pub enum Error {
PluginError(#[from] yaak_plugins::error::Error),
#[error(transparent)]
TauriUtilsError(#[from] yaak_tauri_utils::error::Error),
ApiError(#[from] yaak_api::Error),
#[error(transparent)]
ClipboardError(#[from] tauri_plugin_clipboard_manager::Error),
+3 -2
View File
@@ -10,7 +10,7 @@ use tauri::{AppHandle, Emitter, Manager, Runtime, WebviewWindow};
use ts_rs::TS;
use yaak_common::platform::get_os_str;
use yaak_models::util::UpdateSource;
use yaak_tauri_utils::api_client::yaak_api_client;
use yaak_api::yaak_api_client;
// Check for updates every hour
const MAX_UPDATE_CHECK_SECONDS: u64 = 60 * 60;
@@ -101,7 +101,8 @@ impl YaakNotifier {
let license_check = "disabled".to_string();
let launch_info = get_or_upsert_launch_info(app_handle);
let req = yaak_api_client(app_handle)?
let version = app_handle.package_info().version.to_string();
let req = yaak_api_client(&version)?
.request(Method::GET, "https://notify.yaak.app/notifications")
.query(&[
("version", &launch_info.current_version),
+11 -6
View File
@@ -31,7 +31,7 @@ use yaak_plugins::events::{Color, Icon, PluginContext, ShowToastRequest};
use yaak_plugins::install::{delete_and_uninstall, download_and_install};
use yaak_plugins::manager::PluginManager;
use yaak_plugins::plugin_meta::get_plugin_meta;
use yaak_tauri_utils::api_client::yaak_api_client;
use yaak_api::yaak_api_client;
static EXITING: AtomicBool = AtomicBool::new(false);
@@ -72,7 +72,8 @@ impl PluginUpdater {
info!("Checking for plugin updates");
let http_client = yaak_api_client(window.app_handle())?;
let version = window.app_handle().package_info().version.to_string();
let http_client = yaak_api_client(&version)?;
let plugins = window.app_handle().db().list_plugins()?;
let updates = check_plugin_updates(&http_client, plugins.clone()).await?;
@@ -136,7 +137,8 @@ pub async fn cmd_plugins_search<R: Runtime>(
app_handle: AppHandle<R>,
query: &str,
) -> Result<PluginSearchResponse> {
let http_client = yaak_api_client(&app_handle)?;
let version = app_handle.package_info().version.to_string();
let http_client = yaak_api_client(&version)?;
Ok(search_plugins(&http_client, query).await?)
}
@@ -147,7 +149,8 @@ pub async fn cmd_plugins_install<R: Runtime>(
version: Option<String>,
) -> Result<()> {
let plugin_manager = Arc::new((*window.state::<PluginManager>()).clone());
let http_client = yaak_api_client(window.app_handle())?;
let app_version = window.app_handle().package_info().version.to_string();
let http_client = yaak_api_client(&app_version)?;
let query_manager = window.state::<yaak_models::query_manager::QueryManager>();
let plugin_context = window.plugin_context();
download_and_install(
@@ -177,7 +180,8 @@ pub async fn cmd_plugins_uninstall<R: Runtime>(
pub async fn cmd_plugins_updates<R: Runtime>(
app_handle: AppHandle<R>,
) -> Result<PluginUpdatesResponse> {
let http_client = yaak_api_client(&app_handle)?;
let version = app_handle.package_info().version.to_string();
let http_client = yaak_api_client(&version)?;
let plugins = app_handle.db().list_plugins()?;
Ok(check_plugin_updates(&http_client, plugins).await?)
}
@@ -186,7 +190,8 @@ pub async fn cmd_plugins_updates<R: Runtime>(
pub async fn cmd_plugins_update_all<R: Runtime>(
window: WebviewWindow<R>,
) -> Result<Vec<PluginNameVersion>> {
let http_client = yaak_api_client(window.app_handle())?;
let version = window.app_handle().package_info().version.to_string();
let http_client = yaak_api_client(&version)?;
let plugins = window.db().list_plugins()?;
// Get list of available updates (already filtered to only registry plugins)
+5 -3
View File
@@ -12,7 +12,7 @@ use yaak_models::util::generate_id;
use yaak_plugins::events::{Color, ShowToastRequest};
use yaak_plugins::install::download_and_install;
use yaak_plugins::manager::PluginManager;
use yaak_tauri_utils::api_client::yaak_api_client;
use yaak_api::yaak_api_client;
pub(crate) async fn handle_deep_link<R: Runtime>(
app_handle: &AppHandle<R>,
@@ -46,7 +46,8 @@ pub(crate) async fn handle_deep_link<R: Runtime>(
let plugin_manager = Arc::new((*window.state::<PluginManager>()).clone());
let query_manager = app_handle.db_manager();
let http_client = yaak_api_client(app_handle)?;
let version = app_handle.package_info().version.to_string();
let http_client = yaak_api_client(&version)?;
let plugin_context = window.plugin_context();
let pv = download_and_install(
plugin_manager,
@@ -86,7 +87,8 @@ pub(crate) async fn handle_deep_link<R: Runtime>(
return Ok(());
}
let resp = yaak_api_client(app_handle)?.get(file_url).send().await?;
let version = app_handle.package_info().version.to_string();
let resp = yaak_api_client(&version)?.get(file_url).send().await?;
let json = resp.bytes().await?;
let p = app_handle
.path()
+1 -1
View File
@@ -16,7 +16,7 @@ thiserror = { workspace = true }
ts-rs = { workspace = true }
yaak-common = { workspace = true }
yaak-models = { workspace = true }
yaak-tauri-utils = { workspace = true }
yaak-api = { workspace = true }
[build-dependencies]
tauri-plugin = { workspace = true, features = ["build"] }
+1 -1
View File
@@ -16,7 +16,7 @@ pub enum Error {
ModelError(#[from] yaak_models::error::Error),
#[error(transparent)]
TauriUtilsError(#[from] yaak_tauri_utils::error::Error),
ApiError(#[from] yaak_api::Error),
#[error("Internal server error")]
ServerError,
+7 -4
View File
@@ -11,7 +11,7 @@ use yaak_common::platform::get_os_str;
use yaak_models::db_context::DbContext;
use yaak_models::query_manager::QueryManager;
use yaak_models::util::UpdateSource;
use yaak_tauri_utils::api_client::yaak_api_client;
use yaak_api::yaak_api_client;
/// Extension trait for accessing the QueryManager from Tauri Manager types.
/// This is needed temporarily until all crates are refactored to not use Tauri.
@@ -118,7 +118,8 @@ pub async fn activate_license<R: Runtime>(
license_key: &str,
) -> Result<()> {
info!("Activating license {}", license_key);
let client = yaak_api_client(window.app_handle())?;
let version = window.app_handle().package_info().version.to_string();
let client = yaak_api_client(&version)?;
let payload = ActivateLicenseRequestPayload {
license_key: license_key.to_string(),
app_platform: get_os_str().to_string(),
@@ -155,7 +156,8 @@ pub async fn deactivate_license<R: Runtime>(window: &WebviewWindow<R>) -> Result
let app_handle = window.app_handle();
let activation_id = get_activation_id(app_handle).await;
let client = yaak_api_client(window.app_handle())?;
let version = window.app_handle().package_info().version.to_string();
let client = yaak_api_client(&version)?;
let path = format!("/licenses/activations/{}/deactivate", activation_id);
let payload = DeactivateLicenseRequestPayload {
app_platform: get_os_str().to_string(),
@@ -204,7 +206,8 @@ pub async fn check_license<R: Runtime>(window: &WebviewWindow<R>) -> Result<Lice
(true, _) => {
info!("Checking license activation");
// A license has been activated, so let's check the license server
let client = yaak_api_client(window.app_handle())?;
let version = window.app_handle().package_info().version.to_string();
let client = yaak_api_client(&version)?;
let path = format!("/licenses/activations/{activation_id}/check-v2");
let response = client.post(build_url(&path)).json(&payload).send().await?;
-6
View File
@@ -6,10 +6,4 @@ publish = false
[dependencies]
tauri = { workspace = true }
reqwest = { workspace = true, features = ["gzip"] }
thiserror = { workspace = true }
serde = { workspace = true, features = ["derive"] }
regex = "1.11.0"
yaak-common = { workspace = true }
yaak-models = { workspace = true }
log = { workspace = true }
@@ -1,73 +0,0 @@
use crate::error::Result;
use log::{debug, warn};
use reqwest::{Client, Proxy};
use std::time::Duration;
use tauri::http::{HeaderMap, HeaderValue};
use tauri::{AppHandle, Manager, Runtime};
use yaak_common::platform::{get_ua_arch, get_ua_platform};
use yaak_models::models::{ProxySetting, ProxySettingAuth};
use yaak_models::query_manager::QueryManager;
pub fn yaak_api_client<R: Runtime>(app_handle: &AppHandle<R>) -> Result<Client> {
let platform = get_ua_platform();
let version = app_handle.package_info().version.clone();
let arch = get_ua_arch();
let ua = format!("Yaak/{version} ({platform}; {arch})");
let mut default_headers = HeaderMap::new();
default_headers.insert("Accept", HeaderValue::from_str("application/json").unwrap());
let mut builder = reqwest::ClientBuilder::new()
.timeout(Duration::from_secs(20))
.default_headers(default_headers)
.gzip(true)
.user_agent(ua);
// Apply proxy settings from global app settings
let qm = app_handle.state::<QueryManager>();
let db = qm.inner().connect();
let proxy = db.get_settings().proxy;
match &proxy {
None => { /* System default */ }
Some(ProxySetting::Disabled) => {
builder = builder.no_proxy();
}
Some(ProxySetting::Enabled { http, https, auth, bypass, disabled }) => {
if !disabled {
if !http.is_empty() {
match Proxy::http(http) {
Ok(mut p) => {
if let Some(ProxySettingAuth { user, password }) = auth {
debug!("Using http proxy auth");
p = p.basic_auth(user.as_str(), password.as_str());
}
p = p.no_proxy(reqwest::NoProxy::from_string(bypass));
builder = builder.proxy(p);
}
Err(e) => {
warn!("Failed to apply http proxy: {e:?}");
}
}
}
if !https.is_empty() {
match Proxy::https(https) {
Ok(mut p) => {
if let Some(ProxySettingAuth { user, password }) = auth {
debug!("Using https proxy auth");
p = p.basic_auth(user.as_str(), password.as_str());
}
p = p.no_proxy(reqwest::NoProxy::from_string(bypass));
builder = builder.proxy(p);
}
Err(e) => {
warn!("Failed to apply https proxy: {e:?}");
}
}
}
}
}
}
let client = builder.build()?;
Ok(client)
}
@@ -1,19 +0,0 @@
use serde::{Serialize, Serializer};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
ReqwestError(#[from] reqwest::Error),
}
impl Serialize for Error {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.to_string().as_ref())
}
}
pub type Result<T> = std::result::Result<T, Error>;
-2
View File
@@ -1,3 +1 @@
pub mod api_client;
pub mod error;
pub mod window;