mirror of
https://github.com/Darkatse/TauriTavern.git
synced 2026-10-19 15:45:12 +00:00
fix(sync): enumerate LAN addresses without netlink
OpenHarmony forbids apps from binding netlink sockets, so local-ip-address failed and the whole Sync panel failed to load. Enumerate interfaces through getifaddrs (if-addrs) on every platform, the same source mdns-sd uses. - One port call returns available and default addresses from a single snapshot. - The default prefers running broadcast interfaces, then the default route, so full-tunnel VPNs and cellular links are not chosen. - The pairing return address stays route-based and moves into the adapter, its only caller.
This commit is contained in:
parent
9329278c6d
commit
b7c8b33558
9 changed files with 164 additions and 193 deletions
|
|
@ -47,9 +47,11 @@ LAN 上传保留发起方的覆盖策略,但实际回拉使用目标设备的
|
|||
|
||||
本机广播随接收服务启停,只下载时可以独立发现。发现失败不删除配对或停用仍可用的 HTTPS 连接;停止接收服务不取消已接受的本地下载作业。
|
||||
|
||||
附近发现使用 mDNS / DNS-SD。iOS 与 macOS 使用系统 Bonjour,Android、Windows 与 Linux 使用 Rust mDNS;两套后端共享服务信息与设备目录,记录更新和过期由发现后端处理。
|
||||
本机地址按用途分三类:**可用地址**列出全部非回环 IPv4,包括 VPN 与组网地址;**默认地址**用于配对链接,优先取运行中的广播接口上默认路由所在的地址,广播接口也包括虚拟网桥与 TAP;**回连地址**在配对时取本机到对端路由的源地址。
|
||||
|
||||
LAN 功能受宿主 `sync.lan` 能力门禁和平台权限约束。移动端权限、签名与生命周期要求见 [Android](../AndroidDevelopment.md) 和 [iOS](../iOSDevelopment.md)。
|
||||
附近发现使用 mDNS / DNS-SD。iOS 受直接组播权限限制,使用系统 Bonjour,macOS 共用这一后端;Android、Windows、Linux 与鸿蒙使用 Rust mDNS。两套后端共享服务信息与设备目录,记录更新和过期由发现后端处理。
|
||||
|
||||
LAN 功能受宿主 `sync.lan` 能力门禁和平台权限约束。移动端权限、签名与生命周期要求见 [Android](../AndroidDevelopment.md)、[iOS](../iOSDevelopment.md) 和 [鸿蒙](../OpenHarmonyDevelopment.md)。
|
||||
|
||||
## 面板、事件与自动同步
|
||||
|
||||
|
|
|
|||
|
|
@ -149,26 +149,32 @@ ArkWeb 的 `javaScriptOnDocumentStart` 按字典序执行多个脚本条目,
|
|||
|
||||
鸿蒙是[文件传输](CurrentState/FileTransfer.md)的一个平台实现,与 Android 共用 URI 复制代码。鸿蒙特有的约束:dialog fork 只为面板返回的 URI 授权,授权保存在 Ability 的内存中,进程结束即失效。
|
||||
|
||||
## 8. 权限
|
||||
## 8. 局域网同步
|
||||
|
||||
鸿蒙与 Android、Windows、Linux 共用 Rust mDNS 与本机地址实现,地址规则见[同步](CurrentState/Sync.md#lan-发现与信任)。鸿蒙特有的约束:普通应用不能绑定 `NETLINK_ROUTE` 套接字;本机地址经 libc `getifaddrs` 获取,鸿蒙 musl 的实现不绑定该套接字,接口查询受限时用 ioctl 补齐接口名和标志。鸿蒙没有公开的组播锁接口。
|
||||
|
||||
## 9. 权限
|
||||
|
||||
| 权限 | 用途 |
|
||||
| --- | --- |
|
||||
| `INTERNET` | 模型请求、扩展下载等网络访问 |
|
||||
| `INTERNET` | 模型请求、扩展下载、局域网同步等网络访问 |
|
||||
| `CAMERA` | 条码插件扫码(ScanKit,需要支持的 HarmonyOS 设备) |
|
||||
| `READ_PASTEBOARD` | 网页通过 `navigator.clipboard.readText` 主动读取剪贴板:`/clipboard-get`,以及 Quick Reply 的"从剪贴板添加"。复制和普通粘贴不依赖它。它属于受限权限(需要 ACL),ArkWeb 是否要求这个权限尚未实测 |
|
||||
|
||||
## 9. 未覆盖
|
||||
## 10. 未覆盖
|
||||
|
||||
- **后台生成:** 鸿蒙没有对应的后台执行保护(`generation_background` 返回空),应用退到后台后,生成可能被系统挂起。
|
||||
- **局域网同步:** 编译时走的是 Linux 路径(mdns-sd,用 netlink 获取本机地址),没有在设备上验证。
|
||||
- **局域网同步:** 已在 x86_64 模拟器验证;真机上的 Wi-Fi、热点、VPN 与跨设备互通尚未验证。
|
||||
- **系统通知、扫码、语音合成:** 尚未验证。
|
||||
|
||||
## 10. 设备验收清单
|
||||
## 11. 设备验收清单
|
||||
|
||||
- ARM64 真机和 x86_64 模拟器都能进入主界面,`window.__TAURITAVERN_HOST__` 为 `{ platform: 'ohos', kind: 'mobile' }`。
|
||||
- localStorage 和 IndexedDB 在强制结束进程后仍保留。
|
||||
- 沙箱 iframe 无法通过任何一条 IPC 路径调用原生接口。
|
||||
- 新建聊天、发送、保存、强制结束后重新打开,内容完整;超过单帧预算的聊天提交与上传成功。
|
||||
- 原生选择器导入、保存面板导出可用,取消不报错;`<input type="file">` 可用。
|
||||
- 同步面板(含 TT-Sync)可打开,接收服务能启动;与其他设备互相发现、配对并双向同步。
|
||||
- Wi-Fi 与移动数据同时在线时,配对链接使用 Wi-Fi 地址;组网地址出现在可用地址中,可通过手动地址配对与同步。
|
||||
- TT 设置页能打开;返回键逐层关闭弹窗和抽屉,最后退到后台。
|
||||
- 复制、粘贴与 `/clipboard-get` 正常;外链、全屏、媒体 Range、键盘和安全区表现正常。
|
||||
|
|
|
|||
53
src-tauri/Cargo.lock
generated
53
src-tauri/Cargo.lock
generated
|
|
@ -2735,17 +2735,6 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getset"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6cf442baaabe4213ce7d1239afc26c039180b6456da2cededa316ae2c8a77a77"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gif"
|
||||
version = "0.14.2"
|
||||
|
|
@ -4890,17 +4879,6 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
|
||||
|
||||
[[package]]
|
||||
name = "local-ip-address"
|
||||
version = "0.6.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa08fb2b1ec3ea84575e94b489d06d4ce0cbf052d12acd515838f50e3c3d63e3"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"neli",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.14"
|
||||
|
|
@ -5348,35 +5326,6 @@ dependencies = [
|
|||
"jni-sys 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "neli"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22f9786d56d972959e1408b6a93be6af13b9c1392036c5c1fafa08a1b0c6ee87"
|
||||
dependencies = [
|
||||
"bitflags 2.13.1",
|
||||
"byteorder",
|
||||
"derive_builder",
|
||||
"getset",
|
||||
"libc",
|
||||
"log",
|
||||
"neli-proc-macros",
|
||||
"parking_lot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "neli-proc-macros"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05d8d08c6e98f20a62417478ebf7be8e1425ec9acecc6f63e22da633f6b71609"
|
||||
dependencies = [
|
||||
"either",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "new_debug_unreachable"
|
||||
version = "1.0.6"
|
||||
|
|
@ -9477,7 +9426,7 @@ dependencies = [
|
|||
"base64 0.23.1",
|
||||
"filetime",
|
||||
"futures-util",
|
||||
"local-ip-address",
|
||||
"if-addrs",
|
||||
"mdns-sd",
|
||||
"rand 0.10.3",
|
||||
"reqwest",
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ async-trait = "0.1"
|
|||
axum = { version = "0.8", default-features = false, features = ["http1", "http2", "json", "query", "tokio"] }
|
||||
axum-server = { version = "0.8", features = ["tls-rustls"] }
|
||||
filetime = "0.2"
|
||||
local-ip-address = "0.6"
|
||||
if-addrs = "0.15"
|
||||
rand = { workspace = true }
|
||||
reqwest = { workspace = true, features = ["json", "rustls", "stream"] }
|
||||
rustls = { version = "0.23", default-features = false, features = ["aws_lc_rs", "std", "tls12"] }
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use ttsync_contract::status::StatusResponse;
|
|||
use ttsync_contract::sync::OverwritePolicy;
|
||||
|
||||
use super::LanStatusResponse;
|
||||
use super::discovery::LocalLanAddressDiscovery;
|
||||
use super::discovery::routed_advertise_address;
|
||||
use crate::sync::http_client::{
|
||||
SyncHttpClient, bearer_auth_value, ensure_dataset_scope_v1, ensure_success,
|
||||
};
|
||||
|
|
@ -25,7 +25,7 @@ use tt_domain::models::lan_sync::{
|
|||
LanPairCompleteRequest, LanPairCompleteResponse, LanSyncPairedDevice,
|
||||
};
|
||||
use tt_ports::lan_discovery::LanDeviceDiscovery;
|
||||
use tt_ports::lan_sync::{LanAddressDiscovery, LanPairingClient};
|
||||
use tt_ports::lan_sync::LanPairingClient;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LanSyncClient {
|
||||
|
|
@ -264,9 +264,7 @@ impl LanPairingClient for HttpLanPairingClient {
|
|||
})
|
||||
.await
|
||||
.map_err(|_| DomainError::Transient("Timed out locating LAN Sync device".to_string()))??;
|
||||
let client_base_url = LocalLanAddressDiscovery
|
||||
.routed_advertise_address(&base_url, local_device.port)
|
||||
.await?;
|
||||
let client_base_url = routed_advertise_address(&base_url, local_device.port).await?;
|
||||
let request = LanPairCompleteRequest {
|
||||
device_id: local_device.device_id.clone(),
|
||||
device_name: local_device.device_name.clone(),
|
||||
|
|
|
|||
|
|
@ -1,112 +1,150 @@
|
|||
use async_trait::async_trait;
|
||||
use local_ip_address::{list_afinet_netifas, local_ip};
|
||||
use std::collections::BTreeMap;
|
||||
use std::io;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
|
||||
|
||||
use if_addrs::IfAddr;
|
||||
use url::Url;
|
||||
|
||||
use tt_domain::errors::DomainError;
|
||||
use tt_ports::lan_sync::LanAddressDiscovery;
|
||||
use tt_ports::lan_sync::{LanAddressDiscovery, LocalLanAddresses};
|
||||
|
||||
/// TEST-NET-1 (RFC 5737) is never on-link, so routing to it selects the default route.
|
||||
const DEFAULT_ROUTE_PROBE: SocketAddr =
|
||||
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(192, 0, 2, 1), 9));
|
||||
|
||||
pub struct LocalLanAddressDiscovery;
|
||||
|
||||
#[async_trait]
|
||||
impl LanAddressDiscovery for LocalLanAddressDiscovery {
|
||||
fn list_available_addresses(&self, port: u16) -> Result<Vec<String>, DomainError> {
|
||||
let ifas =
|
||||
list_afinet_netifas().map_err(|error| DomainError::InternalError(error.to_string()))?;
|
||||
|
||||
let mut addresses = ifas
|
||||
.into_iter()
|
||||
.filter_map(|(_name, ip)| match ip {
|
||||
std::net::IpAddr::V4(ip) => {
|
||||
if ip.is_loopback() || ip.is_unspecified() {
|
||||
None
|
||||
} else {
|
||||
Some(format!("https://{}:{}", ip, port))
|
||||
}
|
||||
}
|
||||
std::net::IpAddr::V6(_) => None,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
addresses.sort();
|
||||
addresses.dedup();
|
||||
Ok(addresses)
|
||||
}
|
||||
|
||||
fn default_advertise_address(
|
||||
&self,
|
||||
port: u16,
|
||||
available_addresses: &[String],
|
||||
) -> Option<String> {
|
||||
let route_ip = local_ip().ok().and_then(|ip| match ip {
|
||||
std::net::IpAddr::V4(v4) => Some(format!("https://{}:{}", v4, port)),
|
||||
std::net::IpAddr::V6(_) => None,
|
||||
});
|
||||
|
||||
route_ip
|
||||
.filter(|addr| available_addresses.contains(addr))
|
||||
.or_else(|| available_addresses.first().cloned())
|
||||
}
|
||||
|
||||
async fn routed_advertise_address(
|
||||
&self,
|
||||
peer_base_url: &str,
|
||||
local_port: u16,
|
||||
) -> Result<String, DomainError> {
|
||||
let peer_url = Url::parse(peer_base_url)
|
||||
.map_err(|error| DomainError::InvalidData(error.to_string()))?;
|
||||
if peer_url.scheme() != "https" {
|
||||
return Err(DomainError::InvalidData(
|
||||
"LAN Sync peer URL must use https".to_string(),
|
||||
));
|
||||
}
|
||||
let peer_host = peer_url.host_str().ok_or_else(|| {
|
||||
DomainError::InvalidData("LAN Sync peer URL is missing host".to_string())
|
||||
fn local_addresses(&self, port: u16) -> Result<LocalLanAddresses, DomainError> {
|
||||
let interfaces = if_addrs::get_if_addrs().map_err(|error| {
|
||||
DomainError::InternalError(format!("Failed to enumerate LAN addresses: {error}"))
|
||||
})?;
|
||||
let peer_port = peer_url.port_or_known_default().ok_or_else(|| {
|
||||
DomainError::InvalidData("LAN Sync peer URL is missing port".to_string())
|
||||
})?;
|
||||
|
||||
let remote_addr = tokio::net::lookup_host((peer_host, peer_port))
|
||||
.await
|
||||
.map_err(|error| DomainError::InternalError(error.to_string()))?
|
||||
.find(|addr| addr.is_ipv4())
|
||||
.ok_or_else(|| {
|
||||
DomainError::InvalidData("No IPv4 LAN Sync peer address resolved".to_string())
|
||||
})?;
|
||||
|
||||
let socket = tokio::net::UdpSocket::bind((std::net::Ipv4Addr::UNSPECIFIED, 0))
|
||||
.await
|
||||
.map_err(|error| DomainError::InternalError(error.to_string()))?;
|
||||
socket
|
||||
.connect(remote_addr)
|
||||
.await
|
||||
.map_err(|error| DomainError::InternalError(error.to_string()))?;
|
||||
let local_addr = socket
|
||||
.local_addr()
|
||||
.map_err(|error| DomainError::InternalError(error.to_string()))?;
|
||||
|
||||
match local_addr.ip() {
|
||||
std::net::IpAddr::V4(ip) if !ip.is_unspecified() => {
|
||||
Ok(format!("https://{}:{}", ip, local_port))
|
||||
// Address -> whether it is on a running broadcast interface.
|
||||
let mut addresses = BTreeMap::<Ipv4Addr, bool>::new();
|
||||
for interface in &interfaces {
|
||||
let IfAddr::V4(address) = &interface.addr else {
|
||||
continue;
|
||||
};
|
||||
if address.ip.is_loopback() || address.ip.is_unspecified() {
|
||||
continue;
|
||||
}
|
||||
_ => Err(DomainError::InvalidData(
|
||||
"No routable IPv4 LAN Sync address".to_string(),
|
||||
)),
|
||||
*addresses.entry(address.ip).or_default() |=
|
||||
interface.is_oper_up() && address.broadcast.is_some();
|
||||
}
|
||||
|
||||
// The probe is only a preference; when it fails, candidate order decides.
|
||||
let route = route_source(DEFAULT_ROUTE_PROBE)
|
||||
.inspect_err(|error| tracing::debug!(%error, "LAN default route probe failed"))
|
||||
.ok();
|
||||
Ok(LocalLanAddresses {
|
||||
default: select_default(&addresses, route).map(|ip| lan_url(ip, port)),
|
||||
available: addresses.keys().map(|&ip| lan_url(ip, port)).collect(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Candidates are the broadcast-interface addresses, or all addresses when there are none.
|
||||
/// The route wins among candidates; otherwise the lowest address does.
|
||||
fn select_default(
|
||||
addresses: &BTreeMap<Ipv4Addr, bool>,
|
||||
route: Option<Ipv4Addr>,
|
||||
) -> Option<Ipv4Addr> {
|
||||
let broadcast_only = addresses.values().any(|&broadcast| broadcast);
|
||||
let is_candidate = |ip: &Ipv4Addr| {
|
||||
addresses
|
||||
.get(ip)
|
||||
.is_some_and(|&broadcast| broadcast || !broadcast_only)
|
||||
};
|
||||
route
|
||||
.filter(is_candidate)
|
||||
.or_else(|| addresses.keys().copied().find(is_candidate))
|
||||
}
|
||||
|
||||
fn lan_url(ip: Ipv4Addr, port: u16) -> String {
|
||||
format!("https://{ip}:{port}")
|
||||
}
|
||||
|
||||
/// A UDP connect sends nothing; the kernel only resolves the route and its source address.
|
||||
fn route_source(remote: SocketAddr) -> io::Result<Ipv4Addr> {
|
||||
let socket = UdpSocket::bind((Ipv4Addr::UNSPECIFIED, 0))?;
|
||||
socket.connect(remote)?;
|
||||
match socket.local_addr()?.ip() {
|
||||
IpAddr::V4(ip) if !ip.is_unspecified() => Ok(ip),
|
||||
_ => Err(io::Error::new(
|
||||
io::ErrorKind::AddrNotAvailable,
|
||||
"No routable IPv4 LAN Sync address",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn routed_advertise_address(
|
||||
peer_base_url: &str,
|
||||
local_port: u16,
|
||||
) -> Result<String, DomainError> {
|
||||
let peer_url =
|
||||
Url::parse(peer_base_url).map_err(|error| DomainError::InvalidData(error.to_string()))?;
|
||||
if peer_url.scheme() != "https" {
|
||||
return Err(DomainError::InvalidData(
|
||||
"LAN Sync peer URL must use https".to_string(),
|
||||
));
|
||||
}
|
||||
let peer_host = peer_url
|
||||
.host_str()
|
||||
.ok_or_else(|| DomainError::InvalidData("LAN Sync peer URL is missing host".to_string()))?;
|
||||
let peer_port = peer_url
|
||||
.port_or_known_default()
|
||||
.ok_or_else(|| DomainError::InvalidData("LAN Sync peer URL is missing port".to_string()))?;
|
||||
let remote_addr = tokio::net::lookup_host((peer_host, peer_port))
|
||||
.await
|
||||
.map_err(|error| DomainError::InternalError(error.to_string()))?
|
||||
.find(|addr| addr.is_ipv4())
|
||||
.ok_or_else(|| {
|
||||
DomainError::InvalidData("No IPv4 LAN Sync peer address resolved".to_string())
|
||||
})?;
|
||||
let ip = route_source(remote_addr).map_err(|error| {
|
||||
DomainError::InternalError(format!(
|
||||
"Failed to determine LAN Sync return address: {error}"
|
||||
))
|
||||
})?;
|
||||
Ok(lan_url(ip, local_port))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn default_address_prefers_broadcast_interfaces_then_the_route() {
|
||||
let wifi = Ipv4Addr::new(192, 168, 1, 20);
|
||||
let ethernet = Ipv4Addr::new(192, 168, 1, 10);
|
||||
let tunnel = Ipv4Addr::new(100, 64, 0, 1);
|
||||
for (interfaces, route, expected) in [
|
||||
(
|
||||
vec![(wifi, true), (ethernet, true), (tunnel, false)],
|
||||
Some(wifi),
|
||||
Some(wifi),
|
||||
),
|
||||
(
|
||||
vec![(wifi, true), (tunnel, false)],
|
||||
Some(tunnel),
|
||||
Some(wifi),
|
||||
),
|
||||
(vec![(wifi, false), (tunnel, false)], Some(wifi), Some(wifi)),
|
||||
(vec![(wifi, true), (ethernet, true)], None, Some(ethernet)),
|
||||
(vec![], None, None),
|
||||
] {
|
||||
assert_eq!(
|
||||
select_default(&interfaces.into_iter().collect(), route),
|
||||
expected
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn routed_lan_advertise_address_uses_peer_route() {
|
||||
let address = LocalLanAddressDiscovery
|
||||
.routed_advertise_address("https://127.0.0.1:50000", 56000)
|
||||
let address = routed_advertise_address("https://127.0.0.1:50000", 56000)
|
||||
.await
|
||||
.expect("routed address");
|
||||
|
||||
assert_eq!(address, "https://127.0.0.1:56000");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,15 +93,12 @@ impl LanSyncService {
|
|||
None => (false, settings.port),
|
||||
};
|
||||
|
||||
let available_addresses = self.addresses.list_available_addresses(port)?;
|
||||
let address = self
|
||||
.addresses
|
||||
.default_advertise_address(port, &available_addresses);
|
||||
let addresses = self.addresses.local_addresses(port)?;
|
||||
Ok(LanSyncStatus {
|
||||
device_name: identity.device_name,
|
||||
running,
|
||||
address,
|
||||
available_addresses,
|
||||
address: addresses.default,
|
||||
available_addresses: addresses.available,
|
||||
port,
|
||||
sync_mode,
|
||||
manual_default_mode,
|
||||
|
|
@ -207,10 +204,10 @@ impl LanSyncService {
|
|||
|
||||
pub async fn get_pairing_info(&self) -> Result<LanSyncPairingInfo, DomainError> {
|
||||
let server_info = self.ensure_server_running().await?;
|
||||
let addresses = self.addresses.list_available_addresses(server_info.port)?;
|
||||
let address = self
|
||||
.addresses
|
||||
.default_advertise_address(server_info.port, &addresses)
|
||||
.local_addresses(server_info.port)?
|
||||
.default
|
||||
.ok_or_else(|| {
|
||||
DomainError::InvalidData("No available LAN sync addresses".to_string())
|
||||
})?;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use ttsync_contract::peer::DeviceId;
|
|||
use ttsync_contract::sync::{OverwritePolicy, SyncMode};
|
||||
|
||||
use super::pairing_link::{default_lan_permissions, device_pubkey_b64url};
|
||||
use super::ports::LanPairingApprovalRequest;
|
||||
use super::ports::{LanPairingApprovalRequest, LocalLanAddresses};
|
||||
use super::*;
|
||||
use crate::services::data_change_reconciler::DataChangeReconciler;
|
||||
use crate::services::sync_job_coordinator::{SyncJobEventPublisher, SyncJobExecutor};
|
||||
|
|
@ -215,26 +215,12 @@ impl LanServerControl for MemoryServerControl {
|
|||
|
||||
struct NoopAddressDiscovery;
|
||||
|
||||
#[async_trait]
|
||||
impl LanAddressDiscovery for NoopAddressDiscovery {
|
||||
fn list_available_addresses(&self, _port: u16) -> Result<Vec<String>, DomainError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
fn default_advertise_address(
|
||||
&self,
|
||||
_port: u16,
|
||||
_available_addresses: &[String],
|
||||
) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
async fn routed_advertise_address(
|
||||
&self,
|
||||
_peer_base_url: &str,
|
||||
_local_port: u16,
|
||||
) -> Result<String, DomainError> {
|
||||
Err(DomainError::InternalError("not used".to_string()))
|
||||
fn local_addresses(&self, _port: u16) -> Result<LocalLanAddresses, DomainError> {
|
||||
Ok(LocalLanAddresses {
|
||||
available: Vec::new(),
|
||||
default: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -60,19 +60,14 @@ pub trait LanServerEvents: Send + Sync {
|
|||
fn pairing_completed(&self);
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LocalLanAddresses {
|
||||
pub available: Vec<String>,
|
||||
pub default: Option<String>,
|
||||
}
|
||||
|
||||
pub trait LanAddressDiscovery: Send + Sync {
|
||||
fn list_available_addresses(&self, port: u16) -> Result<Vec<String>, DomainError>;
|
||||
fn default_advertise_address(
|
||||
&self,
|
||||
port: u16,
|
||||
available_addresses: &[String],
|
||||
) -> Option<String>;
|
||||
async fn routed_advertise_address(
|
||||
&self,
|
||||
peer_base_url: &str,
|
||||
local_port: u16,
|
||||
) -> Result<String, DomainError>;
|
||||
fn local_addresses(&self, port: u16) -> Result<LocalLanAddresses, DomainError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue