fix: 修复扩展服务开启和自启动状态错误

This commit is contained in:
mofeng-git
2026-07-17 10:46:34 +08:00
parent 57d1597264
commit d35021ee5d
12 changed files with 430 additions and 136 deletions

View File

@@ -14,11 +14,33 @@ use tokio::sync::{Mutex, OwnedMutexGuard};
#[derive(Debug, Clone, Copy, Default)]
pub struct ConfigApplyOptions {
pub force: bool,
pub preserve_service_state: bool,
pub runtime_only: bool,
}
impl ConfigApplyOptions {
pub const fn forced() -> Self {
Self { force: true }
Self {
force: true,
preserve_service_state: false,
runtime_only: false,
}
}
pub const fn preserving_service_state() -> Self {
Self {
force: false,
preserve_service_state: true,
runtime_only: false,
}
}
pub const fn runtime_only() -> Self {
Self {
force: false,
preserve_service_state: false,
runtime_only: true,
}
}
}
@@ -449,13 +471,27 @@ pub async fn apply_audio_config(
}
pub async fn enforce_stream_codec_constraints(state: &Arc<AppState>) -> Result<Option<String>> {
let config = state.config.get();
let config = state.runtime_third_party_config().await;
let constraints = StreamCodecConstraints::from_config(&config);
state
.stream_manager
.set_runtime_codec_constraints(constraints.clone())
.await;
let enforcement =
enforce_constraints_with_stream_manager(&state.stream_manager, &constraints).await?;
Ok(enforcement.message)
}
async fn validate_runtime_candidate<T>(
state: &Arc<AppState>,
apply: impl FnOnce(&mut crate::config::AppConfig, T),
config: T,
) -> Result<()> {
let mut candidate = state.runtime_third_party_config().await;
apply(&mut candidate, config);
validate_third_party_codec_compatibility(&candidate)
}
fn validate_rustdesk_candidate(
state: &Arc<AppState>,
new_config: &crate::rustdesk::config::RustDeskConfig,
@@ -485,12 +521,26 @@ pub async fn apply_rustdesk_config(
) -> Result<()> {
tracing::info!("Applying RustDesk config changes...");
validate_rustdesk_candidate(state, new_config)?;
if options.runtime_only {
validate_runtime_candidate(
state,
|candidate, config| candidate.rustdesk = config,
new_config.clone(),
)
.await?;
} else {
validate_rustdesk_candidate(state, new_config)?;
}
let mut rustdesk_guard = state.rustdesk.write().await;
let mut credentials_to_save = None;
let need_restart = options.force
|| old_config.codec != new_config.codec
|| old_config.rendezvous_server != new_config.rendezvous_server
|| old_config.device_id != new_config.device_id
|| old_config.device_password != new_config.device_password;
if !new_config.enabled {
if !options.preserve_service_state && !new_config.enabled {
if let Some(ref service) = *rustdesk_guard {
service
.stop()
@@ -501,39 +551,51 @@ pub async fn apply_rustdesk_config(
*rustdesk_guard = None;
}
if new_config.enabled {
let need_restart = options.force
|| old_config.codec != new_config.codec
|| old_config.rendezvous_server != new_config.rendezvous_server
|| old_config.device_id != new_config.device_id
|| old_config.device_password != new_config.device_password;
if !options.preserve_service_state && new_config.enabled {
if rustdesk_guard.is_none() {
tracing::info!("Initializing RustDesk service...");
let service = crate::rustdesk::RustDeskService::new(
let service = std::sync::Arc::new(crate::rustdesk::RustDeskService::new(
new_config.clone(),
state.stream_manager.clone(),
state.hid.clone(),
state.audio.clone(),
);
));
*rustdesk_guard = Some(service.clone());
service.start().await.map_err(|e| {
AppError::Config(format!("Failed to start RustDesk service: {}", e))
})?;
tracing::info!("RustDesk service started with ID: {}", new_config.device_id);
credentials_to_save = service.save_credentials();
*rustdesk_guard = Some(std::sync::Arc::new(service));
} else if need_restart {
} else {
if let Some(ref service) = *rustdesk_guard {
service.restart(new_config.clone()).await.map_err(|e| {
AppError::Config(format!("Failed to restart RustDesk service: {}", e))
})?;
tracing::info!(
"RustDesk service restarted with ID: {}",
new_config.device_id
);
if service.is_listening() {
if need_restart {
service.restart(new_config.clone()).await.map_err(|e| {
AppError::Config(format!("Failed to restart RustDesk service: {}", e))
})?;
tracing::info!(
"RustDesk service restarted with ID: {}",
new_config.device_id
);
}
} else {
service.update_config(new_config.clone());
service.start().await.map_err(|e| {
AppError::Config(format!("Failed to start RustDesk service: {}", e))
})?;
}
credentials_to_save = service.save_credentials();
}
}
} else if options.preserve_service_state && need_restart {
if let Some(ref service) = *rustdesk_guard {
let mut runtime_config = new_config.clone();
runtime_config.enabled = true;
service.restart(runtime_config).await.map_err(|e| {
AppError::Config(format!("Failed to restart RustDesk service: {}", e))
})?;
credentials_to_save = service.save_credentials();
}
}
drop(rustdesk_guard);
@@ -567,11 +629,27 @@ pub async fn apply_vnc_config(
) -> Result<()> {
tracing::info!("Applying VNC config changes...");
validate_vnc_candidate(state, new_config)?;
if options.runtime_only {
validate_runtime_candidate(
state,
|candidate, config| candidate.vnc = config,
new_config.clone(),
)
.await?;
} else {
validate_vnc_candidate(state, new_config)?;
}
if new_config.enabled {
let mut candidate = state.config.get().as_ref().clone();
let runtime_config = state.runtime_third_party_config().await;
let will_run = if options.preserve_service_state {
runtime_config.vnc.enabled
} else {
new_config.enabled
};
if will_run {
let mut candidate = runtime_config;
candidate.vnc = new_config.clone();
candidate.vnc.enabled = true;
let constraints = StreamCodecConstraints::from_config(&candidate);
match enforce_constraints_with_stream_manager(&state.stream_manager, &constraints).await {
Ok(result) if result.changed => {
@@ -588,37 +666,52 @@ pub async fn apply_vnc_config(
}
let mut vnc_guard = state.vnc.write().await;
let need_restart = options.force
|| old_config.bind != new_config.bind
|| old_config.port != new_config.port
|| old_config.encoding != new_config.encoding
|| old_config.password != new_config.password
|| old_config.allow_one_client != new_config.allow_one_client;
if !new_config.enabled {
if !options.preserve_service_state && !new_config.enabled {
if let Some(ref service) = *vnc_guard {
service.stop().await?;
}
*vnc_guard = None;
}
if new_config.enabled {
let need_restart = options.force
|| old_config.bind != new_config.bind
|| old_config.port != new_config.port
|| old_config.encoding != new_config.encoding
|| old_config.password != new_config.password
|| old_config.allow_one_client != new_config.allow_one_client;
if !options.preserve_service_state && new_config.enabled {
if vnc_guard.is_none() {
let service = crate::vnc::VncService::new(
let service = Arc::new(crate::vnc::VncService::new(
new_config.clone(),
state.stream_manager.clone(),
state.hid.clone(),
);
));
*vnc_guard = Some(service.clone());
service.start().await?;
*vnc_guard = Some(Arc::new(service));
tracing::info!("VNC service started");
} else if need_restart {
} else {
if let Some(ref service) = *vnc_guard {
service.restart(new_config.clone()).await?;
tracing::info!("VNC service restarted");
if matches!(
service.status().await,
crate::vnc::VncServiceStatus::Running
) {
if need_restart {
service.restart(new_config.clone()).await?;
tracing::info!("VNC service restarted");
}
} else {
service.update_config(new_config.clone()).await;
service.start().await?;
}
}
}
} else if options.preserve_service_state && need_restart {
if let Some(ref service) = *vnc_guard {
let mut runtime_config = new_config.clone();
runtime_config.enabled = true;
service.restart(runtime_config).await?;
}
}
drop(vnc_guard);
@@ -637,11 +730,28 @@ pub async fn apply_rtsp_config(
) -> Result<()> {
tracing::info!("Applying RTSP config changes...");
validate_rtsp_candidate(state, new_config)?;
if options.runtime_only {
validate_runtime_candidate(
state,
|candidate, config| candidate.rtsp = config,
new_config.clone(),
)
.await?;
} else {
validate_rtsp_candidate(state, new_config)?;
}
let mut rtsp_guard = state.rtsp.write().await;
let need_restart = options.force
|| old_config.bind != new_config.bind
|| old_config.port != new_config.port
|| old_config.path != new_config.path
|| old_config.codec != new_config.codec
|| old_config.username != new_config.username
|| old_config.password != new_config.password
|| old_config.allow_one_client != new_config.allow_one_client;
if !new_config.enabled {
if !options.preserve_service_state && !new_config.enabled {
if let Some(ref service) = *rtsp_guard {
service
.stop()
@@ -651,27 +761,37 @@ pub async fn apply_rtsp_config(
*rtsp_guard = None;
}
if new_config.enabled {
let need_restart = options.force
|| old_config.bind != new_config.bind
|| old_config.port != new_config.port
|| old_config.path != new_config.path
|| old_config.codec != new_config.codec
|| old_config.username != new_config.username
|| old_config.password != new_config.password
|| old_config.allow_one_client != new_config.allow_one_client;
if !options.preserve_service_state && new_config.enabled {
if rtsp_guard.is_none() {
let service = RtspService::new(new_config.clone(), state.stream_manager.clone());
let service = Arc::new(RtspService::new(
new_config.clone(),
state.stream_manager.clone(),
));
*rtsp_guard = Some(service.clone());
service.start().await?;
tracing::info!("RTSP service started");
*rtsp_guard = Some(Arc::new(service));
} else if need_restart {
} else {
if let Some(ref service) = *rtsp_guard {
service.restart(new_config.clone()).await?;
tracing::info!("RTSP service restarted");
if matches!(
service.status().await,
crate::rtsp::RtspServiceStatus::Running
) {
if need_restart {
service.restart(new_config.clone()).await?;
tracing::info!("RTSP service restarted");
}
} else {
service.update_config(new_config.clone()).await;
service.start().await?;
}
}
}
} else if options.preserve_service_state && need_restart {
if let Some(ref service) = *rtsp_guard {
let mut runtime_config = new_config.clone();
runtime_config.enabled = true;
service.restart(runtime_config).await?;
}
}
drop(rtsp_guard);