mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-03-15 07:26:44 +08:00
566 lines
16 KiB
Rust
566 lines
16 KiB
Rust
use crate::video::encoder::BitratePreset;
|
|
use serde::{Deserialize, Serialize};
|
|
use typeshare::typeshare;
|
|
|
|
// Re-export ExtensionsConfig from extensions module
|
|
pub use crate::extensions::ExtensionsConfig;
|
|
// Re-export RustDeskConfig from rustdesk module
|
|
pub use crate::rustdesk::config::RustDeskConfig;
|
|
|
|
/// Main application configuration
|
|
#[typeshare]
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(default)]
|
|
#[derive(Default)]
|
|
pub struct AppConfig {
|
|
/// Whether initial setup has been completed
|
|
pub initialized: bool,
|
|
/// Authentication settings
|
|
pub auth: AuthConfig,
|
|
/// Video capture settings
|
|
pub video: VideoConfig,
|
|
/// HID (keyboard/mouse) settings
|
|
pub hid: HidConfig,
|
|
/// Mass Storage Device settings
|
|
pub msd: MsdConfig,
|
|
/// ATX power control settings
|
|
pub atx: AtxConfig,
|
|
/// Audio settings
|
|
pub audio: AudioConfig,
|
|
/// Streaming settings
|
|
pub stream: StreamConfig,
|
|
/// Web server settings
|
|
pub web: WebConfig,
|
|
/// Extensions settings (ttyd, gostc, easytier)
|
|
pub extensions: ExtensionsConfig,
|
|
/// RustDesk remote access settings
|
|
pub rustdesk: RustDeskConfig,
|
|
}
|
|
|
|
|
|
/// Authentication configuration
|
|
#[typeshare]
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(default)]
|
|
pub struct AuthConfig {
|
|
/// Session timeout in seconds
|
|
pub session_timeout_secs: u32,
|
|
/// Allow multiple concurrent web sessions (single-user mode)
|
|
pub single_user_allow_multiple_sessions: bool,
|
|
/// Enable 2FA
|
|
pub totp_enabled: bool,
|
|
/// TOTP secret (encrypted)
|
|
pub totp_secret: Option<String>,
|
|
}
|
|
|
|
impl Default for AuthConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
session_timeout_secs: 3600 * 24, // 24 hours
|
|
single_user_allow_multiple_sessions: false,
|
|
totp_enabled: false,
|
|
totp_secret: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Video capture configuration
|
|
#[typeshare]
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
#[serde(default)]
|
|
pub struct VideoConfig {
|
|
/// Video device path (e.g., /dev/video0)
|
|
pub device: Option<String>,
|
|
/// Video pixel format (e.g., "MJPEG", "YUYV", "NV12")
|
|
pub format: Option<String>,
|
|
/// Resolution width
|
|
pub width: u32,
|
|
/// Resolution height
|
|
pub height: u32,
|
|
/// Frame rate
|
|
pub fps: u32,
|
|
/// JPEG quality (1-100)
|
|
pub quality: u32,
|
|
}
|
|
|
|
impl Default for VideoConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
device: None,
|
|
format: None, // Auto-detect or use MJPEG as default
|
|
width: 1920,
|
|
height: 1080,
|
|
fps: 30,
|
|
quality: 80,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// HID backend type
|
|
#[typeshare]
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "lowercase")]
|
|
#[derive(Default)]
|
|
pub enum HidBackend {
|
|
/// USB OTG HID gadget
|
|
Otg,
|
|
/// CH9329 serial HID controller
|
|
Ch9329,
|
|
/// Disabled
|
|
#[default]
|
|
None,
|
|
}
|
|
|
|
|
|
/// OTG USB device descriptor configuration
|
|
#[typeshare]
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
pub struct OtgDescriptorConfig {
|
|
/// USB Vendor ID (e.g., 0x1d6b)
|
|
pub vendor_id: u16,
|
|
/// USB Product ID (e.g., 0x0104)
|
|
pub product_id: u16,
|
|
/// Manufacturer string
|
|
pub manufacturer: String,
|
|
/// Product string
|
|
pub product: String,
|
|
/// Serial number (optional, auto-generated if not set)
|
|
pub serial_number: Option<String>,
|
|
}
|
|
|
|
impl Default for OtgDescriptorConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
vendor_id: 0x1d6b, // Linux Foundation
|
|
product_id: 0x0104, // Multifunction Composite Gadget
|
|
manufacturer: "One-KVM".to_string(),
|
|
product: "One-KVM USB Device".to_string(),
|
|
serial_number: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// OTG HID function profile
|
|
#[typeshare]
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[derive(Default)]
|
|
pub enum OtgHidProfile {
|
|
/// Full HID device set (keyboard + relative mouse + absolute mouse + consumer control)
|
|
#[default]
|
|
Full,
|
|
/// Full HID device set without MSD
|
|
FullNoMsd,
|
|
/// Full HID device set without consumer control
|
|
FullNoConsumer,
|
|
/// Full HID device set without consumer control and MSD
|
|
FullNoConsumerNoMsd,
|
|
/// Legacy profile: only keyboard
|
|
LegacyKeyboard,
|
|
/// Legacy profile: only relative mouse
|
|
LegacyMouseRelative,
|
|
/// Custom function selection
|
|
Custom,
|
|
}
|
|
|
|
|
|
/// OTG HID function selection (used when profile is Custom)
|
|
#[typeshare]
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
#[serde(default)]
|
|
pub struct OtgHidFunctions {
|
|
pub keyboard: bool,
|
|
pub mouse_relative: bool,
|
|
pub mouse_absolute: bool,
|
|
pub consumer: bool,
|
|
}
|
|
|
|
impl OtgHidFunctions {
|
|
pub fn full() -> Self {
|
|
Self {
|
|
keyboard: true,
|
|
mouse_relative: true,
|
|
mouse_absolute: true,
|
|
consumer: true,
|
|
}
|
|
}
|
|
|
|
pub fn full_no_consumer() -> Self {
|
|
Self {
|
|
keyboard: true,
|
|
mouse_relative: true,
|
|
mouse_absolute: true,
|
|
consumer: false,
|
|
}
|
|
}
|
|
|
|
pub fn legacy_keyboard() -> Self {
|
|
Self {
|
|
keyboard: true,
|
|
mouse_relative: false,
|
|
mouse_absolute: false,
|
|
consumer: false,
|
|
}
|
|
}
|
|
|
|
pub fn legacy_mouse_relative() -> Self {
|
|
Self {
|
|
keyboard: false,
|
|
mouse_relative: true,
|
|
mouse_absolute: false,
|
|
consumer: false,
|
|
}
|
|
}
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
!self.keyboard && !self.mouse_relative && !self.mouse_absolute && !self.consumer
|
|
}
|
|
}
|
|
|
|
impl Default for OtgHidFunctions {
|
|
fn default() -> Self {
|
|
Self::full()
|
|
}
|
|
}
|
|
|
|
impl OtgHidProfile {
|
|
pub fn resolve_functions(&self, custom: &OtgHidFunctions) -> OtgHidFunctions {
|
|
match self {
|
|
Self::Full => OtgHidFunctions::full(),
|
|
Self::FullNoMsd => OtgHidFunctions::full(),
|
|
Self::FullNoConsumer => OtgHidFunctions::full_no_consumer(),
|
|
Self::FullNoConsumerNoMsd => OtgHidFunctions::full_no_consumer(),
|
|
Self::LegacyKeyboard => OtgHidFunctions::legacy_keyboard(),
|
|
Self::LegacyMouseRelative => OtgHidFunctions::legacy_mouse_relative(),
|
|
Self::Custom => custom.clone(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// HID configuration
|
|
#[typeshare]
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
#[serde(default)]
|
|
pub struct HidConfig {
|
|
/// HID backend type
|
|
pub backend: HidBackend,
|
|
/// OTG keyboard device path
|
|
pub otg_keyboard: String,
|
|
/// OTG mouse device path
|
|
pub otg_mouse: String,
|
|
/// OTG UDC (USB Device Controller) name
|
|
pub otg_udc: Option<String>,
|
|
/// OTG USB device descriptor configuration
|
|
#[serde(default)]
|
|
pub otg_descriptor: OtgDescriptorConfig,
|
|
/// OTG HID function profile
|
|
#[serde(default)]
|
|
pub otg_profile: OtgHidProfile,
|
|
/// OTG HID function selection (used when profile is Custom)
|
|
#[serde(default)]
|
|
pub otg_functions: OtgHidFunctions,
|
|
/// CH9329 serial port
|
|
pub ch9329_port: String,
|
|
/// CH9329 baud rate
|
|
pub ch9329_baudrate: u32,
|
|
/// Mouse mode: absolute or relative
|
|
pub mouse_absolute: bool,
|
|
}
|
|
|
|
impl Default for HidConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
backend: HidBackend::None,
|
|
otg_keyboard: "/dev/hidg0".to_string(),
|
|
otg_mouse: "/dev/hidg1".to_string(),
|
|
otg_udc: None,
|
|
otg_descriptor: OtgDescriptorConfig::default(),
|
|
otg_profile: OtgHidProfile::default(),
|
|
otg_functions: OtgHidFunctions::default(),
|
|
ch9329_port: "/dev/ttyUSB0".to_string(),
|
|
ch9329_baudrate: 9600,
|
|
mouse_absolute: true,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl HidConfig {
|
|
pub fn effective_otg_functions(&self) -> OtgHidFunctions {
|
|
self.otg_profile.resolve_functions(&self.otg_functions)
|
|
}
|
|
}
|
|
|
|
/// MSD configuration
|
|
#[typeshare]
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(default)]
|
|
pub struct MsdConfig {
|
|
/// Enable MSD functionality
|
|
pub enabled: bool,
|
|
/// MSD base directory (absolute path)
|
|
pub msd_dir: String,
|
|
}
|
|
|
|
impl Default for MsdConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
enabled: true,
|
|
msd_dir: String::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl MsdConfig {
|
|
pub fn msd_dir_path(&self) -> std::path::PathBuf {
|
|
std::path::PathBuf::from(&self.msd_dir)
|
|
}
|
|
|
|
pub fn images_dir(&self) -> std::path::PathBuf {
|
|
self.msd_dir_path().join("images")
|
|
}
|
|
|
|
pub fn ventoy_dir(&self) -> std::path::PathBuf {
|
|
self.msd_dir_path().join("ventoy")
|
|
}
|
|
|
|
pub fn drive_path(&self) -> std::path::PathBuf {
|
|
self.ventoy_dir().join("ventoy.img")
|
|
}
|
|
}
|
|
|
|
// Re-export ATX types from atx module for configuration
|
|
pub use crate::atx::{ActiveLevel, AtxDriverType, AtxKeyConfig, AtxLedConfig};
|
|
|
|
/// ATX power control configuration
|
|
///
|
|
/// Each ATX action (power, reset) can be independently configured with its own
|
|
/// hardware binding using the four-tuple: (driver, device, pin, active_level).
|
|
#[typeshare]
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(default)]
|
|
#[derive(Default)]
|
|
pub struct AtxConfig {
|
|
/// Enable ATX functionality
|
|
pub enabled: bool,
|
|
/// Power button configuration (used for both short and long press)
|
|
pub power: AtxKeyConfig,
|
|
/// Reset button configuration
|
|
pub reset: AtxKeyConfig,
|
|
/// LED sensing configuration (optional)
|
|
pub led: AtxLedConfig,
|
|
/// Network interface for WOL packets (empty = auto)
|
|
pub wol_interface: String,
|
|
}
|
|
|
|
|
|
impl AtxConfig {
|
|
/// Convert to AtxControllerConfig for the controller
|
|
pub fn to_controller_config(&self) -> crate::atx::AtxControllerConfig {
|
|
crate::atx::AtxControllerConfig {
|
|
enabled: self.enabled,
|
|
power: self.power.clone(),
|
|
reset: self.reset.clone(),
|
|
led: self.led.clone(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Audio configuration
|
|
///
|
|
/// Note: Sample rate is fixed at 48000Hz and channels at 2 (stereo).
|
|
/// These are optimal for Opus encoding and match WebRTC requirements.
|
|
#[typeshare]
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(default)]
|
|
pub struct AudioConfig {
|
|
/// Enable audio capture
|
|
pub enabled: bool,
|
|
/// ALSA device name
|
|
pub device: String,
|
|
/// Audio quality preset: "voice", "balanced", "high"
|
|
pub quality: String,
|
|
}
|
|
|
|
impl Default for AudioConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
enabled: false,
|
|
device: "default".to_string(),
|
|
quality: "balanced".to_string(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Stream mode
|
|
#[typeshare]
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "lowercase")]
|
|
#[derive(Default)]
|
|
pub enum StreamMode {
|
|
/// WebRTC with H264/H265
|
|
WebRTC,
|
|
/// MJPEG over HTTP
|
|
#[default]
|
|
Mjpeg,
|
|
}
|
|
|
|
|
|
/// Encoder type
|
|
#[typeshare]
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "lowercase")]
|
|
#[derive(Default)]
|
|
pub enum EncoderType {
|
|
/// Auto-detect best encoder
|
|
#[default]
|
|
Auto,
|
|
/// Software encoder (libx264)
|
|
Software,
|
|
/// VAAPI hardware encoder
|
|
Vaapi,
|
|
/// NVIDIA NVENC hardware encoder
|
|
Nvenc,
|
|
/// Intel Quick Sync hardware encoder
|
|
Qsv,
|
|
/// AMD AMF hardware encoder
|
|
Amf,
|
|
/// Rockchip MPP hardware encoder
|
|
Rkmpp,
|
|
/// V4L2 M2M hardware encoder
|
|
V4l2m2m,
|
|
}
|
|
|
|
|
|
impl EncoderType {
|
|
/// Convert to EncoderBackend for registry queries
|
|
pub fn to_backend(&self) -> Option<crate::video::encoder::registry::EncoderBackend> {
|
|
use crate::video::encoder::registry::EncoderBackend;
|
|
match self {
|
|
EncoderType::Auto => None,
|
|
EncoderType::Software => Some(EncoderBackend::Software),
|
|
EncoderType::Vaapi => Some(EncoderBackend::Vaapi),
|
|
EncoderType::Nvenc => Some(EncoderBackend::Nvenc),
|
|
EncoderType::Qsv => Some(EncoderBackend::Qsv),
|
|
EncoderType::Amf => Some(EncoderBackend::Amf),
|
|
EncoderType::Rkmpp => Some(EncoderBackend::Rkmpp),
|
|
EncoderType::V4l2m2m => Some(EncoderBackend::V4l2m2m),
|
|
}
|
|
}
|
|
|
|
/// Get display name for UI
|
|
pub fn display_name(&self) -> &'static str {
|
|
match self {
|
|
EncoderType::Auto => "Auto (Recommended)",
|
|
EncoderType::Software => "Software (CPU)",
|
|
EncoderType::Vaapi => "VAAPI",
|
|
EncoderType::Nvenc => "NVIDIA NVENC",
|
|
EncoderType::Qsv => "Intel Quick Sync",
|
|
EncoderType::Amf => "AMD AMF",
|
|
EncoderType::Rkmpp => "Rockchip MPP",
|
|
EncoderType::V4l2m2m => "V4L2 M2M",
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Streaming configuration
|
|
#[typeshare]
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(default)]
|
|
pub struct StreamConfig {
|
|
/// Stream mode
|
|
pub mode: StreamMode,
|
|
/// Encoder type for H264/H265
|
|
pub encoder: EncoderType,
|
|
/// Bitrate preset (Speed/Balanced/Quality)
|
|
pub bitrate_preset: BitratePreset,
|
|
/// Custom STUN server (e.g., "stun:stun.l.google.com:19302")
|
|
/// If empty, uses public ICE servers from secrets.toml
|
|
pub stun_server: Option<String>,
|
|
/// Custom TURN server (e.g., "turn:turn.example.com:3478")
|
|
/// If empty, uses public ICE servers from secrets.toml
|
|
pub turn_server: Option<String>,
|
|
/// TURN username
|
|
pub turn_username: Option<String>,
|
|
/// TURN password (stored encrypted in DB, not exposed via API)
|
|
pub turn_password: Option<String>,
|
|
/// Auto-pause when no clients connected
|
|
#[typeshare(skip)]
|
|
pub auto_pause_enabled: bool,
|
|
/// Auto-pause delay (seconds)
|
|
#[typeshare(skip)]
|
|
pub auto_pause_delay_secs: u64,
|
|
/// Client timeout for cleanup (seconds)
|
|
#[typeshare(skip)]
|
|
pub client_timeout_secs: u64,
|
|
}
|
|
|
|
impl Default for StreamConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
mode: StreamMode::Mjpeg,
|
|
encoder: EncoderType::Auto,
|
|
bitrate_preset: BitratePreset::Balanced,
|
|
// Empty means use public ICE servers (like RustDesk)
|
|
stun_server: None,
|
|
turn_server: None,
|
|
turn_username: None,
|
|
turn_password: None,
|
|
auto_pause_enabled: false,
|
|
auto_pause_delay_secs: 10,
|
|
client_timeout_secs: 30,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl StreamConfig {
|
|
/// Check if using public ICE servers (user left fields empty)
|
|
pub fn is_using_public_ice_servers(&self) -> bool {
|
|
use crate::webrtc::config::public_ice;
|
|
self.stun_server
|
|
.as_ref()
|
|
.map(|s| s.is_empty())
|
|
.unwrap_or(true)
|
|
&& self
|
|
.turn_server
|
|
.as_ref()
|
|
.map(|s| s.is_empty())
|
|
.unwrap_or(true)
|
|
&& public_ice::is_configured()
|
|
}
|
|
}
|
|
|
|
/// Web server configuration
|
|
#[typeshare]
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(default)]
|
|
pub struct WebConfig {
|
|
/// HTTP port
|
|
pub http_port: u16,
|
|
/// HTTPS port
|
|
pub https_port: u16,
|
|
/// Bind addresses (preferred)
|
|
pub bind_addresses: Vec<String>,
|
|
/// Bind address (legacy)
|
|
pub bind_address: String,
|
|
/// Enable HTTPS
|
|
pub https_enabled: bool,
|
|
/// Custom SSL certificate path
|
|
pub ssl_cert_path: Option<String>,
|
|
/// Custom SSL key path
|
|
pub ssl_key_path: Option<String>,
|
|
}
|
|
|
|
impl Default for WebConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
http_port: 8080,
|
|
https_port: 8443,
|
|
bind_addresses: Vec::new(),
|
|
bind_address: "0.0.0.0".to_string(),
|
|
https_enabled: false,
|
|
ssl_cert_path: None,
|
|
ssl_key_path: None,
|
|
}
|
|
}
|
|
}
|