feat: extensions versioning

This commit is contained in:
Louis 2025-03-26 15:09:13 +07:00
parent cb6261e4d1
commit fc521ecda9
No known key found for this signature in database
GPG Key ID: 44FA9F4D33C37DE2
10 changed files with 90 additions and 30 deletions

View File

@ -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"

View File

@ -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"
]
}
}

View File

@ -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<serde_json::Value> {
let mut path = get_jan_extensions_path(app);
@ -225,5 +234,3 @@ pub fn get_active_extensions(app: AppHandle) -> Vec<serde_json::Value> {
pub fn get_user_home_path(app: AppHandle) -> String {
return get_app_configurations(app.clone()).data_folder;
}

View File

@ -42,10 +42,7 @@ pub fn exists_sync(app_handle: tauri::AppHandle, args: Vec<String>) -> Result<bo
}
#[tauri::command]
pub fn read_file_sync(
app_handle: tauri::AppHandle,
args: Vec<String>,
) -> Result<String, String> {
pub fn read_file_sync(app_handle: tauri::AppHandle, args: Vec<String>) -> Result<String, String> {
if args.is_empty() || args[0].is_empty() {
return Err("read_file_sync error: Invalid argument".to_string());
}

View File

@ -1,3 +1,3 @@
pub mod cmd;
pub mod fs;
pub mod setup;
pub mod setup;

View File

@ -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()

View File

@ -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<String>,
}
@ -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);
}

View File

@ -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": {

View File

@ -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 (

View File

@ -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) {