fix: диагностика перестаёт врать — падения маршрутов и потеря секрета (#38)

Два дефекта одного класса: состояние, которое не отражает реальность. Оба
найдены по данным из #32 и #37.

#32. Присланный лог показывал «сбоев 0» при том, что DC2 и DC4 всегда шли через
«Запасной Telegram IP», а DC203 всегда через «Системный DNS» — то есть
основные закреплённые адреса не использовались ни разу. Причина в счётчике:
ws_failures растёт только когда упали ВСЕ маршруты и соединение не состоялось.
Падения отдельных маршрутов через record_failure не попадали никуда, поэтому
перебор с откатом на запасной адрес выглядел как полное отсутствие проблем.

Добавлен route_failures: растёт на каждое падение маршрута, виден в строке
статуса CLI и в диагностике интерфейса. Теперь по логу сразу видно, что
закреплённый адрес мёртв, а не приходится это выводить.

#37. Симптом: Telegram пишет «прокси настроен неверно и будет отключён», при
этом Check status показывает Available. Это картина несовпадения секрета: TCP
проходит, init не разбирается под другим секретом, соединение закрывается.

Секрет мог меняться молча:

  #[cfg(not(unix))]
  fn write_secret_file(path: &Path, value: &str) {
      let _ = std::fs::write(path, value);   // ошибка выброшена
  }

create_dir_all рядом — так же. Если запись в %APPDATA%\TGLock\secret не
удавалась, программа генерировала новый секрет при каждом запуске и ничего об
этом не сообщала.

Теперь write_secret_file возвращает Result, load_or_create_secret_at отдаёт
StoredSecret с полем write_error, а оба интерфейса показывают предупреждение:
CLI строкой при старте, GUI записью в журнал. Прокси при этом продолжает
работать — просто до перезапуска.

Тесты: every_route_failure_is_counted, a_failed_write_is_reported_instead_of
_swallowed (родитель пути — файл, поэтому каталог не создать),
a_successful_write_reports_no_error (секрет переживает второй запуск).

В диагностике интерфейса добавлена подсказка: падения маршрутов больше нуля
при работающем Telegram — норма, значит закреплённый адрес недоступен и
подключение идёт через запасной.

Co-authored-by: by-sonic <171230345+by-sonic@users.noreply.github.com>
This commit is contained in:
Никита Sonic
2026-08-04 19:43:21 +03:00
committed by GitHub
parent 8491cf9292
commit fe85784550
7 changed files with 231 additions and 29 deletions
+15 -1
View File
@@ -44,6 +44,9 @@ struct StatusSnapshot {
data_center: Option<u16>,
route: String,
failures: u32,
/// Падения отдельных маршрутов. Растёт даже когда соединение в итоге
/// состоялось через запасной адрес (by-sonic/tglock#32).
route_failures: u32,
uptime_seconds: u64,
port: u16,
logs: Vec<LogLine>,
@@ -96,6 +99,7 @@ impl AppState {
data_center: (data_center > 0).then_some(data_center),
route: route.to_owned(),
failures: self.stats.ws_failures.load(Ordering::Relaxed),
route_failures: self.stats.route_failures(),
uptime_seconds: self
.started_at
.lock()
@@ -279,7 +283,17 @@ fn main() {
.app_config_dir()
.map_err(|error| error.to_string())?
.join("settings.json");
app.manage(AppState::new(settings_path));
let state = AppState::new(settings_path);
// Если секрет не удалось записать, ссылка tg://proxy изменится после
// перезапуска и Telegram откажется подключаться к сохранённой.
// Раньше это происходило молча (by-sonic/tglock#37).
if let Some(error) = state.stats.secret_write_error() {
state.log(
format!("Секрет не сохранён ({error}). После перезапуска ссылка изменится"),
true,
);
}
app.manage(state);
Ok(())
})
.invoke_handler(tauri::generate_handler![