Refine WireGuard client routing and authentication UI

This commit is contained in:
opsx committed 2026-10-21 21:10:53 +08:00
1 parent eabb3e4472
commit 41fb9d5631
33 files changed
+1709 -469

No files matched your search

+39 -7
View File
@@ -11,6 +11,8 @@ pub struct Line {
pub remark: String,
pub state: String,
pub endpoint: String,
#[serde(default)]
pub routing_mode: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -21,6 +23,15 @@ pub struct LoginResult {
pub lines: Vec<Line>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClientCapabilities {
pub password: bool,
pub oidc: bool,
#[serde(default)]
pub oidc_provider: Option<String>,
pub wireguard: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeviceConfig {
pub device_id: u64,
@@ -33,6 +44,8 @@ pub struct DeviceConfig {
pub preshared_key: String,
pub endpoint: String,
pub allowed_ips: String,
#[serde(default)]
pub latency_probe: String,
pub dns: String,
pub mtu: u32,
pub keepalive: u32,
@@ -143,13 +156,8 @@ pub async fn login(
device_name: &str,
) -> Result<LoginResult, ApiError> {
// 旧版服务端会把未知 /api/client/* 路径返回 200 HTML;先确认能力,避免发送密码。
let capabilities = client()?
.get(api_url(base_url, "/api/client/capabilities")?)
.send()
.await
.map_err(|err| ApiError::Transport(format!("连接服务端失败:{err}")))?;
let supported: serde_json::Value = parse_response(capabilities).await?;
if supported.get("password").and_then(|v| v.as_bool()) != Some(true) {
let supported = capabilities(base_url).await?;
if !supported.password {
return Err(ApiError::Transport("服务端尚未启用客户端密码登录".into()));
}
let resp = client()?
@@ -167,6 +175,15 @@ pub async fn login(
parse_response(resp).await
}
pub async fn capabilities(base_url: &str) -> Result<ClientCapabilities, ApiError> {
let response = client()?
.get(api_url(base_url, "/api/client/capabilities")?)
.send()
.await
.map_err(|err| ApiError::Transport(format!("连接服务端失败:{err}")))?;
parse_response(response).await
}
pub async fn logout(base_url: &str, token: &str) -> Result<(), ApiError> {
let resp = client()?
.post(api_url(base_url, "/api/client/logout")?)
@@ -292,4 +309,19 @@ mod tests {
);
assert!(api_url("ftp://vpn.example.com", "/api/client/lines").is_err());
}
#[test]
fn client_auth_capabilities_accept_legacy_and_provider_specific_responses() {
let current: ClientCapabilities = serde_json::from_str(
r#"{"password":true,"oidc":false,"wireguard":true}"#,
).unwrap();
assert!(current.password);
assert!(!current.oidc);
assert!(current.oidc_provider.is_none());
let future: ClientCapabilities = serde_json::from_str(
r#"{"password":false,"oidc":true,"oidc_provider":"feishu","wireguard":true}"#,
).unwrap();
assert!(!future.password);
assert_eq!(future.oidc_provider.as_deref(), Some("feishu"));
}
}