From 5e77517a7f716f8072cfe516f0b642333f2ddea2 Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 26 Mar 2025 15:09:13 +0700 Subject: [PATCH] feat: extensions versioning --- src-tauri/Cargo.toml | 1 + src-tauri/capabilities/default.json | 22 +++++++++--- src-tauri/src/core/cmd.rs | 11 ++++-- src-tauri/src/core/fs.rs | 5 +-- src-tauri/src/core/mod.rs | 2 +- src-tauri/src/core/setup.rs | 56 +++++++++++++++++++++++------ src-tauri/src/lib.rs | 5 +-- src-tauri/tauri.conf.json | 2 +- web/services/extensionService.ts | 6 ++++ web/services/tauriService.ts | 10 +++--- 10 files changed, 90 insertions(+), 30 deletions(-) diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 2e271fb25..2772625b7 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -28,3 +28,4 @@ flate2 = "1.0" tar = "0.4" rand = "0.8" tauri-plugin-http = { version = "2", features = ["unsafe-headers"] } +tauri-plugin-store = "2" diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 0cb9b3e61..ca95964ee 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -2,9 +2,13 @@ "$schema": "../gen/schemas/desktop-schema.json", "identifier": "default", "description": "enables the default permissions", - "windows": ["main"], + "windows": [ + "main" + ], "remote": { - "urls": ["http://*"] + "urls": [ + "http://*" + ] }, "permissions": [ "core:default", @@ -12,7 +16,14 @@ "shell:allow-open", { "identifier": "http:default", - "allow": [{ "url": "https://*:*" }, { "url": "http://*:*" }], + "allow": [ + { + "url": "https://*:*" + }, + { + "url": "http://*:*" + } + ], "deny": [] }, { @@ -41,6 +52,7 @@ "sidecar": true } ] - } + }, + "store:default" ] -} +} \ No newline at end of file diff --git a/src-tauri/src/core/cmd.rs b/src-tauri/src/core/cmd.rs index ff5a319bd..f55c0308d 100644 --- a/src-tauri/src/core/cmd.rs +++ b/src-tauri/src/core/cmd.rs @@ -5,6 +5,8 @@ use serde::{Deserialize, Serialize}; use std::path::PathBuf; use tauri::Manager; +use super::setup; + const CONFIGURATION_FILE_NAME: &str = "settings.json"; #[derive(Serialize, Deserialize, Debug, Clone)] @@ -192,6 +194,13 @@ pub fn open_file_explorer(path: String) { } } +#[tauri::command] +pub fn install_extensions(app: AppHandle) { + if let Err(err) = setup::install_extensions(app, true) { + eprintln!("Failed to install extensions: {}", err); + } +} + #[tauri::command] pub fn get_active_extensions(app: AppHandle) -> Vec { let mut path = get_jan_extensions_path(app); @@ -225,5 +234,3 @@ pub fn get_active_extensions(app: AppHandle) -> Vec { pub fn get_user_home_path(app: AppHandle) -> String { return get_app_configurations(app.clone()).data_folder; } - - diff --git a/src-tauri/src/core/fs.rs b/src-tauri/src/core/fs.rs index c8aed9846..dc046689a 100644 --- a/src-tauri/src/core/fs.rs +++ b/src-tauri/src/core/fs.rs @@ -42,10 +42,7 @@ pub fn exists_sync(app_handle: tauri::AppHandle, args: Vec) -> Result, -) -> Result { +pub fn read_file_sync(app_handle: tauri::AppHandle, args: Vec) -> Result { if args.is_empty() || args[0].is_empty() { return Err("read_file_sync error: Invalid argument".to_string()); } diff --git a/src-tauri/src/core/mod.rs b/src-tauri/src/core/mod.rs index f9510420d..7ad92a077 100644 --- a/src-tauri/src/core/mod.rs +++ b/src-tauri/src/core/mod.rs @@ -1,3 +1,3 @@ pub mod cmd; pub mod fs; -pub mod setup; \ No newline at end of file +pub mod setup; diff --git a/src-tauri/src/core/setup.rs b/src-tauri/src/core/setup.rs index c28c3bab9..a03477876 100644 --- a/src-tauri/src/core/setup.rs +++ b/src-tauri/src/core/setup.rs @@ -1,18 +1,54 @@ use flate2::read::GzDecoder; +use std::{ + fs::{self, File}, + io::Read, + path::PathBuf, + sync::{Arc, Mutex}, +}; +use tar::Archive; use tauri::{App, Listener, Manager}; use tauri_plugin_shell::process::CommandEvent; use tauri_plugin_shell::ShellExt; -use std::{fs::{self, File}, io::Read, path::PathBuf, sync::{Arc, Mutex}}; -use tar::Archive; +use tauri_plugin_store::StoreExt; use crate::AppState; use super::cmd::get_jan_extensions_path; -pub fn install_extensions(app: tauri::AppHandle) -> Result<(), String> { +pub fn install_extensions(app: tauri::AppHandle, force: bool) -> Result<(), String> { + let store = app.store("store.json").expect("Store not initialized"); + let stored_version = if let Some(version) = store.get("version") { + if let Some(version_str) = version.as_str() { + version_str.to_string() + } else { + "".to_string() + } + } else { + "".to_string() + }; + + let app_version = app + .config() + .version + .clone() + .unwrap_or_else(|| "".to_string()); + + if !force { + if stored_version == app_version { + return Ok(()); + } + } let extensions_path = get_jan_extensions_path(app.clone()); let pre_install_path = PathBuf::from("./../pre-install"); + // Attempt to remove extensions folder + if extensions_path.exists() { + fs::remove_dir_all(&extensions_path).unwrap_or_else(|_| { + println!("Failed to remove existing extensions folder, it may not exist."); + }); + } + + // Attempt to create it again if !extensions_path.exists() { fs::create_dir_all(&extensions_path).map_err(|e| e.to_string())?; } @@ -106,6 +142,10 @@ pub fn install_extensions(app: tauri::AppHandle) -> Result<(), String> { ) .map_err(|e| e.to_string())?; + // Store the new app version + store.set("version", serde_json::json!(app_version)); + store.save().expect("Failed to save store"); + Ok(()) } @@ -210,7 +250,6 @@ pub fn setup_sidecar(app: &App) -> Result<(), String> { Ok(()) } - fn copy_dir_all(src: PathBuf, dst: PathBuf) -> Result<(), String> { fs::create_dir_all(&dst).map_err(|e| e.to_string())?; println!("Copying from {:?} to {:?}", src, dst); @@ -226,15 +265,10 @@ fn copy_dir_all(src: PathBuf, dst: PathBuf) -> Result<(), String> { Ok(()) } -pub fn setup_engine_binaries(app: &App) -> Result<(), String> { +pub fn setup_engine_binaries(app: &App) -> Result<(), String> { // Copy engine binaries to app_data let app_data_dir = app.handle().path().app_data_dir().unwrap(); - let binaries_dir = app - .handle() - .path() - .resource_dir() - .unwrap() - .join("binaries"); + let binaries_dir = app.handle().path().resource_dir().unwrap().join("binaries"); let themes_dir = app .handle() .path() diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index ce16e557e..46aaeb298 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -3,7 +3,6 @@ use core::setup::{self, setup_engine_binaries, setup_sidecar}; use rand::{distributions::Alphanumeric, Rng}; use tauri::{command, Emitter, State}; - struct AppState { app_token: Option, } @@ -24,6 +23,7 @@ fn generate_app_token() -> String { #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() + .plugin(tauri_plugin_store::Builder::new().build()) .plugin(tauri_plugin_http::init()) .plugin(tauri_plugin_shell::init()) .invoke_handler(tauri::generate_handler![ @@ -44,6 +44,7 @@ pub fn run() { core::cmd::relaunch, core::cmd::open_app_directory, core::cmd::open_file_explorer, + core::cmd::install_extensions, app_token, ]) .manage(AppState { @@ -59,7 +60,7 @@ pub fn run() { } // Install extensions - if let Err(e) = setup::install_extensions(app.handle().clone()) { + if let Err(e) = setup::install_extensions(app.handle().clone(), false) { eprintln!("Failed to install extensions: {}", e); } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index eabdb3954..d829d41fb 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,6 +1,6 @@ { "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", - "productName": "jan-app", + "productName": "Jan", "version": "0.1.0", "identifier": "jan.ai", "build": { diff --git a/web/services/extensionService.ts b/web/services/extensionService.ts index 32fb49c22..fa23120f3 100644 --- a/web/services/extensionService.ts +++ b/web/services/extensionService.ts @@ -16,6 +16,12 @@ export const setupBaseExtensions = async () => { if (typeof window === 'undefined') { return } + + if (IS_TAURI) { + await window.core?.api.installExtensions() + window.location.reload() + } + return const baseExtensions = await window.core?.api.baseExtensions() if ( diff --git a/web/services/tauriService.ts b/web/services/tauriService.ts index ca56e57fb..efb201ccd 100644 --- a/web/services/tauriService.ts +++ b/web/services/tauriService.ts @@ -2,10 +2,12 @@ import { CoreRoutes, APIRoutes } from '@janhq/core' import { invoke } from '@tauri-apps/api/core' // Define API routes based on different route types -export const Routes = [...CoreRoutes, ...APIRoutes].map((r) => ({ - path: `app`, - route: r, -})) +export const Routes = [...CoreRoutes, ...APIRoutes, 'installExtensions'].map( + (r) => ({ + path: `app`, + route: r, + }) +) // Function to open an external URL in a new browser window export function openExternalUrl(url: string) {