Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ccaf521da4 |
Generated
+1
-1
@@ -1224,7 +1224,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syshelper"
|
||||
version = "0.1.1"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
"axum",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "syshelper"
|
||||
version = "0.1.1"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -234,6 +234,31 @@ begin
|
||||
'', SW_HIDE, ewWaitUntilTerminated, Code);
|
||||
end;
|
||||
|
||||
// Без правила брандмауэра панель доступна только с самого компьютера: Windows
|
||||
// режет входящие подключения по умолчанию. Ограничиваемся частными и доменными
|
||||
// сетями — в публичной (кафе, вокзал) панель наружу смотреть не должна.
|
||||
procedure RemoveFirewallRule();
|
||||
var
|
||||
Code: Integer;
|
||||
begin
|
||||
Exec(ExpandConstant('{sys}\netsh.exe'),
|
||||
'advfirewall firewall delete rule name="{#AppName}"',
|
||||
'', SW_HIDE, ewWaitUntilTerminated, Code);
|
||||
end;
|
||||
|
||||
procedure OpenFirewallPort();
|
||||
var
|
||||
Code: Integer;
|
||||
begin
|
||||
// сначала снимаем старое правило, иначе при переустановке они накопятся
|
||||
RemoveFirewallRule();
|
||||
Exec(ExpandConstant('{sys}\netsh.exe'),
|
||||
'advfirewall firewall add rule name="{#AppName}" dir=in action=allow' +
|
||||
' protocol=TCP localport=8787 profile=private,domain' +
|
||||
' program="' + ExpandConstant('{app}\{#AppExeName}') + '"',
|
||||
'', SW_HIDE, ewWaitUntilTerminated, Code);
|
||||
end;
|
||||
|
||||
procedure CurStepChanged(CurStep: TSetupStep);
|
||||
var
|
||||
Started: Boolean;
|
||||
@@ -245,6 +270,7 @@ begin
|
||||
// только затем поднимаем службу.
|
||||
LockDownDataDir();
|
||||
SetPassword();
|
||||
OpenFirewallPort();
|
||||
Started := InstallService();
|
||||
|
||||
// Обе проверки — про молчаливый отказ: без них установка отрапортует об
|
||||
@@ -270,4 +296,5 @@ begin
|
||||
|
||||
StopService();
|
||||
RunSc('delete {#ServiceName}');
|
||||
RemoveFirewallRule();
|
||||
end;
|
||||
|
||||
+13
-8
@@ -33,7 +33,12 @@ mod auth;
|
||||
mod service;
|
||||
mod update;
|
||||
|
||||
const ADDR: &str = "127.0.0.1:8787";
|
||||
/// Слушаем все интерфейсы, чтобы панель открывалась и с телефона или другого
|
||||
/// компьютера в домашней сети. Правило брандмауэра ставит установщик, и только
|
||||
/// для частных сетей — в публичной панель наружу смотреть не должна.
|
||||
const BIND_ADDR: &str = "0.0.0.0:8787";
|
||||
/// Адрес для ярлыков и сообщений: 0.0.0.0 в браузер не введёшь.
|
||||
const PANEL_URL: &str = "http://127.0.0.1:8787";
|
||||
/// Служебные команды не должны мигать консольным окном.
|
||||
#[cfg(windows)]
|
||||
pub(crate) const CREATE_NO_WINDOW: u32 = 0x0800_0000;
|
||||
@@ -125,19 +130,17 @@ fn stamp() -> String {
|
||||
/// Открывает панель в браузере. Своего окна у программы нет, поэтому запуск
|
||||
/// ярлыком без этого выглядел бы как «ничего не произошло».
|
||||
fn open_panel() {
|
||||
let url = format!("http://{ADDR}");
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::process::CommandExt;
|
||||
// пустой заголовок обязателен: иначе start примет URL за имя окна
|
||||
let _ = std::process::Command::new("cmd")
|
||||
.args(["/C", "start", "", &url])
|
||||
.args(["/C", "start", "", PANEL_URL])
|
||||
.creation_flags(CREATE_NO_WINDOW)
|
||||
.spawn();
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
let _ = std::process::Command::new("open").arg(&url).spawn();
|
||||
let _ = std::process::Command::new("open").arg(PANEL_URL).spawn();
|
||||
}
|
||||
|
||||
/// Окна нет, поэтому паника иначе прошла бы совершенно незаметно: процесс
|
||||
@@ -612,12 +615,12 @@ async fn bind_panel(
|
||||
) -> Option<tokio::net::TcpListener> {
|
||||
let mut complained = false;
|
||||
loop {
|
||||
match tokio::net::TcpListener::bind(ADDR).await {
|
||||
match tokio::net::TcpListener::bind(BIND_ADDR).await {
|
||||
Ok(listener) => return Some(listener),
|
||||
Err(e) if !complained => {
|
||||
complained = true; // в журнал раз, а не каждые полминуты
|
||||
state
|
||||
.log(format!("{ADDR} занят ({e}) — жду, правила при этом работают"))
|
||||
.log(format!("{BIND_ADDR} занят ({e}) — жду, правила при этом работают"))
|
||||
.await;
|
||||
}
|
||||
Err(_) => {}
|
||||
@@ -681,7 +684,9 @@ async fn serve(shutdown: tokio::sync::watch::Receiver<bool>) {
|
||||
let Some(listener) = bind_panel(&state, shutdown.clone()).await else {
|
||||
return; // службу остановили, пока ждали порт
|
||||
};
|
||||
state.log(format!("панель на http://{ADDR}")).await;
|
||||
state
|
||||
.log(format!("панель на {PANEL_URL} и на порту 8787 в локальной сети"))
|
||||
.await;
|
||||
|
||||
let mut stop = shutdown.clone();
|
||||
let result = axum::serve(listener, app)
|
||||
|
||||
Reference in New Issue
Block a user