mirror of
https://github.com/hoppscotch/hoppscotch
synced 2024-11-21 14:38:47 +00:00
fix(agent): propagate default values to persistent store (#4511)
This commit is contained in:
parent
6a9d056dee
commit
ecf5d9f6d2
@ -1,20 +1,33 @@
|
||||
{ pkgs, lib, config, inputs, ... }:
|
||||
|
||||
{
|
||||
# https://devenv.sh/packages/
|
||||
packages = with pkgs; [
|
||||
git
|
||||
openssl
|
||||
postgresql_16
|
||||
jq
|
||||
xxd
|
||||
# BE and Tauri stuff
|
||||
let
|
||||
rosettaPkgs =
|
||||
if pkgs.stdenv.isDarwin && pkgs.stdenv.isAarch64
|
||||
then pkgs.pkgsx86_64Darwin
|
||||
else pkgs;
|
||||
|
||||
darwinPackages = with pkgs; [
|
||||
darwin.apple_sdk.frameworks.Security
|
||||
darwin.apple_sdk.frameworks.CoreServices
|
||||
darwin.apple_sdk.frameworks.CoreFoundation
|
||||
darwin.apple_sdk.frameworks.Foundation
|
||||
darwin.apple_sdk.frameworks.AppKit
|
||||
darwin.apple_sdk.frameworks.WebKit
|
||||
];
|
||||
|
||||
linuxPackages = with pkgs; [
|
||||
libsoup_3
|
||||
webkitgtk_4_1
|
||||
librsvg
|
||||
libappindicator
|
||||
libayatana-appindicator
|
||||
libappindicator-gtk3
|
||||
];
|
||||
|
||||
in {
|
||||
# https://devenv.sh/packages/
|
||||
packages = with pkgs; [
|
||||
git
|
||||
postgresql_16
|
||||
# FE and Node stuff
|
||||
nodejs_22
|
||||
nodePackages_latest.typescript-language-server
|
||||
@ -23,15 +36,16 @@
|
||||
prisma-engines
|
||||
# Cargo
|
||||
cargo-edit
|
||||
];
|
||||
] ++ lib.optionals pkgs.stdenv.isDarwin darwinPackages
|
||||
++ lib.optionals pkgs.stdenv.isLinux linuxPackages;
|
||||
|
||||
# https://devenv.sh/basics/
|
||||
#
|
||||
# NOTE: Setting these `PRISMA_*` environment variable fixes
|
||||
# Error: Failed to fetch sha256 checksum at https://binaries.prisma.sh/all_commits/<hash>/linux-nixos/libquery_engine.so.node.gz.sha256 - 404 Not Found
|
||||
# See: https://github.com/prisma/prisma/discussions/3120
|
||||
env = {
|
||||
APP_GREET = "Hoppscotch";
|
||||
} // lib.optionalAttrs pkgs.stdenv.isLinux {
|
||||
# NOTE: Setting these `PRISMA_*` environment variable fixes
|
||||
# Error: Failed to fetch sha256 checksum at https://binaries.prisma.sh/all_commits/<hash>/linux-nixos/libquery_engine.so.node.gz.sha256 - 404 Not Found
|
||||
# See: https://github.com/prisma/prisma/discussions/3120
|
||||
PRISMA_QUERY_ENGINE_LIBRARY = "${pkgs.prisma-engines}/lib/libquery_engine.node";
|
||||
PRISMA_QUERY_ENGINE_BINARY = "${pkgs.prisma-engines}/bin/query-engine";
|
||||
PRISMA_SCHEMA_ENGINE_BINARY = "${pkgs.prisma-engines}/bin/schema-engine";
|
||||
@ -39,15 +53,25 @@
|
||||
LD_LIBRARY_PATH = lib.makeLibraryPath [
|
||||
pkgs.libappindicator
|
||||
pkgs.libayatana-appindicator
|
||||
pkgs.libappindicator-gtk3
|
||||
];
|
||||
} // lib.optionalAttrs pkgs.stdenv.isDarwin {
|
||||
# Place to put macOS-specific environment variables
|
||||
};
|
||||
|
||||
# https://devenv.sh/scripts/
|
||||
scripts.hello.exec = "echo hello from $APP_GREET";
|
||||
scripts = {
|
||||
hello.exec = "echo hello from $APP_GREET";
|
||||
e.exec = "emacs";
|
||||
};
|
||||
|
||||
enterShell = ''
|
||||
git --version
|
||||
${lib.optionalString pkgs.stdenv.isDarwin ''
|
||||
# Place to put macOS-specific shell initialization
|
||||
''}
|
||||
${lib.optionalString pkgs.stdenv.isLinux ''
|
||||
# Place to put Linux-specific shell initialization
|
||||
''}
|
||||
'';
|
||||
|
||||
# https://devenv.sh/tests/
|
||||
@ -59,33 +83,29 @@
|
||||
dotenv.enable = true;
|
||||
|
||||
# https://devenv.sh/languages/
|
||||
languages.javascript = {
|
||||
enable = true;
|
||||
pnpm = {
|
||||
languages = {
|
||||
typescript.enable = true;
|
||||
javascript = {
|
||||
enable = true;
|
||||
pnpm.enable = true;
|
||||
npm.enable = true;
|
||||
};
|
||||
npm = {
|
||||
rust = {
|
||||
enable = true;
|
||||
channel = "nightly";
|
||||
components = [
|
||||
"rustc"
|
||||
"cargo"
|
||||
"clippy"
|
||||
"rustfmt"
|
||||
"rust-analyzer"
|
||||
"llvm-tools-preview"
|
||||
"rust-src"
|
||||
"rustc-codegen-cranelift-preview"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
languages.typescript.enable = true;
|
||||
|
||||
languages.rust = {
|
||||
enable = true;
|
||||
channel = "nightly";
|
||||
components = [
|
||||
"rustc"
|
||||
"cargo"
|
||||
"clippy"
|
||||
"rustfmt"
|
||||
"rust-analyzer"
|
||||
"llvm-tools-preview"
|
||||
"rust-src"
|
||||
"rustc-codegen-cranelift-preview"
|
||||
];
|
||||
};
|
||||
|
||||
# https://devenv.sh/pre-commit-hooks/
|
||||
# pre-commit.hooks.shellcheck.enable = true;
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "hoppscotch-agent",
|
||||
"private": true,
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
2
packages/hoppscotch-agent/src-tauri/Cargo.lock
generated
2
packages/hoppscotch-agent/src-tauri/Cargo.lock
generated
@ -2111,7 +2111,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hoppscotch-agent"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"axum",
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "hoppscotch-agent"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
description = "A cross-platform HTTP request agent for Hoppscotch for advanced request handling including custom headers, certificates, proxies, and local system integration."
|
||||
authors = ["AndrewBastin", "CuriousCorrelation"]
|
||||
edition = "2021"
|
||||
|
@ -14,7 +14,7 @@ use tauri::{AppHandle, Emitter};
|
||||
use x25519_dalek::{EphemeralSecret, PublicKey};
|
||||
|
||||
use crate::{
|
||||
error::{AppError, AppResult},
|
||||
error::{AgentError, AgentResult},
|
||||
model::{AuthKeyResponse, ConfirmedRegistrationRequest, HandshakeResponse},
|
||||
state::{AppState, Registration},
|
||||
util::EncryptedJson,
|
||||
@ -32,7 +32,7 @@ fn generate_otp() -> String {
|
||||
|
||||
pub async fn handshake(
|
||||
State((_, app_handle)): State<(Arc<AppState>, AppHandle)>,
|
||||
) -> AppResult<Json<HandshakeResponse>> {
|
||||
) -> AgentResult<Json<HandshakeResponse>> {
|
||||
Ok(Json(HandshakeResponse {
|
||||
status: "success".to_string(),
|
||||
__hoppscotch__agent__: true,
|
||||
@ -42,7 +42,7 @@ pub async fn handshake(
|
||||
|
||||
pub async fn receive_registration(
|
||||
State((state, app_handle)): State<(Arc<AppState>, AppHandle)>,
|
||||
) -> AppResult<Json<serde_json::Value>> {
|
||||
) -> AgentResult<Json<serde_json::Value>> {
|
||||
let otp = generate_otp();
|
||||
|
||||
let mut active_registration_code = state.active_registration_code.write().await;
|
||||
@ -57,7 +57,7 @@ pub async fn receive_registration(
|
||||
|
||||
app_handle
|
||||
.emit("registration_received", otp)
|
||||
.map_err(|_| AppError::InternalServerError)?;
|
||||
.map_err(|_| AgentError::InternalServerError)?;
|
||||
|
||||
Ok(Json(
|
||||
json!({ "message": "Registration received and stored" }),
|
||||
@ -67,12 +67,12 @@ pub async fn receive_registration(
|
||||
pub async fn verify_registration(
|
||||
State((state, app_handle)): State<(Arc<AppState>, AppHandle)>,
|
||||
Json(confirmed_registration): Json<ConfirmedRegistrationRequest>,
|
||||
) -> AppResult<Json<AuthKeyResponse>> {
|
||||
) -> AgentResult<Json<AuthKeyResponse>> {
|
||||
state
|
||||
.validate_registration(&confirmed_registration.registration)
|
||||
.await
|
||||
.then_some(())
|
||||
.ok_or(AppError::InvalidRegistration)?;
|
||||
.ok_or(AgentError::InvalidRegistration)?;
|
||||
|
||||
let auth_key = Uuid::new_v4().to_string();
|
||||
let created_at = Utc::now();
|
||||
@ -85,9 +85,9 @@ pub async fn verify_registration(
|
||||
let their_public_key = {
|
||||
let public_key_slice: &[u8; 32] =
|
||||
&base16::decode(&confirmed_registration.client_public_key_b16)
|
||||
.map_err(|_| AppError::InvalidClientPublicKey)?[0..32]
|
||||
.map_err(|_| AgentError::InvalidClientPublicKey)?[0..32]
|
||||
.try_into()
|
||||
.map_err(|_| AppError::InvalidClientPublicKey)?;
|
||||
.map_err(|_| AgentError::InvalidClientPublicKey)?;
|
||||
|
||||
PublicKey::from(public_key_slice.to_owned())
|
||||
};
|
||||
@ -111,7 +111,7 @@ pub async fn verify_registration(
|
||||
|
||||
app_handle
|
||||
.emit("authenticated", &auth_payload)
|
||||
.map_err(|_| AppError::InternalServerError)?;
|
||||
.map_err(|_| AgentError::InternalServerError)?;
|
||||
|
||||
Ok(Json(AuthKeyResponse {
|
||||
auth_key,
|
||||
@ -125,22 +125,22 @@ pub async fn run_request<T>(
|
||||
TypedHeader(auth_header): TypedHeader<Authorization<Bearer>>,
|
||||
headers: HeaderMap,
|
||||
body: Bytes,
|
||||
) -> AppResult<EncryptedJson<ResponseWithMetadata>> {
|
||||
) -> AgentResult<EncryptedJson<ResponseWithMetadata>> {
|
||||
let nonce = headers
|
||||
.get("X-Hopp-Nonce")
|
||||
.ok_or(AppError::Unauthorized)?
|
||||
.ok_or(AgentError::Unauthorized)?
|
||||
.to_str()
|
||||
.map_err(|_| AppError::Unauthorized)?;
|
||||
.map_err(|_| AgentError::Unauthorized)?;
|
||||
|
||||
let req: RequestWithMetadata = state
|
||||
.validate_access_and_get_data(auth_header.token(), nonce, &body)
|
||||
.ok_or(AppError::Unauthorized)?;
|
||||
.ok_or(AgentError::Unauthorized)?;
|
||||
|
||||
let req_id = req.req_id;
|
||||
|
||||
let reg_info = state
|
||||
.get_registration_info(auth_header.token())
|
||||
.ok_or(AppError::Unauthorized)?;
|
||||
.ok_or(AgentError::Unauthorized)?;
|
||||
|
||||
let cancel_token = tokio_util::sync::CancellationToken::new();
|
||||
state.add_cancellation_token(req.req_id, cancel_token.clone());
|
||||
@ -164,11 +164,11 @@ pub async fn run_request<T>(
|
||||
res = tokio::task::spawn_blocking(move || hoppscotch_relay::run_request_task(&req, cancel_token_clone)) => {
|
||||
match res {
|
||||
Ok(task_result) => Ok(task_result?),
|
||||
Err(_) => Err(AppError::InternalServerError),
|
||||
Err(_) => Err(AgentError::InternalServerError),
|
||||
}
|
||||
},
|
||||
_ = cancel_token.cancelled() => {
|
||||
Err(AppError::RequestCancelled)
|
||||
Err(AgentError::RequestCancelled)
|
||||
}
|
||||
};
|
||||
|
||||
@ -190,7 +190,7 @@ pub async fn run_request<T>(
|
||||
pub async fn registered_handshake(
|
||||
State((state, _)): State<(Arc<AppState>, AppHandle)>,
|
||||
TypedHeader(auth_header): TypedHeader<Authorization<Bearer>>,
|
||||
) -> AppResult<EncryptedJson<serde_json::Value>> {
|
||||
) -> AgentResult<EncryptedJson<serde_json::Value>> {
|
||||
let reg_info = state.get_registration_info(auth_header.token());
|
||||
|
||||
match reg_info {
|
||||
@ -198,7 +198,7 @@ pub async fn registered_handshake(
|
||||
key_b16: reg.shared_secret_b16,
|
||||
data: json!(true),
|
||||
}),
|
||||
None => Err(AppError::Unauthorized),
|
||||
None => Err(AgentError::Unauthorized),
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,15 +206,15 @@ pub async fn cancel_request<T>(
|
||||
State((state, _app_handle)): State<(Arc<AppState>, T)>,
|
||||
TypedHeader(auth_header): TypedHeader<Authorization<Bearer>>,
|
||||
Path(req_id): Path<usize>,
|
||||
) -> AppResult<Json<serde_json::Value>> {
|
||||
) -> AgentResult<Json<serde_json::Value>> {
|
||||
if !state.validate_access(auth_header.token()) {
|
||||
return Err(AppError::Unauthorized);
|
||||
return Err(AgentError::Unauthorized);
|
||||
}
|
||||
|
||||
if let Some((_, token)) = state.remove_cancellation_token(req_id) {
|
||||
token.cancel();
|
||||
Ok(Json(json!({"message": "Request cancelled successfully"})))
|
||||
} else {
|
||||
Err(AppError::RequestNotFound)
|
||||
Err(AgentError::RequestNotFound)
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use serde_json::json;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum AppError {
|
||||
pub enum AgentError {
|
||||
#[error("Invalid Registration")]
|
||||
InvalidRegistration,
|
||||
#[error("Invalid Client Public Key")]
|
||||
@ -40,28 +40,30 @@ pub enum AppError {
|
||||
RegistrationInsertError,
|
||||
#[error("Failed to save registrations to store")]
|
||||
RegistrationSaveError,
|
||||
#[error("Serde error: {0}")]
|
||||
Serde(#[from] serde_json::Error),
|
||||
#[error("Store error: {0}")]
|
||||
TauriPluginStore(#[from] tauri_plugin_store::Error),
|
||||
#[error("Relay error: {0}")]
|
||||
Relay(#[from] hoppscotch_relay::RelayError),
|
||||
}
|
||||
|
||||
impl IntoResponse for AppError {
|
||||
impl IntoResponse for AgentError {
|
||||
fn into_response(self) -> Response {
|
||||
let (status, error_message) = match self {
|
||||
AppError::InvalidRegistration => (StatusCode::BAD_REQUEST, self.to_string()),
|
||||
AppError::InvalidClientPublicKey => (StatusCode::BAD_REQUEST, self.to_string()),
|
||||
AppError::Unauthorized => (StatusCode::UNAUTHORIZED, self.to_string()),
|
||||
AppError::RequestNotFound => (StatusCode::NOT_FOUND, self.to_string()),
|
||||
AppError::InternalServerError => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()),
|
||||
AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg),
|
||||
AppError::ClientCertError => (StatusCode::BAD_REQUEST, self.to_string()),
|
||||
AppError::RootCertError => (StatusCode::BAD_REQUEST, self.to_string()),
|
||||
AppError::InvalidMethod => (StatusCode::BAD_REQUEST, self.to_string()),
|
||||
AppError::InvalidUrl => (StatusCode::BAD_REQUEST, self.to_string()),
|
||||
AppError::InvalidHeaders => (StatusCode::BAD_REQUEST, self.to_string()),
|
||||
AppError::RequestRunError(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg),
|
||||
AppError::RequestCancelled => (StatusCode::BAD_REQUEST, self.to_string()),
|
||||
AgentError::InvalidRegistration => (StatusCode::BAD_REQUEST, self.to_string()),
|
||||
AgentError::InvalidClientPublicKey => (StatusCode::BAD_REQUEST, self.to_string()),
|
||||
AgentError::Unauthorized => (StatusCode::UNAUTHORIZED, self.to_string()),
|
||||
AgentError::RequestNotFound => (StatusCode::NOT_FOUND, self.to_string()),
|
||||
AgentError::InternalServerError => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()),
|
||||
AgentError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg),
|
||||
AgentError::ClientCertError => (StatusCode::BAD_REQUEST, self.to_string()),
|
||||
AgentError::RootCertError => (StatusCode::BAD_REQUEST, self.to_string()),
|
||||
AgentError::InvalidMethod => (StatusCode::BAD_REQUEST, self.to_string()),
|
||||
AgentError::InvalidUrl => (StatusCode::BAD_REQUEST, self.to_string()),
|
||||
AgentError::InvalidHeaders => (StatusCode::BAD_REQUEST, self.to_string()),
|
||||
AgentError::RequestRunError(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg),
|
||||
AgentError::RequestCancelled => (StatusCode::BAD_REQUEST, self.to_string()),
|
||||
_ => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"Internal Server Error".to_string(),
|
||||
@ -76,4 +78,4 @@ impl IntoResponse for AppError {
|
||||
}
|
||||
}
|
||||
|
||||
pub type AppResult<T> = std::result::Result<T, AppError>;
|
||||
pub type AgentResult<T> = std::result::Result<T, AgentError>;
|
||||
|
2
packages/hoppscotch-agent/src-tauri/src/global.rs
Normal file
2
packages/hoppscotch-agent/src-tauri/src/global.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub const AGENT_STORE: &str = "app_data.bin";
|
||||
pub const REGISTRATIONS: &str = "registrations";
|
@ -1,6 +1,7 @@
|
||||
pub mod controller;
|
||||
pub mod dialog;
|
||||
pub mod error;
|
||||
pub mod global;
|
||||
pub mod model;
|
||||
pub mod route;
|
||||
pub mod server;
|
||||
|
@ -3,11 +3,14 @@ use axum::body::Bytes;
|
||||
use chrono::{DateTime, Utc};
|
||||
use dashmap::DashMap;
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use tauri_plugin_store::StoreBuilder;
|
||||
use tauri_plugin_store::StoreExt;
|
||||
use tokio::sync::RwLock;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::error::{AppError, AppResult};
|
||||
use crate::{
|
||||
error::{AgentError, AgentResult},
|
||||
global::{AGENT_STORE, REGISTRATIONS},
|
||||
};
|
||||
|
||||
/// Describes one registered app instance
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@ -34,18 +37,20 @@ pub struct AppState {
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
pub fn new(app_handle: tauri::AppHandle) -> AppResult<Self> {
|
||||
let store = StoreBuilder::new(&app_handle, "app_data.bin").build()?;
|
||||
|
||||
let _ = store.reload();
|
||||
pub fn new(app_handle: tauri::AppHandle) -> AgentResult<Self> {
|
||||
let store = app_handle.store(AGENT_STORE)?;
|
||||
|
||||
// Try loading and parsing registrations from the store, if that failed,
|
||||
// load the default list
|
||||
let registrations = store
|
||||
.get("registrations")
|
||||
.get(REGISTRATIONS)
|
||||
.and_then(|val| serde_json::from_value(val.clone()).ok())
|
||||
.unwrap_or_else(|| DashMap::new());
|
||||
|
||||
// Try to save the latest registrations list
|
||||
let _ = store.set(REGISTRATIONS, serde_json::to_value(®istrations)?);
|
||||
let _ = store.save();
|
||||
|
||||
Ok(Self {
|
||||
active_registration_code: RwLock::new(None),
|
||||
cancellation_tokens: DashMap::new(),
|
||||
@ -62,38 +67,50 @@ impl AppState {
|
||||
}
|
||||
|
||||
/// Provides you an opportunity to update the registrations list
|
||||
/// and also persists the data to the disk
|
||||
/// and also persists the data to the disk.
|
||||
/// This function bypasses `store.reload()` to avoid issues from stale or inconsistent
|
||||
/// data on disk. By relying solely on the in-memory `self.registrations`,
|
||||
/// we make sure that updates are applied based on the most recent changes in memory.
|
||||
pub fn update_registrations(
|
||||
&self,
|
||||
app_handle: tauri::AppHandle,
|
||||
update_func: impl FnOnce(&DashMap<String, Registration>),
|
||||
) -> Result<(), AppError> {
|
||||
) -> Result<(), AgentError> {
|
||||
update_func(&self.registrations);
|
||||
|
||||
let store = StoreBuilder::new(&app_handle, "app_data.bin").build()?;
|
||||
let store = app_handle.store(AGENT_STORE)?;
|
||||
|
||||
let _ = store.reload()?;
|
||||
if store.has(REGISTRATIONS) {
|
||||
// We've confirmed `REGISTRATIONS` exists in the store
|
||||
store
|
||||
.delete(REGISTRATIONS)
|
||||
.then_some(())
|
||||
.ok_or(AgentError::RegistrationClearError)?;
|
||||
} else {
|
||||
log::debug!("`REGISTRATIONS` key not found in store; continuing with update.");
|
||||
}
|
||||
|
||||
let _ = store
|
||||
.delete("registrations")
|
||||
.then_some(())
|
||||
.ok_or(AppError::RegistrationClearError)?;
|
||||
// Since we've established `self.registrations` as the source of truth,
|
||||
// we avoid reloading the store from disk and instead choose to override it.
|
||||
|
||||
let _ = store.set(
|
||||
"registrations",
|
||||
serde_json::to_value(self.registrations.clone()).unwrap(),
|
||||
store.set(
|
||||
REGISTRATIONS,
|
||||
serde_json::to_value(self.registrations.clone())?,
|
||||
);
|
||||
|
||||
store.save().map_err(|_| AppError::RegistrationSaveError)?;
|
||||
// Explicitly save the changes
|
||||
store.save()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Clear all the registrations
|
||||
pub fn clear_registrations(&self, app_handle: tauri::AppHandle) -> Result<(), AgentError> {
|
||||
Ok(self.update_registrations(app_handle, |registrations| registrations.clear())?)
|
||||
}
|
||||
|
||||
pub async fn validate_registration(&self, registration: &str) -> bool {
|
||||
match *self.active_registration_code.read().await {
|
||||
Some(ref code) => code == registration,
|
||||
None => false,
|
||||
}
|
||||
self.active_registration_code.read().await.as_deref() == Some(registration)
|
||||
}
|
||||
|
||||
pub fn remove_cancellation_token(&self, req_id: usize) -> Option<(usize, CancellationToken)> {
|
||||
|
@ -57,18 +57,19 @@ pub fn create_tray(app: &AppHandle) -> tauri::Result<()> {
|
||||
.menu_on_left_click(true)
|
||||
.on_menu_event(move |app, event| match event.id.as_ref() {
|
||||
"quit" => {
|
||||
log::info!("Exiting the agent...");
|
||||
app.exit(-1);
|
||||
}
|
||||
"clear_registrations" => {
|
||||
let app_state = app.state::<Arc<AppState>>();
|
||||
|
||||
app_state
|
||||
.update_registrations(app.clone(), |regs| {
|
||||
regs.clear();
|
||||
})
|
||||
.expect("Failed to clear registrations");
|
||||
.clear_registrations(app.clone())
|
||||
.expect("Invariant violation: Failed to clear registrations");
|
||||
}
|
||||
_ => {
|
||||
log::warn!("Unhandled menu event: {:?}", event.id);
|
||||
}
|
||||
_ => {}
|
||||
})
|
||||
.on_tray_icon_event(|tray, event| {
|
||||
if let TrayIconEvent::Click {
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2.0.0-rc",
|
||||
"productName": "Hoppscotch Agent",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"identifier": "io.hoppscotch.agent",
|
||||
"build": {
|
||||
"beforeDevCommand": "pnpm dev",
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2.0.0-rc",
|
||||
"productName": "Hoppscotch Agent Portable",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"identifier": "io.hoppscotch.agent",
|
||||
"build": {
|
||||
"beforeDevCommand": "pnpm dev",
|
||||
|
Loading…
Reference in New Issue
Block a user