mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-01-29 09:01:54 +08:00
feat!: 移除内置公共服务器
- 移除公共 RustDesk ID 服务器 (用户需自行配置) - 移除公共 TURN 服务器 (仅保留 Google STUN) - 清理废弃代码: PublicServerInfo, is_using_public_server 等 - 更新前端 UI 和国际化文本 - 重新生成 TypeScript 类型 破坏性变更: 不再提供内置公共服务器。用户必须配置自己的 RustDesk 服务器和 TURN 服务器才能在生产环境中使用。
This commit is contained in:
@@ -5,8 +5,6 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use typeshare::typeshare;
|
||||
|
||||
use crate::secrets;
|
||||
|
||||
/// RustDesk configuration
|
||||
#[typeshare]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -15,9 +13,8 @@ pub struct RustDeskConfig {
|
||||
/// Enable RustDesk protocol
|
||||
pub enabled: bool,
|
||||
|
||||
/// Rendezvous server address (hbbs), e.g., "rs.example.com" or "192.168.1.100"
|
||||
/// Port defaults to 21116 if not specified
|
||||
/// If empty, uses the public server from secrets.toml
|
||||
/// Rendezvous server address (hbbs), e.g., "rs.example.com" or "192.168.1.100:21116"
|
||||
/// Required for RustDesk to function
|
||||
pub rendezvous_server: String,
|
||||
|
||||
/// Relay server address (hbbr), if different from rendezvous server
|
||||
@@ -79,39 +76,17 @@ impl Default for RustDeskConfig {
|
||||
|
||||
impl RustDeskConfig {
|
||||
/// Check if the configuration is valid for starting the service
|
||||
/// Returns true if enabled and has a valid server (user-configured or public)
|
||||
/// Returns true if enabled and has a valid server
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.enabled
|
||||
&& !self.effective_rendezvous_server().is_empty()
|
||||
&& !self.rendezvous_server.is_empty()
|
||||
&& !self.device_id.is_empty()
|
||||
&& !self.device_password.is_empty()
|
||||
}
|
||||
|
||||
/// Check if using the public server (user left rendezvous_server empty)
|
||||
pub fn is_using_public_server(&self) -> bool {
|
||||
self.rendezvous_server.is_empty() && secrets::rustdesk::has_public_server()
|
||||
}
|
||||
|
||||
/// Get the effective rendezvous server (user-configured or public fallback)
|
||||
/// Get the rendezvous server (user-configured)
|
||||
pub fn effective_rendezvous_server(&self) -> &str {
|
||||
if self.rendezvous_server.is_empty() {
|
||||
secrets::rustdesk::PUBLIC_SERVER
|
||||
} else {
|
||||
&self.rendezvous_server
|
||||
}
|
||||
}
|
||||
|
||||
/// Get public server info for display (server address and public key)
|
||||
/// Returns None if no public server is configured
|
||||
pub fn public_server_info() -> Option<PublicServerInfo> {
|
||||
if secrets::rustdesk::has_public_server() {
|
||||
Some(PublicServerInfo {
|
||||
server: secrets::rustdesk::PUBLIC_SERVER.to_string(),
|
||||
public_key: secrets::rustdesk::PUBLIC_KEY.to_string(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
&self.rendezvous_server
|
||||
}
|
||||
|
||||
/// Generate a new random device ID
|
||||
@@ -148,8 +123,10 @@ impl RustDeskConfig {
|
||||
|
||||
/// Get the rendezvous server address with default port
|
||||
pub fn rendezvous_addr(&self) -> String {
|
||||
let server = self.effective_rendezvous_server();
|
||||
if server.contains(':') {
|
||||
let server = &self.rendezvous_server;
|
||||
if server.is_empty() {
|
||||
String::new()
|
||||
} else if server.contains(':') {
|
||||
server.to_string()
|
||||
} else {
|
||||
format!("{}:21116", server)
|
||||
@@ -165,8 +142,8 @@ impl RustDeskConfig {
|
||||
format!("{}:21117", s)
|
||||
}
|
||||
}).or_else(|| {
|
||||
// Default: same host as effective rendezvous server
|
||||
let server = self.effective_rendezvous_server();
|
||||
// Default: same host as rendezvous server
|
||||
let server = &self.rendezvous_server;
|
||||
if !server.is_empty() {
|
||||
let host = server.split(':').next().unwrap_or("");
|
||||
if !host.is_empty() {
|
||||
@@ -181,16 +158,6 @@ impl RustDeskConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// Public server information for display to users
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[typeshare]
|
||||
pub struct PublicServerInfo {
|
||||
/// Public server address
|
||||
pub server: String,
|
||||
/// Public key for client connection
|
||||
pub public_key: String,
|
||||
}
|
||||
|
||||
/// Generate a random 9-digit device ID
|
||||
pub fn generate_device_id() -> String {
|
||||
use rand::Rng;
|
||||
@@ -239,6 +206,10 @@ mod tests {
|
||||
|
||||
config.rendezvous_server = "example.com:21116".to_string();
|
||||
assert_eq!(config.rendezvous_addr(), "example.com:21116");
|
||||
|
||||
// Empty server returns empty string
|
||||
config.rendezvous_server = String::new();
|
||||
assert_eq!(config.rendezvous_addr(), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -252,6 +223,10 @@ mod tests {
|
||||
// Explicit relay server
|
||||
config.relay_server = Some("relay.example.com".to_string());
|
||||
assert_eq!(config.relay_addr(), Some("relay.example.com:21117".to_string()));
|
||||
|
||||
// No rendezvous server, relay is None
|
||||
config.rendezvous_server = String::new();
|
||||
assert_eq!(config.relay_addr(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -262,10 +237,8 @@ mod tests {
|
||||
config.rendezvous_server = "custom.example.com".to_string();
|
||||
assert_eq!(config.effective_rendezvous_server(), "custom.example.com");
|
||||
|
||||
// When empty, falls back to public server (if configured)
|
||||
// When empty, returns empty
|
||||
config.rendezvous_server = String::new();
|
||||
// This will return PUBLIC_SERVER from secrets
|
||||
let effective = config.effective_rendezvous_server();
|
||||
assert!(!effective.is_empty() || !secrets::rustdesk::has_public_server());
|
||||
assert_eq!(config.effective_rendezvous_server(), "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,16 +239,10 @@ impl RustDeskService {
|
||||
let config = service_config_punch.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
// Get relay_key from config, or use public server's relay_key if using public server
|
||||
// Get relay_key from config (no public server fallback)
|
||||
let relay_key = {
|
||||
let cfg = config.read();
|
||||
cfg.relay_key.clone().unwrap_or_else(|| {
|
||||
if cfg.is_using_public_server() {
|
||||
crate::secrets::rustdesk::RELAY_KEY.to_string()
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
})
|
||||
cfg.relay_key.clone().unwrap_or_default()
|
||||
};
|
||||
|
||||
// Try P2P direct connection first
|
||||
@@ -295,16 +289,10 @@ impl RustDeskService {
|
||||
let config = service_config.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
// Get relay_key from config, or use public server's relay_key if using public server
|
||||
// Get relay_key from config (no public server fallback)
|
||||
let relay_key = {
|
||||
let cfg = config.read();
|
||||
cfg.relay_key.clone().unwrap_or_else(|| {
|
||||
if cfg.is_using_public_server() {
|
||||
crate::secrets::rustdesk::RELAY_KEY.to_string()
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
})
|
||||
cfg.relay_key.clone().unwrap_or_default()
|
||||
};
|
||||
|
||||
if let Err(e) = handle_relay_request(
|
||||
|
||||
@@ -4,7 +4,7 @@ use axum::{extract::State, Json};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::rustdesk::config::{PublicServerInfo, RustDeskConfig};
|
||||
use crate::rustdesk::config::RustDeskConfig;
|
||||
use crate::state::AppState;
|
||||
|
||||
use super::apply::apply_rustdesk_config;
|
||||
@@ -23,8 +23,6 @@ pub struct RustDeskConfigResponse {
|
||||
pub has_keypair: bool,
|
||||
/// 是否已设置 relay key
|
||||
pub has_relay_key: bool,
|
||||
/// 是否使用公共服务器(用户留空时)
|
||||
pub using_public_server: bool,
|
||||
}
|
||||
|
||||
impl From<&RustDeskConfig> for RustDeskConfigResponse {
|
||||
@@ -37,7 +35,6 @@ impl From<&RustDeskConfig> for RustDeskConfigResponse {
|
||||
has_password: !config.device_password.is_empty(),
|
||||
has_keypair: config.public_key.is_some() && config.private_key.is_some(),
|
||||
has_relay_key: config.relay_key.is_some(),
|
||||
using_public_server: config.is_using_public_server(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,8 +45,6 @@ pub struct RustDeskStatusResponse {
|
||||
pub config: RustDeskConfigResponse,
|
||||
pub service_status: String,
|
||||
pub rendezvous_status: Option<String>,
|
||||
/// 公共服务器信息(仅当有公共服务器配置时返回)
|
||||
pub public_server: Option<PublicServerInfo>,
|
||||
}
|
||||
|
||||
/// 获取 RustDesk 配置
|
||||
@@ -73,14 +68,10 @@ pub async fn get_rustdesk_status(State(state): State<Arc<AppState>>) -> Json<Rus
|
||||
}
|
||||
};
|
||||
|
||||
// 获取公共服务器信息
|
||||
let public_server = RustDeskConfig::public_server_info();
|
||||
|
||||
Json(RustDeskStatusResponse {
|
||||
config: RustDeskConfigResponse::from(&config),
|
||||
service_status,
|
||||
rendezvous_status,
|
||||
public_server,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1814,35 +1814,62 @@ pub struct IceServerInfo {
|
||||
}
|
||||
|
||||
/// Get ICE servers configuration for client-side WebRTC
|
||||
/// Returns user-configured servers, or Google STUN as fallback if none configured
|
||||
pub async fn webrtc_ice_servers(State(state): State<Arc<AppState>>) -> Json<IceServersResponse> {
|
||||
use crate::webrtc::config::public_ice;
|
||||
|
||||
let config = state.config.get();
|
||||
let mut ice_servers = Vec::new();
|
||||
|
||||
// Add STUN server
|
||||
if let Some(ref stun) = config.stream.stun_server {
|
||||
if !stun.is_empty() {
|
||||
// Check if user has configured custom ICE servers
|
||||
let has_custom_stun = config
|
||||
.stream
|
||||
.stun_server
|
||||
.as_ref()
|
||||
.map(|s| !s.is_empty())
|
||||
.unwrap_or(false);
|
||||
let has_custom_turn = config
|
||||
.stream
|
||||
.turn_server
|
||||
.as_ref()
|
||||
.map(|s| !s.is_empty())
|
||||
.unwrap_or(false);
|
||||
|
||||
if has_custom_stun || has_custom_turn {
|
||||
// Use user-configured ICE servers
|
||||
if let Some(ref stun) = config.stream.stun_server {
|
||||
if !stun.is_empty() {
|
||||
ice_servers.push(IceServerInfo {
|
||||
urls: vec![stun.clone()],
|
||||
username: None,
|
||||
credential: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref turn) = config.stream.turn_server {
|
||||
if !turn.is_empty() {
|
||||
let username = config.stream.turn_username.clone();
|
||||
let credential = config.stream.turn_password.clone();
|
||||
if username.is_some() && credential.is_some() {
|
||||
ice_servers.push(IceServerInfo {
|
||||
urls: vec![turn.clone()],
|
||||
username,
|
||||
credential,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No custom servers configured - use Google STUN as default
|
||||
if let Some(stun) = public_ice::stun_server() {
|
||||
ice_servers.push(IceServerInfo {
|
||||
urls: vec![stun.clone()],
|
||||
urls: vec![stun],
|
||||
username: None,
|
||||
credential: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Add TURN server (with credentials)
|
||||
if let Some(ref turn) = config.stream.turn_server {
|
||||
if !turn.is_empty() {
|
||||
let username = config.stream.turn_username.clone();
|
||||
let credential = config.stream.turn_password.clone();
|
||||
// Only add TURN if credentials are provided
|
||||
if username.is_some() && credential.is_some() {
|
||||
ice_servers.push(IceServerInfo {
|
||||
urls: vec![turn.clone()],
|
||||
username,
|
||||
credential,
|
||||
});
|
||||
}
|
||||
}
|
||||
// Note: TURN servers are not provided - users must configure their own
|
||||
}
|
||||
|
||||
Json(IceServersResponse { ice_servers })
|
||||
|
||||
@@ -4,21 +4,21 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::secrets;
|
||||
|
||||
/// Public ICE server utilities
|
||||
/// ICE server utilities - Only provides Google STUN, no TURN
|
||||
pub mod public_ice {
|
||||
use super::*;
|
||||
|
||||
/// Check if public ICE servers are configured (at compile time)
|
||||
/// Always returns true (we have Google STUN)
|
||||
pub fn is_configured() -> bool {
|
||||
secrets::ice::is_configured()
|
||||
}
|
||||
|
||||
/// Check if public TURN servers are configured (requires credentials)
|
||||
/// Always returns false (TURN not provided)
|
||||
pub fn has_turn() -> bool {
|
||||
secrets::ice::has_turn()
|
||||
}
|
||||
|
||||
/// Get the public STUN server URL
|
||||
/// Get the Google STUN server URL
|
||||
pub fn stun_server() -> Option<String> {
|
||||
let server = secrets::ice::STUN_SERVER;
|
||||
if server.is_empty() {
|
||||
@@ -28,33 +28,15 @@ pub mod public_ice {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get public TURN servers as TurnServer structs
|
||||
/// Always returns empty vector (TURN not provided)
|
||||
pub fn turn_servers() -> Vec<TurnServer> {
|
||||
if !secrets::ice::has_turn() {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
let urls: Vec<String> = secrets::ice::TURN_URLS
|
||||
.split(',')
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect();
|
||||
|
||||
if urls.is_empty() {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
vec![TurnServer {
|
||||
urls,
|
||||
username: secrets::ice::TURN_USERNAME.to_string(),
|
||||
credential: secrets::ice::TURN_PASSWORD.to_string(),
|
||||
}]
|
||||
vec![]
|
||||
}
|
||||
|
||||
/// Get all public ICE servers (STUN + TURN) for WebRTC configuration
|
||||
/// Get all public ICE servers (STUN only, no TURN)
|
||||
pub fn get_all_servers() -> (Vec<String>, Vec<TurnServer>) {
|
||||
let stun_servers = stun_server().into_iter().collect();
|
||||
let turn_servers = turn_servers();
|
||||
let turn_servers = vec![];
|
||||
(stun_servers, turn_servers)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,46 +199,56 @@ impl PeerConnection {
|
||||
pc.on_data_channel(Box::new(move |dc: Arc<RTCDataChannel>| {
|
||||
let data_channel = data_channel.clone();
|
||||
let hid = hid_clone.clone();
|
||||
let label = dc.label().to_string();
|
||||
|
||||
Box::pin(async move {
|
||||
info!("Data channel opened with HID support: {}", dc.label());
|
||||
// Handle both reliable (hid) and unreliable (hid-unreliable) channels
|
||||
let is_hid_channel = label == "hid" || label == "hid-unreliable";
|
||||
|
||||
// Store data channel
|
||||
*data_channel.write().await = Some(dc.clone());
|
||||
if is_hid_channel {
|
||||
info!("HID DataChannel opened: {} (unreliable: {})", label, label == "hid-unreliable");
|
||||
|
||||
// Set up message handler with HID processing
|
||||
// Immediately spawn task in tokio runtime for low latency
|
||||
dc.on_message(Box::new(move |msg: DataChannelMessage| {
|
||||
let hid = hid.clone();
|
||||
// Store the reliable data channel for sending responses
|
||||
if label == "hid" {
|
||||
*data_channel.write().await = Some(dc.clone());
|
||||
}
|
||||
|
||||
tokio::spawn(async move {
|
||||
debug!("DataChannel HID message: {} bytes", msg.data.len());
|
||||
// Set up message handler with HID processing
|
||||
// Both channels use the same HID processing logic
|
||||
dc.on_message(Box::new(move |msg: DataChannelMessage| {
|
||||
let hid = hid.clone();
|
||||
|
||||
// Parse and process HID message
|
||||
if let Some(event) = parse_hid_message(&msg.data) {
|
||||
match event {
|
||||
HidChannelEvent::Keyboard(kb_event) => {
|
||||
if let Err(e) = hid.send_keyboard(kb_event).await {
|
||||
debug!("Failed to send keyboard event: {}", e);
|
||||
tokio::spawn(async move {
|
||||
debug!("DataChannel HID message: {} bytes", msg.data.len());
|
||||
|
||||
// Parse and process HID message
|
||||
if let Some(event) = parse_hid_message(&msg.data) {
|
||||
match event {
|
||||
HidChannelEvent::Keyboard(kb_event) => {
|
||||
if let Err(e) = hid.send_keyboard(kb_event).await {
|
||||
debug!("Failed to send keyboard event: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
HidChannelEvent::Mouse(ms_event) => {
|
||||
if let Err(e) = hid.send_mouse(ms_event).await {
|
||||
debug!("Failed to send mouse event: {}", e);
|
||||
HidChannelEvent::Mouse(ms_event) => {
|
||||
if let Err(e) = hid.send_mouse(ms_event).await {
|
||||
debug!("Failed to send mouse event: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
HidChannelEvent::Consumer(consumer_event) => {
|
||||
if let Err(e) = hid.send_consumer(consumer_event).await {
|
||||
debug!("Failed to send consumer event: {}", e);
|
||||
HidChannelEvent::Consumer(consumer_event) => {
|
||||
if let Err(e) = hid.send_consumer(consumer_event).await {
|
||||
debug!("Failed to send consumer event: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Return empty future (actual work is spawned above)
|
||||
Box::pin(async {})
|
||||
}));
|
||||
// Return empty future (actual work is spawned above)
|
||||
Box::pin(async {})
|
||||
}));
|
||||
} else {
|
||||
info!("Non-HID DataChannel opened: {}", label);
|
||||
}
|
||||
})
|
||||
}));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user