feat!: 移除内置公共服务器

- 移除公共 RustDesk ID 服务器 (用户需自行配置)
- 移除公共 TURN 服务器 (仅保留 Google STUN)
- 清理废弃代码: PublicServerInfo, is_using_public_server 等
- 更新前端 UI 和国际化文本
- 重新生成 TypeScript 类型

破坏性变更: 不再提供内置公共服务器。用户必须配置自己的
RustDesk 服务器和 TURN 服务器才能在生产环境中使用。
This commit is contained in:
mofeng-git
2026-01-08 16:53:19 +08:00
parent 9ab3d052f9
commit 3fa91772f0
20 changed files with 635 additions and 500 deletions

View File

@@ -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 配置更新 */

View File

@@ -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',

View File

@@ -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 客户端连接',

View File

@@ -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;

View File

@@ -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)

View File

@@ -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">