mirror of
https://github.com/SECTL/SecScore.git
synced 2026-07-21 11:49:02 +08:00
修复快速设置学生丢失并新增重置入口
This commit is contained in:
+161
-40
@@ -1,62 +1,183 @@
|
|||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tauri::State;
|
use tauri::{AppHandle, Emitter, State};
|
||||||
|
|
||||||
|
use crate::db::connection::{create_sqlite_connection, DatabaseType};
|
||||||
|
use crate::db::migration::Migration;
|
||||||
|
use crate::services::settings::{SettingsKey, SettingsValue, SettingsService};
|
||||||
|
use crate::services::{AutoScoreService, ThemeService};
|
||||||
use crate::services::permission::PermissionLevel;
|
use crate::services::permission::PermissionLevel;
|
||||||
use crate::state::AppState;
|
use crate::state::AppState;
|
||||||
|
|
||||||
use super::response::IpcResponse;
|
use super::response::IpcResponse;
|
||||||
|
|
||||||
fn check_admin_permission(state: &Arc<RwLock<AppState>>) -> Result<(), String> {
|
fn check_admin_permission(state: &Arc<RwLock<AppState>>) -> Result<(), String> {
|
||||||
let state_guard = state.read();
|
let state_guard = state.read();
|
||||||
let mut permissions = state_guard.permissions.write();
|
let mut permissions = state_guard.permissions.write();
|
||||||
let sender_id = 0;
|
let sender_id = 0;
|
||||||
if !permissions.require_permission(sender_id, PermissionLevel::Admin) {
|
if !permissions.require_permission(sender_id, PermissionLevel::Admin) {
|
||||||
return Err("Permission denied: Admin required".to_string());
|
return Err("Permission denied: Admin required".to_string());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
fn sqlite_db_path(app_handle: &AppHandle) -> Result<String, String> {
|
||||||
pub async fn data_export_json(
|
if cfg!(all(debug_assertions, desktop)) {
|
||||||
state: State<'_, Arc<RwLock<AppState>>>,
|
return Ok("data.sql".to_string());
|
||||||
) -> Result<IpcResponse<String>, String> {
|
}
|
||||||
check_admin_permission(&state)?;
|
|
||||||
|
|
||||||
let state_guard = state.read();
|
let app_data_dir = app_handle
|
||||||
let settings = state_guard.settings.read();
|
.path()
|
||||||
let settings_value = settings.get_all();
|
.app_data_dir()
|
||||||
let settings_json = serde_json::to_value(settings_value)
|
.map_err(|e| format!("Failed to get app data directory: {}", e))?;
|
||||||
.map_err(|e| format!("Failed to serialize settings: {}", e))?;
|
let data_dir = app_data_dir.join("data");
|
||||||
|
std::fs::create_dir_all(&data_dir)
|
||||||
let data_service = state_guard.data.read();
|
.map_err(|e| format!("Failed to create data directory: {}", e))?;
|
||||||
let students: Vec<crate::services::data::StudentExport> = Vec::new();
|
let db_path = data_dir.join("data.sql");
|
||||||
let events: Vec<crate::services::data::EventExport> = Vec::new();
|
db_path
|
||||||
|
.to_str()
|
||||||
let export_data = data_service.export_json(settings_json, students, events);
|
.map(|s| s.to_string())
|
||||||
|
.ok_or_else(|| "Invalid sqlite database path".to_string())
|
||||||
let json_string = serde_json::to_string_pretty(&export_data)
|
|
||||||
.map_err(|e| format!("Failed to serialize export data: {}", e))?;
|
|
||||||
|
|
||||||
Ok(IpcResponse::success(json_string))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
pub async fn data_export_json(
|
||||||
|
state: State<'_, Arc<RwLock<AppState>>>,
|
||||||
|
) -> Result<IpcResponse<String>, String> {
|
||||||
|
check_admin_permission(&state)?;
|
||||||
|
|
||||||
|
let state_guard = state.read();
|
||||||
|
let settings = state_guard.settings.read();
|
||||||
|
let settings_value = settings.get_all();
|
||||||
|
let settings_json = serde_json::to_value(settings_value)
|
||||||
|
.map_err(|e| format!("Failed to serialize settings: {}", e))?;
|
||||||
|
|
||||||
|
let data_service = state_guard.data.read();
|
||||||
|
let students: Vec<crate::services::data::StudentExport> = Vec::new();
|
||||||
|
let events: Vec<crate::services::data::EventExport> = Vec::new();
|
||||||
|
|
||||||
|
let export_data = data_service.export_json(settings_json, students, events);
|
||||||
|
|
||||||
|
let json_string = serde_json::to_string_pretty(&export_data)
|
||||||
|
.map_err(|e| format!("Failed to serialize export data: {}", e))?;
|
||||||
|
|
||||||
|
Ok(IpcResponse::success(json_string))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
pub async fn data_import_json(
|
pub async fn data_import_json(
|
||||||
json_text: String,
|
json_text: String,
|
||||||
state: State<'_, Arc<RwLock<AppState>>>,
|
state: State<'_, Arc<RwLock<AppState>>>,
|
||||||
) -> Result<IpcResponse<()>, String> {
|
) -> Result<IpcResponse<()>, String> {
|
||||||
check_admin_permission(&state)?;
|
check_admin_permission(&state)?;
|
||||||
|
|
||||||
let state_guard = state.read();
|
let state_guard = state.read();
|
||||||
let mut data_service = state_guard.data.write();
|
let mut data_service = state_guard.data.write();
|
||||||
let result = data_service.import_json(&json_text);
|
let result = data_service.import_json(&json_text);
|
||||||
|
|
||||||
if result.success {
|
if result.success {
|
||||||
Ok(IpcResponse::success_empty())
|
Ok(IpcResponse::success_empty())
|
||||||
} else {
|
} else {
|
||||||
Ok(IpcResponse::error(
|
Ok(IpcResponse::error(
|
||||||
result.message.as_deref().unwrap_or("Import failed"),
|
result.message.as_deref().unwrap_or("Import failed"),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn data_reset_all(
|
||||||
|
app_handle: AppHandle,
|
||||||
|
state: State<'_, Arc<RwLock<AppState>>>,
|
||||||
|
) -> Result<IpcResponse<()>, String> {
|
||||||
|
check_admin_permission(&state)?;
|
||||||
|
|
||||||
|
let sqlite_path = sqlite_db_path(&app_handle)?;
|
||||||
|
let sqlite_conn = create_sqlite_connection(&sqlite_path)
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to open sqlite database: {}", e))?;
|
||||||
|
|
||||||
|
let (active_conn, active_db_type) = {
|
||||||
|
let state_guard = state.read();
|
||||||
|
let active_conn = state_guard
|
||||||
|
.db
|
||||||
|
.read()
|
||||||
|
.clone()
|
||||||
|
.ok_or_else(|| "Database not connected".to_string())?;
|
||||||
|
let db_type = {
|
||||||
|
let settings = state_guard.settings.read();
|
||||||
|
match settings.get_value(SettingsKey::PgConnectionStatus) {
|
||||||
|
SettingsValue::Json(json) => match json.get("type").and_then(|value| value.as_str()) {
|
||||||
|
Some("postgresql") => DatabaseType::PostgreSQL,
|
||||||
|
_ => DatabaseType::SQLite,
|
||||||
|
},
|
||||||
|
_ => DatabaseType::SQLite,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(active_conn, db_type)
|
||||||
|
};
|
||||||
|
|
||||||
|
if active_db_type == DatabaseType::PostgreSQL {
|
||||||
|
Migration::reset_database(&active_conn, false)
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to reset PostgreSQL database: {}", e))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Migration::reset_database(&sqlite_conn, true)
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to reset SQLite database: {}", e))?;
|
||||||
|
|
||||||
|
{
|
||||||
|
let state_guard = state.read();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut db = state_guard.db.write();
|
||||||
|
*db = Some(sqlite_conn.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut settings = state_guard.settings.write();
|
||||||
|
*settings = SettingsService::new();
|
||||||
|
settings.attach_db(Some(sqlite_conn.clone()));
|
||||||
|
settings
|
||||||
|
.initialize()
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to reinitialize settings: {}", e))?;
|
||||||
|
settings
|
||||||
|
.set_value(
|
||||||
|
SettingsKey::PgConnectionStatus,
|
||||||
|
SettingsValue::Json(serde_json::json!({
|
||||||
|
"connected": true,
|
||||||
|
"type": "sqlite"
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to restore sqlite status: {}", e))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut permissions = state_guard.permissions.write();
|
||||||
|
permissions.clear_all_permissions();
|
||||||
|
permissions.update_password_status(false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut theme = state_guard.theme.write();
|
||||||
|
*theme = ThemeService::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut auto_score = state_guard.auto_score.write();
|
||||||
|
*auto_score = AutoScoreService::new();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = app_handle.emit(
|
||||||
|
"settings:changed",
|
||||||
|
serde_json::json!({
|
||||||
|
"key": "is_wizard_completed",
|
||||||
|
"value": false
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(IpcResponse::success_empty())
|
||||||
|
}
|
||||||
|
|||||||
+386
-385
@@ -1,388 +1,389 @@
|
|||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub mod db;
|
pub mod db;
|
||||||
pub mod models;
|
pub mod models;
|
||||||
pub mod services;
|
pub mod services;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
use crate::db::connection::DatabaseType;
|
use crate::db::connection::DatabaseType;
|
||||||
use crate::db::connection::{create_postgres_connection, create_sqlite_connection};
|
use crate::db::connection::{create_postgres_connection, create_sqlite_connection};
|
||||||
use crate::db::migration::run_migration;
|
use crate::db::migration::run_migration;
|
||||||
use crate::services::settings::{SettingsKey, SettingsValue};
|
use crate::services::settings::{SettingsKey, SettingsValue};
|
||||||
use crate::{commands::*, state::AppState};
|
use crate::{commands::*, state::AppState};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
#[cfg(desktop)]
|
#[cfg(desktop)]
|
||||||
use tauri::{
|
use tauri::{
|
||||||
image::Image,
|
image::Image,
|
||||||
menu::{Menu, MenuItem},
|
menu::{Menu, MenuItem},
|
||||||
tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
|
tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
|
||||||
WindowEvent,
|
WindowEvent,
|
||||||
};
|
};
|
||||||
use tauri::{App, Manager};
|
use tauri::{App, Manager};
|
||||||
use tokio::time::{timeout, Duration};
|
use tokio::time::{timeout, Duration};
|
||||||
|
|
||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.plugin(tauri_plugin_shell::init())
|
.plugin(tauri_plugin_shell::init())
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
let state = AppState::new(app.handle().clone());
|
let state = AppState::new(app.handle().clone());
|
||||||
app.manage(Arc::new(RwLock::new(state)));
|
app.manage(Arc::new(RwLock::new(state)));
|
||||||
setup_app(app)?;
|
setup_app(app)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
student_query,
|
student_query,
|
||||||
student_create,
|
student_create,
|
||||||
student_update,
|
student_update,
|
||||||
student_delete,
|
student_delete,
|
||||||
student_import_from_xlsx,
|
student_import_from_xlsx,
|
||||||
tags_get_all,
|
tags_get_all,
|
||||||
tags_get_by_student,
|
tags_get_by_student,
|
||||||
tags_create,
|
tags_create,
|
||||||
tags_delete,
|
tags_delete,
|
||||||
tags_update_student_tags,
|
tags_update_student_tags,
|
||||||
reason_query,
|
reason_query,
|
||||||
reason_create,
|
reason_create,
|
||||||
reason_update,
|
reason_update,
|
||||||
reason_delete,
|
reason_delete,
|
||||||
reward_setting_query,
|
reward_setting_query,
|
||||||
reward_setting_create,
|
reward_setting_create,
|
||||||
reward_setting_update,
|
reward_setting_update,
|
||||||
reward_setting_delete,
|
reward_setting_delete,
|
||||||
reward_redeem,
|
reward_redeem,
|
||||||
reward_redemption_query,
|
reward_redemption_query,
|
||||||
event_query,
|
event_query,
|
||||||
event_create,
|
event_create,
|
||||||
event_delete,
|
event_delete,
|
||||||
event_query_by_student,
|
event_query_by_student,
|
||||||
leaderboard_query,
|
leaderboard_query,
|
||||||
db_settlement_query,
|
db_settlement_query,
|
||||||
db_settlement_create,
|
db_settlement_create,
|
||||||
db_settlement_leaderboard,
|
db_settlement_leaderboard,
|
||||||
settings_get_all,
|
settings_get_all,
|
||||||
settings_get,
|
settings_get,
|
||||||
settings_set,
|
settings_set,
|
||||||
auth_get_status,
|
auth_get_status,
|
||||||
auth_login,
|
auth_login,
|
||||||
auth_logout,
|
auth_logout,
|
||||||
auth_set_passwords,
|
auth_set_passwords,
|
||||||
auth_generate_recovery,
|
auth_generate_recovery,
|
||||||
auth_reset_by_recovery,
|
auth_reset_by_recovery,
|
||||||
auth_clear_all,
|
auth_clear_all,
|
||||||
theme_list,
|
theme_list,
|
||||||
theme_current,
|
theme_current,
|
||||||
theme_set,
|
theme_set,
|
||||||
theme_save,
|
theme_save,
|
||||||
theme_delete,
|
theme_delete,
|
||||||
auto_score_get_rules,
|
auto_score_get_rules,
|
||||||
auto_score_add_rule,
|
auto_score_add_rule,
|
||||||
auto_score_update_rule,
|
auto_score_update_rule,
|
||||||
auto_score_delete_rule,
|
auto_score_delete_rule,
|
||||||
auto_score_toggle_rule,
|
auto_score_toggle_rule,
|
||||||
auto_score_get_status,
|
auto_score_get_status,
|
||||||
auto_score_sort_rules,
|
auto_score_sort_rules,
|
||||||
board_query_sql,
|
board_query_sql,
|
||||||
log_query,
|
log_query,
|
||||||
log_clear,
|
log_clear,
|
||||||
log_set_level,
|
log_set_level,
|
||||||
log_write,
|
log_write,
|
||||||
data_export_json,
|
data_export_json,
|
||||||
data_import_json,
|
data_import_json,
|
||||||
|
data_reset_all,
|
||||||
window_minimize,
|
window_minimize,
|
||||||
window_maximize,
|
window_maximize,
|
||||||
window_close,
|
window_close,
|
||||||
window_is_maximized,
|
window_is_maximized,
|
||||||
toggle_devtools,
|
toggle_devtools,
|
||||||
window_resize,
|
window_resize,
|
||||||
window_set_resizable,
|
window_set_resizable,
|
||||||
db_test_connection,
|
db_test_connection,
|
||||||
db_switch_connection,
|
db_switch_connection,
|
||||||
db_get_status,
|
db_get_status,
|
||||||
db_sync,
|
db_sync,
|
||||||
db_sync_preview,
|
db_sync_preview,
|
||||||
db_sync_apply,
|
db_sync_apply,
|
||||||
fs_get_config_structure,
|
fs_get_config_structure,
|
||||||
fs_read_json,
|
fs_read_json,
|
||||||
fs_write_json,
|
fs_write_json,
|
||||||
fs_read_text,
|
fs_read_text,
|
||||||
fs_write_text,
|
fs_write_text,
|
||||||
fs_delete_file,
|
fs_delete_file,
|
||||||
fs_list_files,
|
fs_list_files,
|
||||||
fs_file_exists,
|
fs_file_exists,
|
||||||
http_server_start,
|
http_server_start,
|
||||||
http_server_stop,
|
http_server_stop,
|
||||||
http_server_status,
|
http_server_status,
|
||||||
mcp_server_start,
|
mcp_server_start,
|
||||||
mcp_server_stop,
|
mcp_server_stop,
|
||||||
mcp_server_status,
|
mcp_server_status,
|
||||||
register_url_protocol,
|
register_url_protocol,
|
||||||
app_quit,
|
app_quit,
|
||||||
app_restart,
|
app_restart,
|
||||||
])
|
])
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup_app(app: &mut App) -> Result<(), Box<dyn std::error::Error>> {
|
pub fn setup_app(app: &mut App) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
setup_database(app)?;
|
setup_database(app)?;
|
||||||
|
|
||||||
setup_tray(app)?;
|
setup_tray(app)?;
|
||||||
|
|
||||||
setup_window_events(app)?;
|
setup_window_events(app)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_database(app: &mut App) -> Result<(), Box<dyn std::error::Error>> {
|
fn setup_database(app: &mut App) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
const DB_CONNECT_TIMEOUT_SECS: u64 = 15;
|
const DB_CONNECT_TIMEOUT_SECS: u64 = 15;
|
||||||
const DB_MIGRATION_TIMEOUT_SECS: u64 = 20;
|
const DB_MIGRATION_TIMEOUT_SECS: u64 = 20;
|
||||||
|
|
||||||
let handle = app.handle().clone();
|
let handle = app.handle().clone();
|
||||||
let db_path = if cfg!(all(debug_assertions, desktop)) {
|
let db_path = if cfg!(all(debug_assertions, desktop)) {
|
||||||
std::path::PathBuf::from("data.sql")
|
std::path::PathBuf::from("data.sql")
|
||||||
} else {
|
} else {
|
||||||
let app_data_dir = handle
|
let app_data_dir = handle
|
||||||
.path()
|
.path()
|
||||||
.app_data_dir()
|
.app_data_dir()
|
||||||
.map_err(|e| format!("Failed to get app data directory: {}", e))?;
|
.map_err(|e| format!("Failed to get app data directory: {}", e))?;
|
||||||
let data_dir = app_data_dir.join("data");
|
let data_dir = app_data_dir.join("data");
|
||||||
std::fs::create_dir_all(&data_dir)
|
std::fs::create_dir_all(&data_dir)
|
||||||
.map_err(|e| format!("Failed to create data directory: {}", e))?;
|
.map_err(|e| format!("Failed to create data directory: {}", e))?;
|
||||||
data_dir.join("data.sql")
|
data_dir.join("data.sql")
|
||||||
};
|
};
|
||||||
|
|
||||||
let db_path_str = db_path.to_str().ok_or("Invalid database path")?.to_string();
|
let db_path_str = db_path.to_str().ok_or("Invalid database path")?.to_string();
|
||||||
|
|
||||||
let db_result = tauri::async_runtime::block_on(async {
|
let db_result = tauri::async_runtime::block_on(async {
|
||||||
let sqlite_conn = create_sqlite_connection(&db_path_str).await?;
|
let sqlite_conn = create_sqlite_connection(&db_path_str).await?;
|
||||||
run_migration(&sqlite_conn, DatabaseType::SQLite).await?;
|
run_migration(&sqlite_conn, DatabaseType::SQLite).await?;
|
||||||
|
|
||||||
let state = handle.state::<crate::state::SafeAppState>();
|
let state = handle.state::<crate::state::SafeAppState>();
|
||||||
let state_guard = state.write();
|
let state_guard = state.write();
|
||||||
let mut active_conn = sqlite_conn.clone();
|
let mut active_conn = sqlite_conn.clone();
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut settings = state_guard.settings.write();
|
let mut settings = state_guard.settings.write();
|
||||||
settings.attach_db(Some(sqlite_conn.clone()));
|
settings.attach_db(Some(sqlite_conn.clone()));
|
||||||
settings
|
settings
|
||||||
.initialize()
|
.initialize()
|
||||||
.await
|
.await
|
||||||
.map_err(|e| format!("Failed to initialize settings from sqlite: {}", e))?;
|
.map_err(|e| format!("Failed to initialize settings from sqlite: {}", e))?;
|
||||||
|
|
||||||
let pg_connection_string = match settings.get_value(SettingsKey::PgConnectionString) {
|
let pg_connection_string = match settings.get_value(SettingsKey::PgConnectionString) {
|
||||||
SettingsValue::String(s) => s,
|
SettingsValue::String(s) => s,
|
||||||
_ => String::new(),
|
_ => String::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if !pg_connection_string.trim().is_empty() {
|
if !pg_connection_string.trim().is_empty() {
|
||||||
match timeout(
|
match timeout(
|
||||||
Duration::from_secs(DB_CONNECT_TIMEOUT_SECS),
|
Duration::from_secs(DB_CONNECT_TIMEOUT_SECS),
|
||||||
create_postgres_connection(&pg_connection_string),
|
create_postgres_connection(&pg_connection_string),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let timeout_message = "PostgreSQL auto-connect timeout".to_string();
|
let timeout_message = "PostgreSQL auto-connect timeout".to_string();
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"PostgreSQL auto-connect timed out on startup, fallback to sqlite"
|
"PostgreSQL auto-connect timed out on startup, fallback to sqlite"
|
||||||
);
|
);
|
||||||
settings
|
settings
|
||||||
.set_value(
|
.set_value(
|
||||||
SettingsKey::PgConnectionStatus,
|
SettingsKey::PgConnectionStatus,
|
||||||
SettingsValue::Json(serde_json::json!({
|
SettingsValue::Json(serde_json::json!({
|
||||||
"connected": false,
|
"connected": false,
|
||||||
"type": "sqlite",
|
"type": "sqlite",
|
||||||
"error": timeout_message
|
"error": timeout_message
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| format!("Failed to save pg status: {}", err))?;
|
.map_err(|err| format!("Failed to save pg status: {}", err))?;
|
||||||
}
|
}
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => {
|
||||||
eprintln!("PostgreSQL auto-connect failed, fallback to sqlite: {}", e);
|
eprintln!("PostgreSQL auto-connect failed, fallback to sqlite: {}", e);
|
||||||
settings
|
settings
|
||||||
.set_value(
|
.set_value(
|
||||||
SettingsKey::PgConnectionStatus,
|
SettingsKey::PgConnectionStatus,
|
||||||
SettingsValue::Json(serde_json::json!({
|
SettingsValue::Json(serde_json::json!({
|
||||||
"connected": false,
|
"connected": false,
|
||||||
"type": "sqlite",
|
"type": "sqlite",
|
||||||
"error": e.to_string()
|
"error": e.to_string()
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| format!("Failed to save pg status: {}", err))?;
|
.map_err(|err| format!("Failed to save pg status: {}", err))?;
|
||||||
}
|
}
|
||||||
Ok(Ok(pg_conn)) => {
|
Ok(Ok(pg_conn)) => {
|
||||||
let migration_result = timeout(
|
let migration_result = timeout(
|
||||||
Duration::from_secs(DB_MIGRATION_TIMEOUT_SECS),
|
Duration::from_secs(DB_MIGRATION_TIMEOUT_SECS),
|
||||||
run_migration(&pg_conn, DatabaseType::PostgreSQL),
|
run_migration(&pg_conn, DatabaseType::PostgreSQL),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if let Err(_) = migration_result {
|
if let Err(_) = migration_result {
|
||||||
let timeout_message = "PostgreSQL migration timeout".to_string();
|
let timeout_message = "PostgreSQL migration timeout".to_string();
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"PostgreSQL migration timed out on startup, fallback to sqlite"
|
"PostgreSQL migration timed out on startup, fallback to sqlite"
|
||||||
);
|
);
|
||||||
settings
|
settings
|
||||||
.set_value(
|
.set_value(
|
||||||
SettingsKey::PgConnectionStatus,
|
SettingsKey::PgConnectionStatus,
|
||||||
SettingsValue::Json(serde_json::json!({
|
SettingsValue::Json(serde_json::json!({
|
||||||
"connected": false,
|
"connected": false,
|
||||||
"type": "sqlite",
|
"type": "sqlite",
|
||||||
"error": timeout_message
|
"error": timeout_message
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| format!("Failed to save pg status: {}", err))?;
|
.map_err(|err| format!("Failed to save pg status: {}", err))?;
|
||||||
} else if let Ok(Err(e)) = migration_result {
|
} else if let Ok(Err(e)) = migration_result {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"PostgreSQL migration failed on startup, fallback to sqlite: {}",
|
"PostgreSQL migration failed on startup, fallback to sqlite: {}",
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
settings
|
settings
|
||||||
.set_value(
|
.set_value(
|
||||||
SettingsKey::PgConnectionStatus,
|
SettingsKey::PgConnectionStatus,
|
||||||
SettingsValue::Json(serde_json::json!({
|
SettingsValue::Json(serde_json::json!({
|
||||||
"connected": false,
|
"connected": false,
|
||||||
"type": "sqlite",
|
"type": "sqlite",
|
||||||
"error": e.to_string()
|
"error": e.to_string()
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| format!("Failed to save pg status: {}", err))?;
|
.map_err(|err| format!("Failed to save pg status: {}", err))?;
|
||||||
} else {
|
} else {
|
||||||
active_conn = pg_conn;
|
active_conn = pg_conn;
|
||||||
settings
|
settings
|
||||||
.set_value(
|
.set_value(
|
||||||
SettingsKey::PgConnectionStatus,
|
SettingsKey::PgConnectionStatus,
|
||||||
SettingsValue::Json(serde_json::json!({
|
SettingsValue::Json(serde_json::json!({
|
||||||
"connected": true,
|
"connected": true,
|
||||||
"type": "postgresql"
|
"type": "postgresql"
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| format!("Failed to save pg status: {}", err))?;
|
.map_err(|err| format!("Failed to save pg status: {}", err))?;
|
||||||
eprintln!("Auto connected to PostgreSQL from saved connection string");
|
eprintln!("Auto connected to PostgreSQL from saved connection string");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
settings
|
settings
|
||||||
.set_value(
|
.set_value(
|
||||||
SettingsKey::PgConnectionStatus,
|
SettingsKey::PgConnectionStatus,
|
||||||
SettingsValue::Json(serde_json::json!({
|
SettingsValue::Json(serde_json::json!({
|
||||||
"connected": true,
|
"connected": true,
|
||||||
"type": "sqlite"
|
"type": "sqlite"
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| format!("Failed to save sqlite status: {}", err))?;
|
.map_err(|err| format!("Failed to save sqlite status: {}", err))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut db_guard = state_guard.db.write();
|
let mut db_guard = state_guard.db.write();
|
||||||
*db_guard = Some(active_conn);
|
*db_guard = Some(active_conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok::<_, Box<dyn std::error::Error>>(())
|
Ok::<_, Box<dyn std::error::Error>>(())
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Err(e) = db_result {
|
if let Err(e) = db_result {
|
||||||
eprintln!("Failed to connect to database: {}", e);
|
eprintln!("Failed to connect to database: {}", e);
|
||||||
} else {
|
} else {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"Database bootstrap completed, sqlite settings db: {}",
|
"Database bootstrap completed, sqlite settings db: {}",
|
||||||
db_path_str
|
db_path_str
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(desktop)]
|
#[cfg(desktop)]
|
||||||
fn setup_tray(app: &mut App) -> Result<(), Box<dyn std::error::Error>> {
|
fn setup_tray(app: &mut App) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let show_item = MenuItem::with_id(app, "show", "显示窗口", true, None::<&str>)?;
|
let show_item = MenuItem::with_id(app, "show", "显示窗口", true, None::<&str>)?;
|
||||||
let hide_item = MenuItem::with_id(app, "hide", "隐藏窗口", true, None::<&str>)?;
|
let hide_item = MenuItem::with_id(app, "hide", "隐藏窗口", true, None::<&str>)?;
|
||||||
let quit_item = MenuItem::with_id(app, "quit", "退出", true, None::<&str>)?;
|
let quit_item = MenuItem::with_id(app, "quit", "退出", true, None::<&str>)?;
|
||||||
|
|
||||||
let menu = Menu::with_items(app, &[&show_item, &hide_item, &quit_item])?;
|
let menu = Menu::with_items(app, &[&show_item, &hide_item, &quit_item])?;
|
||||||
|
|
||||||
let _tray = TrayIconBuilder::new()
|
let _tray = TrayIconBuilder::new()
|
||||||
.icon(Image::from_bytes(include_bytes!("../icons/icon.png"))?)
|
.icon(Image::from_bytes(include_bytes!("../icons/icon.png"))?)
|
||||||
.menu(&menu)
|
.menu(&menu)
|
||||||
.show_menu_on_left_click(false)
|
.show_menu_on_left_click(false)
|
||||||
.on_menu_event(|app, event| match event.id.as_ref() {
|
.on_menu_event(|app, event| match event.id.as_ref() {
|
||||||
"show" => {
|
"show" => {
|
||||||
if let Some(window) = app.get_webview_window("main") {
|
if let Some(window) = app.get_webview_window("main") {
|
||||||
let _ = window.show();
|
let _ = window.show();
|
||||||
let _ = window.set_focus();
|
let _ = window.set_focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"hide" => {
|
"hide" => {
|
||||||
if let Some(window) = app.get_webview_window("main") {
|
if let Some(window) = app.get_webview_window("main") {
|
||||||
let _ = window.hide();
|
let _ = window.hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"quit" => {
|
"quit" => {
|
||||||
app.exit(0);
|
app.exit(0);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
})
|
})
|
||||||
.on_tray_icon_event(|tray, event| {
|
.on_tray_icon_event(|tray, event| {
|
||||||
if let TrayIconEvent::Click {
|
if let TrayIconEvent::Click {
|
||||||
button: MouseButton::Left,
|
button: MouseButton::Left,
|
||||||
button_state: MouseButtonState::Up,
|
button_state: MouseButtonState::Up,
|
||||||
..
|
..
|
||||||
} = event
|
} = event
|
||||||
{
|
{
|
||||||
let app = tray.app_handle();
|
let app = tray.app_handle();
|
||||||
if let Some(window) = app.get_webview_window("main") {
|
if let Some(window) = app.get_webview_window("main") {
|
||||||
let _ = window.show();
|
let _ = window.show();
|
||||||
let _ = window.set_focus();
|
let _ = window.set_focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.build(app)?;
|
.build(app)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(desktop))]
|
#[cfg(not(desktop))]
|
||||||
fn setup_tray(_app: &mut App) -> Result<(), Box<dyn std::error::Error>> {
|
fn setup_tray(_app: &mut App) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(desktop)]
|
#[cfg(desktop)]
|
||||||
fn setup_window_events(app: &mut App) -> Result<(), Box<dyn std::error::Error>> {
|
fn setup_window_events(app: &mut App) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
if let Some(window) = app.get_webview_window("main") {
|
if let Some(window) = app.get_webview_window("main") {
|
||||||
let _ = window.set_size(tauri::Size::Logical(tauri::LogicalSize {
|
let _ = window.set_size(tauri::Size::Logical(tauri::LogicalSize {
|
||||||
width: 1180.0,
|
width: 1180.0,
|
||||||
height: 680.0,
|
height: 680.0,
|
||||||
}));
|
}));
|
||||||
let _ = window.center();
|
let _ = window.center();
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
{
|
{
|
||||||
let _ = window.set_shadow(true);
|
let _ = window.set_shadow(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
let window_clone = window.clone();
|
let window_clone = window.clone();
|
||||||
window.on_window_event(move |event| {
|
window.on_window_event(move |event| {
|
||||||
if let WindowEvent::CloseRequested { api, .. } = event {
|
if let WindowEvent::CloseRequested { api, .. } = event {
|
||||||
api.prevent_close();
|
api.prevent_close();
|
||||||
let _ = window_clone.hide();
|
let _ = window_clone.hide();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(desktop))]
|
#[cfg(not(desktop))]
|
||||||
fn setup_window_events(_app: &mut App) -> Result<(), Box<dyn std::error::Error>> {
|
fn setup_window_events(_app: &mut App) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
+985
-963
File diff suppressed because it is too large
Load Diff
+1383
-1321
File diff suppressed because it is too large
Load Diff
+805
-797
File diff suppressed because it is too large
Load Diff
+795
-787
File diff suppressed because it is too large
Load Diff
+401
-400
@@ -1,404 +1,405 @@
|
|||||||
import { invoke } from "@tauri-apps/api/core"
|
import { invoke } from "@tauri-apps/api/core"
|
||||||
import { listen, UnlistenFn } from "@tauri-apps/api/event"
|
import { listen, UnlistenFn } from "@tauri-apps/api/event"
|
||||||
|
|
||||||
export interface themeConfig {
|
export interface themeConfig {
|
||||||
name: string
|
name: string
|
||||||
id: string
|
id: string
|
||||||
mode: "light" | "dark"
|
mode: "light" | "dark"
|
||||||
config: {
|
config: {
|
||||||
tdesign: Record<string, string>
|
tdesign: Record<string, string>
|
||||||
custom: Record<string, string>
|
custom: Record<string, string>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface settingChange {
|
export interface settingChange {
|
||||||
key: string
|
key: string
|
||||||
value: any
|
value: any
|
||||||
oldValue: any
|
oldValue: any
|
||||||
}
|
}
|
||||||
|
|
||||||
export type settingsKey =
|
export type settingsKey =
|
||||||
| "is_wizard_completed"
|
| "is_wizard_completed"
|
||||||
| "log_level"
|
| "log_level"
|
||||||
| "window_zoom"
|
| "window_zoom"
|
||||||
| "search_keyboard_layout"
|
| "search_keyboard_layout"
|
||||||
| "disable_search_keyboard"
|
| "disable_search_keyboard"
|
||||||
| "themes_custom"
|
| "themes_custom"
|
||||||
| "auto_score_enabled"
|
| "auto_score_enabled"
|
||||||
| "auto_score_rules"
|
| "auto_score_rules"
|
||||||
| "current_theme_id"
|
| "current_theme_id"
|
||||||
| "dashboards_config"
|
| "dashboards_config"
|
||||||
| "pg_connection_string"
|
| "pg_connection_string"
|
||||||
| "pg_connection_status"
|
| "pg_connection_status"
|
||||||
|
|
||||||
export interface settingsSpec {
|
export interface settingsSpec {
|
||||||
is_wizard_completed: boolean
|
is_wizard_completed: boolean
|
||||||
log_level: string
|
log_level: string
|
||||||
window_zoom: number
|
window_zoom: number
|
||||||
search_keyboard_layout: "t9" | "qwerty26"
|
search_keyboard_layout: "t9" | "qwerty26"
|
||||||
disable_search_keyboard: boolean
|
disable_search_keyboard: boolean
|
||||||
themes_custom: themeConfig[]
|
themes_custom: themeConfig[]
|
||||||
auto_score_enabled: boolean
|
auto_score_enabled: boolean
|
||||||
auto_score_rules: any[]
|
auto_score_rules: any[]
|
||||||
current_theme_id: string
|
current_theme_id: string
|
||||||
dashboards_config: any[]
|
dashboards_config: any[]
|
||||||
pg_connection_string: string
|
pg_connection_string: string
|
||||||
pg_connection_status: {
|
pg_connection_status: {
|
||||||
connected: boolean
|
connected: boolean
|
||||||
type: "sqlite" | "postgresql"
|
type: "sqlite" | "postgresql"
|
||||||
error?: string
|
error?: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const api = {
|
const api = {
|
||||||
// Theme
|
// Theme
|
||||||
getThemes: (): Promise<{ success: boolean; data: themeConfig[] }> => invoke("theme_list"),
|
getThemes: (): Promise<{ success: boolean; data: themeConfig[] }> => invoke("theme_list"),
|
||||||
getCurrentTheme: (): Promise<{ success: boolean; data: themeConfig }> => invoke("theme_current"),
|
getCurrentTheme: (): Promise<{ success: boolean; data: themeConfig }> => invoke("theme_current"),
|
||||||
setTheme: (themeId: string): Promise<{ success: boolean }> => invoke("theme_set", { themeId }),
|
setTheme: (themeId: string): Promise<{ success: boolean }> => invoke("theme_set", { themeId }),
|
||||||
saveTheme: (theme: themeConfig): Promise<{ success: boolean }> => invoke("theme_save", { theme }),
|
saveTheme: (theme: themeConfig): Promise<{ success: boolean }> => invoke("theme_save", { theme }),
|
||||||
deleteTheme: (themeId: string): Promise<{ success: boolean }> =>
|
deleteTheme: (themeId: string): Promise<{ success: boolean }> =>
|
||||||
invoke("theme_delete", { themeId }),
|
invoke("theme_delete", { themeId }),
|
||||||
onThemeChanged: (callback: (theme: themeConfig) => void): Promise<UnlistenFn> => {
|
onThemeChanged: (callback: (theme: themeConfig) => void): Promise<UnlistenFn> => {
|
||||||
return listen<themeConfig | { theme?: themeConfig }>("theme:updated", (event) => {
|
return listen<themeConfig | { theme?: themeConfig }>("theme:updated", (event) => {
|
||||||
const payload = event.payload as themeConfig | { theme?: themeConfig } | undefined
|
const payload = event.payload as themeConfig | { theme?: themeConfig } | undefined
|
||||||
const theme =
|
const theme =
|
||||||
payload && typeof payload === "object" && "theme" in payload
|
payload && typeof payload === "object" && "theme" in payload
|
||||||
? payload.theme
|
? payload.theme
|
||||||
: (payload as themeConfig | undefined)
|
: (payload as themeConfig | undefined)
|
||||||
if (theme) callback(theme)
|
if (theme) callback(theme)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// DB - Student
|
// DB - Student
|
||||||
queryStudents: (params?: any): Promise<{ success: boolean; data: any[] }> =>
|
queryStudents: (params?: any): Promise<{ success: boolean; data: any[] }> =>
|
||||||
invoke("student_query", { params }),
|
invoke("student_query", { params }),
|
||||||
createStudent: (data: {
|
createStudent: (data: {
|
||||||
name: string
|
name: string
|
||||||
}): Promise<{ success: boolean; data?: number; message?: string }> =>
|
}): Promise<{ success: boolean; data?: number; message?: string }> =>
|
||||||
invoke("student_create", { data }),
|
invoke("student_create", { data }),
|
||||||
updateStudent: (id: number, data: any): Promise<{ success: boolean }> =>
|
updateStudent: (id: number, data: any): Promise<{ success: boolean }> =>
|
||||||
invoke("student_update", { id, data }),
|
invoke("student_update", { id, data }),
|
||||||
deleteStudent: (id: number): Promise<{ success: boolean }> => invoke("student_delete", { id }),
|
deleteStudent: (id: number): Promise<{ success: boolean }> => invoke("student_delete", { id }),
|
||||||
importStudentsFromXlsx: (params: {
|
importStudentsFromXlsx: (params: {
|
||||||
names: string[]
|
names: string[]
|
||||||
}): Promise<{ success: boolean; data: { inserted: number; skipped: number; total: number } }> =>
|
}): Promise<{ success: boolean; data: { inserted: number; skipped: number; total: number } }> =>
|
||||||
invoke("student_import_from_xlsx", { params }),
|
invoke("student_import_from_xlsx", { params }),
|
||||||
|
|
||||||
// DB - Tags
|
// DB - Tags
|
||||||
tagsGetAll: (): Promise<{ success: boolean; data: any[] }> => invoke("tags_get_all"),
|
tagsGetAll: (): Promise<{ success: boolean; data: any[] }> => invoke("tags_get_all"),
|
||||||
tagsGetByStudent: (studentId: number): Promise<{ success: boolean; data: any[] }> =>
|
tagsGetByStudent: (studentId: number): Promise<{ success: boolean; data: any[] }> =>
|
||||||
invoke("tags_get_by_student", { studentId }),
|
invoke("tags_get_by_student", { studentId }),
|
||||||
tagsCreate: (name: string): Promise<{ success: boolean; data: any }> =>
|
tagsCreate: (name: string): Promise<{ success: boolean; data: any }> =>
|
||||||
invoke("tags_create", { name }),
|
invoke("tags_create", { name }),
|
||||||
tagsDelete: (id: number): Promise<{ success: boolean }> => invoke("tags_delete", { id }),
|
tagsDelete: (id: number): Promise<{ success: boolean }> => invoke("tags_delete", { id }),
|
||||||
tagsUpdateStudentTags: (studentId: number, tagIds: number[]): Promise<{ success: boolean }> =>
|
tagsUpdateStudentTags: (studentId: number, tagIds: number[]): Promise<{ success: boolean }> =>
|
||||||
invoke("tags_update_student_tags", { studentId, tagIds }),
|
invoke("tags_update_student_tags", { studentId, tagIds }),
|
||||||
|
|
||||||
// DB - Reason
|
// DB - Reason
|
||||||
queryReasons: (): Promise<{ success: boolean; data: any[] }> => invoke("reason_query"),
|
queryReasons: (): Promise<{ success: boolean; data: any[] }> => invoke("reason_query"),
|
||||||
createReason: (data: any): Promise<{ success: boolean; data?: number; message?: string }> =>
|
createReason: (data: any): Promise<{ success: boolean; data?: number; message?: string }> =>
|
||||||
invoke("reason_create", { data }),
|
invoke("reason_create", { data }),
|
||||||
updateReason: (id: number, data: any): Promise<{ success: boolean }> =>
|
updateReason: (id: number, data: any): Promise<{ success: boolean }> =>
|
||||||
invoke("reason_update", { id, data }),
|
invoke("reason_update", { id, data }),
|
||||||
deleteReason: (id: number): Promise<{ success: boolean }> => invoke("reason_delete", { id }),
|
deleteReason: (id: number): Promise<{ success: boolean }> => invoke("reason_delete", { id }),
|
||||||
|
|
||||||
// DB - Reward
|
// DB - Reward
|
||||||
rewardSettingQuery: (): Promise<{ success: boolean; data: any[] }> => invoke("reward_setting_query"),
|
rewardSettingQuery: (): Promise<{ success: boolean; data: any[] }> => invoke("reward_setting_query"),
|
||||||
rewardSettingCreate: (data: {
|
rewardSettingCreate: (data: {
|
||||||
name: string
|
name: string
|
||||||
cost_points: number
|
cost_points: number
|
||||||
}): Promise<{ success: boolean; data?: number; message?: string }> =>
|
}): Promise<{ success: boolean; data?: number; message?: string }> =>
|
||||||
invoke("reward_setting_create", { data }),
|
invoke("reward_setting_create", { data }),
|
||||||
rewardSettingUpdate: (id: number, data: any): Promise<{ success: boolean; message?: string }> =>
|
rewardSettingUpdate: (id: number, data: any): Promise<{ success: boolean; message?: string }> =>
|
||||||
invoke("reward_setting_update", { id, data }),
|
invoke("reward_setting_update", { id, data }),
|
||||||
rewardSettingDelete: (id: number): Promise<{ success: boolean; message?: string }> =>
|
rewardSettingDelete: (id: number): Promise<{ success: boolean; message?: string }> =>
|
||||||
invoke("reward_setting_delete", { id }),
|
invoke("reward_setting_delete", { id }),
|
||||||
rewardRedeem: (data: {
|
rewardRedeem: (data: {
|
||||||
student_name: string
|
student_name: string
|
||||||
reward_id: number
|
reward_id: number
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
success: boolean
|
success: boolean
|
||||||
data?: { redemption_id: number; remaining_reward_points: number }
|
data?: { redemption_id: number; remaining_reward_points: number }
|
||||||
message?: string
|
message?: string
|
||||||
}> => invoke("reward_redeem", { data }),
|
}> => invoke("reward_redeem", { data }),
|
||||||
rewardRedemptionQuery: (params?: { limit?: number }): Promise<{ success: boolean; data: any[] }> =>
|
rewardRedemptionQuery: (params?: { limit?: number }): Promise<{ success: boolean; data: any[] }> =>
|
||||||
invoke("reward_redemption_query", { params }),
|
invoke("reward_redemption_query", { params }),
|
||||||
|
|
||||||
// DB - Event
|
// DB - Event
|
||||||
queryEvents: (params?: { limit?: number }): Promise<{ success: boolean; data: any[] }> =>
|
queryEvents: (params?: { limit?: number }): Promise<{ success: boolean; data: any[] }> =>
|
||||||
invoke("event_query", { params }),
|
invoke("event_query", { params }),
|
||||||
createEvent: (data: {
|
createEvent: (data: {
|
||||||
studentName: string
|
studentName: string
|
||||||
reasonContent: string
|
reasonContent: string
|
||||||
delta: number
|
delta: number
|
||||||
}): Promise<{ success: boolean; data?: number; message?: string }> =>
|
}): Promise<{ success: boolean; data?: number; message?: string }> =>
|
||||||
invoke("event_create", { data }),
|
invoke("event_create", { data }),
|
||||||
deleteEvent: (uuid: string): Promise<{ success: boolean }> => invoke("event_delete", { uuid }),
|
deleteEvent: (uuid: string): Promise<{ success: boolean }> => invoke("event_delete", { uuid }),
|
||||||
queryEventsByStudent: (params: {
|
queryEventsByStudent: (params: {
|
||||||
studentName: string
|
studentName: string
|
||||||
limit?: number
|
limit?: number
|
||||||
startTime?: string
|
startTime?: string
|
||||||
}): Promise<{ success: boolean; data: any[] }> => invoke("event_query_by_student", { params }),
|
}): Promise<{ success: boolean; data: any[] }> => invoke("event_query_by_student", { params }),
|
||||||
queryLeaderboard: (params: {
|
queryLeaderboard: (params: {
|
||||||
range: "today" | "week" | "month"
|
range: "today" | "week" | "month"
|
||||||
}): Promise<{ success: boolean; data: { startTime: string; rows: any[] } }> =>
|
}): Promise<{ success: boolean; data: { startTime: string; rows: any[] } }> =>
|
||||||
invoke("leaderboard_query", { params }),
|
invoke("leaderboard_query", { params }),
|
||||||
boardQuerySql: (params: {
|
boardQuerySql: (params: {
|
||||||
sql: string
|
sql: string
|
||||||
limit?: number
|
limit?: number
|
||||||
}): Promise<{ success: boolean; data: any[]; message?: string }> =>
|
}): Promise<{ success: boolean; data: any[]; message?: string }> =>
|
||||||
invoke("board_query_sql", { params }),
|
invoke("board_query_sql", { params }),
|
||||||
|
|
||||||
// Settlement
|
// Settlement
|
||||||
querySettlements: (): Promise<{ success: boolean; data: any[] }> => invoke("db_settlement_query"),
|
querySettlements: (): Promise<{ success: boolean; data: any[] }> => invoke("db_settlement_query"),
|
||||||
createSettlement: (): Promise<{ success: boolean; data: any }> => invoke("db_settlement_create"),
|
createSettlement: (): Promise<{ success: boolean; data: any }> => invoke("db_settlement_create"),
|
||||||
querySettlementLeaderboard: (params: {
|
querySettlementLeaderboard: (params: {
|
||||||
settlementId: number
|
settlementId: number
|
||||||
}): Promise<{ success: boolean; data: any }> => invoke("db_settlement_leaderboard", { params }),
|
}): Promise<{ success: boolean; data: any }> => invoke("db_settlement_leaderboard", { params }),
|
||||||
|
|
||||||
// Settings & Sync
|
// Settings & Sync
|
||||||
getAllSettings: (): Promise<{ success: boolean; data: settingsSpec }> =>
|
getAllSettings: (): Promise<{ success: boolean; data: settingsSpec }> =>
|
||||||
invoke("settings_get_all"),
|
invoke("settings_get_all"),
|
||||||
getSetting: <K extends settingsKey>(
|
getSetting: <K extends settingsKey>(
|
||||||
key: K
|
key: K
|
||||||
): Promise<{ success: boolean; data: settingsSpec[K] }> => invoke("settings_get", { key }),
|
): Promise<{ success: boolean; data: settingsSpec[K] }> => invoke("settings_get", { key }),
|
||||||
setSetting: <K extends settingsKey>(
|
setSetting: <K extends settingsKey>(
|
||||||
key: K,
|
key: K,
|
||||||
value: settingsSpec[K]
|
value: settingsSpec[K]
|
||||||
): Promise<{ success: boolean }> => invoke("settings_set", { key, value }),
|
): Promise<{ success: boolean }> => invoke("settings_set", { key, value }),
|
||||||
onSettingChanged: (callback: (change: settingChange) => void): Promise<UnlistenFn> => {
|
onSettingChanged: (callback: (change: settingChange) => void): Promise<UnlistenFn> => {
|
||||||
return listen<settingChange>("settings:changed", (event) => {
|
return listen<settingChange>("settings:changed", (event) => {
|
||||||
callback(event.payload)
|
callback(event.payload)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// Auth & Security
|
// Auth & Security
|
||||||
authGetStatus: (): Promise<{
|
authGetStatus: (): Promise<{
|
||||||
success: boolean
|
success: boolean
|
||||||
data: {
|
data: {
|
||||||
permission: string
|
permission: string
|
||||||
hasAdminPassword: boolean
|
hasAdminPassword: boolean
|
||||||
hasPointsPassword: boolean
|
hasPointsPassword: boolean
|
||||||
hasRecoveryString: boolean
|
hasRecoveryString: boolean
|
||||||
}
|
}
|
||||||
}> => invoke("auth_get_status"),
|
}> => invoke("auth_get_status"),
|
||||||
authLogin: (
|
authLogin: (
|
||||||
password: string
|
password: string
|
||||||
): Promise<{ success: boolean; data?: { permission: string }; message?: string }> =>
|
): Promise<{ success: boolean; data?: { permission: string }; message?: string }> =>
|
||||||
invoke("auth_login", { password }),
|
invoke("auth_login", { password }),
|
||||||
authLogout: (): Promise<{ success: boolean; data: { permission: string } }> =>
|
authLogout: (): Promise<{ success: boolean; data: { permission: string } }> =>
|
||||||
invoke("auth_logout"),
|
invoke("auth_logout"),
|
||||||
authSetPasswords: (payload: {
|
authSetPasswords: (payload: {
|
||||||
adminPassword?: string | null
|
adminPassword?: string | null
|
||||||
pointsPassword?: string | null
|
pointsPassword?: string | null
|
||||||
}): Promise<{ success: boolean; data?: { recoveryString: string }; message?: string }> =>
|
}): Promise<{ success: boolean; data?: { recoveryString: string }; message?: string }> =>
|
||||||
invoke("auth_set_passwords", payload),
|
invoke("auth_set_passwords", payload),
|
||||||
authGenerateRecovery: (): Promise<{ success: boolean; data: { recoveryString: string } }> =>
|
authGenerateRecovery: (): Promise<{ success: boolean; data: { recoveryString: string } }> =>
|
||||||
invoke("auth_generate_recovery"),
|
invoke("auth_generate_recovery"),
|
||||||
authResetByRecovery: (
|
authResetByRecovery: (
|
||||||
recoveryString: string
|
recoveryString: string
|
||||||
): Promise<{ success: boolean; data?: { recoveryString: string }; message?: string }> =>
|
): Promise<{ success: boolean; data?: { recoveryString: string }; message?: string }> =>
|
||||||
invoke("auth_reset_by_recovery", { recoveryString }),
|
invoke("auth_reset_by_recovery", { recoveryString }),
|
||||||
authClearAll: (): Promise<{ success: boolean }> => invoke("auth_clear_all"),
|
authClearAll: (): Promise<{ success: boolean }> => invoke("auth_clear_all"),
|
||||||
|
|
||||||
// Data import/export
|
// Data import/export
|
||||||
exportDataJson: (): Promise<{ success: boolean; data: string }> => invoke("data_export_json"),
|
exportDataJson: (): Promise<{ success: boolean; data: string }> => invoke("data_export_json"),
|
||||||
importDataJson: (jsonText: string): Promise<{ success: boolean }> =>
|
importDataJson: (jsonText: string): Promise<{ success: boolean }> =>
|
||||||
invoke("data_import_json", { jsonText }),
|
invoke("data_import_json", { jsonText }),
|
||||||
|
dataResetAll: (): Promise<{ success: boolean }> => invoke("data_reset_all"),
|
||||||
// Window
|
|
||||||
windowMinimize: (): Promise<void> => invoke("window_minimize"),
|
// Window
|
||||||
windowMaximize: (): Promise<boolean> => invoke("window_maximize"),
|
windowMinimize: (): Promise<void> => invoke("window_minimize"),
|
||||||
windowClose: (): Promise<void> => invoke("window_close"),
|
windowMaximize: (): Promise<boolean> => invoke("window_maximize"),
|
||||||
windowIsMaximized: (): Promise<boolean> => invoke("window_is_maximized"),
|
windowClose: (): Promise<void> => invoke("window_close"),
|
||||||
toggleDevTools: (): Promise<void> => invoke("toggle_devtools"),
|
windowIsMaximized: (): Promise<boolean> => invoke("window_is_maximized"),
|
||||||
windowResize: (width: number, height: number): Promise<void> =>
|
toggleDevTools: (): Promise<void> => invoke("toggle_devtools"),
|
||||||
invoke("window_resize", { width, height }),
|
windowResize: (width: number, height: number): Promise<void> =>
|
||||||
windowSetResizable: (resizable: boolean): Promise<void> =>
|
invoke("window_resize", { width, height }),
|
||||||
invoke("window_set_resizable", { resizable }),
|
windowSetResizable: (resizable: boolean): Promise<void> =>
|
||||||
onWindowMaximizedChanged: (callback: (maximized: boolean) => void): Promise<UnlistenFn> => {
|
invoke("window_set_resizable", { resizable }),
|
||||||
return listen<boolean>("window:maximized-changed", (event) => {
|
onWindowMaximizedChanged: (callback: (maximized: boolean) => void): Promise<UnlistenFn> => {
|
||||||
callback(event.payload)
|
return listen<boolean>("window:maximized-changed", (event) => {
|
||||||
})
|
callback(event.payload)
|
||||||
},
|
})
|
||||||
onNavigate: (callback: (route: string) => void): Promise<UnlistenFn> => {
|
},
|
||||||
return listen<string>("app:navigate", (event) => {
|
onNavigate: (callback: (route: string) => void): Promise<UnlistenFn> => {
|
||||||
callback(event.payload)
|
return listen<string>("app:navigate", (event) => {
|
||||||
})
|
callback(event.payload)
|
||||||
},
|
})
|
||||||
|
},
|
||||||
// Logger
|
|
||||||
queryLogs: (
|
// Logger
|
||||||
input?: number | { lines?: number }
|
queryLogs: (
|
||||||
): Promise<{ success: boolean; data: string[] }> => {
|
input?: number | { lines?: number }
|
||||||
const lines = typeof input === "number" ? input : input?.lines
|
): Promise<{ success: boolean; data: string[] }> => {
|
||||||
return invoke("log_query", { lines })
|
const lines = typeof input === "number" ? input : input?.lines
|
||||||
},
|
return invoke("log_query", { lines })
|
||||||
clearLogs: (): Promise<{ success: boolean }> => invoke("log_clear"),
|
},
|
||||||
setLogLevel: (level: string): Promise<{ success: boolean }> => invoke("log_set_level", { level }),
|
clearLogs: (): Promise<{ success: boolean }> => invoke("log_clear"),
|
||||||
writeLog: (payload: {
|
setLogLevel: (level: string): Promise<{ success: boolean }> => invoke("log_set_level", { level }),
|
||||||
level: string
|
writeLog: (payload: {
|
||||||
message: string
|
level: string
|
||||||
meta?: any
|
message: string
|
||||||
}): Promise<{ success: boolean }> => invoke("log_write", { payload }),
|
meta?: any
|
||||||
|
}): Promise<{ success: boolean }> => invoke("log_write", { payload }),
|
||||||
// Database Connection
|
|
||||||
dbTestConnection: (
|
// Database Connection
|
||||||
connectionString: string
|
dbTestConnection: (
|
||||||
): Promise<{ success: boolean; data: { success: boolean; error?: string } }> =>
|
connectionString: string
|
||||||
invoke("db_test_connection", { connectionString }),
|
): Promise<{ success: boolean; data: { success: boolean; error?: string } }> =>
|
||||||
dbSwitchConnection: (
|
invoke("db_test_connection", { connectionString }),
|
||||||
connectionString: string
|
dbSwitchConnection: (
|
||||||
): Promise<{ success: boolean; data: { type: "sqlite" | "postgresql" } }> =>
|
connectionString: string
|
||||||
invoke("db_switch_connection", { connectionString }),
|
): Promise<{ success: boolean; data: { type: "sqlite" | "postgresql" } }> =>
|
||||||
dbGetStatus: (): Promise<{
|
invoke("db_switch_connection", { connectionString }),
|
||||||
success: boolean
|
dbGetStatus: (): Promise<{
|
||||||
data: { type: string; connected: boolean; error?: string }
|
success: boolean
|
||||||
}> => invoke("db_get_status"),
|
data: { type: string; connected: boolean; error?: string }
|
||||||
dbSync: (): Promise<{ success: boolean; data: { success: boolean; message?: string } }> =>
|
}> => invoke("db_get_status"),
|
||||||
invoke("db_sync"),
|
dbSync: (): Promise<{ success: boolean; data: { success: boolean; message?: string } }> =>
|
||||||
dbSyncPreview: (): Promise<{
|
invoke("db_sync"),
|
||||||
success: boolean
|
dbSyncPreview: (): Promise<{
|
||||||
data: {
|
success: boolean
|
||||||
can_sync: boolean
|
data: {
|
||||||
need_sync: boolean
|
can_sync: boolean
|
||||||
local_only: number
|
need_sync: boolean
|
||||||
remote_only: number
|
local_only: number
|
||||||
conflicts: Array<{
|
remote_only: number
|
||||||
table: string
|
conflicts: Array<{
|
||||||
key: string
|
table: string
|
||||||
local_summary: string
|
key: string
|
||||||
remote_summary: string
|
local_summary: string
|
||||||
}>
|
remote_summary: string
|
||||||
message?: string
|
}>
|
||||||
}
|
message?: string
|
||||||
}> => invoke("db_sync_preview"),
|
}
|
||||||
dbSyncApply: (strategy: "keep_local" | "keep_remote"): Promise<{
|
}> => invoke("db_sync_preview"),
|
||||||
success: boolean
|
dbSyncApply: (strategy: "keep_local" | "keep_remote"): Promise<{
|
||||||
data: { success: boolean; synced_records: number; resolved_conflicts: number; message?: string }
|
success: boolean
|
||||||
}> => invoke("db_sync_apply", { strategy }),
|
data: { success: boolean; synced_records: number; resolved_conflicts: number; message?: string }
|
||||||
|
}> => invoke("db_sync_apply", { strategy }),
|
||||||
// HTTP Server
|
|
||||||
httpServerStart: (config?: {
|
// HTTP Server
|
||||||
port?: number
|
httpServerStart: (config?: {
|
||||||
host?: string
|
port?: number
|
||||||
corsOrigin?: string
|
host?: string
|
||||||
}): Promise<{ success: boolean; data: { url: string; config: any } }> =>
|
corsOrigin?: string
|
||||||
invoke("http_server_start", { config }),
|
}): Promise<{ success: boolean; data: { url: string; config: any } }> =>
|
||||||
httpServerStop: (): Promise<{ success: boolean }> => invoke("http_server_stop"),
|
invoke("http_server_start", { config }),
|
||||||
httpServerStatus: (): Promise<{
|
httpServerStop: (): Promise<{ success: boolean }> => invoke("http_server_stop"),
|
||||||
success: boolean
|
httpServerStatus: (): Promise<{
|
||||||
data: { isRunning: boolean; config?: any; url?: string }
|
success: boolean
|
||||||
}> => invoke("http_server_status"),
|
data: { isRunning: boolean; config?: any; url?: string }
|
||||||
|
}> => invoke("http_server_status"),
|
||||||
// MCP Server
|
|
||||||
mcpServerStart: (config?: {
|
// MCP Server
|
||||||
port?: number
|
mcpServerStart: (config?: {
|
||||||
host?: string
|
port?: number
|
||||||
}): Promise<{ success: boolean; data?: { url: string; config: { port: number; host: string } } }> =>
|
host?: string
|
||||||
invoke("mcp_server_start", { config }),
|
}): Promise<{ success: boolean; data?: { url: string; config: { port: number; host: string } } }> =>
|
||||||
mcpServerStop: (): Promise<{ success: boolean }> => invoke("mcp_server_stop"),
|
invoke("mcp_server_start", { config }),
|
||||||
mcpServerStatus: (): Promise<{
|
mcpServerStop: (): Promise<{ success: boolean }> => invoke("mcp_server_stop"),
|
||||||
success: boolean
|
mcpServerStatus: (): Promise<{
|
||||||
data?: {
|
success: boolean
|
||||||
is_running: boolean
|
data?: {
|
||||||
config: { port: number; host: string }
|
is_running: boolean
|
||||||
url?: string | null
|
config: { port: number; host: string }
|
||||||
}
|
url?: string | null
|
||||||
}> => invoke("mcp_server_status"),
|
}
|
||||||
|
}> => invoke("mcp_server_status"),
|
||||||
// File System
|
|
||||||
fsGetConfigStructure: (): Promise<{
|
// File System
|
||||||
success: boolean
|
fsGetConfigStructure: (): Promise<{
|
||||||
data: { configRoot: string; automatic: string; script: string }
|
success: boolean
|
||||||
}> => invoke("fs_get_config_structure"),
|
data: { configRoot: string; automatic: string; script: string }
|
||||||
fsReadJson: (
|
}> => invoke("fs_get_config_structure"),
|
||||||
relativePath: string,
|
fsReadJson: (
|
||||||
folder?: "automatic" | "script"
|
relativePath: string,
|
||||||
): Promise<{ success: boolean; data: any }> => invoke("fs_read_json", { relativePath, folder }),
|
folder?: "automatic" | "script"
|
||||||
fsWriteJson: (
|
): Promise<{ success: boolean; data: any }> => invoke("fs_read_json", { relativePath, folder }),
|
||||||
relativePath: string,
|
fsWriteJson: (
|
||||||
data: any,
|
relativePath: string,
|
||||||
folder?: "automatic" | "script"
|
data: any,
|
||||||
): Promise<{ success: boolean }> => invoke("fs_write_json", { relativePath, data, folder }),
|
folder?: "automatic" | "script"
|
||||||
fsReadText: (
|
): Promise<{ success: boolean }> => invoke("fs_write_json", { relativePath, data, folder }),
|
||||||
relativePath: string,
|
fsReadText: (
|
||||||
folder?: "automatic" | "script"
|
relativePath: string,
|
||||||
): Promise<{ success: boolean; data: string }> =>
|
folder?: "automatic" | "script"
|
||||||
invoke("fs_read_text", { relativePath, folder }),
|
): Promise<{ success: boolean; data: string }> =>
|
||||||
fsWriteText: (
|
invoke("fs_read_text", { relativePath, folder }),
|
||||||
content: string,
|
fsWriteText: (
|
||||||
relativePath: string,
|
content: string,
|
||||||
folder?: "automatic" | "script"
|
relativePath: string,
|
||||||
): Promise<{ success: boolean }> => invoke("fs_write_text", { content, relativePath, folder }),
|
folder?: "automatic" | "script"
|
||||||
fsDeleteFile: (
|
): Promise<{ success: boolean }> => invoke("fs_write_text", { content, relativePath, folder }),
|
||||||
relativePath: string,
|
fsDeleteFile: (
|
||||||
folder?: "automatic" | "script"
|
relativePath: string,
|
||||||
): Promise<{ success: boolean }> => invoke("fs_delete_file", { relativePath, folder }),
|
folder?: "automatic" | "script"
|
||||||
fsListFiles: (folder?: "automatic" | "script"): Promise<{ success: boolean; data: any[] }> =>
|
): Promise<{ success: boolean }> => invoke("fs_delete_file", { relativePath, folder }),
|
||||||
invoke("fs_list_files", { folder }),
|
fsListFiles: (folder?: "automatic" | "script"): Promise<{ success: boolean; data: any[] }> =>
|
||||||
fsFileExists: (
|
invoke("fs_list_files", { folder }),
|
||||||
relativePath: string,
|
fsFileExists: (
|
||||||
folder?: "automatic" | "script"
|
relativePath: string,
|
||||||
): Promise<{ success: boolean; data: boolean }> =>
|
folder?: "automatic" | "script"
|
||||||
invoke("fs_file_exists", { relativePath, folder }),
|
): Promise<{ success: boolean; data: boolean }> =>
|
||||||
|
invoke("fs_file_exists", { relativePath, folder }),
|
||||||
// App
|
|
||||||
registerUrlProtocol: (): Promise<{
|
// App
|
||||||
success: boolean
|
registerUrlProtocol: (): Promise<{
|
||||||
data?: { registered: boolean }
|
success: boolean
|
||||||
message?: string
|
data?: { registered: boolean }
|
||||||
}> => invoke("register_url_protocol"),
|
message?: string
|
||||||
appQuit: (): Promise<void> => invoke("app_quit"),
|
}> => invoke("register_url_protocol"),
|
||||||
appRestart: (): Promise<void> => invoke("app_restart"),
|
appQuit: (): Promise<void> => invoke("app_quit"),
|
||||||
|
appRestart: (): Promise<void> => invoke("app_restart"),
|
||||||
// Auto Score
|
|
||||||
autoScoreGetRules: (): Promise<{ success: boolean; data: any[] }> =>
|
// Auto Score
|
||||||
invoke("auto_score_get_rules"),
|
autoScoreGetRules: (): Promise<{ success: boolean; data: any[] }> =>
|
||||||
autoScoreAddRule: (rule: any): Promise<{ success: boolean; data?: number; message?: string }> =>
|
invoke("auto_score_get_rules"),
|
||||||
invoke("auto_score_add_rule", { rule }),
|
autoScoreAddRule: (rule: any): Promise<{ success: boolean; data?: number; message?: string }> =>
|
||||||
autoScoreUpdateRule: (
|
invoke("auto_score_add_rule", { rule }),
|
||||||
rule: any
|
autoScoreUpdateRule: (
|
||||||
): Promise<{ success: boolean; data?: boolean; message?: string }> =>
|
rule: any
|
||||||
invoke("auto_score_update_rule", { rule }),
|
): Promise<{ success: boolean; data?: boolean; message?: string }> =>
|
||||||
autoScoreDeleteRule: (
|
invoke("auto_score_update_rule", { rule }),
|
||||||
ruleId: number
|
autoScoreDeleteRule: (
|
||||||
): Promise<{ success: boolean; data?: boolean; message?: string }> =>
|
ruleId: number
|
||||||
invoke("auto_score_delete_rule", { ruleId }),
|
): Promise<{ success: boolean; data?: boolean; message?: string }> =>
|
||||||
autoScoreToggleRule: (params: {
|
invoke("auto_score_delete_rule", { ruleId }),
|
||||||
ruleId: number
|
autoScoreToggleRule: (params: {
|
||||||
enabled: boolean
|
ruleId: number
|
||||||
}): Promise<{ success: boolean; data?: boolean; message?: string }> =>
|
enabled: boolean
|
||||||
invoke("auto_score_toggle_rule", params),
|
}): Promise<{ success: boolean; data?: boolean; message?: string }> =>
|
||||||
autoScoreGetStatus: (): Promise<{ success: boolean; data: { enabled: boolean } }> =>
|
invoke("auto_score_toggle_rule", params),
|
||||||
invoke("auto_score_get_status"),
|
autoScoreGetStatus: (): Promise<{ success: boolean; data: { enabled: boolean } }> =>
|
||||||
autoScoreSortRules: (
|
invoke("auto_score_get_status"),
|
||||||
ruleIds: number[]
|
autoScoreSortRules: (
|
||||||
): Promise<{ success: boolean; data?: boolean; message?: string }> =>
|
ruleIds: number[]
|
||||||
invoke("auto_score_sort_rules", { ruleIds }),
|
): Promise<{ success: boolean; data?: boolean; message?: string }> =>
|
||||||
|
invoke("auto_score_sort_rules", { ruleIds }),
|
||||||
// Generic invoke wrapper for backward compatibility with callers using `api.invoke`
|
|
||||||
invoke: async (channel: string, ...args: any[]): Promise<any> => {
|
// Generic invoke wrapper for backward compatibility with callers using `api.invoke`
|
||||||
switch (channel) {
|
invoke: async (channel: string, ...args: any[]): Promise<any> => {
|
||||||
case "auto-score:getRules":
|
switch (channel) {
|
||||||
return api.autoScoreGetRules()
|
case "auto-score:getRules":
|
||||||
case "auto-score:addRule":
|
return api.autoScoreGetRules()
|
||||||
return api.autoScoreAddRule(args[0])
|
case "auto-score:addRule":
|
||||||
case "auto-score:updateRule":
|
return api.autoScoreAddRule(args[0])
|
||||||
return api.autoScoreUpdateRule(args[0])
|
case "auto-score:updateRule":
|
||||||
case "auto-score:deleteRule":
|
return api.autoScoreUpdateRule(args[0])
|
||||||
return api.autoScoreDeleteRule(args[0])
|
case "auto-score:deleteRule":
|
||||||
case "auto-score:toggleRule":
|
return api.autoScoreDeleteRule(args[0])
|
||||||
return api.autoScoreToggleRule(args[0])
|
case "auto-score:toggleRule":
|
||||||
case "auto-score:sortRules":
|
return api.autoScoreToggleRule(args[0])
|
||||||
return api.autoScoreSortRules(args[0])
|
case "auto-score:sortRules":
|
||||||
case "auto-score:getStatus":
|
return api.autoScoreSortRules(args[0])
|
||||||
return api.autoScoreGetStatus()
|
case "auto-score:getStatus":
|
||||||
default:
|
return api.autoScoreGetStatus()
|
||||||
throw new Error(`Unsupported legacy invoke channel: ${channel}`)
|
default:
|
||||||
}
|
throw new Error(`Unsupported legacy invoke channel: ${channel}`)
|
||||||
},
|
}
|
||||||
}
|
},
|
||||||
|
}
|
||||||
export default api
|
|
||||||
export { api }
|
export default api
|
||||||
|
export { api }
|
||||||
|
|||||||
Reference in New Issue
Block a user