mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-01-28 08:31:52 +08:00
feat!: 移除内置公共服务器
- 移除公共 RustDesk ID 服务器 (用户需自行配置) - 移除公共 TURN 服务器 (仅保留 Google STUN) - 清理废弃代码: PublicServerInfo, is_using_public_server 等 - 更新前端 UI 和国际化文本 - 重新生成 TypeScript 类型 破坏性变更: 不再提供内置公共服务器。用户必须配置自己的 RustDesk 服务器和 TURN 服务器才能在生产环境中使用。
This commit is contained in:
143
build.rs
143
build.rs
@@ -20,14 +20,13 @@ fn main() {
|
||||
// Compile protobuf files for RustDesk protocol
|
||||
compile_protos();
|
||||
|
||||
// Generate secrets module from secrets.toml
|
||||
// Generate minimal secrets module
|
||||
generate_secrets();
|
||||
|
||||
// Rerun if the script itself changes
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
println!("cargo:rerun-if-changed=protos/rendezvous.proto");
|
||||
println!("cargo:rerun-if-changed=protos/message.proto");
|
||||
println!("cargo:rerun-if-changed=secrets.toml");
|
||||
}
|
||||
|
||||
/// Compile protobuf files using protobuf-codegen (same as RustDesk server)
|
||||
@@ -52,123 +51,59 @@ pub mod message;
|
||||
std::fs::write(protos_dir.join("mod.rs"), mod_content).unwrap();
|
||||
}
|
||||
|
||||
/// Generate secrets module from secrets.toml
|
||||
///
|
||||
/// This reads the secrets.toml file and generates a Rust module with
|
||||
/// compile-time constants for sensitive configuration values.
|
||||
/// Generate minimal secrets module with Google STUN server hardcoded
|
||||
fn generate_secrets() {
|
||||
let out_dir = std::env::var("OUT_DIR").unwrap();
|
||||
let dest_path = Path::new(&out_dir).join("secrets_generated.rs");
|
||||
|
||||
// Default values if secrets.toml doesn't exist
|
||||
let mut rustdesk_public_server = String::new();
|
||||
let mut rustdesk_public_key = String::new();
|
||||
let mut rustdesk_relay_key = String::new();
|
||||
let mut ice_stun_server = String::new();
|
||||
let mut ice_turn_urls = String::new();
|
||||
let mut ice_turn_username = String::new();
|
||||
let mut ice_turn_password = String::new();
|
||||
|
||||
// Try to read secrets.toml
|
||||
if let Ok(content) = fs::read_to_string("secrets.toml") {
|
||||
if let Ok(value) = content.parse::<toml::Value>() {
|
||||
// RustDesk section
|
||||
if let Some(rustdesk) = value.get("rustdesk") {
|
||||
if let Some(v) = rustdesk.get("public_server").and_then(|v| v.as_str()) {
|
||||
rustdesk_public_server = v.to_string();
|
||||
}
|
||||
if let Some(v) = rustdesk.get("public_key").and_then(|v| v.as_str()) {
|
||||
rustdesk_public_key = v.to_string();
|
||||
}
|
||||
if let Some(v) = rustdesk.get("relay_key").and_then(|v| v.as_str()) {
|
||||
rustdesk_relay_key = v.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
// ICE section (for WebRTC)
|
||||
if let Some(ice) = value.get("ice") {
|
||||
if let Some(v) = ice.get("stun_server").and_then(|v| v.as_str()) {
|
||||
ice_stun_server = v.to_string();
|
||||
}
|
||||
if let Some(v) = ice.get("turn_urls").and_then(|v| v.as_str()) {
|
||||
ice_turn_urls = v.to_string();
|
||||
}
|
||||
if let Some(v) = ice.get("turn_username").and_then(|v| v.as_str()) {
|
||||
ice_turn_username = v.to_string();
|
||||
}
|
||||
if let Some(v) = ice.get("turn_password").and_then(|v| v.as_str()) {
|
||||
ice_turn_password = v.to_string();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("cargo:warning=Failed to parse secrets.toml");
|
||||
}
|
||||
} else {
|
||||
println!("cargo:warning=secrets.toml not found, using empty defaults");
|
||||
}
|
||||
|
||||
// Generate the secrets module
|
||||
let code = format!(
|
||||
r#"// Auto-generated secrets module
|
||||
// DO NOT EDIT - This file is generated by build.rs from secrets.toml
|
||||
|
||||
/// RustDesk public server configuration
|
||||
pub mod rustdesk {{
|
||||
/// Public RustDesk ID server address (used when user leaves field empty)
|
||||
pub const PUBLIC_SERVER: &str = "{}";
|
||||
|
||||
/// Public key for the RustDesk server (for client connection)
|
||||
pub const PUBLIC_KEY: &str = "{}";
|
||||
|
||||
/// Relay server authentication key (licence_key for relay server)
|
||||
pub const RELAY_KEY: &str = "{}";
|
||||
|
||||
/// Check if public server is configured
|
||||
pub const fn has_public_server() -> bool {{
|
||||
!PUBLIC_SERVER.is_empty()
|
||||
}}
|
||||
}}
|
||||
// Generate the secrets module - no public servers provided
|
||||
let code = r#"// Auto-generated secrets module
|
||||
// DO NOT EDIT - This file is generated by build.rs
|
||||
|
||||
/// ICE server configuration (for WebRTC NAT traversal)
|
||||
pub mod ice {{
|
||||
/// Public STUN server URL
|
||||
pub const STUN_SERVER: &str = "{}";
|
||||
pub mod ice {
|
||||
/// Google public STUN server URL (hardcoded)
|
||||
pub const STUN_SERVER: &str = "stun:stun.l.google.com:19302";
|
||||
|
||||
/// Public TURN server URLs (comma-separated)
|
||||
pub const TURN_URLS: &str = "{}";
|
||||
/// TURN server URLs - not provided, users must configure their own
|
||||
pub const TURN_URLS: &str = "";
|
||||
|
||||
/// TURN authentication username
|
||||
pub const TURN_USERNAME: &str = "{}";
|
||||
pub const TURN_USERNAME: &str = "";
|
||||
|
||||
/// TURN authentication password
|
||||
pub const TURN_PASSWORD: &str = "{}";
|
||||
pub const TURN_PASSWORD: &str = "";
|
||||
|
||||
/// Check if public ICE servers are configured
|
||||
pub const fn is_configured() -> bool {{
|
||||
!STUN_SERVER.is_empty() || !TURN_URLS.is_empty()
|
||||
}}
|
||||
/// Always returns true since we have STUN
|
||||
pub const fn is_configured() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Check if TURN servers are configured (requires credentials)
|
||||
pub const fn has_turn() -> bool {{
|
||||
!TURN_URLS.is_empty() && !TURN_USERNAME.is_empty() && !TURN_PASSWORD.is_empty()
|
||||
}}
|
||||
}}
|
||||
"#,
|
||||
escape_string(&rustdesk_public_server),
|
||||
escape_string(&rustdesk_public_key),
|
||||
escape_string(&rustdesk_relay_key),
|
||||
escape_string(&ice_stun_server),
|
||||
escape_string(&ice_turn_urls),
|
||||
escape_string(&ice_turn_username),
|
||||
escape_string(&ice_turn_password),
|
||||
);
|
||||
|
||||
fs::write(&dest_path, code).expect("Failed to write secrets_generated.rs");
|
||||
/// Always returns false since TURN is not provided
|
||||
pub const fn has_turn() -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Escape special characters in a string for use in Rust string literals
|
||||
fn escape_string(s: &str) -> String {
|
||||
s.replace('\\', "\\\\").replace('"', "\\\"")
|
||||
/// RustDesk public server configuration - NOT PROVIDED
|
||||
pub mod rustdesk {
|
||||
/// Public RustDesk ID server - NOT PROVIDED
|
||||
pub const PUBLIC_SERVER: &str = "";
|
||||
|
||||
/// Public key for the RustDesk server - NOT PROVIDED
|
||||
pub const PUBLIC_KEY: &str = "";
|
||||
|
||||
/// Relay server authentication key - NOT PROVIDED
|
||||
pub const RELAY_KEY: &str = "";
|
||||
|
||||
/// Always returns false
|
||||
pub const fn has_public_server() -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
fs::write(&dest_path, code).expect("Failed to write secrets_generated.rs");
|
||||
}
|
||||
|
||||
/// Convert days since Unix epoch to year-month-day
|
||||
|
||||
@@ -8,40 +8,28 @@ FROM debian:12-slim
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
# Install runtime dependencies and create directories
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
# Runtime libraries
|
||||
libasound2 \
|
||||
libv4l-0 \
|
||||
libudev1 \
|
||||
zlib1g \
|
||||
libjpeg62-turbo \
|
||||
libyuv0 \
|
||||
# FFmpeg runtime
|
||||
libavcodec59 \
|
||||
libavformat59 \
|
||||
libavutil57 \
|
||||
libswscale6 \
|
||||
libswresample4 \
|
||||
# Video codecs
|
||||
libx264-164 \
|
||||
libx265-199 \
|
||||
libvpx7 \
|
||||
# Audio codec
|
||||
libopus0 \
|
||||
# Hardware acceleration
|
||||
libva2 \
|
||||
libva-drm2 \
|
||||
libdrm2 \
|
||||
# X11 (for VAAPI)
|
||||
libx11-6 \
|
||||
libxcb1 \
|
||||
# Utilities
|
||||
ca-certificates \
|
||||
# Intel Media SDK (x86_64 only, ignored on other archs)
|
||||
$([ "$TARGETPLATFORM" = "linux/amd64" ] && echo "libmfx1") \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& mkdir -p /etc/one-kvm/ventoy
|
||||
# Install runtime dependencies in a single layer
|
||||
# Static linked: FFmpeg core, libyuv, libvpx, libjpeg-turbo
|
||||
# Dynamic linked: hardware acceleration drivers, GPL codecs (x264/x265)
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
# Core runtime (all platforms)
|
||||
libasound2 \
|
||||
libv4l-0 \
|
||||
libudev1 \
|
||||
libdrm2 \
|
||||
libopus0 \
|
||||
ca-certificates \
|
||||
# GPL codecs (must be dynamic for license compliance)
|
||||
libx264-164 \
|
||||
libx265-199 && \
|
||||
# Platform-specific hardware acceleration
|
||||
if [ "$TARGETPLATFORM" = "linux/amd64" ]; then \
|
||||
apt-get install -y --no-install-recommends \
|
||||
libva2 libva-drm2 libva-x11-2 libx11-6 libxcb1 libmfx1; \
|
||||
fi && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
mkdir -p /etc/one-kvm/ventoy
|
||||
|
||||
# Copy init script
|
||||
COPY --chmod=755 init.sh /init.sh
|
||||
|
||||
@@ -28,37 +28,83 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
git \
|
||||
libclang-dev \
|
||||
llvm \
|
||||
protobuf-compiler \
|
||||
mold \
|
||||
meson \
|
||||
ninja-build \
|
||||
wget \
|
||||
file \
|
||||
gcc-aarch64-linux-gnu \
|
||||
g++-aarch64-linux-gnu \
|
||||
libc6-dev-arm64-cross \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install ARM64 development libraries (without system ffmpeg)
|
||||
# Install ARM64 development libraries (without VAAPI/X11 for ARM)
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
libssl-dev:arm64 \
|
||||
libasound2-dev:arm64 \
|
||||
libv4l-dev:arm64 \
|
||||
libudev-dev:arm64 \
|
||||
zlib1g-dev:arm64 \
|
||||
libjpeg62-turbo-dev:arm64 \
|
||||
libyuv-dev:arm64 \
|
||||
# Note: libjpeg-turbo, libyuv, libvpx are built from source below for static linking
|
||||
libx264-dev:arm64 \
|
||||
libx265-dev:arm64 \
|
||||
libvpx-dev:arm64 \
|
||||
libopus-dev:arm64 \
|
||||
libva-dev:arm64 \
|
||||
libdrm-dev:arm64 \
|
||||
libx11-dev:arm64 \
|
||||
libxcb1-dev:arm64 \
|
||||
libxau-dev:arm64 \
|
||||
libxdmcp-dev:arm64 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Build static libjpeg-turbo from source (cross-compile for ARM64)
|
||||
RUN git clone --depth 1 https://github.com/libjpeg-turbo/libjpeg-turbo /tmp/libjpeg-turbo \
|
||||
&& cd /tmp/libjpeg-turbo \
|
||||
&& cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
|
||||
-DCMAKE_SYSTEM_NAME=Linux \
|
||||
-DCMAKE_SYSTEM_PROCESSOR=aarch64 \
|
||||
-DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc \
|
||||
-DCMAKE_INSTALL_PREFIX=/opt/one-kvm-libs/aarch64-linux-gnu \
|
||||
-DENABLE_SHARED=OFF -DENABLE_STATIC=ON \
|
||||
&& cmake --build build -j$(nproc) \
|
||||
&& cmake --install build \
|
||||
&& rm -rf /tmp/libjpeg-turbo
|
||||
|
||||
# Build static libyuv from source (cross-compile for ARM64)
|
||||
RUN git clone --depth 1 https://github.com/lemenkov/libyuv /tmp/libyuv \
|
||||
&& cd /tmp/libyuv \
|
||||
&& mkdir build && cd build \
|
||||
&& cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
|
||||
-DCMAKE_SYSTEM_NAME=Linux \
|
||||
-DCMAKE_SYSTEM_PROCESSOR=aarch64 \
|
||||
-DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc \
|
||||
-DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ \
|
||||
-DCMAKE_PREFIX_PATH=/opt/one-kvm-libs/aarch64-linux-gnu \
|
||||
-DCMAKE_INSTALL_PREFIX=/opt/one-kvm-libs/aarch64-linux-gnu \
|
||||
&& make -j$(nproc) \
|
||||
&& mkdir -p /opt/one-kvm-libs/aarch64-linux-gnu/lib \
|
||||
&& cp libyuv.a /opt/one-kvm-libs/aarch64-linux-gnu/lib/ \
|
||||
&& cp -r ../include /opt/one-kvm-libs/aarch64-linux-gnu/ \
|
||||
&& rm -rf /tmp/libyuv
|
||||
|
||||
# Build static libvpx from source (cross-compile for ARM64)
|
||||
# CC/CXX/LD/AR must be environment variables, not configure arguments
|
||||
RUN git clone --depth 1 https://github.com/webmproject/libvpx /tmp/libvpx \
|
||||
&& cd /tmp/libvpx \
|
||||
&& echo "=== libvpx: Configuring for ARM64 ===" \
|
||||
&& export CC=aarch64-linux-gnu-gcc \
|
||||
&& export CXX=aarch64-linux-gnu-g++ \
|
||||
&& export LD=aarch64-linux-gnu-ld \
|
||||
&& export AR=aarch64-linux-gnu-ar \
|
||||
&& export CROSS=aarch64-linux-gnu- \
|
||||
&& ./configure --prefix=/opt/one-kvm-libs/aarch64-linux-gnu \
|
||||
--target=arm64-linux-gcc \
|
||||
--enable-static --disable-shared --enable-pic \
|
||||
--disable-examples --disable-tools --disable-docs \
|
||||
--disable-unit-tests \
|
||||
&& echo "=== libvpx: Building ===" \
|
||||
&& make -j$(nproc) \
|
||||
&& echo "=== libvpx: Checking architecture ===" \
|
||||
&& file libvpx.a \
|
||||
&& make install \
|
||||
&& echo "=== libvpx: Verifying installed library ===" \
|
||||
&& file /opt/one-kvm-libs/aarch64-linux-gnu/lib/libvpx.a \
|
||||
&& rm -rf /tmp/libvpx
|
||||
|
||||
# Download and build FFmpeg with RKMPP support
|
||||
RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
||||
&& wget -q https://files.mofeng.run/src/image/other/ffmpeg.tar.gz \
|
||||
@@ -73,7 +119,7 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
||||
-DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr/aarch64-linux-gnu \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_SHARED_LIBS=ON \
|
||||
-DBUILD_SHARED_LIBS=OFF \
|
||||
-DBUILD_TEST=OFF \
|
||||
&& make -j$(nproc) \
|
||||
&& make install \
|
||||
@@ -94,6 +140,7 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
||||
&& meson setup build --prefix=/usr/aarch64-linux-gnu --libdir=lib \
|
||||
--cross-file=/tmp/aarch64-cross.txt \
|
||||
--buildtype=release \
|
||||
--default-library=static \
|
||||
-Dcpp_args=-fpermissive \
|
||||
-Dlibdrm=false \
|
||||
-Dlibrga_demo=false \
|
||||
@@ -102,7 +149,7 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
||||
&& cd .. \
|
||||
# Create pkg-config wrapper for cross-compilation
|
||||
&& echo '#!/bin/sh' > /tmp/aarch64-pkg-config \
|
||||
&& echo 'export PKG_CONFIG_LIBDIR=/usr/aarch64-linux-gnu/lib/pkgconfig:/usr/lib/aarch64-linux-gnu/pkgconfig' >> /tmp/aarch64-pkg-config \
|
||||
&& echo 'export PKG_CONFIG_LIBDIR=/opt/one-kvm-libs/aarch64-linux-gnu/lib/pkgconfig:/usr/aarch64-linux-gnu/lib/pkgconfig:/usr/lib/aarch64-linux-gnu/pkgconfig' >> /tmp/aarch64-pkg-config \
|
||||
&& echo 'export PKG_CONFIG_PATH=""' >> /tmp/aarch64-pkg-config \
|
||||
&& echo 'export PKG_CONFIG_SYSROOT_DIR=""' >> /tmp/aarch64-pkg-config \
|
||||
&& echo 'exec pkg-config "$@"' >> /tmp/aarch64-pkg-config \
|
||||
@@ -110,7 +157,7 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
||||
# Build FFmpeg with RKMPP (minimal build for encoding only)
|
||||
&& cd ffmpeg-rockchip \
|
||||
&& ./configure \
|
||||
--prefix=/usr/aarch64-linux-gnu \
|
||||
--prefix=/opt/one-kvm-libs/aarch64-linux-gnu \
|
||||
--cross-prefix=aarch64-linux-gnu- \
|
||||
--arch=aarch64 \
|
||||
--target-os=linux \
|
||||
@@ -118,13 +165,14 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
||||
--pkg-config=/tmp/aarch64-pkg-config \
|
||||
--enable-gpl \
|
||||
--enable-version3 \
|
||||
--enable-shared \
|
||||
--disable-static \
|
||||
# Hardware acceleration
|
||||
--disable-shared \
|
||||
--enable-static \
|
||||
--enable-pic \
|
||||
# Hardware acceleration (ARM: RKMPP + V4L2, no VAAPI)
|
||||
--enable-libdrm \
|
||||
--enable-rkmpp \
|
||||
--enable-rkrga \
|
||||
--enable-vaapi \
|
||||
--disable-vaapi \
|
||||
--enable-v4l2-m2m \
|
||||
# Software encoding libraries
|
||||
--enable-libx264 \
|
||||
@@ -151,10 +199,6 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
||||
--disable-decoders \
|
||||
# Disable all encoders, enable only needed ones
|
||||
--disable-encoders \
|
||||
--enable-encoder=h264_vaapi \
|
||||
--enable-encoder=hevc_vaapi \
|
||||
--enable-encoder=vp8_vaapi \
|
||||
--enable-encoder=vp9_vaapi \
|
||||
--enable-encoder=h264_rkmpp \
|
||||
--enable-encoder=hevc_rkmpp \
|
||||
--enable-encoder=h264_v4l2m2m \
|
||||
@@ -194,14 +238,22 @@ RUN rustup target add aarch64-unknown-linux-gnu
|
||||
# Create symlink for mold to work with cross-compiler
|
||||
RUN ln -s /usr/bin/mold /usr/bin/aarch64-linux-gnu-ld.mold
|
||||
|
||||
# Copy entrypoint script
|
||||
COPY build/cross/entrypoint.sh /usr/local/bin/cross-entrypoint.sh
|
||||
RUN chmod +x /usr/local/bin/cross-entrypoint.sh
|
||||
|
||||
# Configure environment for cross-compilation
|
||||
# Use PKG_CONFIG_LIBDIR to completely replace default search paths
|
||||
# This ensures we use our custom-built FFmpeg instead of system FFmpeg
|
||||
ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \
|
||||
CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc \
|
||||
CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++ \
|
||||
AR_aarch64_unknown_linux_gnu=aarch64-linux-gnu-ar \
|
||||
PKG_CONFIG_LIBDIR=/usr/aarch64-linux-gnu/lib/pkgconfig:/usr/lib/aarch64-linux-gnu/pkgconfig \
|
||||
PKG_CONFIG_LIBDIR=/opt/one-kvm-libs/aarch64-linux-gnu/lib/pkgconfig:/usr/aarch64-linux-gnu/lib/pkgconfig:/usr/lib/aarch64-linux-gnu/pkgconfig \
|
||||
PKG_CONFIG_PATH="" \
|
||||
PKG_CONFIG_ALLOW_CROSS=1 \
|
||||
LIBRARY_PATH="/opt/one-kvm-libs/aarch64-linux-gnu/lib" \
|
||||
CPATH="/opt/one-kvm-libs/aarch64-linux-gnu/include" \
|
||||
FFMPEG_STATIC=1 \
|
||||
LIBYUV_STATIC=1 \
|
||||
RUSTFLAGS="-C linker=aarch64-linux-gnu-gcc -C link-arg=-fuse-ld=mold"
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/cross-entrypoint.sh"]
|
||||
|
||||
@@ -28,37 +28,79 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
git \
|
||||
libclang-dev \
|
||||
llvm \
|
||||
protobuf-compiler \
|
||||
mold \
|
||||
meson \
|
||||
ninja-build \
|
||||
wget \
|
||||
file \
|
||||
gcc-arm-linux-gnueabihf \
|
||||
g++-arm-linux-gnueabihf \
|
||||
libc6-dev-armhf-cross \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install ARMv7 development libraries (without system ffmpeg)
|
||||
# Install ARMv7 development libraries (without VAAPI/X11 for ARM)
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
libssl-dev:armhf \
|
||||
libasound2-dev:armhf \
|
||||
libv4l-dev:armhf \
|
||||
libudev-dev:armhf \
|
||||
zlib1g-dev:armhf \
|
||||
libjpeg62-turbo-dev:armhf \
|
||||
libyuv-dev:armhf \
|
||||
# Note: libjpeg-turbo, libyuv, libvpx are built from source below for static linking
|
||||
libx264-dev:armhf \
|
||||
libx265-dev:armhf \
|
||||
libvpx-dev:armhf \
|
||||
libopus-dev:armhf \
|
||||
libva-dev:armhf \
|
||||
libdrm-dev:armhf \
|
||||
libx11-dev:armhf \
|
||||
libxcb1-dev:armhf \
|
||||
libxau-dev:armhf \
|
||||
libxdmcp-dev:armhf \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Build static libjpeg-turbo from source (cross-compile for ARMv7)
|
||||
RUN git clone --depth 1 https://github.com/libjpeg-turbo/libjpeg-turbo /tmp/libjpeg-turbo \
|
||||
&& cd /tmp/libjpeg-turbo \
|
||||
&& cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
|
||||
-DCMAKE_SYSTEM_NAME=Linux \
|
||||
-DCMAKE_SYSTEM_PROCESSOR=arm \
|
||||
-DCMAKE_C_COMPILER=arm-linux-gnueabihf-gcc \
|
||||
-DCMAKE_INSTALL_PREFIX=/opt/one-kvm-libs/armv7-linux-gnueabihf \
|
||||
-DENABLE_SHARED=OFF -DENABLE_STATIC=ON \
|
||||
&& cmake --build build -j$(nproc) \
|
||||
&& cmake --install build \
|
||||
&& rm -rf /tmp/libjpeg-turbo
|
||||
|
||||
# Build static libyuv from source (cross-compile for ARMv7)
|
||||
RUN git clone --depth 1 https://github.com/lemenkov/libyuv /tmp/libyuv \
|
||||
&& cd /tmp/libyuv \
|
||||
&& mkdir build && cd build \
|
||||
&& cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
|
||||
-DCMAKE_SYSTEM_NAME=Linux \
|
||||
-DCMAKE_SYSTEM_PROCESSOR=arm \
|
||||
-DCMAKE_C_COMPILER=arm-linux-gnueabihf-gcc \
|
||||
-DCMAKE_CXX_COMPILER=arm-linux-gnueabihf-g++ \
|
||||
-DCMAKE_PREFIX_PATH=/opt/one-kvm-libs/armv7-linux-gnueabihf \
|
||||
-DCMAKE_INSTALL_PREFIX=/opt/one-kvm-libs/armv7-linux-gnueabihf \
|
||||
&& make -j$(nproc) \
|
||||
&& mkdir -p /opt/one-kvm-libs/armv7-linux-gnueabihf/lib \
|
||||
&& cp libyuv.a /opt/one-kvm-libs/armv7-linux-gnueabihf/lib/ \
|
||||
&& cp -r ../include /opt/one-kvm-libs/armv7-linux-gnueabihf/ \
|
||||
&& rm -rf /tmp/libyuv
|
||||
|
||||
# Build static libvpx from source (cross-compile for ARMv7)
|
||||
# CC/CXX/LD/AR must be environment variables, not configure arguments
|
||||
RUN git clone --depth 1 https://github.com/webmproject/libvpx /tmp/libvpx \
|
||||
&& cd /tmp/libvpx \
|
||||
&& export CC=arm-linux-gnueabihf-gcc \
|
||||
&& export CXX=arm-linux-gnueabihf-g++ \
|
||||
&& export LD=arm-linux-gnueabihf-ld \
|
||||
&& export AR=arm-linux-gnueabihf-ar \
|
||||
&& export CROSS=arm-linux-gnueabihf- \
|
||||
&& ./configure --prefix=/opt/one-kvm-libs/armv7-linux-gnueabihf \
|
||||
--target=armv7-linux-gcc \
|
||||
--enable-static --disable-shared --enable-pic \
|
||||
--disable-examples --disable-tools --disable-docs \
|
||||
--disable-unit-tests \
|
||||
&& make -j$(nproc) \
|
||||
&& echo "=== libvpx: Checking architecture ===" \
|
||||
&& file libvpx.a \
|
||||
&& make install \
|
||||
&& rm -rf /tmp/libvpx
|
||||
|
||||
# Download and build FFmpeg with RKMPP support
|
||||
RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
||||
&& wget -q https://files.mofeng.run/src/image/other/ffmpeg.tar.gz \
|
||||
@@ -73,7 +115,7 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
||||
-DCMAKE_CXX_COMPILER=arm-linux-gnueabihf-g++ \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr/arm-linux-gnueabihf \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_SHARED_LIBS=ON \
|
||||
-DBUILD_SHARED_LIBS=OFF \
|
||||
-DBUILD_TEST=OFF \
|
||||
&& make -j$(nproc) \
|
||||
&& make install \
|
||||
@@ -94,6 +136,7 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
||||
&& meson setup build --prefix=/usr/arm-linux-gnueabihf --libdir=lib \
|
||||
--cross-file=/tmp/armhf-cross.txt \
|
||||
--buildtype=release \
|
||||
--default-library=static \
|
||||
-Dcpp_args=-fpermissive \
|
||||
-Dlibdrm=false \
|
||||
-Dlibrga_demo=false \
|
||||
@@ -102,7 +145,7 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
||||
&& cd .. \
|
||||
# Create pkg-config wrapper for cross-compilation
|
||||
&& echo '#!/bin/sh' > /tmp/armhf-pkg-config \
|
||||
&& echo 'export PKG_CONFIG_LIBDIR=/usr/arm-linux-gnueabihf/lib/pkgconfig:/usr/lib/arm-linux-gnueabihf/pkgconfig' >> /tmp/armhf-pkg-config \
|
||||
&& echo 'export PKG_CONFIG_LIBDIR=/opt/one-kvm-libs/armv7-linux-gnueabihf/lib/pkgconfig:/usr/arm-linux-gnueabihf/lib/pkgconfig:/usr/lib/arm-linux-gnueabihf/pkgconfig' >> /tmp/armhf-pkg-config \
|
||||
&& echo 'export PKG_CONFIG_PATH=""' >> /tmp/armhf-pkg-config \
|
||||
&& echo 'export PKG_CONFIG_SYSROOT_DIR=""' >> /tmp/armhf-pkg-config \
|
||||
&& echo 'exec pkg-config "$@"' >> /tmp/armhf-pkg-config \
|
||||
@@ -110,7 +153,7 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
||||
# Build FFmpeg with RKMPP (minimal build for encoding only)
|
||||
&& cd ffmpeg-rockchip \
|
||||
&& ./configure \
|
||||
--prefix=/usr/arm-linux-gnueabihf \
|
||||
--prefix=/opt/one-kvm-libs/armv7-linux-gnueabihf \
|
||||
--cross-prefix=arm-linux-gnueabihf- \
|
||||
--arch=arm \
|
||||
--target-os=linux \
|
||||
@@ -118,13 +161,14 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
||||
--pkg-config=/tmp/armhf-pkg-config \
|
||||
--enable-gpl \
|
||||
--enable-version3 \
|
||||
--enable-shared \
|
||||
--disable-static \
|
||||
# Hardware acceleration
|
||||
--disable-shared \
|
||||
--enable-static \
|
||||
--enable-pic \
|
||||
# Hardware acceleration (ARM: RKMPP + V4L2, no VAAPI)
|
||||
--enable-libdrm \
|
||||
--enable-rkmpp \
|
||||
--enable-rkrga \
|
||||
--enable-vaapi \
|
||||
--disable-vaapi \
|
||||
--enable-v4l2-m2m \
|
||||
# Software encoding libraries
|
||||
--enable-libx264 \
|
||||
@@ -151,10 +195,6 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
||||
--disable-decoders \
|
||||
# Disable all encoders, enable only needed ones
|
||||
--disable-encoders \
|
||||
--enable-encoder=h264_vaapi \
|
||||
--enable-encoder=hevc_vaapi \
|
||||
--enable-encoder=vp8_vaapi \
|
||||
--enable-encoder=vp9_vaapi \
|
||||
--enable-encoder=h264_rkmpp \
|
||||
--enable-encoder=hevc_rkmpp \
|
||||
--enable-encoder=h264_v4l2m2m \
|
||||
@@ -194,14 +234,22 @@ RUN rustup target add armv7-unknown-linux-gnueabihf
|
||||
# Create symlink for mold to work with cross-compiler
|
||||
RUN ln -s /usr/bin/mold /usr/bin/arm-linux-gnueabihf-ld.mold
|
||||
|
||||
# Copy entrypoint script
|
||||
COPY build/cross/entrypoint.sh /usr/local/bin/cross-entrypoint.sh
|
||||
RUN chmod +x /usr/local/bin/cross-entrypoint.sh
|
||||
|
||||
# Configure environment for cross-compilation
|
||||
# Use PKG_CONFIG_LIBDIR to completely replace default search paths
|
||||
# This ensures we use our custom-built FFmpeg instead of system FFmpeg
|
||||
ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc \
|
||||
CC_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-gcc \
|
||||
CXX_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-g++ \
|
||||
AR_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-ar \
|
||||
PKG_CONFIG_LIBDIR=/usr/arm-linux-gnueabihf/lib/pkgconfig:/usr/lib/arm-linux-gnueabihf/pkgconfig \
|
||||
PKG_CONFIG_LIBDIR=/opt/one-kvm-libs/armv7-linux-gnueabihf/lib/pkgconfig:/usr/arm-linux-gnueabihf/lib/pkgconfig:/usr/lib/arm-linux-gnueabihf/pkgconfig \
|
||||
PKG_CONFIG_PATH="" \
|
||||
PKG_CONFIG_ALLOW_CROSS=1 \
|
||||
LIBRARY_PATH="/opt/one-kvm-libs/armv7-linux-gnueabihf/lib" \
|
||||
CPATH="/opt/one-kvm-libs/armv7-linux-gnueabihf/include" \
|
||||
FFMPEG_STATIC=1 \
|
||||
LIBYUV_STATIC=1 \
|
||||
RUSTFLAGS="-C linker=arm-linux-gnueabihf-gcc -C link-arg=-fuse-ld=mold"
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/cross-entrypoint.sh"]
|
||||
|
||||
@@ -17,6 +17,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
ENV PATH="/root/.cargo/bin:${PATH}"
|
||||
|
||||
# Install build dependencies
|
||||
# Note: libyuv, libvpx are built from source below for static linking
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
# Build tools
|
||||
build-essential \
|
||||
@@ -27,8 +28,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
git \
|
||||
libclang-dev \
|
||||
llvm \
|
||||
protobuf-compiler \
|
||||
libssl-dev \
|
||||
mold \
|
||||
wget \
|
||||
# Core system libraries
|
||||
@@ -36,37 +35,69 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
libv4l-dev \
|
||||
libudev-dev \
|
||||
zlib1g-dev \
|
||||
# Video/image processing
|
||||
libjpeg62-turbo-dev \
|
||||
libyuv-dev \
|
||||
# Video codec libraries (for FFmpeg build)
|
||||
# Note: libjpeg-turbo is built from source below for static linking
|
||||
# Video codec libraries (dynamic, for software fallback)
|
||||
libx264-dev \
|
||||
libx265-dev \
|
||||
libvpx-dev \
|
||||
# Audio codec
|
||||
libopus-dev \
|
||||
# Hardware acceleration
|
||||
libva-dev \
|
||||
libdrm-dev \
|
||||
libmfx-dev \
|
||||
# X11 libraries
|
||||
# X11 libraries (for VAAPI)
|
||||
libx11-dev \
|
||||
libxcb1-dev \
|
||||
libxau-dev \
|
||||
libxdmcp-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Build static libjpeg-turbo from source (needed by libyuv)
|
||||
RUN git clone --depth 1 https://github.com/libjpeg-turbo/libjpeg-turbo /tmp/libjpeg-turbo \
|
||||
&& cd /tmp/libjpeg-turbo \
|
||||
&& cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
|
||||
-DCMAKE_INSTALL_PREFIX=/opt/one-kvm-libs/x86_64-linux-gnu \
|
||||
-DENABLE_SHARED=OFF -DENABLE_STATIC=ON \
|
||||
&& cmake --build build -j$(nproc) \
|
||||
&& cmake --install build \
|
||||
&& rm -rf /tmp/libjpeg-turbo
|
||||
|
||||
# Build static libyuv from source (uses libjpeg-turbo headers)
|
||||
RUN git clone --depth 1 https://github.com/lemenkov/libyuv /tmp/libyuv \
|
||||
&& cd /tmp/libyuv \
|
||||
&& mkdir build && cd build \
|
||||
&& cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
|
||||
-DCMAKE_PREFIX_PATH=/opt/one-kvm-libs/x86_64-linux-gnu \
|
||||
-DCMAKE_INSTALL_PREFIX=/opt/one-kvm-libs/x86_64-linux-gnu \
|
||||
&& make -j$(nproc) \
|
||||
&& mkdir -p /opt/one-kvm-libs/x86_64-linux-gnu/lib \
|
||||
&& cp libyuv.a /opt/one-kvm-libs/x86_64-linux-gnu/lib/ \
|
||||
&& cp -r ../include /opt/one-kvm-libs/x86_64-linux-gnu/ \
|
||||
&& rm -rf /tmp/libyuv
|
||||
|
||||
# Build static libvpx from source
|
||||
RUN git clone --depth 1 https://github.com/webmproject/libvpx /tmp/libvpx \
|
||||
&& cd /tmp/libvpx \
|
||||
&& ./configure --prefix=/opt/one-kvm-libs/x86_64-linux-gnu \
|
||||
--enable-static --disable-shared --enable-pic \
|
||||
--disable-examples --disable-tools --disable-docs \
|
||||
&& make -j$(nproc) \
|
||||
&& make install \
|
||||
&& rm -rf /tmp/libvpx
|
||||
|
||||
# Download and build FFmpeg with minimal configuration for encoding only
|
||||
RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
||||
&& wget -q https://files.mofeng.run/src/image/other/ffmpeg.tar.gz \
|
||||
&& tar -xzf ffmpeg.tar.gz \
|
||||
&& cd ffmpeg/ffmpeg-rockchip \
|
||||
&& export PKG_CONFIG_PATH="/opt/one-kvm-libs/x86_64-linux-gnu/lib/pkgconfig:$PKG_CONFIG_PATH" \
|
||||
&& ./configure \
|
||||
--prefix=/usr/local \
|
||||
--prefix=/opt/one-kvm-libs/x86_64-linux-gnu \
|
||||
--enable-gpl \
|
||||
--enable-version3 \
|
||||
--enable-shared \
|
||||
--disable-static \
|
||||
--disable-shared \
|
||||
--enable-static \
|
||||
--enable-pic \
|
||||
# Hardware acceleration
|
||||
--enable-libdrm \
|
||||
--enable-vaapi \
|
||||
@@ -128,13 +159,22 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
||||
--disable-debug \
|
||||
&& make -j$(nproc) \
|
||||
&& make install \
|
||||
&& ldconfig \
|
||||
&& cd / \
|
||||
&& rm -rf /tmp/ffmpeg-build
|
||||
|
||||
# Add Rust target
|
||||
RUN rustup target add x86_64-unknown-linux-gnu
|
||||
|
||||
# Configure mold as the linker and use custom FFmpeg
|
||||
# Copy entrypoint script
|
||||
COPY build/cross/entrypoint.sh /usr/local/bin/cross-entrypoint.sh
|
||||
RUN chmod +x /usr/local/bin/cross-entrypoint.sh
|
||||
|
||||
# Configure environment for static linking
|
||||
ENV RUSTFLAGS="-C link-arg=-fuse-ld=mold" \
|
||||
PKG_CONFIG_PATH="/usr/local/lib/pkgconfig:${PKG_CONFIG_PATH}"
|
||||
PKG_CONFIG_PATH="/opt/one-kvm-libs/x86_64-linux-gnu/lib/pkgconfig" \
|
||||
LIBRARY_PATH="/opt/one-kvm-libs/x86_64-linux-gnu/lib" \
|
||||
CPATH="/opt/one-kvm-libs/x86_64-linux-gnu/include" \
|
||||
FFMPEG_STATIC=1 \
|
||||
LIBYUV_STATIC=1
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/cross-entrypoint.sh"]
|
||||
|
||||
@@ -14,7 +14,6 @@ RustDesk 模块实现 RustDesk 协议集成,允许使用标准 RustDesk 客户
|
||||
- 视频/音频/HID 转换
|
||||
- 端到端加密 (Curve25519 + XSalsa20-Poly1305)
|
||||
- 签名认证 (Ed25519)
|
||||
- 公共服务器支持 (通过 secrets.toml)
|
||||
- 动态编码器协商 (H264/H265/VP8/VP9)
|
||||
- 输入节流 (防止 HID EAGAIN)
|
||||
- CapsLock 状态同步
|
||||
@@ -660,7 +659,7 @@ pub struct RustDeskConfig {
|
||||
|
||||
/// Rendezvous 服务器地址 (hbbs)
|
||||
/// 格式: "rs.example.com" 或 "192.168.1.100:21116"
|
||||
/// 如果为空,使用 secrets.toml 中配置的公共服务器
|
||||
/// 必填项 - 不再提供公共服务器,需自行配置
|
||||
pub rendezvous_server: String,
|
||||
|
||||
/// 中继服务器地址 (hbbr),默认与 rendezvous 同主机
|
||||
@@ -703,15 +702,9 @@ impl RustDeskConfig {
|
||||
/// 检查配置是否有效
|
||||
pub fn is_valid(&self) -> bool;
|
||||
|
||||
/// 检查是否使用公共服务器
|
||||
pub fn is_using_public_server(&self) -> bool;
|
||||
|
||||
/// 获取有效的 Rendezvous 服务器地址
|
||||
pub fn effective_rendezvous_server(&self) -> &str;
|
||||
|
||||
/// 获取公共服务器信息 (如果配置了)
|
||||
pub fn public_server_info() -> Option<PublicServerInfo>;
|
||||
|
||||
/// 获取带默认端口的 Rendezvous 地址
|
||||
pub fn rendezvous_addr(&self) -> String;
|
||||
|
||||
@@ -721,14 +714,6 @@ impl RustDeskConfig {
|
||||
/// 确保 UUID 存在 (自动生成并标记需要保存)
|
||||
pub fn ensure_uuid(&mut self) -> ([u8; 16], bool);
|
||||
}
|
||||
|
||||
/// 公共服务器信息
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[typeshare]
|
||||
pub struct PublicServerInfo {
|
||||
pub server: String, // 服务器地址
|
||||
pub public_key: String, // 公钥 (Base64)
|
||||
}
|
||||
```
|
||||
|
||||
### 配置文件示例
|
||||
@@ -744,23 +729,7 @@ device_password = "mypassword"
|
||||
# 密钥和 UUID 由程序自动生成和保存
|
||||
```
|
||||
|
||||
**使用公共服务器:**
|
||||
```toml
|
||||
[rustdesk]
|
||||
enabled = true
|
||||
rendezvous_server = "" # 留空使用 secrets.toml 中的公共服务器
|
||||
device_id = "123456789"
|
||||
device_password = "mypassword"
|
||||
```
|
||||
|
||||
**secrets.toml 公共服务器配置:**
|
||||
```toml
|
||||
[rustdesk]
|
||||
# 公共服务器配置 (可选)
|
||||
public_server = "rs-ny.rustdesk.com"
|
||||
public_key = "xxx...base64...xxx"
|
||||
relay_key = "xxx...key...xxx"
|
||||
```
|
||||
**注意**: 不再提供公共服务器,需自行配置 RustDesk 服务器。
|
||||
|
||||
---
|
||||
|
||||
@@ -868,7 +837,7 @@ pub enum RustDeskError {
|
||||
```rust
|
||||
let config = RustDeskConfig {
|
||||
enabled: true,
|
||||
rendezvous_server: "".to_string(), // 使用公共服务器
|
||||
rendezvous_server: "hbbs.example.com:21116".to_string(), // 必填 - 配置您的服务器
|
||||
device_id: "123456789".to_string(),
|
||||
device_password: "mypassword".to_string(),
|
||||
..Default::default()
|
||||
@@ -1271,7 +1240,7 @@ docker run -d --name hbbr \
|
||||
| 协议 | RustDesk Protocol | 同 |
|
||||
| P2P | 支持 | 支持 |
|
||||
| 中继 | 支持 | 提供中继服务 |
|
||||
| 公共服务器 | 可配置 (secrets.toml) | N/A |
|
||||
| 公共服务器 | 不提供,需自建 | N/A |
|
||||
| 多连接 | 支持 | N/A |
|
||||
| 输入节流 | 60Hz 限流 | 无限制 |
|
||||
|
||||
|
||||
@@ -97,10 +97,76 @@ mod ffmpeg {
|
||||
build_ffmpeg_ram(builder);
|
||||
}
|
||||
|
||||
/// Link system FFmpeg using pkg-config (for Linux development)
|
||||
/// Link system FFmpeg using pkg-config or custom path
|
||||
/// Supports both static and dynamic linking based on FFMPEG_STATIC env var
|
||||
fn link_system_ffmpeg(builder: &mut Build) {
|
||||
use std::process::Command;
|
||||
|
||||
// Check if static linking is requested
|
||||
let use_static = std::env::var("FFMPEG_STATIC").map(|v| v == "1").unwrap_or(false);
|
||||
let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
|
||||
|
||||
// Try custom library path first:
|
||||
// 1. Check ONE_KVM_LIBS_PATH environment variable (explicit override)
|
||||
// 2. Fall back to architecture-based detection
|
||||
let custom_lib_path = if let Ok(path) = std::env::var("ONE_KVM_LIBS_PATH") {
|
||||
path
|
||||
} else {
|
||||
match target_arch.as_str() {
|
||||
"x86_64" => "/opt/one-kvm-libs/x86_64-linux-gnu",
|
||||
"aarch64" => "/opt/one-kvm-libs/aarch64-linux-gnu",
|
||||
"arm" => "/opt/one-kvm-libs/armv7-linux-gnueabihf",
|
||||
_ => "",
|
||||
}
|
||||
.to_string()
|
||||
};
|
||||
|
||||
// Check if our custom FFmpeg exists
|
||||
if !custom_lib_path.is_empty() {
|
||||
let lib_dir = Path::new(&custom_lib_path).join("lib");
|
||||
let include_dir = Path::new(&custom_lib_path).join("include");
|
||||
let avcodec_lib = lib_dir.join("libavcodec.a");
|
||||
|
||||
if avcodec_lib.exists() {
|
||||
println!("cargo:info=Using custom FFmpeg from {}", custom_lib_path);
|
||||
println!("cargo:rustc-link-search=native={}", lib_dir.display());
|
||||
builder.include(&include_dir);
|
||||
|
||||
// Static link FFmpeg core libraries
|
||||
println!("cargo:rustc-link-lib=static=avcodec");
|
||||
println!("cargo:rustc-link-lib=static=avutil");
|
||||
|
||||
// Link hardware acceleration dependencies (dynamic)
|
||||
// These vary by architecture
|
||||
if target_arch == "x86_64" {
|
||||
// VAAPI for x86_64
|
||||
println!("cargo:rustc-link-lib=va");
|
||||
println!("cargo:rustc-link-lib=va-drm");
|
||||
println!("cargo:rustc-link-lib=va-x11"); // Required for vaGetDisplay
|
||||
println!("cargo:rustc-link-lib=mfx");
|
||||
} else {
|
||||
// RKMPP for ARM
|
||||
println!("cargo:rustc-link-lib=rockchip_mpp");
|
||||
println!("cargo:rustc-link-lib=rga");
|
||||
}
|
||||
|
||||
// Software codec dependencies (dynamic - GPL)
|
||||
println!("cargo:rustc-link-lib=x264");
|
||||
println!("cargo:rustc-link-lib=x265");
|
||||
|
||||
// VPX - check if static version exists in our custom path
|
||||
let vpx_static = lib_dir.join("libvpx.a");
|
||||
if vpx_static.exists() {
|
||||
println!("cargo:rustc-link-lib=static=vpx");
|
||||
} else {
|
||||
println!("cargo:rustc-link-lib=vpx");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to pkg-config
|
||||
// Only need libavcodec and libavutil for encoding
|
||||
let libs = ["libavcodec", "libavutil"];
|
||||
|
||||
@@ -120,9 +186,15 @@ mod ffmpeg {
|
||||
}
|
||||
}
|
||||
|
||||
// Get libs - always use dynamic linking on Linux
|
||||
// Get libs - use --static flag for static linking
|
||||
let pkg_config_args = if use_static {
|
||||
vec!["--static", "--libs", lib]
|
||||
} else {
|
||||
vec!["--libs", lib]
|
||||
};
|
||||
|
||||
if let Ok(output) = Command::new("pkg-config")
|
||||
.args(["--libs", lib])
|
||||
.args(&pkg_config_args)
|
||||
.output()
|
||||
{
|
||||
if output.status.success() {
|
||||
@@ -131,7 +203,18 @@ mod ffmpeg {
|
||||
if flag.starts_with("-L") {
|
||||
println!("cargo:rustc-link-search=native={}", &flag[2..]);
|
||||
} else if flag.starts_with("-l") {
|
||||
println!("cargo:rustc-link-lib={}", &flag[2..]);
|
||||
let lib_name = &flag[2..];
|
||||
if use_static {
|
||||
// For static linking, link FFmpeg libs statically, others dynamically
|
||||
if lib_name.starts_with("av") || lib_name == "swresample" {
|
||||
println!("cargo:rustc-link-lib=static={}", lib_name);
|
||||
} else {
|
||||
// Runtime libraries (va, drm, etc.) must be dynamic
|
||||
println!("cargo:rustc-link-lib={}", lib_name);
|
||||
}
|
||||
} else {
|
||||
println!("cargo:rustc-link-lib={}", lib_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -142,7 +225,11 @@ mod ffmpeg {
|
||||
}
|
||||
}
|
||||
|
||||
println!("cargo:info=Using system FFmpeg via pkg-config (dynamic linking)");
|
||||
if use_static {
|
||||
println!("cargo:info=Using system FFmpeg via pkg-config (static linking)");
|
||||
} else {
|
||||
println!("cargo:info=Using system FFmpeg via pkg-config (dynamic linking)");
|
||||
}
|
||||
}
|
||||
|
||||
fn link_vcpkg(builder: &mut Build, mut path: PathBuf) -> PathBuf {
|
||||
@@ -203,14 +290,15 @@ mod ffmpeg {
|
||||
let dyn_libs: Vec<&str> = if target_os == "windows" {
|
||||
["User32", "bcrypt", "ole32", "advapi32"].to_vec()
|
||||
} else if target_os == "linux" {
|
||||
// Note: VA-API libraries (va, va-drm, va-x11) must remain dynamic
|
||||
// as they load drivers at runtime. Same for libmfx.
|
||||
// All dependencies use dynamic linking on Linux.
|
||||
let mut v = vec!["drm", "X11", "stdc++"];
|
||||
// Base libraries for all Linux platforms
|
||||
let mut v = vec!["drm", "stdc++"];
|
||||
|
||||
// x86_64: needs X11 for VAAPI and zlib
|
||||
if target_arch == "x86_64" {
|
||||
v.push("X11");
|
||||
v.push("z");
|
||||
}
|
||||
// ARM (aarch64, arm): no X11 needed, uses RKMPP/V4L2
|
||||
v
|
||||
} else {
|
||||
panic!("Unsupported OS: {}. Only Windows and Linux are supported.", target_os);
|
||||
|
||||
@@ -148,40 +148,31 @@ fn link_vcpkg(mut path: PathBuf) -> bool {
|
||||
|
||||
println!("cargo:rustc-link-search=native={}", lib_path.display());
|
||||
|
||||
// For Linux: use dynamic linking
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
println!("cargo:rustc-link-lib=yuv");
|
||||
println!("cargo:rustc-link-lib=jpeg");
|
||||
println!("cargo:rustc-link-lib=stdc++");
|
||||
}
|
||||
// Check if static linking is requested via environment variable
|
||||
let use_static = env::var("LIBYUV_STATIC").map(|v| v == "1").unwrap_or(false);
|
||||
|
||||
// For Windows/macOS: keep static linking
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
let static_lib = lib_path.join("libyuv.a");
|
||||
let static_lib_win = lib_path.join("yuv.lib");
|
||||
|
||||
if static_lib.exists() || static_lib_win.exists() {
|
||||
println!("cargo:rustc-link-lib=static=yuv");
|
||||
} else {
|
||||
println!("cargo:rustc-link-lib=yuv");
|
||||
}
|
||||
|
||||
let jpeg_static = lib_path.join("libjpeg.a");
|
||||
let jpeg_static_win = lib_path.join("jpeg.lib");
|
||||
let turbojpeg_static = lib_path.join("libturbojpeg.a");
|
||||
let static_lib = lib_path.join("libyuv.a");
|
||||
let jpeg_static = lib_path.join("libjpeg.a");
|
||||
let turbojpeg_static = lib_path.join("libturbojpeg.a");
|
||||
|
||||
if use_static && static_lib.exists() {
|
||||
// Static linking (for deb packaging)
|
||||
println!("cargo:rustc-link-lib=static=yuv");
|
||||
if turbojpeg_static.exists() {
|
||||
println!("cargo:rustc-link-lib=static=turbojpeg");
|
||||
} else if jpeg_static.exists() || jpeg_static_win.exists() {
|
||||
} else if jpeg_static.exists() {
|
||||
println!("cargo:rustc-link-lib=static=jpeg");
|
||||
} else {
|
||||
println!("cargo:rustc-link-lib=jpeg");
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
println!("cargo:rustc-link-lib=stdc++");
|
||||
println!("cargo:info=Using libyuv from vcpkg (static linking)");
|
||||
} else {
|
||||
// Dynamic linking (default for development)
|
||||
println!("cargo:rustc-link-lib=yuv");
|
||||
println!("cargo:rustc-link-lib=jpeg");
|
||||
println!("cargo:rustc-link-lib=stdc++");
|
||||
println!("cargo:info=Using libyuv from vcpkg (dynamic linking)");
|
||||
}
|
||||
|
||||
println!(
|
||||
@@ -238,21 +229,72 @@ fn link_pkg_config() -> bool {
|
||||
}
|
||||
|
||||
fn link_system() -> bool {
|
||||
// Try common system library paths (dynamic linking only)
|
||||
let lib_paths = [
|
||||
// Check if static linking is requested
|
||||
let use_static = env::var("LIBYUV_STATIC").map(|v| v == "1").unwrap_or(false);
|
||||
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
|
||||
|
||||
// Build custom library paths based on target architecture:
|
||||
// 1. Check ONE_KVM_LIBS_PATH environment variable (explicit override)
|
||||
// 2. Fall back to architecture-based detection
|
||||
let custom_lib_path = if let Ok(path) = env::var("ONE_KVM_LIBS_PATH") {
|
||||
format!("{}/lib", path)
|
||||
} else {
|
||||
match target_arch.as_str() {
|
||||
"x86_64" => "/opt/one-kvm-libs/x86_64-linux-gnu/lib",
|
||||
"aarch64" => "/opt/one-kvm-libs/aarch64-linux-gnu/lib",
|
||||
"arm" => "/opt/one-kvm-libs/armv7-linux-gnueabihf/lib",
|
||||
_ => "",
|
||||
}
|
||||
.to_string()
|
||||
};
|
||||
|
||||
// Try common system library paths (custom paths first)
|
||||
let mut lib_paths: Vec<String> = Vec::new();
|
||||
|
||||
// Add custom build path first (highest priority)
|
||||
if !custom_lib_path.is_empty() {
|
||||
lib_paths.push(custom_lib_path);
|
||||
}
|
||||
|
||||
// Then standard paths
|
||||
lib_paths.extend([
|
||||
"/usr/local/lib", // Custom builds
|
||||
"/usr/local/lib64",
|
||||
"/usr/lib",
|
||||
"/usr/lib64",
|
||||
"/usr/local/lib",
|
||||
"/usr/local/lib64",
|
||||
"/usr/lib/x86_64-linux-gnu", // Debian/Ubuntu x86_64
|
||||
"/usr/lib/aarch64-linux-gnu", // Debian/Ubuntu ARM64
|
||||
"/usr/lib/arm-linux-gnueabihf", // Debian/Ubuntu ARMv7
|
||||
];
|
||||
].iter().map(|s| s.to_string()));
|
||||
|
||||
for path in &lib_paths {
|
||||
let lib_path = Path::new(path);
|
||||
let libyuv_static = lib_path.join("libyuv.a");
|
||||
let libyuv_so = lib_path.join("libyuv.so");
|
||||
|
||||
// Prefer static library if LIBYUV_STATIC=1
|
||||
if use_static && libyuv_static.exists() {
|
||||
println!("cargo:rustc-link-search=native={}", path);
|
||||
println!("cargo:rustc-link-lib=static=yuv");
|
||||
|
||||
// Check for static libjpeg-turbo in the same directory
|
||||
let turbojpeg_static = lib_path.join("libturbojpeg.a");
|
||||
let jpeg_static = lib_path.join("libjpeg.a");
|
||||
if turbojpeg_static.exists() {
|
||||
println!("cargo:rustc-link-lib=static=turbojpeg");
|
||||
} else if jpeg_static.exists() {
|
||||
println!("cargo:rustc-link-lib=static=jpeg");
|
||||
} else {
|
||||
// Fall back to dynamic jpeg
|
||||
println!("cargo:rustc-link-lib=jpeg");
|
||||
}
|
||||
|
||||
println!("cargo:rustc-link-lib=stdc++");
|
||||
println!("cargo:info=Using system libyuv from {} (static linking)", path);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fall back to dynamic linking
|
||||
if libyuv_so.exists() {
|
||||
println!("cargo:rustc-link-search=native={}", path);
|
||||
println!("cargo:rustc-link-lib=yuv");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
})
|
||||
}));
|
||||
|
||||
|
||||
@@ -256,12 +256,6 @@ export const extensionsApi = {
|
||||
|
||||
// ===== RustDesk 配置 API =====
|
||||
|
||||
/** 公共服务器信息 */
|
||||
export interface PublicServerInfo {
|
||||
server: string
|
||||
public_key: string
|
||||
}
|
||||
|
||||
/** RustDesk 配置响应 */
|
||||
export interface RustDeskConfigResponse {
|
||||
enabled: boolean
|
||||
@@ -271,7 +265,6 @@ export interface RustDeskConfigResponse {
|
||||
has_password: boolean
|
||||
has_keypair: boolean
|
||||
has_relay_key: boolean
|
||||
using_public_server: boolean
|
||||
}
|
||||
|
||||
/** RustDesk 状态响应 */
|
||||
@@ -279,7 +272,6 @@ export interface RustDeskStatusResponse {
|
||||
config: RustDeskConfigResponse
|
||||
service_status: string
|
||||
rendezvous_status: string | null
|
||||
public_server: PublicServerInfo | null
|
||||
}
|
||||
|
||||
/** RustDesk 配置更新 */
|
||||
|
||||
@@ -570,18 +570,17 @@ export default {
|
||||
// WebRTC / ICE
|
||||
webrtcSettings: 'WebRTC Settings',
|
||||
webrtcSettingsDesc: 'Configure STUN/TURN servers for NAT traversal',
|
||||
usingPublicIceServers: 'Using public ICE servers',
|
||||
publicIceServersHint: 'Leave empty to use built-in public STUN/TURN servers for NAT traversal',
|
||||
publicIceServersHint: 'Empty uses Google public STUN, configure your own TURN for production',
|
||||
stunServer: 'STUN Server',
|
||||
stunServerPlaceholder: 'stun:stun.l.google.com:19302',
|
||||
stunServerHint: 'Custom STUN server (leave empty to use public server)',
|
||||
stunServerHint: 'Custom STUN server (leave empty to use Google public server)',
|
||||
turnServer: 'TURN Server',
|
||||
turnServerPlaceholder: 'turn:turn.example.com:3478',
|
||||
turnServerHint: 'Custom TURN relay server (leave empty to use public server)',
|
||||
turnServerHint: 'Custom TURN relay server (required for production)',
|
||||
turnUsername: 'TURN Username',
|
||||
turnPassword: 'TURN Password',
|
||||
turnPasswordConfigured: 'Password already configured. Leave empty to keep current password.',
|
||||
turnCredentialsHint: 'Credentials for TURN server authentication (only needed for custom servers)',
|
||||
turnCredentialsHint: 'Credentials for TURN server authentication',
|
||||
iceConfigNote: 'Note: Changes require reconnecting the WebRTC session to take effect.',
|
||||
},
|
||||
virtualKeyboard: {
|
||||
@@ -703,7 +702,7 @@ export default {
|
||||
serverSettings: 'Server Settings',
|
||||
rendezvousServer: 'ID Server',
|
||||
rendezvousServerPlaceholder: 'hbbs.example.com:21116',
|
||||
rendezvousServerHint: 'Leave empty to use public server',
|
||||
rendezvousServerHint: 'Configure your RustDesk server address',
|
||||
relayServer: 'Relay Server',
|
||||
relayServerPlaceholder: 'hbbr.example.com:21117',
|
||||
relayServerHint: 'Relay server address, auto-derived from ID server if empty',
|
||||
@@ -711,10 +710,6 @@ export default {
|
||||
relayKeyPlaceholder: 'Enter relay server key',
|
||||
relayKeySet: '••••••••',
|
||||
relayKeyHint: 'Authentication key for relay server (if server uses -k option)',
|
||||
publicServerInfo: 'Public Server Info',
|
||||
publicServerAddress: 'Server Address',
|
||||
publicServerKey: 'Connection Key',
|
||||
usingPublicServer: 'Using public server',
|
||||
deviceInfo: 'Device Info',
|
||||
deviceId: 'Device ID',
|
||||
deviceIdHint: 'Use this ID in RustDesk client to connect',
|
||||
|
||||
@@ -570,18 +570,17 @@ export default {
|
||||
// WebRTC / ICE
|
||||
webrtcSettings: 'WebRTC 设置',
|
||||
webrtcSettingsDesc: '配置 STUN/TURN 服务器以实现 NAT 穿透',
|
||||
usingPublicIceServers: '正在使用公共 ICE 服务器',
|
||||
publicIceServersHint: '留空以使用内置的公共 STUN/TURN 服务器进行 NAT 穿透',
|
||||
publicIceServersHint: '留空将使用 Google 公共 STUN 服务器,TURN 服务器需自行配置',
|
||||
stunServer: 'STUN 服务器',
|
||||
stunServerPlaceholder: 'stun:stun.l.google.com:19302',
|
||||
stunServerHint: '自定义 STUN 服务器(留空则使用公共服务器)',
|
||||
stunServerHint: '自定义 STUN 服务器(留空则使用 Google 公共服务器)',
|
||||
turnServer: 'TURN 服务器',
|
||||
turnServerPlaceholder: 'turn:turn.example.com:3478',
|
||||
turnServerHint: '自定义 TURN 中继服务器(留空则使用公共服务器)',
|
||||
turnServerHint: '自定义 TURN 中继服务器(生产环境必须配置)',
|
||||
turnUsername: 'TURN 用户名',
|
||||
turnPassword: 'TURN 密码',
|
||||
turnPasswordConfigured: '密码已配置。留空则保持当前密码。',
|
||||
turnCredentialsHint: '用于 TURN 服务器认证的凭据(仅自定义服务器需要)',
|
||||
turnCredentialsHint: '用于 TURN 服务器认证的凭据',
|
||||
iceConfigNote: '注意:更改后需要重新连接 WebRTC 会话才能生效。',
|
||||
},
|
||||
virtualKeyboard: {
|
||||
@@ -703,7 +702,7 @@ export default {
|
||||
serverSettings: '服务器设置',
|
||||
rendezvousServer: 'ID 服务器',
|
||||
rendezvousServerPlaceholder: 'hbbs.example.com:21116',
|
||||
rendezvousServerHint: '留空则使用公共服务器',
|
||||
rendezvousServerHint: '请配置您的 RustDesk 服务器地址',
|
||||
relayServer: '中继服务器',
|
||||
relayServerPlaceholder: 'hbbr.example.com:21117',
|
||||
relayServerHint: '中继服务器地址,留空则自动从 ID 服务器推导',
|
||||
@@ -711,10 +710,6 @@ export default {
|
||||
relayKeyPlaceholder: '输入中继服务器密钥',
|
||||
relayKeySet: '••••••••',
|
||||
relayKeyHint: '中继服务器认证密钥(如果服务器使用 -k 选项)',
|
||||
publicServerInfo: '公共服务器信息',
|
||||
publicServerAddress: '服务器地址',
|
||||
publicServerKey: '连接密钥',
|
||||
usingPublicServer: '正在使用公共服务器',
|
||||
deviceInfo: '设备信息',
|
||||
deviceId: '设备 ID',
|
||||
deviceIdHint: '此 ID 用于 RustDesk 客户端连接',
|
||||
|
||||
@@ -314,9 +314,8 @@ export interface RustDeskConfig {
|
||||
/** Enable RustDesk protocol */
|
||||
enabled: boolean;
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
rendezvous_server: string;
|
||||
/**
|
||||
@@ -517,14 +516,6 @@ export interface MsdConfigUpdate {
|
||||
virtual_drive_size_mb?: number;
|
||||
}
|
||||
|
||||
/** Public server information for display to users */
|
||||
export interface PublicServerInfo {
|
||||
/** Public server address */
|
||||
server: string;
|
||||
/** Public key for client connection */
|
||||
public_key: string;
|
||||
}
|
||||
|
||||
export interface RustDeskConfigUpdate {
|
||||
enabled?: boolean;
|
||||
rendezvous_server?: string;
|
||||
|
||||
@@ -105,6 +105,12 @@ const mousePosition = ref({ x: 0, y: 0 })
|
||||
const lastMousePosition = ref({ x: 0, y: 0 }) // Track last position for relative mode
|
||||
const isPointerLocked = ref(false) // Track pointer lock state
|
||||
|
||||
// Mouse move throttling (60 Hz = ~16.67ms interval)
|
||||
const MOUSE_SEND_INTERVAL_MS = 16
|
||||
let mouseSendTimer: ReturnType<typeof setInterval> | null = null
|
||||
let pendingMouseMove: { type: 'move' | 'move_abs'; x: number; y: number } | null = null
|
||||
let accumulatedDelta = { x: 0, y: 0 } // For relative mode: accumulate deltas between sends
|
||||
|
||||
// Cursor visibility (from localStorage, updated via storage event)
|
||||
const cursorVisible = ref(localStorage.getItem('hidShowCursor') !== 'false')
|
||||
|
||||
@@ -1479,20 +1485,21 @@ function handleMouseMove(e: MouseEvent) {
|
||||
const y = Math.round((e.clientY - rect.top) / rect.height * 32767)
|
||||
|
||||
mousePosition.value = { x, y }
|
||||
sendMouseEvent({ type: 'move_abs', x, y })
|
||||
// Queue for throttled sending (absolute mode: just update pending position)
|
||||
pendingMouseMove = { type: 'move_abs', x, y }
|
||||
ensureMouseSendTimer()
|
||||
} else {
|
||||
// Relative mode: use movementX/Y when pointer is locked
|
||||
if (isPointerLocked.value) {
|
||||
const dx = e.movementX
|
||||
const dy = e.movementY
|
||||
|
||||
// Only send if there's actual movement
|
||||
// Only accumulate if there's actual movement
|
||||
if (dx !== 0 || dy !== 0) {
|
||||
// Clamp to i8 range (-127 to 127)
|
||||
const clampedDx = Math.max(-127, Math.min(127, dx))
|
||||
const clampedDy = Math.max(-127, Math.min(127, dy))
|
||||
|
||||
sendMouseEvent({ type: 'move', x: clampedDx, y: clampedDy })
|
||||
// Accumulate deltas for throttled sending
|
||||
accumulatedDelta.x += dx
|
||||
accumulatedDelta.y += dy
|
||||
ensureMouseSendTimer()
|
||||
}
|
||||
|
||||
// Update display position (accumulated delta for display only)
|
||||
@@ -1504,6 +1511,50 @@ function handleMouseMove(e: MouseEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
// Start the mouse send timer if not already running
|
||||
function ensureMouseSendTimer() {
|
||||
if (mouseSendTimer !== null) return
|
||||
|
||||
// Send immediately on first event, then throttle
|
||||
flushMouseMove()
|
||||
|
||||
mouseSendTimer = setInterval(() => {
|
||||
if (!flushMouseMove()) {
|
||||
// No pending data, stop the timer
|
||||
if (mouseSendTimer !== null) {
|
||||
clearInterval(mouseSendTimer)
|
||||
mouseSendTimer = null
|
||||
}
|
||||
}
|
||||
}, MOUSE_SEND_INTERVAL_MS)
|
||||
}
|
||||
|
||||
// Flush pending mouse move data, returns true if data was sent
|
||||
function flushMouseMove(): boolean {
|
||||
if (mouseMode.value === 'absolute') {
|
||||
if (pendingMouseMove) {
|
||||
sendMouseEvent(pendingMouseMove)
|
||||
pendingMouseMove = null
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
// Relative mode: send accumulated delta
|
||||
if (accumulatedDelta.x !== 0 || accumulatedDelta.y !== 0) {
|
||||
// Clamp to i8 range (-127 to 127)
|
||||
const clampedDx = Math.max(-127, Math.min(127, accumulatedDelta.x))
|
||||
const clampedDy = Math.max(-127, Math.min(127, accumulatedDelta.y))
|
||||
|
||||
sendMouseEvent({ type: 'move', x: clampedDx, y: clampedDy })
|
||||
|
||||
// Subtract sent amount (keep remainder for next send if clamped)
|
||||
accumulatedDelta.x -= clampedDx
|
||||
accumulatedDelta.y -= clampedDy
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Track pressed mouse button for window-level mouseup handling
|
||||
const pressedMouseButton = ref<'left' | 'right' | 'middle' | null>(null)
|
||||
|
||||
@@ -1746,6 +1797,12 @@ onUnmounted(() => {
|
||||
// Reset initial device info flag
|
||||
initialDeviceInfoReceived = false
|
||||
|
||||
// Clear mouse send timer
|
||||
if (mouseSendTimer !== null) {
|
||||
clearInterval(mouseSendTimer)
|
||||
mouseSendTimer = null
|
||||
}
|
||||
|
||||
// Clear ttyd poll interval
|
||||
if (ttydPollInterval) {
|
||||
clearInterval(ttydPollInterval)
|
||||
|
||||
@@ -47,12 +47,6 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog'
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from '@/components/ui/tooltip'
|
||||
import {
|
||||
Monitor,
|
||||
Keyboard,
|
||||
@@ -82,7 +76,6 @@ import {
|
||||
ExternalLink,
|
||||
Copy,
|
||||
ScreenShare,
|
||||
CircleHelp,
|
||||
} from 'lucide-vue-next'
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
@@ -2094,28 +2087,7 @@ onMounted(async () => {
|
||||
v-model="rustdeskLocalConfig.rendezvous_server"
|
||||
:placeholder="t('extensions.rustdesk.rendezvousServerPlaceholder')"
|
||||
/>
|
||||
<div class="flex items-center gap-1">
|
||||
<p class="text-xs text-muted-foreground">{{ t('extensions.rustdesk.rendezvousServerHint') }}</p>
|
||||
<TooltipProvider v-if="rustdeskStatus?.public_server">
|
||||
<Tooltip>
|
||||
<TooltipTrigger as-child>
|
||||
<CircleHelp class="h-3.5 w-3.5 text-muted-foreground cursor-help" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="right" class="max-w-xs">
|
||||
<div class="space-y-1.5 text-xs">
|
||||
<p class="font-medium">{{ t('extensions.rustdesk.publicServerInfo') }}</p>
|
||||
<div class="space-y-1">
|
||||
<p><span class="text-muted-foreground">{{ t('extensions.rustdesk.publicServerAddress') }}:</span> {{ rustdeskStatus.public_server.server }}</p>
|
||||
<p><span class="text-muted-foreground">{{ t('extensions.rustdesk.publicServerKey') }}:</span> <code class="text-[10px] break-all">{{ rustdeskStatus.public_server.public_key }}</code></p>
|
||||
</div>
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
<p v-if="rustdeskStatus?.config?.using_public_server" class="text-xs text-blue-500">
|
||||
{{ t('extensions.rustdesk.usingPublicServer') }}
|
||||
</p>
|
||||
<p class="text-xs text-muted-foreground">{{ t('extensions.rustdesk.rendezvousServerHint') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
|
||||
Reference in New Issue
Block a user