Use API client for notifications/license

This commit is contained in:
Gregory Schier
2025-06-25 08:17:17 -07:00
parent 8817be679b
commit bb0cc16a70
8 changed files with 39 additions and 29 deletions

View File

@@ -12,7 +12,7 @@ pub enum Error {
#[error(transparent)] #[error(transparent)]
SyncError(#[from] yaak_sync::error::Error), SyncError(#[from] yaak_sync::error::Error),
#[error(transparent)] #[error(transparent)]
CryptoError(#[from] yaak_crypto::error::Error), CryptoError(#[from] yaak_crypto::error::Error),
@@ -28,18 +28,21 @@ pub enum Error {
#[error(transparent)] #[error(transparent)]
PluginError(#[from] yaak_plugins::error::Error), PluginError(#[from] yaak_plugins::error::Error),
#[error(transparent)]
CommonError(#[from] yaak_common::error::Error),
#[error("Updater error: {0}")] #[error("Updater error: {0}")]
UpdaterError(#[from] tauri_plugin_updater::Error), UpdaterError(#[from] tauri_plugin_updater::Error),
#[error("JSON error: {0}")] #[error("JSON error: {0}")]
JsonError(#[from] serde_json::error::Error), JsonError(#[from] serde_json::error::Error),
#[error("Tauri error: {0}")] #[error("Tauri error: {0}")]
TauriError(#[from] tauri::Error), TauriError(#[from] tauri::Error),
#[error("Event source error: {0}")] #[error("Event source error: {0}")]
EventSourceError(#[from] eventsource_client::Error), EventSourceError(#[from] eventsource_client::Error),
#[error("I/O error: {0}")] #[error("I/O error: {0}")]
IOError(#[from] io::Error), IOError(#[from] io::Error),

View File

@@ -2,12 +2,12 @@ use std::time::SystemTime;
use crate::error::Result; use crate::error::Result;
use crate::history::get_num_launches; use crate::history::get_num_launches;
use chrono::{DateTime, Duration, Utc}; use chrono::{DateTime, Utc};
use log::debug; use log::debug;
use reqwest::Method; use reqwest::Method;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::Value;
use tauri::{AppHandle, Emitter, Manager, Runtime, WebviewWindow}; use tauri::{AppHandle, Emitter, Manager, Runtime, WebviewWindow};
use yaak_common::api_client::yaak_api_client;
use yaak_common::platform::get_os; use yaak_common::platform::get_os;
use yaak_license::{LicenseCheckStatus, check_license}; use yaak_license::{LicenseCheckStatus, check_license};
use yaak_models::query_manager::QueryManagerExt; use yaak_models::query_manager::QueryManagerExt;
@@ -28,6 +28,7 @@ pub struct YaakNotifier {
#[serde(default, rename_all = "camelCase")] #[serde(default, rename_all = "camelCase")]
pub struct YaakNotification { pub struct YaakNotification {
timestamp: DateTime<Utc>, timestamp: DateTime<Utc>,
timeout: Option<f64>,
id: String, id: String,
message: String, message: String,
action: Option<YaakNotificationAction>, action: Option<YaakNotificationAction>,
@@ -81,8 +82,8 @@ impl YaakNotifier {
let settings = window.db().get_settings(); let settings = window.db().get_settings();
let num_launches = get_num_launches(app_handle).await; let num_launches = get_num_launches(app_handle).await;
let info = app_handle.package_info().clone(); let info = app_handle.package_info().clone();
let req = reqwest::Client::default() let req = yaak_api_client(app_handle)?
.request(Method::GET, "https://notify.yaak.app/notifications") .request(Method::GET, "http://localhost:9444/notifications")
.query(&[ .query(&[
("version", info.version.to_string().as_str()), ("version", info.version.to_string().as_str()),
("launches", num_launches.to_string().as_str()), ("launches", num_launches.to_string().as_str()),
@@ -96,22 +97,9 @@ impl YaakNotifier {
return Ok(()); return Ok(());
} }
let result = resp.json::<Value>().await?; for notification in resp.json::<Vec<YaakNotification>>().await? {
// Support both single and multiple notifications.
// TODO: Remove support for single after April 2025
let notifications = match result {
Value::Array(a) => a
.into_iter()
.map(|a| serde_json::from_value(a).unwrap())
.collect::<Vec<YaakNotification>>(),
a @ _ => vec![serde_json::from_value(a).unwrap()],
};
for notification in notifications {
let age = notification.timestamp.signed_duration_since(Utc::now());
let seen = get_kv(app_handle).await?; let seen = get_kv(app_handle).await?;
if seen.contains(&notification.id) || (age > Duration::days(2)) { if seen.contains(&notification.id) {
debug!("Already seen notification {}", notification.id); debug!("Already seen notification {}", notification.id);
continue; continue;
} }

View File

@@ -1,5 +1,5 @@
use crate::error::Result; use crate::error::Result;
use crate::platform::get_ua_platform; use crate::platform::{get_ua_arch, get_ua_platform};
use reqwest::Client; use reqwest::Client;
use std::time::Duration; use std::time::Duration;
use tauri::http::{HeaderMap, HeaderValue}; use tauri::http::{HeaderMap, HeaderValue};
@@ -8,7 +8,8 @@ use tauri::{AppHandle, Runtime};
pub fn yaak_api_client<R: Runtime>(app_handle: &AppHandle<R>) -> Result<Client> { pub fn yaak_api_client<R: Runtime>(app_handle: &AppHandle<R>) -> Result<Client> {
let platform = get_ua_platform(); let platform = get_ua_platform();
let version = app_handle.package_info().version.clone(); let version = app_handle.package_info().version.clone();
let ua = format!("Yaak/{version} ({platform})"); let arch = get_ua_arch();
let ua = format!("Yaak/{version} ({platform}; {arch})");
let mut default_headers = HeaderMap::new(); let mut default_headers = HeaderMap::new();
default_headers.insert("Accept", HeaderValue::from_str("application/json").unwrap()); default_headers.insert("Accept", HeaderValue::from_str("application/json").unwrap());

View File

@@ -21,3 +21,16 @@ pub fn get_ua_platform() -> &'static str {
"Unknown" "Unknown"
} }
} }
pub fn get_ua_arch() -> &'static str {
if cfg!(target_arch = "x86_64") {
"x86_64"
} else if cfg!(target_arch = "x86") {
"i386"
} else if cfg!(target_arch = "arm") {
"ARM"
} else if cfg!(target_arch = "aarch64") {
"ARM64"
} else {
"Unknown"
}}

View File

@@ -15,6 +15,9 @@ pub enum Error {
#[error(transparent)] #[error(transparent)]
ModelError(#[from] yaak_models::error::Error), ModelError(#[from] yaak_models::error::Error),
#[error(transparent)]
CommonError(#[from] yaak_common::error::Error),
#[error("Internal server error")] #[error("Internal server error")]
ServerError, ServerError,
} }

View File

@@ -7,6 +7,7 @@ use std::ops::Add;
use std::time::Duration; use std::time::Duration;
use tauri::{AppHandle, Emitter, Manager, Runtime, WebviewWindow, is_dev}; use tauri::{AppHandle, Emitter, Manager, Runtime, WebviewWindow, is_dev};
use ts_rs::TS; use ts_rs::TS;
use yaak_common::api_client::yaak_api_client;
use yaak_common::platform::get_os; use yaak_common::platform::get_os;
use yaak_models::query_manager::QueryManagerExt; use yaak_models::query_manager::QueryManagerExt;
use yaak_models::util::UpdateSource; use yaak_models::util::UpdateSource;
@@ -170,7 +171,7 @@ pub async fn check_license<R: Runtime>(window: &WebviewWindow<R>) -> Result<Lice
(true, _) => { (true, _) => {
info!("Checking license activation"); info!("Checking license activation");
// A license has been activated, so let's check the license server // A license has been activated, so let's check the license server
let client = reqwest::Client::new(); let client = yaak_api_client(window.app_handle())?;
let path = format!("/licenses/activations/{activation_id}/check"); let path = format!("/licenses/activations/{activation_id}/check");
let response = client.post(build_url(&path)).json(&payload).send().await?; let response = client.post(build_url(&path)).json(&payload).send().await?;

View File

@@ -10,7 +10,7 @@ export type ToastInstance = {
id: string; id: string;
uniqueKey: string; uniqueKey: string;
message: ReactNode; message: ReactNode;
timeout: 3000 | 5000 | 8000 | null; timeout: 3000 | 5000 | 8000 | (number & {}) | null;
onClose?: ToastProps['onClose']; onClose?: ToastProps['onClose'];
} & Omit<ToastProps, 'onClose' | 'open' | 'children' | 'timeout'>; } & Omit<ToastProps, 'onClose' | 'open' | 'children' | 'timeout'>;

View File

@@ -13,6 +13,7 @@ export function useNotificationToast() {
id: string; id: string;
timestamp: string; timestamp: string;
message: string; message: string;
timeout?: number | null;
action?: null | { action?: null | {
url: string; url: string;
label: string; label: string;
@@ -23,7 +24,7 @@ export function useNotificationToast() {
const actionLabel = payload.action?.label; const actionLabel = payload.action?.label;
showToast({ showToast({
id: payload.id, id: payload.id,
timeout: null, timeout: payload.timeout ?? undefined,
message: payload.message, message: payload.message,
onClose: () => { onClose: () => {
markRead(payload.id) markRead(payload.id)