Files
Stirling-PDF/frontend/src-tauri/src/commands/connection.rs
2026-02-06 18:06:01 +00:00

259 lines
7.6 KiB
Rust
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use crate::state::connection_state::{
AppConnectionState,
ConnectionMode,
ServerConfig,
};
use crate::utils::{add_log, app_data_dir, system_provisioning_dir};
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
use tauri::{AppHandle, Manager, State};
use tauri_plugin_store::StoreExt;
const STORE_FILE: &str = "connection.json";
const FIRST_LAUNCH_KEY: &str = "setup_completed";
const CONNECTION_MODE_KEY: &str = "connection_mode";
const SERVER_CONFIG_KEY: &str = "server_config";
const LOCK_CONNECTION_KEY: &str = "lock_connection_mode";
const PROVISIONING_FILE_NAME: &str = "stirling-provisioning.json";
#[derive(Debug, Serialize, Deserialize)]
pub struct ConnectionConfig {
pub mode: ConnectionMode,
pub server_config: Option<ServerConfig>,
pub lock_connection_mode: bool,
}
#[tauri::command]
pub async fn get_connection_config(
app_handle: AppHandle,
state: State<'_, AppConnectionState>,
) -> Result<ConnectionConfig, String> {
// Try to load from store
let store = app_handle
.store(STORE_FILE)
.map_err(|e| format!("Failed to access store: {}", e))?;
let mode = store
.get(CONNECTION_MODE_KEY)
.and_then(|v| serde_json::from_value(v.clone()).ok())
.unwrap_or(ConnectionMode::SaaS);
let server_config: Option<ServerConfig> = store
.get(SERVER_CONFIG_KEY)
.and_then(|v| serde_json::from_value(v.clone()).ok());
let lock_connection_mode = store
.get(LOCK_CONNECTION_KEY)
.and_then(|v| v.as_bool())
.unwrap_or(false);
// Update in-memory state
if let Ok(mut conn_state) = state.0.lock() {
conn_state.mode = mode.clone();
conn_state.server_config = server_config.clone();
conn_state.lock_connection_mode = lock_connection_mode;
}
Ok(ConnectionConfig {
mode,
server_config,
lock_connection_mode,
})
}
#[tauri::command]
pub async fn set_connection_mode(
app_handle: AppHandle,
state: State<'_, AppConnectionState>,
mode: ConnectionMode,
server_config: Option<ServerConfig>,
lock_connection_mode: Option<bool>,
) -> Result<(), String> {
log::info!("Setting connection mode: {:?}", mode);
// Update in-memory state
if let Ok(mut conn_state) = state.0.lock() {
conn_state.mode = mode.clone();
conn_state.server_config = server_config.clone();
if let Some(lock) = lock_connection_mode {
conn_state.lock_connection_mode = lock;
}
}
// Save to store
let store = app_handle
.store(STORE_FILE)
.map_err(|e| format!("Failed to access store: {}", e))?;
store.set(
CONNECTION_MODE_KEY,
serde_json::to_value(&mode).map_err(|e| format!("Failed to serialize mode: {}", e))?,
);
if let Some(config) = &server_config {
store.set(
SERVER_CONFIG_KEY,
serde_json::to_value(config)
.map_err(|e| format!("Failed to serialize config: {}", e))?,
);
} else {
store.delete(SERVER_CONFIG_KEY);
}
if let Some(lock) = lock_connection_mode {
store.set(
LOCK_CONNECTION_KEY,
serde_json::to_value(lock)
.map_err(|e| format!("Failed to serialize lock flag: {}", e))?,
);
}
// Mark setup as completed
store.set(FIRST_LAUNCH_KEY, serde_json::json!(true));
store
.save()
.map_err(|e| format!("Failed to save store: {}", e))?;
log::info!("Connection mode saved successfully");
Ok(())
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct ProvisioningConfig {
server_url: Option<String>,
lock_connection_mode: Option<bool>,
}
fn provisioning_file_paths() -> Vec<PathBuf> {
let mut paths = Vec::new();
paths.push(app_data_dir().join(PROVISIONING_FILE_NAME));
if let Some(system_dir) = system_provisioning_dir() {
paths.push(system_dir.join(PROVISIONING_FILE_NAME));
}
paths
}
pub fn apply_provisioning_if_present(app_handle: &AppHandle) -> Result<(), String> {
let provisioning_paths = provisioning_file_paths();
let provisioning_path = provisioning_paths
.into_iter()
.find(|path| path.exists());
let provisioning_path = match provisioning_path {
Some(path) => path,
None => return Ok(()),
};
add_log(format!(
"🧩 Provisioning file detected: {}",
provisioning_path.display()
));
let raw = fs::read_to_string(&provisioning_path)
.map_err(|e| format!("Failed to read provisioning file: {}", e))?;
let parsed: ProvisioningConfig = serde_json::from_str(&raw)
.map_err(|e| format!("Failed to parse provisioning file: {}", e))?;
let server_url = parsed
.server_url
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty());
if server_url.is_none() {
add_log("⚠️ Provisioning file missing serverUrl; skipping apply".to_string());
return Ok(());
}
let lock_flag = parsed.lock_connection_mode.unwrap_or(false);
let store = app_handle
.store(STORE_FILE)
.map_err(|e| format!("Failed to access store: {}", e))?;
store.set(
CONNECTION_MODE_KEY,
serde_json::to_value(&ConnectionMode::SelfHosted)
.map_err(|e| format!("Failed to serialize mode: {}", e))?,
);
let server_config = ServerConfig {
url: server_url.clone().unwrap(),
};
store.set(
SERVER_CONFIG_KEY,
serde_json::to_value(&server_config)
.map_err(|e| format!("Failed to serialize config: {}", e))?,
);
store.set(
LOCK_CONNECTION_KEY,
serde_json::to_value(lock_flag)
.map_err(|e| format!("Failed to serialize lock flag: {}", e))?,
);
store.set(FIRST_LAUNCH_KEY, serde_json::json!(true));
store
.save()
.map_err(|e| format!("Failed to save store: {}", e))?;
if let Ok(mut conn_state) = app_handle.state::<AppConnectionState>().0.lock() {
conn_state.mode = ConnectionMode::SelfHosted;
conn_state.server_config = Some(server_config);
conn_state.lock_connection_mode = lock_flag;
}
let user_app_data = app_data_dir();
if provisioning_path.starts_with(&user_app_data) {
match fs::remove_file(&provisioning_path) {
Ok(_) => add_log("✅ Provisioning file applied and removed".to_string()),
Err(err) => add_log(format!(
"⚠️ Provisioning applied but failed to remove file: {}",
err
)),
}
} else {
add_log(" Provisioning applied from system location; leaving file in place".to_string());
}
Ok(())
}
#[tauri::command]
pub async fn is_first_launch(app_handle: AppHandle) -> Result<bool, String> {
let store = app_handle
.store(STORE_FILE)
.map_err(|e| format!("Failed to access store: {}", e))?;
let setup_completed = store
.get(FIRST_LAUNCH_KEY)
.and_then(|v| v.as_bool())
.unwrap_or(false);
Ok(!setup_completed)
}
#[tauri::command]
pub async fn reset_setup_completion(app_handle: AppHandle) -> Result<(), String> {
log::info!("Resetting setup completion flag");
let store = app_handle
.store(STORE_FILE)
.map_err(|e| format!("Failed to access store: {}", e))?;
// Reset setup completion flag to force SetupWizard on next launch
store.set(FIRST_LAUNCH_KEY, serde_json::json!(false));
store
.save()
.map_err(|e| format!("Failed to save store: {}", e))?;
log::info!("Setup completion flag reset successfully");
Ok(())
}