a7b4e3318e
- Makefile: `desktop-check`, `desktop-build` (с ключом подписи обновлений, без ключа — сборка без артефактов обновления) и `desktop-run`; кэши cargo и npm держатся в .cache/, как у остальных инструментов проекта. - desktop/README.md: сборка, запуск, аргументы командной строки, настройки, deep links и открытые пункты Фазы 6. - bundle.targets = all: бандлы по умолчанию для текущей ОС (macOS: .app/.dmg). - Логирование подключается в setup и больше не роняет приложение, если каталог логов недоступен на запись (песочница, домашний каталог только для чтения). - Разбор аргументов вынесен в Cli::from_args и покрыт тестами (--instance, --minimized, --settings, --autostart on|off). - cargo fmt по модулям обёртки.
64 lines
2.8 KiB
Rust
64 lines
2.8 KiB
Rust
//! Адрес инстанса и границы навигации.
|
|
//!
|
|
//! Desktop-обёртка загружает тот же клиент, что и браузер, но только с одного
|
|
//! адреса: переходы на сторонние сайты уходят в системный браузер
|
|
//! (docs/client-tauri.md §6).
|
|
|
|
use url::Url;
|
|
|
|
/// Приводит введённый пользователем адрес к абсолютному URL.
|
|
///
|
|
/// Допускаются `http` и `https`; схема по умолчанию — `https`, чтобы в поле
|
|
/// настроек можно было написать просто `gl.mhspx.su`.
|
|
pub fn normalize(input: &str) -> Result<Url, String> {
|
|
let trimmed = input.trim();
|
|
if trimmed.is_empty() {
|
|
return Err("адрес инстанса не заполнен".to_string());
|
|
}
|
|
let candidate = if trimmed.contains("://") {
|
|
trimmed.to_string()
|
|
} else {
|
|
format!("https://{trimmed}")
|
|
};
|
|
let url = Url::parse(&candidate).map_err(|error| format!("некорректный адрес: {error}"))?;
|
|
match url.scheme() {
|
|
"http" | "https" => {}
|
|
other => {
|
|
return Err(format!(
|
|
"поддерживаются только http и https, а не «{other}»"
|
|
))
|
|
}
|
|
}
|
|
if url.host_str().unwrap_or_default().is_empty() {
|
|
return Err("в адресе нет имени хоста".to_string());
|
|
}
|
|
Ok(url)
|
|
}
|
|
|
|
/// Строка адреса для загрузки в окно: без завершающего слэша.
|
|
pub fn display(url: &Url) -> String {
|
|
url.as_str().trim_end_matches('/').to_string()
|
|
}
|
|
|
|
/// Хост адреса — граница, внутри которой разрешена навигация webview.
|
|
pub fn host(url: &Url) -> String {
|
|
url.host_str().unwrap_or_default().to_ascii_lowercase()
|
|
}
|
|
|
|
/// Разрешена ли навигация внутри окна.
|
|
///
|
|
/// Свой инстанс и локальные страницы обёртки (`tauri://localhost`) — да,
|
|
/// всё остальное открывается в системном браузере.
|
|
pub fn navigation_allowed(target: &Url, instance_host: &str) -> bool {
|
|
match target.scheme() {
|
|
// Локальные ресурсы обёртки: страница настроек.
|
|
"tauri" | "asset" => true,
|
|
"http" | "https" => {
|
|
let host = target.host_str().unwrap_or_default().to_ascii_lowercase();
|
|
// На Windows/Linux локальная страница отдаётся как http://tauri.localhost.
|
|
host == "tauri.localhost" || (!instance_host.is_empty() && host == instance_host)
|
|
}
|
|
_ => false,
|
|
}
|
|
}
|