feat: add per-account lock file protection via fs2

Prevent running multiple tele-tui instances with the same account by
using advisory file locks (flock). Lock is acquired before raw mode so
errors print to normal terminal. Account switching acquires new lock
before releasing old. Also log set_tdlib_parameters errors via tracing
instead of silently discarding them.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Mikhail Kilin
2026-02-24 14:23:30 +03:00
parent 044b859cec
commit 25c57c55fb
8 changed files with 206 additions and 4 deletions

View File

@@ -81,6 +81,15 @@ async fn main() -> Result<(), io::Error> {
)
.unwrap_or(db_path);
// Acquire per-account lock BEFORE raw mode (so error prints to normal terminal)
let account_lock = accounts::acquire_lock(
account_arg.as_deref().unwrap_or(&accounts_config.default_account),
)
.unwrap_or_else(|e| {
eprintln!("Error: {}", e);
std::process::exit(1);
});
// Отключаем логи TDLib ДО создания клиента
disable_tdlib_logs();
@@ -102,6 +111,7 @@ async fn main() -> Result<(), io::Error> {
// Create app state with account-specific db_path
let mut app = App::new(config, db_path);
app.current_account_name = account_name;
app.account_lock = Some(account_lock);
// Запускаем инициализацию TDLib в фоне (только для реального клиента)
let client_id = app.td_client.client_id();
@@ -110,7 +120,7 @@ async fn main() -> Result<(), io::Error> {
let db_path_str = app.td_client.db_path.to_string_lossy().to_string();
tokio::spawn(async move {
let _ = tdlib_rs::functions::set_tdlib_parameters(
if let Err(e) = tdlib_rs::functions::set_tdlib_parameters(
false, // use_test_dc
db_path_str, // database_directory
"".to_string(), // files_directory
@@ -127,7 +137,10 @@ async fn main() -> Result<(), io::Error> {
env!("CARGO_PKG_VERSION").to_string(), // application_version
client_id,
)
.await;
.await
{
tracing::error!("set_tdlib_parameters failed: {:?}", e);
}
});
let res = run_app(&mut terminal, &mut app).await;
@@ -406,6 +419,21 @@ async fn run_app<B: ratatui::backend::Backend, T: tdlib::TdClientTrait>(
// Check pending account switch
if let Some((account_name, new_db_path)) = app.pending_account_switch.take() {
// 0. Acquire lock for new account before switching
match accounts::acquire_lock(&account_name) {
Ok(new_lock) => {
// Release old lock
if let Some(old_lock) = app.account_lock.take() {
accounts::release_lock(old_lock);
}
app.account_lock = Some(new_lock);
}
Err(e) => {
app.error_message = Some(e);
continue;
}
}
// 1. Stop playback
app.stop_playback();