130 lines
4.6 KiB
Rust
130 lines
4.6 KiB
Rust
use crate::error::Result;
|
|
use crate::settings;
|
|
use rusqlite::Connection;
|
|
use std::path::{Path, PathBuf};
|
|
use std::sync::Mutex;
|
|
|
|
const APP_DIR_NAME: &str = "XianrenStudio";
|
|
|
|
/// 应用核心状态:数据库连接与运行时目录。
|
|
pub struct CoreApp {
|
|
pub db: Mutex<Connection>,
|
|
pub data_dir: PathBuf,
|
|
pub models_dir: PathBuf,
|
|
pub engines_dir: PathBuf,
|
|
pub logs_dir: PathBuf,
|
|
}
|
|
|
|
impl CoreApp {
|
|
pub fn init(data_dir: Option<PathBuf>) -> Result<Self> {
|
|
let data_dir = match data_dir {
|
|
Some(d) => d,
|
|
None => dirs::data_dir()
|
|
.unwrap_or_else(|| PathBuf::from("."))
|
|
.join(APP_DIR_NAME),
|
|
};
|
|
|
|
let models_dir = data_dir.join("models");
|
|
let engines_dir = data_dir.join("engines");
|
|
let logs_dir = data_dir.join("logs");
|
|
let recommend_dir = data_dir.join("recommendations");
|
|
|
|
std::fs::create_dir_all(&data_dir)?;
|
|
std::fs::create_dir_all(&models_dir)?;
|
|
std::fs::create_dir_all(&engines_dir)?;
|
|
std::fs::create_dir_all(&logs_dir)?;
|
|
std::fs::create_dir_all(&recommend_dir)?;
|
|
|
|
let db_path = data_dir.join("xianren.db");
|
|
let conn = open_db(&db_path)?;
|
|
|
|
let app = Self {
|
|
db: Mutex::new(conn),
|
|
data_dir,
|
|
models_dir,
|
|
engines_dir,
|
|
logs_dir,
|
|
};
|
|
|
|
app.seed_default_settings();
|
|
Ok(app)
|
|
}
|
|
|
|
fn seed_default_settings(&self) {
|
|
let db = self.db.lock().unwrap();
|
|
let defaults = [
|
|
("model_dir", self.models_dir.to_string_lossy().to_string()),
|
|
(
|
|
"engine_bin",
|
|
self.engines_dir
|
|
.join("cpu")
|
|
.join("llama-server.exe")
|
|
.to_string_lossy()
|
|
.to_string(),
|
|
),
|
|
("backend", "auto".to_string()),
|
|
("hf_endpoint", "https://hf-mirror.com".to_string()),
|
|
("api_port", "1234".to_string()),
|
|
("api_key", String::new()),
|
|
("api_enabled", "false".to_string()),
|
|
("upload_max_mb", "10".to_string()),
|
|
("auto_title", "true".to_string()),
|
|
("suggest_enabled", "true".to_string()),
|
|
("suggest_count", "3".to_string()),
|
|
(
|
|
"tavily_api_key",
|
|
"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".to_string(),
|
|
),
|
|
("tool_web_search_enabled", "true".to_string()),
|
|
("auto_load_models", "[]".to_string()),
|
|
(
|
|
"recommend_dir",
|
|
self.data_dir
|
|
.join("recommendations")
|
|
.to_string_lossy()
|
|
.to_string(),
|
|
),
|
|
];
|
|
for (key, value) in defaults {
|
|
// 仅补默认值,不覆盖用户已保存的设置
|
|
let _ = settings::insert_default(&db, key, &value);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn open_db(path: &Path) -> Result<Connection> {
|
|
let conn = Connection::open(path)?;
|
|
conn.pragma_update(None, "journal_mode", "WAL")?;
|
|
conn.pragma_update(None, "foreign_keys", "ON")?;
|
|
conn.execute_batch(include_str!("schema.sql"))?;
|
|
// 旧库迁移:为已存在的 models 表补充新增列
|
|
ensure_column(&conn, "models", "kind", "TEXT NOT NULL DEFAULT 'local'")?;
|
|
ensure_column(&conn, "models", "base_url", "TEXT")?;
|
|
ensure_column(&conn, "models", "api_key", "TEXT")?;
|
|
ensure_column(&conn, "models", "api_model", "TEXT")?;
|
|
ensure_column(&conn, "models", "enabled", "INTEGER NOT NULL DEFAULT 0")?;
|
|
ensure_column(&conn, "messages", "elapsed_ms", "INTEGER")?;
|
|
ensure_column(&conn, "messages", "first_token_ms", "INTEGER")?;
|
|
ensure_column(&conn, "messages", "images_json", "TEXT NOT NULL DEFAULT '[]'")?;
|
|
ensure_column(&conn, "messages", "model_id", "TEXT")?;
|
|
ensure_column(&conn, "conversations", "pinned", "INTEGER NOT NULL DEFAULT 0")?;
|
|
ensure_column(&conn, "conversations", "favorite", "INTEGER NOT NULL DEFAULT 0")?;
|
|
ensure_column(&conn, "conversations", "tools_json", "TEXT NOT NULL DEFAULT '[]'")?;
|
|
ensure_column(&conn, "conversations", "agent_id", "TEXT")?;
|
|
Ok(conn)
|
|
}
|
|
|
|
fn ensure_column(conn: &Connection, table: &str, column: &str, decl: &str) -> rusqlite::Result<()> {
|
|
let mut stmt = conn.prepare(&format!("PRAGMA table_info({table})"))?;
|
|
let columns: Vec<String> = stmt
|
|
.query_map([], |row| row.get::<_, String>(1))?
|
|
.collect::<std::result::Result<_, _>>()?;
|
|
if !columns.iter().any(|c| c == column) {
|
|
conn.execute(
|
|
&format!("ALTER TABLE {table} ADD COLUMN {column} {decl}"),
|
|
[],
|
|
)?;
|
|
}
|
|
Ok(())
|
|
}
|