v0.1.0-pre.050
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/src/tauri.rs
|
||||
// version: 6
|
||||
// version: 7
|
||||
|
||||
//! Tauri runtime assembly and private command wrappers.
|
||||
|
||||
@@ -52,8 +52,33 @@ pub fn run() -> kb_core::Result<()> {
|
||||
demo_ws_connect,
|
||||
demo_ws_unsubscribe,
|
||||
demo_ws_disconnect,
|
||||
open_demo_sql_diag_window,
|
||||
load_demo_sql_diag,
|
||||
open_demo_sql_pg_raw_window,
|
||||
load_demo_sql_pg_raw,
|
||||
open_demo_sql_pg_core_window,
|
||||
load_demo_sql_pg_core,
|
||||
open_demo_sql_replay_candidates_window,
|
||||
demo_sql_replay_options,
|
||||
load_demo_sql_replay_transactions,
|
||||
load_demo_sql_replay_programs,
|
||||
load_demo_sql_replay_entities,
|
||||
export_demo_sql_replay_csv,
|
||||
]);
|
||||
builder = builder.plugin(tracing_builder.build::<tauri::Wry>());
|
||||
builder = builder.on_window_event(|window, event| {
|
||||
if window.label() != "demo_ws" {
|
||||
return;
|
||||
}
|
||||
if !matches!(event, tauri::WindowEvent::Destroyed) {
|
||||
return;
|
||||
}
|
||||
let app_handle = window.app_handle().clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let state = app_handle.state::<crate::AppState>();
|
||||
crate::disconnect_demo_ws_app_state(&state, false).await;
|
||||
});
|
||||
});
|
||||
builder = builder.setup(|app| {
|
||||
let splash_window = match app.get_webview_window("splash") {
|
||||
std::option::Option::Some(window) => window,
|
||||
@@ -96,13 +121,6 @@ pub fn run() -> kb_core::Result<()> {
|
||||
std::option::Option::None,
|
||||
std::option::Option::None,
|
||||
);
|
||||
crate::emit_splash_order(
|
||||
&splash_window,
|
||||
"add_log",
|
||||
std::option::Option::Some("Pool WebSocket initialisé"),
|
||||
std::option::Option::None,
|
||||
std::option::Option::None,
|
||||
);
|
||||
crate::emit_splash_order(
|
||||
&splash_window,
|
||||
"fadein",
|
||||
@@ -434,7 +452,11 @@ fn open_demo_ws_window(
|
||||
fn demo_ws_list_pool_clients(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
) -> std::result::Result<std::vec::Vec<kb_onchain_transport::WsPoolClientSnapshot>, std::string::String> {
|
||||
return std::result::Result::Ok(state.ws_pool().snapshot());
|
||||
let pool = match state.demo_ws_pool() {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
return std::result::Result::Ok(pool.snapshot());
|
||||
}
|
||||
|
||||
/// Lists selectable WebSocket roles and methods for the demo UI.
|
||||
@@ -443,7 +465,10 @@ fn demo_ws_options(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
) -> std::result::Result<crate::DemoWsOptionsPayload, std::string::String> {
|
||||
return std::result::Result::Ok(crate::DemoWsOptionsPayload {
|
||||
roles: crate::build_ws_role_options(state.ws_pool().snapshot()),
|
||||
roles: crate::build_ws_role_options(match state.demo_ws_pool() {
|
||||
std::result::Result::Ok(pool) => pool.snapshot(),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
}),
|
||||
methods: crate::build_ws_method_options(),
|
||||
});
|
||||
}
|
||||
@@ -488,3 +513,338 @@ async fn demo_ws_disconnect(
|
||||
) -> std::result::Result<crate::DemoWsStatusPayload, std::string::String> {
|
||||
return crate::demo_ws_disconnect_inner(state.inner()).await;
|
||||
}
|
||||
|
||||
/// Opens or focuses the SQL diagnostic demo window.
|
||||
#[tauri::command]
|
||||
fn open_demo_sql_diag_window(
|
||||
app_handle: tauri::AppHandle,
|
||||
) -> std::result::Result<(), std::string::String> {
|
||||
return crate::open_sql_demo_window(
|
||||
app_handle,
|
||||
"demo_sql_diag",
|
||||
"demo_sql_diag.html",
|
||||
"Khadhroony Bot3 - SQL diagnostics",
|
||||
);
|
||||
}
|
||||
|
||||
/// Loads read-only SQL diagnostics from the active profile.
|
||||
#[tauri::command]
|
||||
#[allow(clippy::question_mark_used)]
|
||||
async fn load_demo_sql_diag(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
) -> std::result::Result<crate::DemoSqlDiagPayload, std::string::String> {
|
||||
let profile = state.active_profile().clone();
|
||||
let store_result = crate::connect_postgres_store(&profile).await;
|
||||
let store = match store_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let diagnostics_result = store.backend_diagnostics().await;
|
||||
let diagnostics = match diagnostics_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
let tables_result = store.known_table_diagnostics().await;
|
||||
let tables = match tables_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
return std::result::Result::Ok(crate::DemoSqlDiagPayload {
|
||||
config_path: state.config_path().to_string(),
|
||||
active_profile_name: profile.name.clone(),
|
||||
backend: profile.database.backend.clone(),
|
||||
masked_dsn: store.options().masked_dsn(),
|
||||
current_schema: diagnostics.descriptor.current_schema,
|
||||
health_status: crate::debug_status(diagnostics.health.status),
|
||||
health_message: diagnostics.health.message,
|
||||
migration_status: crate::debug_status(diagnostics.migrations.status),
|
||||
migration_message: diagnostics.migrations.message,
|
||||
server_version: diagnostics.server_version,
|
||||
tables: crate::table_snapshots_from_pg(tables.as_slice()),
|
||||
});
|
||||
}
|
||||
|
||||
/// Opens or focuses the PostgreSQL canonical acquisition demo window.
|
||||
#[tauri::command]
|
||||
fn open_demo_sql_pg_raw_window(
|
||||
app_handle: tauri::AppHandle,
|
||||
) -> std::result::Result<(), std::string::String> {
|
||||
return crate::open_sql_demo_window(
|
||||
app_handle,
|
||||
"demo_sql_pg_raw",
|
||||
"demo_sql_pg_raw.html",
|
||||
"Khadhroony Bot3 - PostgreSQL canonical acquisition",
|
||||
);
|
||||
}
|
||||
|
||||
/// Loads read-only raw table diagnostics from PostgreSQL.
|
||||
#[tauri::command]
|
||||
#[allow(clippy::question_mark_used)]
|
||||
async fn load_demo_sql_pg_raw(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
) -> std::result::Result<crate::DemoSqlPgRawPayload, std::string::String> {
|
||||
let profile = state.active_profile().clone();
|
||||
let store_result = crate::connect_postgres_store(&profile).await;
|
||||
let store = match store_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let tables_result = store.raw_table_diagnostics().await;
|
||||
let tables = match tables_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
return std::result::Result::Ok(crate::DemoSqlPgRawPayload {
|
||||
active_profile_name: profile.name.clone(),
|
||||
masked_dsn: store.options().masked_dsn(),
|
||||
tables: crate::table_snapshots_from_pg(tables.as_slice()),
|
||||
});
|
||||
}
|
||||
|
||||
/// Opens or focuses the PostgreSQL core store demo window.
|
||||
#[tauri::command]
|
||||
fn open_demo_sql_pg_core_window(
|
||||
app_handle: tauri::AppHandle,
|
||||
) -> std::result::Result<(), std::string::String> {
|
||||
return crate::open_sql_demo_window(
|
||||
app_handle,
|
||||
"demo_sql_pg_core",
|
||||
"demo_sql_pg_core.html",
|
||||
"Khadhroony Bot3 - PostgreSQL core store",
|
||||
);
|
||||
}
|
||||
|
||||
/// Loads read-only core table diagnostics from PostgreSQL.
|
||||
#[tauri::command]
|
||||
#[allow(clippy::question_mark_used)]
|
||||
async fn load_demo_sql_pg_core(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
) -> std::result::Result<crate::DemoSqlPgCorePayload, std::string::String> {
|
||||
let profile = state.active_profile().clone();
|
||||
let store_result = crate::connect_postgres_store(&profile).await;
|
||||
let store = match store_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let tables_result = store.core_table_diagnostics().await;
|
||||
let tables = match tables_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
return std::result::Result::Ok(crate::DemoSqlPgCorePayload {
|
||||
active_profile_name: profile.name.clone(),
|
||||
masked_dsn: store.options().masked_dsn(),
|
||||
tables: crate::table_snapshots_from_pg(tables.as_slice()),
|
||||
});
|
||||
}
|
||||
|
||||
/// Opens or focuses the SQL replay candidate browser.
|
||||
#[tauri::command]
|
||||
fn open_demo_sql_replay_candidates_window(
|
||||
app_handle: tauri::AppHandle,
|
||||
) -> std::result::Result<(), std::string::String> {
|
||||
return crate::open_sql_demo_window(
|
||||
app_handle,
|
||||
"demo_sql_replay_candidates",
|
||||
"demo_sql_replay_candidates.html",
|
||||
"Khadhroony Bot3 - SQL replay candidates",
|
||||
);
|
||||
}
|
||||
|
||||
/// Writes a bounded replay-candidate CSV file into `./data/exports_csv/`.
|
||||
#[tauri::command]
|
||||
#[allow(clippy::question_mark_used)]
|
||||
async fn export_demo_sql_replay_csv(
|
||||
file_name: std::string::String,
|
||||
content: std::string::String,
|
||||
) -> std::result::Result<std::string::String, std::string::String> {
|
||||
if content.is_empty() {
|
||||
return std::result::Result::Err("CSV export content must not be empty".to_owned());
|
||||
}
|
||||
if content.len() > 16 * 1024 * 1024 {
|
||||
return std::result::Result::Err("CSV export content exceeds 16 MiB".to_owned());
|
||||
}
|
||||
let normalized_name_result = crate::validated_csv_file_name(&file_name);
|
||||
let normalized_name = match normalized_name_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let current_dir_result = std::env::current_dir();
|
||||
let current_dir = match current_dir_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(format!("cannot resolve current directory: {error}"));
|
||||
},
|
||||
};
|
||||
let export_dir = crate::csv_export_directory_from_current_dir(¤t_dir);
|
||||
let create_result = tokio::fs::create_dir_all(&export_dir).await;
|
||||
if let std::result::Result::Err(error) = create_result {
|
||||
return std::result::Result::Err(format!("cannot create CSV export directory: {error}"));
|
||||
}
|
||||
let export_path_result = crate::available_csv_export_path(&export_dir, normalized_name).await;
|
||||
let export_path = match export_path_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let mut bytes = std::vec::Vec::with_capacity(content.len() + 3);
|
||||
bytes.extend_from_slice(&[0xEF, 0xBB, 0xBF]);
|
||||
bytes.extend_from_slice(content.as_bytes());
|
||||
let write_result = tokio::fs::write(&export_path, bytes).await;
|
||||
if let std::result::Result::Err(error) = write_result {
|
||||
return std::result::Result::Err(format!("cannot write CSV export: {error}"));
|
||||
}
|
||||
let display_path = export_path.to_string_lossy().into_owned();
|
||||
tracing::info!(target: crate::TRACING_TARGET, path = %display_path, "CSV replay candidate export written");
|
||||
return std::result::Result::Ok(display_path);
|
||||
}
|
||||
|
||||
/// Loads static browser options from the active PostgreSQL profile.
|
||||
#[tauri::command]
|
||||
#[allow(clippy::question_mark_used)]
|
||||
async fn demo_sql_replay_options(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
) -> std::result::Result<crate::DemoSqlReplayOptionsPayload, std::string::String> {
|
||||
let profile = state.active_profile().clone();
|
||||
let options_result = crate::postgres_store_options_from_profile(&profile, false);
|
||||
let options = match options_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
let mut known_programs =
|
||||
std::vec::Vec::with_capacity(kb_program_ids::registered_program_ids().len());
|
||||
for entry in kb_program_ids::registered_program_ids() {
|
||||
known_programs.push(crate::DemoSqlReplayKnownProgramOption {
|
||||
code: entry.code().to_owned(),
|
||||
program_id: entry.program_id().to_owned(),
|
||||
});
|
||||
}
|
||||
return std::result::Result::Ok(crate::DemoSqlReplayOptionsPayload {
|
||||
active_profile_name: profile.name,
|
||||
masked_dsn: options.masked_dsn(),
|
||||
maximum_limit: kb_store::MAX_REPLAY_CANDIDATE_ROWS,
|
||||
known_programs,
|
||||
});
|
||||
}
|
||||
|
||||
/// Loads bounded transaction replay candidates from PostgreSQL.
|
||||
#[tauri::command]
|
||||
#[allow(clippy::question_mark_used)]
|
||||
async fn load_demo_sql_replay_transactions(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
request: crate::DemoSqlReplayTransactionRequest,
|
||||
) -> std::result::Result<std::vec::Vec<crate::DemoSqlReplayTransactionRow>, std::string::String> {
|
||||
let program_scope_result = crate::program_scope_from_code(request.program_scope.as_str());
|
||||
let program_scope = match program_scope_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let entity_kind_result = crate::optional_entity_kind_from_code(request.entity_kind.as_deref());
|
||||
let entity_kind = match entity_kind_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let filter_result = kb_store::PostgresReplayTransactionFilter::new(
|
||||
request.signature_contains,
|
||||
request.min_slot,
|
||||
request.max_slot,
|
||||
request.raw_processing_state,
|
||||
request.ledger_status,
|
||||
request.program_id,
|
||||
program_scope,
|
||||
entity_kind,
|
||||
request.entity_value,
|
||||
request.limit,
|
||||
request.newest_first,
|
||||
);
|
||||
let filter = match filter_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
let store_result = crate::connect_postgres_store(state.active_profile()).await;
|
||||
let store = match store_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let rows_result = store.replay_transaction_candidates(&filter).await;
|
||||
let rows = match rows_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
tracing::debug!(target: crate::TRACING_TARGET, rows = rows.len(), "loaded replay transaction candidates");
|
||||
let mut output = std::vec::Vec::with_capacity(rows.len());
|
||||
for row in rows {
|
||||
output.push(crate::transaction_row_from_pg(row));
|
||||
}
|
||||
return std::result::Result::Ok(output);
|
||||
}
|
||||
|
||||
/// Loads bounded program summaries from PostgreSQL.
|
||||
#[tauri::command]
|
||||
#[allow(clippy::question_mark_used)]
|
||||
async fn load_demo_sql_replay_programs(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
request: crate::DemoSqlReplayProgramRequest,
|
||||
) -> std::result::Result<std::vec::Vec<crate::DemoSqlReplayProgramRow>, std::string::String> {
|
||||
let filter_result =
|
||||
kb_store::PostgresReplayProgramFilter::new(request.program_id_contains, request.limit);
|
||||
let filter = match filter_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
let store_result = crate::connect_postgres_store(state.active_profile()).await;
|
||||
let store = match store_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let rows_result = store.replay_program_summaries(&filter).await;
|
||||
let rows = match rows_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
tracing::debug!(target: crate::TRACING_TARGET, rows = rows.len(), "loaded replay program summaries");
|
||||
let mut output = std::vec::Vec::with_capacity(rows.len());
|
||||
for row in rows {
|
||||
output.push(crate::program_row_from_pg(row));
|
||||
}
|
||||
return std::result::Result::Ok(output);
|
||||
}
|
||||
|
||||
/// Loads bounded mint, owner or account-key summaries from PostgreSQL core tables.
|
||||
#[tauri::command]
|
||||
#[allow(clippy::question_mark_used)]
|
||||
async fn load_demo_sql_replay_entities(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
request: crate::DemoSqlReplayEntityRequest,
|
||||
) -> std::result::Result<std::vec::Vec<crate::DemoSqlReplayEntityRow>, std::string::String> {
|
||||
let entity_kind_result = crate::entity_kind_from_code(request.entity_kind.as_str());
|
||||
let entity_kind = match entity_kind_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let filter_result = kb_store::PostgresReplayEntityFilter::new(
|
||||
entity_kind,
|
||||
request.entity_value_contains,
|
||||
request.limit,
|
||||
);
|
||||
let filter = match filter_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
let store_result = crate::connect_postgres_store(state.active_profile()).await;
|
||||
let store = match store_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let rows_result = store.replay_entity_summaries(&filter).await;
|
||||
let rows = match rows_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
tracing::debug!(target: crate::TRACING_TARGET, rows = rows.len(), "loaded replay entity summaries");
|
||||
let mut output = std::vec::Vec::with_capacity(rows.len());
|
||||
for row in rows {
|
||||
output.push(crate::entity_row_from_pg(row));
|
||||
}
|
||||
return std::result::Result::Ok(output);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user