forked from boos/BoosAPI
e0a5f57793
Vendor the rebranded BoosAPI Desktop client (Tauri + Vue) into metapi as a monorepo subdirectory under desktop/. Source only — node_modules, dist, Cargo target and the upstream .git history are excluded. desktop/ keeps its own independent Cargo workspace (desktop/src-tauri with crates/*); it is not part of the backend boosapi crate, so a `cargo check` at the metapi root still compiles only the backend. Build the desktop app from desktop/src-tauri per desktop/README.md. Note: app icons are still placeholder assets pending the final BoosAPI logo. The updater signing keys live outside the repo and are not included.
293 lines
11 KiB
Rust
293 lines
11 KiB
Rust
pub use sea_orm_migration::prelude::*;
|
|
|
|
mod m20240101_000001_init;
|
|
mod m20240102_000001_add_token_fields;
|
|
mod m20240103_000001_add_mcp_timeout_headers;
|
|
mod m20240104_000001_add_mcp_icon;
|
|
mod m20250105_000001_context_compression;
|
|
mod m20250106_000001_add_message_status;
|
|
mod m20250107_000001_add_provider_custom_headers;
|
|
mod m20250108_000001_add_provider_icon;
|
|
mod m20250109_000001_add_conversation_categories;
|
|
mod m20250110_000001_add_memory_item_index_status;
|
|
mod m20250111_000001_add_memory_item_index_error;
|
|
mod m20250113_000001_add_memory_namespace_settings;
|
|
mod m20250114_000001_add_memory_namespace_icon_sort;
|
|
mod m20250115_000001_add_knowledge_base_icon_sort;
|
|
mod m20250116_000001_add_knowledge_base_retrieval_settings;
|
|
mod m20250117_000001_add_knowledge_base_chunking_config;
|
|
mod m20250118_000001_add_knowledge_document_type;
|
|
mod m20250119_000001_add_knowledge_document_index_error;
|
|
mod m20250120_000001_add_message_timing;
|
|
mod m20250121_000001_add_conversation_parent_id;
|
|
mod m20250122_000001_merge_thinking_to_content;
|
|
mod m20250123_000001_add_category_system_prompt;
|
|
mod m20250717_000001_add_agent_support;
|
|
mod m20250718_000001_add_sdk_context_backup;
|
|
mod m20250719_000001_add_skill_states;
|
|
mod m20250720_000001_add_provider_builtin_id;
|
|
mod m20260417_000001_add_category_default_templates;
|
|
mod m20260428_000001_add_drawing_history;
|
|
mod m20260430_000001_add_conversation_thinking_level;
|
|
mod m20260501_000001_add_knowledge_base_rerank_settings;
|
|
mod m20260504_000001_split_openai_compatible_provider_types;
|
|
mod m20260515_000001_add_knowledge_base_index_schedule;
|
|
mod m20260518_000001_add_builtin_model_deletions;
|
|
|
|
pub struct Migrator;
|
|
|
|
#[async_trait::async_trait]
|
|
impl MigratorTrait for Migrator {
|
|
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
|
|
vec![
|
|
Box::new(m20240101_000001_init::Migration),
|
|
Box::new(m20240102_000001_add_token_fields::Migration),
|
|
Box::new(m20240103_000001_add_mcp_timeout_headers::Migration),
|
|
Box::new(m20240104_000001_add_mcp_icon::Migration),
|
|
Box::new(m20250105_000001_context_compression::Migration),
|
|
Box::new(m20250106_000001_add_message_status::Migration),
|
|
Box::new(m20250107_000001_add_provider_custom_headers::Migration),
|
|
Box::new(m20250108_000001_add_provider_icon::Migration),
|
|
Box::new(m20250109_000001_add_conversation_categories::Migration),
|
|
Box::new(m20250110_000001_add_memory_item_index_status::Migration),
|
|
Box::new(m20250111_000001_add_memory_item_index_error::Migration),
|
|
Box::new(m20250113_000001_add_memory_namespace_settings::Migration),
|
|
Box::new(m20250114_000001_add_memory_namespace_icon_sort::Migration),
|
|
Box::new(m20250115_000001_add_knowledge_base_icon_sort::Migration),
|
|
Box::new(m20250116_000001_add_knowledge_base_retrieval_settings::Migration),
|
|
Box::new(m20250117_000001_add_knowledge_base_chunking_config::Migration),
|
|
Box::new(m20250118_000001_add_knowledge_document_type::Migration),
|
|
Box::new(m20250119_000001_add_knowledge_document_index_error::Migration),
|
|
Box::new(m20250120_000001_add_message_timing::Migration),
|
|
Box::new(m20250121_000001_add_conversation_parent_id::Migration),
|
|
Box::new(m20250122_000001_merge_thinking_to_content::Migration),
|
|
Box::new(m20250123_000001_add_category_system_prompt::Migration),
|
|
Box::new(m20250717_000001_add_agent_support::Migration),
|
|
Box::new(m20250718_000001_add_sdk_context_backup::Migration),
|
|
Box::new(m20250719_000001_add_skill_states::Migration),
|
|
Box::new(m20250720_000001_add_provider_builtin_id::Migration),
|
|
Box::new(m20260417_000001_add_category_default_templates::Migration),
|
|
Box::new(m20260428_000001_add_drawing_history::Migration),
|
|
Box::new(m20260430_000001_add_conversation_thinking_level::Migration),
|
|
Box::new(m20260501_000001_add_knowledge_base_rerank_settings::Migration),
|
|
Box::new(m20260504_000001_split_openai_compatible_provider_types::Migration),
|
|
Box::new(m20260515_000001_add_knowledge_base_index_schedule::Migration),
|
|
Box::new(m20260518_000001_add_builtin_model_deletions::Migration),
|
|
]
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use sea_orm_migration::sea_orm::{
|
|
ConnectOptions, ConnectionTrait, Database, DatabaseConnection, DbBackend, Statement,
|
|
};
|
|
|
|
async fn sqlite_test_db() -> DatabaseConnection {
|
|
let mut opts = ConnectOptions::new("sqlite::memory:");
|
|
opts.max_connections(1)
|
|
.min_connections(1)
|
|
.sqlx_logging(false);
|
|
Database::connect(opts)
|
|
.await
|
|
.expect("connect sqlite test db")
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn migrator_up_adds_category_default_template_columns_on_sqlite() {
|
|
let db = sqlite_test_db().await;
|
|
|
|
Migrator::up(&db, None)
|
|
.await
|
|
.expect("run sqlite migrations");
|
|
|
|
let manager = SchemaManager::new(&db);
|
|
for column in [
|
|
"default_provider_id",
|
|
"default_model_id",
|
|
"default_temperature",
|
|
"default_max_tokens",
|
|
"default_top_p",
|
|
"default_frequency_penalty",
|
|
] {
|
|
assert!(
|
|
manager
|
|
.has_column("conversation_categories", column)
|
|
.await
|
|
.expect("check migrated column"),
|
|
"missing column {column}"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn migrator_up_adds_drawing_history_tables_on_sqlite() {
|
|
let db = sqlite_test_db().await;
|
|
|
|
Migrator::up(&db, None)
|
|
.await
|
|
.expect("run sqlite migrations");
|
|
|
|
let manager = SchemaManager::new(&db);
|
|
for table in ["drawing_generations", "drawing_images"] {
|
|
assert!(
|
|
manager.has_table(table).await.expect("check drawing table"),
|
|
"missing table {table}"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn migrator_up_adds_builtin_model_deletions_table_on_sqlite() {
|
|
let db = sqlite_test_db().await;
|
|
|
|
Migrator::up(&db, None)
|
|
.await
|
|
.expect("run sqlite migrations");
|
|
|
|
let manager = SchemaManager::new(&db);
|
|
assert!(
|
|
manager
|
|
.has_table("builtin_model_deletions")
|
|
.await
|
|
.expect("check builtin model deletions table"),
|
|
"missing builtin_model_deletions table"
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn migrator_up_adds_conversation_thinking_level_on_sqlite() {
|
|
let db = sqlite_test_db().await;
|
|
|
|
Migrator::up(&db, None)
|
|
.await
|
|
.expect("run sqlite migrations");
|
|
|
|
let manager = SchemaManager::new(&db);
|
|
assert!(
|
|
manager
|
|
.has_column("conversations", "thinking_level")
|
|
.await
|
|
.expect("check thinking level column"),
|
|
"missing conversations.thinking_level"
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn migrator_up_adds_knowledge_base_rerank_settings_on_sqlite() {
|
|
let db = sqlite_test_db().await;
|
|
|
|
Migrator::up(&db, None)
|
|
.await
|
|
.expect("run sqlite migrations");
|
|
|
|
let manager = SchemaManager::new(&db);
|
|
for column in ["rerank_provider", "rerank_candidate_k"] {
|
|
assert!(
|
|
manager
|
|
.has_column("knowledge_bases", column)
|
|
.await
|
|
.expect("check knowledge base rerank column"),
|
|
"missing knowledge_bases.{column}"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn migrator_up_adds_knowledge_base_index_schedule_on_sqlite() {
|
|
let db = sqlite_test_db().await;
|
|
|
|
Migrator::up(&db, None)
|
|
.await
|
|
.expect("run sqlite migrations");
|
|
|
|
let manager = SchemaManager::new(&db);
|
|
for column in ["index_concurrency", "index_interval_ms"] {
|
|
assert!(
|
|
manager
|
|
.has_column("knowledge_bases", column)
|
|
.await
|
|
.expect("check knowledge base index schedule column"),
|
|
"missing knowledge_bases.{column}"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn split_openai_compatible_provider_types_migration_updates_builtin_rows() {
|
|
let db = sqlite_test_db().await;
|
|
let manager = SchemaManager::new(&db);
|
|
|
|
m20240101_000001_init::Migration
|
|
.up(&manager)
|
|
.await
|
|
.expect("run init migration");
|
|
m20250720_000001_add_provider_builtin_id::Migration
|
|
.up(&manager)
|
|
.await
|
|
.expect("add builtin_id column");
|
|
|
|
db.execute_unprepared(
|
|
r#"INSERT INTO providers
|
|
(id, name, provider_type, api_host, enabled, sort_order, created_at, updated_at, builtin_id)
|
|
VALUES
|
|
('provider-deepseek', 'DeepSeek', 'openai', 'https://api.deepseek.com', 1, 0, 1, 1, 'deepseek'),
|
|
('provider-xai', 'xAI', 'openai', 'https://api.x.ai', 1, 0, 1, 1, 'xai'),
|
|
('provider-glm', 'GLM', 'openai', 'https://open.bigmodel.cn/api/paas', 1, 0, 1, 1, 'glm'),
|
|
('provider-siliconflow', 'SiliconFlow', 'openai', 'https://api.siliconflow.cn', 1, 0, 1, 1, 'siliconflow'),
|
|
('provider-custom', 'Custom', 'openai', 'https://api.example.com', 1, 0, 1, 1, NULL)"#,
|
|
)
|
|
.await
|
|
.expect("insert provider rows");
|
|
|
|
m20260504_000001_split_openai_compatible_provider_types::Migration
|
|
.up(&manager)
|
|
.await
|
|
.expect("split provider types");
|
|
|
|
let rows = db
|
|
.query_all(Statement::from_string(
|
|
DbBackend::Sqlite,
|
|
"SELECT id, provider_type FROM providers ORDER BY id".to_string(),
|
|
))
|
|
.await
|
|
.expect("query providers");
|
|
let values: Vec<(String, String)> = rows
|
|
.into_iter()
|
|
.map(|row| {
|
|
(
|
|
row.try_get("", "id").unwrap(),
|
|
row.try_get("", "provider_type").unwrap(),
|
|
)
|
|
})
|
|
.collect();
|
|
|
|
assert_eq!(
|
|
values,
|
|
vec![
|
|
("provider-custom".to_string(), "openai".to_string()),
|
|
("provider-deepseek".to_string(), "deepseek".to_string()),
|
|
("provider-glm".to_string(), "glm".to_string()),
|
|
(
|
|
"provider-siliconflow".to_string(),
|
|
"siliconflow".to_string()
|
|
),
|
|
("provider-xai".to_string(), "xai".to_string()),
|
|
]
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn migrator_refresh_round_trips_latest_sqlite_schema() {
|
|
let db = sqlite_test_db().await;
|
|
|
|
Migrator::up(&db, None)
|
|
.await
|
|
.expect("run sqlite migrations");
|
|
Migrator::refresh(&db)
|
|
.await
|
|
.expect("refresh sqlite migrations");
|
|
}
|
|
}
|