mirror of
https://github.com/infinilabs/coco-app.git
synced 2025-12-28 16:06:28 +01:00
fix: setup & build error (#147)
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
use std::collections::HashMap;
|
||||
// use std::collections::HashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::common::health::Status;
|
||||
// use crate::common::health::Status;
|
||||
|
||||
#[derive(Debug,Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RequestAccessTokenResponse {
|
||||
pub access_token: String,
|
||||
pub expire_at: u32,
|
||||
|
||||
@@ -2,22 +2,18 @@ use crate::common::search::{QueryResponse, QuerySource};
|
||||
use thiserror::Error;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use std::{future::Future, pin::Pin};
|
||||
use serde::Serialize;
|
||||
// use std::{future::Future, pin::Pin};
|
||||
use crate::common::search::SearchQuery;
|
||||
use serde::Serialize;
|
||||
|
||||
#[async_trait]
|
||||
pub trait SearchSource: Send + Sync {
|
||||
fn get_type (&self) -> QuerySource;
|
||||
fn get_type(&self) -> QuerySource;
|
||||
|
||||
async fn search(
|
||||
&self,
|
||||
query: SearchQuery,
|
||||
) -> Result<QueryResponse, SearchError>;
|
||||
async fn search(&self, query: SearchQuery) -> Result<QueryResponse, SearchError>;
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Error,Serialize)]
|
||||
#[derive(Debug, Error, Serialize)]
|
||||
pub enum SearchError {
|
||||
#[error("HTTP request failed: {0}")]
|
||||
HttpError(String),
|
||||
@@ -45,4 +41,4 @@ impl From<reqwest::Error> for SearchError {
|
||||
SearchError::HttpError(err.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ mod util;
|
||||
mod setup;
|
||||
|
||||
use crate::common::register::SearchSourceRegistry;
|
||||
use crate::common::traits::SearchSource;
|
||||
// use crate::common::traits::SearchSource;
|
||||
use crate::common::{MAIN_WINDOW_LABEL, SETTINGS_WINDOW_LABEL};
|
||||
use crate::server::search::CocoSearchSource;
|
||||
use crate::server::servers::{load_or_insert_default_server, load_servers_token};
|
||||
@@ -18,7 +18,10 @@ use reqwest::Client;
|
||||
use std::path::PathBuf;
|
||||
#[cfg(target_os = "macos")]
|
||||
use tauri::ActivationPolicy;
|
||||
use tauri::{AppHandle, Emitter, Listener, Manager, PhysicalPosition, Runtime, State, WebviewWindow, Window, WindowEvent};
|
||||
use tauri::{
|
||||
AppHandle, Emitter, Listener, Manager, PhysicalPosition, Runtime, State, WebviewWindow, Window,
|
||||
WindowEvent,
|
||||
};
|
||||
use tauri_plugin_autostart::MacosLauncher;
|
||||
use tauri_plugin_deep_link::DeepLinkExt;
|
||||
use tokio::runtime::Runtime as RT;
|
||||
@@ -64,7 +67,7 @@ pub fn run() {
|
||||
app.emit("single-instance", Payload { args: argv, cwd })
|
||||
.unwrap();
|
||||
}))
|
||||
.plugin(tauri_plugin_store::Builder::default().build())
|
||||
.plugin(tauri_plugin_store::Builder::default().build());
|
||||
|
||||
// Conditional compilation for macOS
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -187,7 +190,7 @@ pub async fn init<R: Runtime>(app_handle: &AppHandle<R>) {
|
||||
let coco_servers = server::servers::get_all_servers();
|
||||
|
||||
// Get the registry from Tauri's state
|
||||
let registry:State<SearchSourceRegistry> = app_handle.state::<SearchSourceRegistry>();
|
||||
let registry: State<SearchSourceRegistry> = app_handle.state::<SearchSourceRegistry>();
|
||||
|
||||
for server in coco_servers {
|
||||
let source = CocoSearchSource::new(server.clone(), Client::new());
|
||||
|
||||
@@ -31,15 +31,22 @@ fn extract_icon_from_app_bundle(app_dir: &Path, app_data_folder: &Path) -> Optio
|
||||
|
||||
if icns_path.exists() {
|
||||
// If the icon exists, convert it to PNG
|
||||
if let Some(output_path) = convert_icns_to_png(&app_dir, &icns_path, app_data_folder) {
|
||||
if let Some(output_path) =
|
||||
convert_icns_to_png(&app_dir, &icns_path, app_data_folder)
|
||||
{
|
||||
return Some(output_path);
|
||||
}
|
||||
} else {
|
||||
// If the icon name doesn't end with .icns, try appending it
|
||||
if !icon_name.ends_with(".icns") {
|
||||
let icns_path_with_extension = app_dir.join(format!("Contents/Resources/{}.icns", icon_name));
|
||||
let icns_path_with_extension =
|
||||
app_dir.join(format!("Contents/Resources/{}.icns", icon_name));
|
||||
if icns_path_with_extension.exists() {
|
||||
if let Some(output_path) = convert_icns_to_png(&app_dir, &icns_path_with_extension, app_data_folder) {
|
||||
if let Some(output_path) = convert_icns_to_png(
|
||||
&app_dir,
|
||||
&icns_path_with_extension,
|
||||
app_data_folder,
|
||||
) {
|
||||
return Some(output_path);
|
||||
}
|
||||
}
|
||||
@@ -64,7 +71,8 @@ fn extract_icon_from_app_bundle(app_dir: &Path, app_data_folder: &Path) -> Optio
|
||||
|
||||
// Fallback: If no icon found, return a default system icon
|
||||
if let Some(system_icon_path) = get_system_icon(app_dir) {
|
||||
if let Some(output_path) = convert_icns_to_png(&app_dir, &system_icon_path, app_data_folder) {
|
||||
if let Some(output_path) = convert_icns_to_png(&app_dir, &system_icon_path, app_data_folder)
|
||||
{
|
||||
return Some(output_path);
|
||||
}
|
||||
}
|
||||
@@ -135,7 +143,11 @@ fn get_png_from_resources(app_dir: &Path) -> Option<PathBuf> {
|
||||
}
|
||||
|
||||
/// Converts an ICNS file to PNG using macOS's `sips` command.
|
||||
fn convert_icns_to_png(app_dir: &Path, icns_path: &Path, app_data_folder: &Path) -> Option<PathBuf> {
|
||||
fn convert_icns_to_png(
|
||||
app_dir: &Path,
|
||||
icns_path: &Path,
|
||||
app_data_folder: &Path,
|
||||
) -> Option<PathBuf> {
|
||||
if let Some(app_name) = app_dir.file_name().and_then(|name| name.to_str()) {
|
||||
let icon_storage_dir = app_data_folder.join("coco-appIcons");
|
||||
fs::create_dir_all(&icon_storage_dir).ok();
|
||||
@@ -156,8 +168,8 @@ fn convert_icns_to_png(app_dir: &Path, icns_path: &Path, app_data_folder: &Path)
|
||||
.arg(icns_path)
|
||||
.arg("--out")
|
||||
.arg(&output_png_path)
|
||||
.stdout(Stdio::null()) // Redirect stdout to null
|
||||
.stderr(Stdio::null()) // Redirect stderr to null
|
||||
.stdout(Stdio::null()) // Redirect stdout to null
|
||||
.stderr(Stdio::null()) // Redirect stderr to null
|
||||
.status();
|
||||
|
||||
if let Ok(status) = status {
|
||||
@@ -173,14 +185,18 @@ fn convert_icns_to_png(app_dir: &Path, icns_path: &Path, app_data_folder: &Path)
|
||||
|
||||
/// Converts a PNG file to PNG (essentially just copying it to a new location).
|
||||
fn convert_png_to_png(png_path: &Path, app_data_folder: &Path) -> Option<PathBuf> {
|
||||
if let Some(app_name) = png_path.parent().and_then(|p| p.file_name()).and_then(|name| name.to_str()) {
|
||||
if let Some(app_name) = png_path
|
||||
.parent()
|
||||
.and_then(|p| p.file_name())
|
||||
.and_then(|name| name.to_str())
|
||||
{
|
||||
let icon_storage_dir = app_data_folder.join("coco-appIcons");
|
||||
fs::create_dir_all(&icon_storage_dir).ok();
|
||||
|
||||
let output_png_path = icon_storage_dir.join(format!("{}.png", app_name));
|
||||
|
||||
// Copy the PNG file to the output directory
|
||||
if let Err(e) = fs::copy(png_path, &output_png_path) {
|
||||
if let Err(_e) = fs::copy(png_path, &output_png_path) {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -190,7 +206,7 @@ fn convert_png_to_png(png_path: &Path, app_data_folder: &Path) -> Option<PathBuf
|
||||
}
|
||||
|
||||
/// Fallback function to fetch a system icon if the app doesn't have its own.
|
||||
fn get_system_icon(app_dir: &Path) -> Option<PathBuf> {
|
||||
fn get_system_icon(_app_dir: &Path) -> Option<PathBuf> {
|
||||
// Just a placeholder for getting a default icon if no app-specific icon is found
|
||||
let default_icon_path = Path::new("/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/GenericApplicationIcon.icns");
|
||||
|
||||
@@ -201,13 +217,12 @@ fn get_system_icon(app_dir: &Path) -> Option<PathBuf> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl ApplicationSearchSource {
|
||||
pub fn new(base_score: f64, app_dirs: Vec<PathBuf>) -> Self {
|
||||
let mut icons = HashMap::new();
|
||||
|
||||
// Collect search locations as strings
|
||||
let mut applications = Trie::new();
|
||||
let applications = Trie::new();
|
||||
|
||||
// Iterate over the directories to find .app files and extract icons
|
||||
for app_dir in &app_dirs {
|
||||
@@ -217,15 +232,22 @@ impl ApplicationSearchSource {
|
||||
if file_path.is_dir() && file_path.extension() == Some("app".as_ref()) {
|
||||
if let Some(app_data_folder) = data_dir() {
|
||||
let file_path_str = file_path.to_string_lossy().to_string(); // Convert to owned String if needed
|
||||
if file_path.parent().unwrap().to_str().unwrap().contains(".app/Contents/") {
|
||||
if file_path
|
||||
.parent()
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.contains(".app/Contents/")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let search_word = file_path.file_name()
|
||||
.unwrap() // unwrap() might panic if there's no file name
|
||||
let search_word = file_path
|
||||
.file_name()
|
||||
.unwrap() // unwrap() might panic if there's no file name
|
||||
.to_str()
|
||||
.unwrap() // unwrap() might panic if it's not valid UTF-8
|
||||
.unwrap() // unwrap() might panic if it's not valid UTF-8
|
||||
.trim_end_matches(".app")
|
||||
.to_lowercase(); // to_lowercase returns a String, which is owned
|
||||
.to_lowercase(); // to_lowercase returns a String, which is owned
|
||||
|
||||
//TODO, replace this hard-coded name to actual local app name in case it may change
|
||||
if search_word.is_empty() || search_word.eq("coco ai") {
|
||||
@@ -234,7 +256,9 @@ impl ApplicationSearchSource {
|
||||
|
||||
let search_word_ref = search_word.as_str(); // Get a reference to the string slice
|
||||
applications.insert(search_word_ref, file_path_str.clone());
|
||||
if let Some(icon_path) = extract_icon_from_app_bundle(&file_path, &app_data_folder) {
|
||||
if let Some(icon_path) =
|
||||
extract_icon_from_app_bundle(&file_path, &app_data_folder)
|
||||
{
|
||||
icons.insert(file_path_str, icon_path);
|
||||
} else {
|
||||
dbg!("No icon found for:", &file_path);
|
||||
@@ -253,7 +277,6 @@ impl ApplicationSearchSource {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Extracts the clean app name by removing `.app`
|
||||
fn clean_app_name(path: &Path) -> Option<String> {
|
||||
path.file_name()?
|
||||
@@ -266,7 +289,10 @@ impl SearchSource for ApplicationSearchSource {
|
||||
fn get_type(&self) -> QuerySource {
|
||||
QuerySource {
|
||||
r#type: LOCAL_QUERY_SOURCE_TYPE.into(),
|
||||
name: hostname::get().unwrap_or("My Computer".into()).to_string_lossy().into(),
|
||||
name: hostname::get()
|
||||
.unwrap_or("My Computer".into())
|
||||
.to_string_lossy()
|
||||
.into(),
|
||||
id: "local_applications".into(),
|
||||
}
|
||||
}
|
||||
@@ -289,7 +315,9 @@ impl SearchSource for ApplicationSearchSource {
|
||||
let mut total_hits = 0;
|
||||
let mut hits = Vec::new();
|
||||
|
||||
let mut results = self.application_paths.search_within_distance_scored(&query_string, 3);
|
||||
let mut results = self
|
||||
.application_paths
|
||||
.search_within_distance_scored(&query_string, 3);
|
||||
|
||||
// Check for NaN or extreme score values and handle them properly
|
||||
results.sort_by(|a, b| {
|
||||
@@ -298,7 +326,9 @@ impl SearchSource for ApplicationSearchSource {
|
||||
std::cmp::Ordering::Equal
|
||||
} else {
|
||||
// Otherwise, compare the scores as usual
|
||||
b.score.partial_cmp(&a.score).unwrap_or(std::cmp::Ordering::Equal)
|
||||
b.score
|
||||
.partial_cmp(&a.score)
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use crate::common::register::SearchSourceRegistry;
|
||||
use crate::common::search::{FailedRequest, MultiSourceQueryResponse, QueryHits, QuerySource, SearchQuery};
|
||||
use crate::common::traits::{SearchError, SearchSource};
|
||||
use crate::common::search::{
|
||||
FailedRequest, MultiSourceQueryResponse, QueryHits, QuerySource, SearchQuery,
|
||||
};
|
||||
use crate::common::traits::SearchError;
|
||||
use futures::stream::FuturesUnordered;
|
||||
use futures::StreamExt;
|
||||
use std::collections::HashMap;
|
||||
@@ -52,7 +54,8 @@ pub async fn query_coco_fusion<R: Runtime>(
|
||||
|
||||
all_hits.push((source_id.clone(), query_hit.clone(), score));
|
||||
|
||||
hits_per_source.entry(source_id.clone())
|
||||
hits_per_source
|
||||
.entry(source_id.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
.push((query_hit, score));
|
||||
}
|
||||
@@ -90,7 +93,11 @@ pub async fn query_coco_fusion<R: Runtime>(
|
||||
}
|
||||
|
||||
let total_sources = hits_per_source.len();
|
||||
let max_hits_per_source = if total_sources > 0 { size as usize / total_sources } else { size as usize };
|
||||
let max_hits_per_source = if total_sources > 0 {
|
||||
size as usize / total_sources
|
||||
} else {
|
||||
size as usize
|
||||
};
|
||||
|
||||
let mut final_hits = Vec::new();
|
||||
let mut seen_docs = std::collections::HashSet::new(); // To track documents we've already added
|
||||
@@ -113,7 +120,8 @@ pub async fn query_coco_fusion<R: Runtime>(
|
||||
// Sort all hits by score descending, removing duplicates by document ID
|
||||
all_hits.sort_by(|a, b| b.2.partial_cmp(&a.2).unwrap_or(std::cmp::Ordering::Equal));
|
||||
|
||||
let extra_hits = all_hits.into_iter()
|
||||
let extra_hits = all_hits
|
||||
.into_iter()
|
||||
.filter(|(source_id, _, _)| hits_per_source.contains_key(source_id)) // Only take from known sources
|
||||
.filter_map(|(_, doc, _)| {
|
||||
if !seen_docs.contains(&doc.document.id) {
|
||||
@@ -130,11 +138,15 @@ pub async fn query_coco_fusion<R: Runtime>(
|
||||
}
|
||||
|
||||
// **Sort final hits by score descending**
|
||||
final_hits.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap_or(std::cmp::Ordering::Equal));
|
||||
final_hits.sort_by(|a, b| {
|
||||
b.score
|
||||
.partial_cmp(&a.score)
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
});
|
||||
|
||||
Ok(MultiSourceQueryResponse {
|
||||
failed: failed_requests,
|
||||
hits: final_hits,
|
||||
total_hits,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,8 @@ use std::sync::{Arc, RwLock};
|
||||
use tauri::{AppHandle, Runtime};
|
||||
|
||||
lazy_static! {
|
||||
static ref CONNECTOR_CACHE: Arc<RwLock<HashMap<String, HashMap<String, Connector>>>> = Arc::new(RwLock::new(HashMap::new()));
|
||||
static ref CONNECTOR_CACHE: Arc<RwLock<HashMap<String, HashMap<String, Connector>>>> =
|
||||
Arc::new(RwLock::new(HashMap::new()));
|
||||
}
|
||||
|
||||
pub fn save_connectors_to_cache(server_id: &str, connectors: Vec<Connector>) {
|
||||
@@ -21,15 +22,13 @@ pub fn save_connectors_to_cache(server_id: &str, connectors: Vec<Connector>) {
|
||||
}
|
||||
|
||||
pub fn get_connector_by_id(server_id: &str, connector_id: &str) -> Option<Connector> {
|
||||
let cache = CONNECTOR_CACHE.read().unwrap(); // Async read lock
|
||||
let cache = CONNECTOR_CACHE.read().unwrap(); // Async read lock
|
||||
let server_cache = cache.get(server_id)?;
|
||||
let connector = server_cache.get(connector_id)?;
|
||||
Some(connector.clone())
|
||||
}
|
||||
|
||||
pub async fn refresh_all_connectors<R: Runtime>(
|
||||
app_handle: &AppHandle<R>,
|
||||
) -> Result<(), String> {
|
||||
pub async fn refresh_all_connectors<R: Runtime>(app_handle: &AppHandle<R>) -> Result<(), String> {
|
||||
// dbg!("Attempting to refresh all connectors");
|
||||
|
||||
let servers = get_all_servers();
|
||||
@@ -38,7 +37,8 @@ pub async fn refresh_all_connectors<R: Runtime>(
|
||||
let mut server_map = HashMap::new();
|
||||
for server in servers {
|
||||
// dbg!("start fetch connectors for server: {}", &server.id);
|
||||
let connectors = match get_connectors_by_server(app_handle.clone(), server.id.clone()).await {
|
||||
let connectors = match get_connectors_by_server(app_handle.clone(), server.id.clone()).await
|
||||
{
|
||||
Ok(connectors) => {
|
||||
let connectors_map: HashMap<String, Connector> = connectors
|
||||
.into_iter()
|
||||
@@ -46,7 +46,7 @@ pub async fn refresh_all_connectors<R: Runtime>(
|
||||
.collect();
|
||||
connectors_map
|
||||
}
|
||||
Err(e) => {
|
||||
Err(_e) => {
|
||||
// dbg!("Failed to get connectors for server {}: {}", &server.id, e);
|
||||
HashMap::new() // Return empty map on failure
|
||||
}
|
||||
@@ -57,7 +57,7 @@ pub async fn refresh_all_connectors<R: Runtime>(
|
||||
}
|
||||
|
||||
// After all tasks have finished, perform a read operation on the cache
|
||||
let cache_size = {
|
||||
let _cache_size = {
|
||||
// Insert connectors into the cache (async write lock)
|
||||
let mut cache = CONNECTOR_CACHE.write().unwrap(); // Async write lock
|
||||
cache.clear();
|
||||
@@ -71,7 +71,9 @@ pub async fn refresh_all_connectors<R: Runtime>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_connectors_from_cache_or_remote(server_id: &str) -> Result<Vec<Connector>, String> {
|
||||
pub async fn get_connectors_from_cache_or_remote(
|
||||
server_id: &str,
|
||||
) -> Result<Vec<Connector>, String> {
|
||||
// Acquire the read lock and check cache for connectors
|
||||
let cache = CONNECTOR_CACHE.read().unwrap(); // Acquire read lock
|
||||
if let Some(connectors) = cache.get(server_id).cloned() {
|
||||
@@ -85,9 +87,10 @@ pub async fn get_connectors_from_cache_or_remote(server_id: &str) -> Result<Vec<
|
||||
let connectors = fetch_connectors_by_server(server_id).await?;
|
||||
|
||||
// Convert the Vec<Connector> into HashMap<String, Connector>
|
||||
let connectors_map: HashMap<String, Connector> = connectors.clone()
|
||||
let connectors_map: HashMap<String, Connector> = connectors
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|connector| (connector.id.clone(), connector)) // Assuming Connector has an `id` field
|
||||
.map(|connector| (connector.id.clone(), connector)) // Assuming Connector has an `id` field
|
||||
.collect();
|
||||
|
||||
// Optionally, update the cache after fetching data from remote
|
||||
@@ -116,7 +119,10 @@ pub async fn fetch_connectors_by_server(id: &str) -> Result<Vec<Connector>, Stri
|
||||
if resp.status().is_success() {
|
||||
dbg!("Received successful response for id: {}", &id);
|
||||
} else {
|
||||
dbg!("Failed to fetch connectors. Response status: {}", resp.status());
|
||||
dbg!(
|
||||
"Failed to fetch connectors. Response status: {}",
|
||||
resp.status()
|
||||
);
|
||||
}
|
||||
|
||||
// Parse the search results directly from the response body
|
||||
@@ -137,7 +143,7 @@ pub async fn fetch_connectors_by_server(id: &str) -> Result<Vec<Connector>, Stri
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_connectors_by_server<R: Runtime>(
|
||||
app_handle: AppHandle<R>,
|
||||
_app_handle: AppHandle<R>,
|
||||
id: String,
|
||||
) -> Result<Vec<Connector>, String> {
|
||||
//fetch_connectors_by_server
|
||||
|
||||
@@ -9,30 +9,27 @@ use std::sync::{Arc, RwLock};
|
||||
use tauri::{AppHandle, Runtime};
|
||||
|
||||
lazy_static! {
|
||||
static ref DATASOURCE_CACHE: Arc<RwLock<HashMap<String,HashMap<String,DataSource>>>> = Arc::new(RwLock::new(HashMap::new()));
|
||||
static ref DATASOURCE_CACHE: Arc<RwLock<HashMap<String, HashMap<String, DataSource>>>> =
|
||||
Arc::new(RwLock::new(HashMap::new()));
|
||||
}
|
||||
|
||||
pub fn save_datasource_to_cache(server_id: &str, datasources: Vec<DataSource>) {
|
||||
let mut cache = DATASOURCE_CACHE.write().unwrap(); // Acquire write lock
|
||||
let datasources_map: HashMap<String, DataSource> = datasources
|
||||
.into_iter()
|
||||
.map(|datasource| {
|
||||
(datasource.id.clone(), datasource)
|
||||
})
|
||||
.map(|datasource| (datasource.id.clone(), datasource))
|
||||
.collect();
|
||||
cache.insert(server_id.to_string(), datasources_map);
|
||||
}
|
||||
|
||||
pub fn get_datasources_from_cache(server_id: &str) -> Option<HashMap<String, DataSource>> {
|
||||
let cache = DATASOURCE_CACHE.read().unwrap(); // Acquire read lock
|
||||
// dbg!("cache: {:?}", &cache);
|
||||
// dbg!("cache: {:?}", &cache);
|
||||
let server_cache = cache.get(server_id)?; // Get the server's cache
|
||||
Some(server_cache.clone())
|
||||
}
|
||||
|
||||
pub async fn refresh_all_datasources<R: Runtime>(
|
||||
app_handle: &AppHandle<R>,
|
||||
) -> Result<(), String> {
|
||||
pub async fn refresh_all_datasources<R: Runtime>(app_handle: &AppHandle<R>) -> Result<(), String> {
|
||||
// dbg!("Attempting to refresh all datasources");
|
||||
|
||||
let servers = get_all_servers();
|
||||
@@ -43,26 +40,25 @@ pub async fn refresh_all_datasources<R: Runtime>(
|
||||
// dbg!("fetch datasources for server: {}", &server.id);
|
||||
|
||||
// Attempt to get datasources by server, and continue even if it fails
|
||||
let mut connectors = match get_datasources_by_server(app_handle.clone(), server.id.clone()).await {
|
||||
Ok(connectors) => {
|
||||
// Process connectors only after fetching them
|
||||
let connectors_map: HashMap<String, DataSource> = connectors
|
||||
.into_iter()
|
||||
.map(|mut connector| {
|
||||
(connector.id.clone(), connector)
|
||||
})
|
||||
.collect();
|
||||
// dbg!("connectors_map: {:?}", &connectors_map);
|
||||
connectors_map
|
||||
}
|
||||
Err(e) => {
|
||||
// dbg!("Failed to get dataSources for server {}: {}", &server.id, e);
|
||||
HashMap::new()
|
||||
}
|
||||
};
|
||||
let connectors =
|
||||
match get_datasources_by_server(app_handle.clone(), server.id.clone()).await {
|
||||
Ok(connectors) => {
|
||||
// Process connectors only after fetching them
|
||||
let connectors_map: HashMap<String, DataSource> = connectors
|
||||
.into_iter()
|
||||
.map(|connector| (connector.id.clone(), connector))
|
||||
.collect();
|
||||
// dbg!("connectors_map: {:?}", &connectors_map);
|
||||
connectors_map
|
||||
}
|
||||
Err(_e) => {
|
||||
// dbg!("Failed to get dataSources for server {}: {}", &server.id, e);
|
||||
HashMap::new()
|
||||
}
|
||||
};
|
||||
|
||||
let mut new_map = HashMap::new();
|
||||
for (id, mut datasource) in connectors.iter() {
|
||||
for (id, datasource) in connectors.iter() {
|
||||
// dbg!("connector: {:?}", &datasource);
|
||||
if let Some(existing_connector) = get_connector_by_id(&server.id, &datasource.id) {
|
||||
// If found in cache, update the connector's info
|
||||
@@ -77,7 +73,7 @@ pub async fn refresh_all_datasources<R: Runtime>(
|
||||
}
|
||||
|
||||
// Perform a read operation after all writes are done
|
||||
let cache_size = {
|
||||
let _cache_size = {
|
||||
let mut cache = DATASOURCE_CACHE.write().unwrap();
|
||||
cache.clear();
|
||||
cache.extend(server_map);
|
||||
@@ -90,7 +86,7 @@ pub async fn refresh_all_datasources<R: Runtime>(
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_datasources_by_server<R: Runtime>(
|
||||
app_handle: AppHandle<R>,
|
||||
_app_handle: AppHandle<R>,
|
||||
id: String,
|
||||
) -> Result<Vec<DataSource>, String> {
|
||||
// dbg!("get_datasources_by_server: id = {}", &id);
|
||||
@@ -104,7 +100,7 @@ pub async fn get_datasources_by_server<R: Runtime>(
|
||||
})?;
|
||||
|
||||
// Parse the search results from the response
|
||||
let mut datasources: Vec<DataSource> = parse_search_results(resp).await.map_err(|e| {
|
||||
let datasources: Vec<DataSource> = parse_search_results(resp).await.map_err(|e| {
|
||||
// dbg!("Error parsing search results: {}", &e);
|
||||
e.to_string()
|
||||
})?;
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
use std::future::Future;
|
||||
// use std::future::Future;
|
||||
use std::time::Duration;
|
||||
use lazy_static::lazy_static;
|
||||
use tauri::AppHandle;
|
||||
// use lazy_static::lazy_static;
|
||||
// use tauri::AppHandle;
|
||||
use crate::server::servers::{get_server_by_id, get_server_token};
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use reqwest::{Client, Method, RequestBuilder};
|
||||
use tokio::sync::Mutex;
|
||||
use reqwest::{Client, Method, RequestBuilder, Response, StatusCode};
|
||||
|
||||
pub static HTTP_CLIENT: Lazy<Mutex<Client>> = Lazy::new(|| {
|
||||
let client = Client::builder()
|
||||
.read_timeout(Duration::from_secs(3)) // Set a timeout of 3 second
|
||||
.connect_timeout(Duration::from_secs(3)) // Set a timeout of 3 second
|
||||
.timeout(Duration::from_secs(10)) // Set a timeout of 10 seconds
|
||||
.timeout(Duration::from_secs(10)) // Set a timeout of 10 seconds
|
||||
.danger_accept_invalid_certs(true) // example for self-signed certificates
|
||||
.build()
|
||||
.expect("Failed to build client");
|
||||
@@ -35,7 +35,6 @@ impl HttpClient {
|
||||
headers: Option<reqwest::header::HeaderMap>,
|
||||
body: Option<reqwest::Body>,
|
||||
) -> Result<reqwest::Response, String> {
|
||||
|
||||
let request_builder = Self::get_request_builder(method, url, headers, body).await;
|
||||
|
||||
// Send the request
|
||||
@@ -91,28 +90,25 @@ impl HttpClient {
|
||||
// Retrieve the token for the server (token is optional)
|
||||
let token = get_server_token(server_id).map(|t| t.access_token.clone());
|
||||
|
||||
|
||||
// Create headers map (optional "X-API-TOKEN" header)
|
||||
let mut headers = reqwest::header::HeaderMap::new();
|
||||
if let Some(t) = token {
|
||||
headers.insert("X-API-TOKEN", reqwest::header::HeaderValue::from_str(&t).unwrap());
|
||||
headers.insert(
|
||||
"X-API-TOKEN",
|
||||
reqwest::header::HeaderValue::from_str(&t).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
// dbg!(&headers);
|
||||
|
||||
Self::send_raw_request(method, &url, Some(headers), body).await
|
||||
|
||||
} else {
|
||||
Err("Server not found".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Convenience method for GET requests (as it's the most common)
|
||||
pub async fn get(
|
||||
server_id: &str,
|
||||
path: &str,
|
||||
) -> Result<reqwest::Response, String> {
|
||||
pub async fn get(server_id: &str, path: &str) -> Result<reqwest::Response, String> {
|
||||
HttpClient::send_request(server_id, Method::GET, path, None).await
|
||||
}
|
||||
|
||||
@@ -122,7 +118,7 @@ impl HttpClient {
|
||||
path: &str,
|
||||
body: reqwest::Body,
|
||||
) -> Result<reqwest::Response, String> {
|
||||
HttpClient::send_request( server_id, Method::POST, path, Some(body)).await
|
||||
HttpClient::send_request(server_id, Method::POST, path, Some(body)).await
|
||||
}
|
||||
|
||||
// Convenience method for PUT requests
|
||||
@@ -131,14 +127,11 @@ impl HttpClient {
|
||||
path: &str,
|
||||
body: reqwest::Body,
|
||||
) -> Result<reqwest::Response, String> {
|
||||
HttpClient::send_request( server_id, Method::PUT, path, Some(body)).await
|
||||
HttpClient::send_request(server_id, Method::PUT, path, Some(body)).await
|
||||
}
|
||||
|
||||
// Convenience method for DELETE requests
|
||||
pub async fn delete(
|
||||
server_id: &str,
|
||||
path: &str,
|
||||
) -> Result<reqwest::Response, String> {
|
||||
pub async fn delete(server_id: &str, path: &str) -> Result<reqwest::Response, String> {
|
||||
HttpClient::send_request(server_id, Method::DELETE, path, None).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
//! This file contains Rust APIs related to Coco Server management.
|
||||
|
||||
use futures::FutureExt;
|
||||
use futures::StreamExt;
|
||||
use futures::TryFutureExt;
|
||||
use reqwest::Client;
|
||||
use serde::Serialize;
|
||||
use std::sync::LazyLock;
|
||||
use tauri::Runtime;
|
||||
use tauri_plugin_store::StoreExt;
|
||||
// use futures::FutureExt;
|
||||
// use futures::StreamExt;
|
||||
// use futures::TryFutureExt;
|
||||
// use reqwest::Client;
|
||||
// use serde::Serialize;
|
||||
// use std::sync::LazyLock;
|
||||
// use tauri::Runtime;
|
||||
// use tauri_plugin_store::StoreExt;
|
||||
|
||||
pub mod servers;
|
||||
pub mod auth;
|
||||
pub mod servers;
|
||||
// pub mod health;
|
||||
pub mod datasource;
|
||||
pub mod connector;
|
||||
pub mod search;
|
||||
pub mod profile;
|
||||
pub mod datasource;
|
||||
pub mod http_client;
|
||||
|
||||
pub mod profile;
|
||||
pub mod search;
|
||||
|
||||
@@ -4,11 +4,11 @@ use tauri::{AppHandle, Runtime};
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_user_profiles<R: Runtime>(
|
||||
app_handle: AppHandle<R>,
|
||||
_app_handle: AppHandle<R>,
|
||||
server_id: String,
|
||||
) -> Result<UserProfile, String> {
|
||||
// Use the generic GET method from HttpClient
|
||||
let response = HttpClient::get( &server_id, "/account/profile")
|
||||
let response = HttpClient::get(&server_id, "/account/profile")
|
||||
.await
|
||||
.map_err(|e| format!("Error fetching profile: {}", e))?;
|
||||
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
use crate::common::document::Document;
|
||||
use crate::common::search::{parse_search_response, QueryHits, QueryResponse, QuerySource, SearchQuery};
|
||||
use crate::common::search::{
|
||||
parse_search_response, QueryHits, QueryResponse, QuerySource, SearchQuery,
|
||||
};
|
||||
use crate::common::server::Server;
|
||||
use crate::common::traits::{SearchError, SearchSource};
|
||||
use crate::server::http_client::HttpClient;
|
||||
use crate::server::servers::get_server_token;
|
||||
use async_trait::async_trait;
|
||||
use futures::stream::StreamExt;
|
||||
// use futures::stream::StreamExt;
|
||||
use ordered_float::OrderedFloat;
|
||||
use reqwest::{Client, Method, RequestBuilder};
|
||||
use std::collections::HashMap;
|
||||
use std::hash::Hash;
|
||||
// use std::hash::Hash;
|
||||
pub(crate) struct DocumentsSizedCollector {
|
||||
size: u64,
|
||||
/// Documents and scores
|
||||
@@ -41,7 +43,7 @@ impl DocumentsSizedCollector {
|
||||
}
|
||||
}
|
||||
|
||||
fn documents(self) -> impl ExactSizeIterator<Item=Document> {
|
||||
fn documents(self) -> impl ExactSizeIterator<Item = Document> {
|
||||
self.docs.into_iter().map(|(_, doc, _)| doc)
|
||||
}
|
||||
|
||||
@@ -81,7 +83,12 @@ impl CocoSearchSource {
|
||||
self.build_request(query.from, query.size, &query.query_strings)
|
||||
}
|
||||
|
||||
fn build_request(&self, from: u64, size: u64, query_strings: &HashMap<String, String>) -> RequestBuilder {
|
||||
fn build_request(
|
||||
&self,
|
||||
from: u64,
|
||||
size: u64,
|
||||
query_strings: &HashMap<String, String>,
|
||||
) -> RequestBuilder {
|
||||
let url = HttpClient::join_url(&self.server.endpoint, "/query/_search");
|
||||
let mut request_builder = self.client.request(Method::GET, url);
|
||||
|
||||
@@ -91,7 +98,8 @@ impl CocoSearchSource {
|
||||
}
|
||||
}
|
||||
|
||||
request_builder.query(&[("from", &from.to_string()), ("size", &size.to_string())])
|
||||
request_builder
|
||||
.query(&[("from", &from.to_string()), ("size", &size.to_string())])
|
||||
.query(query_strings)
|
||||
}
|
||||
}
|
||||
@@ -107,12 +115,9 @@ impl SearchSource for CocoSearchSource {
|
||||
}
|
||||
|
||||
// Directly return Result<QueryResponse, SearchError> instead of Future
|
||||
async fn search(
|
||||
&self,
|
||||
query: SearchQuery,
|
||||
) -> Result<QueryResponse, SearchError> {
|
||||
let server_id = self.server.id.clone();
|
||||
let server_name = self.server.name.clone();
|
||||
async fn search(&self, query: SearchQuery) -> Result<QueryResponse, SearchError> {
|
||||
let _server_id = self.server.id.clone();
|
||||
let _server_name = self.server.name.clone();
|
||||
let request_builder = self.build_request_from_query(&query);
|
||||
|
||||
// Send the HTTP request asynchronously
|
||||
@@ -127,10 +132,13 @@ impl SearchSource for CocoSearchSource {
|
||||
match parse_search_response(response).await {
|
||||
Ok(response) => {
|
||||
let total_hits = response.hits.total.value as usize;
|
||||
let hits: Vec<(Document, f64)> = response.hits.hits.into_iter()
|
||||
let hits: Vec<(Document, f64)> = response
|
||||
.hits
|
||||
.hits
|
||||
.into_iter()
|
||||
.map(|hit| {
|
||||
// Handling Option<f64> in hit._score by defaulting to 0.0 if None
|
||||
(hit._source, hit._score.unwrap_or(0.0)) // Use 0.0 if _score is None
|
||||
(hit._source, hit._score.unwrap_or(0.0)) // Use 0.0 if _score is None
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -160,4 +168,4 @@ impl SearchSource for CocoSearchSource {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,7 +255,7 @@ pub async fn load_or_insert_default_server<R: Runtime>(
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn list_coco_servers<R: Runtime>(
|
||||
app_handle: AppHandle<R>,
|
||||
_app_handle: AppHandle<R>,
|
||||
) -> Result<Vec<Server>, String> {
|
||||
let servers: Vec<Server> = get_all_servers();
|
||||
Ok(servers)
|
||||
@@ -443,7 +443,7 @@ pub async fn logout_coco_server<R: Runtime>(
|
||||
dbg!("Attempting to log out server by id:", &id);
|
||||
|
||||
// Check if server token exists
|
||||
if let Some(token) = get_server_token(id.as_str()) {
|
||||
if let Some(_token) = get_server_token(id.as_str()) {
|
||||
dbg!("Found server token for id:", &id);
|
||||
|
||||
// Remove the server token from cache
|
||||
|
||||
Reference in New Issue
Block a user