mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-06-14 03:32:00 +08:00
Merge branch 'main' into main
This commit is contained in:
@@ -7,7 +7,11 @@ use std::sync::Arc;
|
||||
use crate::config::*;
|
||||
use crate::error::{AppError, Result};
|
||||
use crate::events::SystemEvent;
|
||||
use crate::rtsp::RtspService;
|
||||
use crate::state::AppState;
|
||||
use crate::video::codec_constraints::{
|
||||
enforce_constraints_with_stream_manager, StreamCodecConstraints,
|
||||
};
|
||||
|
||||
/// 应用 Video 配置变更
|
||||
pub async fn apply_video_config(
|
||||
@@ -191,9 +195,7 @@ pub async fn apply_hid_config(
|
||||
|
||||
// Low-endpoint UDCs (e.g., musb) cannot handle consumer control endpoints reliably
|
||||
if new_config.backend == HidBackend::Otg {
|
||||
if let Some(udc) =
|
||||
crate::otg::configfs::resolve_udc_name(new_config.otg_udc.as_deref())
|
||||
{
|
||||
if let Some(udc) = crate::otg::configfs::resolve_udc_name(new_config.otg_udc.as_deref()) {
|
||||
if crate::otg::configfs::is_low_endpoint_udc(&udc) && new_hid_functions.consumer {
|
||||
tracing::warn!(
|
||||
"UDC {} has low endpoint resources, disabling consumer control",
|
||||
@@ -446,6 +448,15 @@ pub async fn apply_audio_config(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Apply stream codec constraints derived from global config.
|
||||
pub async fn enforce_stream_codec_constraints(state: &Arc<AppState>) -> Result<Option<String>> {
|
||||
let config = state.config.get();
|
||||
let constraints = StreamCodecConstraints::from_config(&config);
|
||||
let enforcement =
|
||||
enforce_constraints_with_stream_manager(&state.stream_manager, &constraints).await?;
|
||||
Ok(enforcement.message)
|
||||
}
|
||||
|
||||
/// 应用 RustDesk 配置变更
|
||||
pub async fn apply_rustdesk_config(
|
||||
state: &Arc<AppState>,
|
||||
@@ -455,6 +466,7 @@ pub async fn apply_rustdesk_config(
|
||||
tracing::info!("Applying RustDesk config changes...");
|
||||
|
||||
let mut rustdesk_guard = state.rustdesk.write().await;
|
||||
let mut credentials_to_save = None;
|
||||
|
||||
// Check if service needs to be stopped
|
||||
if old_config.enabled && !new_config.enabled {
|
||||
@@ -466,7 +478,6 @@ pub async fn apply_rustdesk_config(
|
||||
tracing::info!("RustDesk service stopped");
|
||||
}
|
||||
*rustdesk_guard = None;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Check if service needs to be started or restarted
|
||||
@@ -475,8 +486,6 @@ pub async fn apply_rustdesk_config(
|
||||
|| old_config.device_id != new_config.device_id
|
||||
|| old_config.device_password != new_config.device_password;
|
||||
|
||||
let mut credentials_to_save = None;
|
||||
|
||||
if rustdesk_guard.is_none() {
|
||||
// Create new service
|
||||
tracing::info!("Initializing RustDesk service...");
|
||||
@@ -509,28 +518,82 @@ pub async fn apply_rustdesk_config(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save credentials to persistent config store (outside the lock)
|
||||
drop(rustdesk_guard);
|
||||
if let Some(updated_config) = credentials_to_save {
|
||||
tracing::info!("Saving RustDesk credentials to config store...");
|
||||
if let Err(e) = state
|
||||
.config
|
||||
.update(|cfg| {
|
||||
cfg.rustdesk.public_key = updated_config.public_key.clone();
|
||||
cfg.rustdesk.private_key = updated_config.private_key.clone();
|
||||
cfg.rustdesk.signing_public_key = updated_config.signing_public_key.clone();
|
||||
cfg.rustdesk.signing_private_key = updated_config.signing_private_key.clone();
|
||||
cfg.rustdesk.uuid = updated_config.uuid.clone();
|
||||
})
|
||||
.await
|
||||
{
|
||||
tracing::warn!("Failed to save RustDesk credentials: {}", e);
|
||||
} else {
|
||||
tracing::info!("RustDesk credentials saved successfully");
|
||||
}
|
||||
// Save credentials to persistent config store (outside the lock)
|
||||
drop(rustdesk_guard);
|
||||
if let Some(updated_config) = credentials_to_save {
|
||||
tracing::info!("Saving RustDesk credentials to config store...");
|
||||
if let Err(e) = state
|
||||
.config
|
||||
.update(|cfg| {
|
||||
cfg.rustdesk.public_key = updated_config.public_key.clone();
|
||||
cfg.rustdesk.private_key = updated_config.private_key.clone();
|
||||
cfg.rustdesk.signing_public_key = updated_config.signing_public_key.clone();
|
||||
cfg.rustdesk.signing_private_key = updated_config.signing_private_key.clone();
|
||||
cfg.rustdesk.uuid = updated_config.uuid.clone();
|
||||
})
|
||||
.await
|
||||
{
|
||||
tracing::warn!("Failed to save RustDesk credentials: {}", e);
|
||||
} else {
|
||||
tracing::info!("RustDesk credentials saved successfully");
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(message) = enforce_stream_codec_constraints(state).await? {
|
||||
tracing::info!("{}", message);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 应用 RTSP 配置变更
|
||||
pub async fn apply_rtsp_config(
|
||||
state: &Arc<AppState>,
|
||||
old_config: &RtspConfig,
|
||||
new_config: &RtspConfig,
|
||||
) -> Result<()> {
|
||||
tracing::info!("Applying RTSP config changes...");
|
||||
|
||||
let mut rtsp_guard = state.rtsp.write().await;
|
||||
|
||||
if old_config.enabled && !new_config.enabled {
|
||||
if let Some(ref service) = *rtsp_guard {
|
||||
if let Err(e) = service.stop().await {
|
||||
tracing::error!("Failed to stop RTSP service: {}", e);
|
||||
}
|
||||
}
|
||||
*rtsp_guard = None;
|
||||
}
|
||||
|
||||
if new_config.enabled {
|
||||
let need_restart = 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 rtsp_guard.is_none() {
|
||||
let service = RtspService::new(new_config.clone(), state.stream_manager.clone());
|
||||
service.start().await?;
|
||||
tracing::info!("RTSP service started");
|
||||
*rtsp_guard = Some(Arc::new(service));
|
||||
} else if need_restart {
|
||||
if let Some(ref service) = *rtsp_guard {
|
||||
service.restart(new_config.clone()).await?;
|
||||
tracing::info!("RTSP service restarted");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drop(rtsp_guard);
|
||||
|
||||
if let Some(message) = enforce_stream_codec_constraints(state).await? {
|
||||
tracing::info!("{}", message);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ mod audio;
|
||||
mod auth;
|
||||
mod hid;
|
||||
mod msd;
|
||||
mod rtsp;
|
||||
mod rustdesk;
|
||||
mod stream;
|
||||
pub(crate) mod video;
|
||||
@@ -35,6 +36,7 @@ pub use audio::{get_audio_config, update_audio_config};
|
||||
pub use auth::{get_auth_config, update_auth_config};
|
||||
pub use hid::{get_hid_config, update_hid_config};
|
||||
pub use msd::{get_msd_config, update_msd_config};
|
||||
pub use rtsp::{get_rtsp_config, get_rtsp_status, update_rtsp_config};
|
||||
pub use rustdesk::{
|
||||
get_device_password, get_rustdesk_config, get_rustdesk_status, regenerate_device_id,
|
||||
regenerate_device_password, update_rustdesk_config,
|
||||
@@ -50,10 +52,29 @@ use std::sync::Arc;
|
||||
use crate::config::AppConfig;
|
||||
use crate::state::AppState;
|
||||
|
||||
fn sanitize_config_for_api(config: &mut AppConfig) {
|
||||
// Auth secrets
|
||||
config.auth.totp_secret = None;
|
||||
|
||||
// Stream secrets
|
||||
config.stream.turn_password = None;
|
||||
|
||||
// RustDesk secrets
|
||||
config.rustdesk.device_password.clear();
|
||||
config.rustdesk.relay_key = None;
|
||||
config.rustdesk.public_key = None;
|
||||
config.rustdesk.private_key = None;
|
||||
config.rustdesk.signing_public_key = None;
|
||||
config.rustdesk.signing_private_key = None;
|
||||
|
||||
// RTSP secrets
|
||||
config.rtsp.password = None;
|
||||
}
|
||||
|
||||
/// 获取完整配置
|
||||
pub async fn get_all_config(State(state): State<Arc<AppState>>) -> Json<AppConfig> {
|
||||
let mut config = (*state.config.get()).clone();
|
||||
// 不暴露敏感信息
|
||||
config.auth.totp_secret = None;
|
||||
sanitize_config_for_api(&mut config);
|
||||
Json(config)
|
||||
}
|
||||
|
||||
70
src/web/handlers/config/rtsp.rs
Normal file
70
src/web/handlers/config/rtsp.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
use axum::{extract::State, Json};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::error::{AppError, Result};
|
||||
use crate::state::AppState;
|
||||
|
||||
use super::apply::apply_rtsp_config;
|
||||
use super::types::{RtspConfigResponse, RtspConfigUpdate, RtspStatusResponse};
|
||||
|
||||
/// Get RTSP config
|
||||
pub async fn get_rtsp_config(State(state): State<Arc<AppState>>) -> Json<RtspConfigResponse> {
|
||||
let config = state.config.get();
|
||||
Json(RtspConfigResponse::from(&config.rtsp))
|
||||
}
|
||||
|
||||
/// Get RTSP status (config + service status)
|
||||
pub async fn get_rtsp_status(State(state): State<Arc<AppState>>) -> Json<RtspStatusResponse> {
|
||||
let config = state.config.get().rtsp.clone();
|
||||
let status = {
|
||||
let guard = state.rtsp.read().await;
|
||||
if let Some(ref service) = *guard {
|
||||
service.status().await
|
||||
} else {
|
||||
crate::rtsp::RtspServiceStatus::Stopped
|
||||
}
|
||||
};
|
||||
|
||||
Json(RtspStatusResponse::new(&config, status))
|
||||
}
|
||||
|
||||
/// Update RTSP config
|
||||
pub async fn update_rtsp_config(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Json(req): Json<RtspConfigUpdate>,
|
||||
) -> Result<Json<RtspConfigResponse>> {
|
||||
req.validate()?;
|
||||
|
||||
let old_config = state.config.get().rtsp.clone();
|
||||
|
||||
state
|
||||
.config
|
||||
.update(|config| {
|
||||
req.apply_to(&mut config.rtsp);
|
||||
})
|
||||
.await?;
|
||||
|
||||
let new_config = state.config.get().rtsp.clone();
|
||||
if let Err(err) = apply_rtsp_config(&state, &old_config, &new_config).await {
|
||||
tracing::error!("Failed to apply RTSP config: {}", err);
|
||||
if let Err(rollback_err) = state
|
||||
.config
|
||||
.update(|config| {
|
||||
config.rtsp = old_config.clone();
|
||||
})
|
||||
.await
|
||||
{
|
||||
tracing::error!(
|
||||
"Failed to rollback RTSP config after apply failure: {}",
|
||||
rollback_err
|
||||
);
|
||||
return Err(AppError::ServiceUnavailable(format!(
|
||||
"RTSP apply failed: {}; rollback failed: {}",
|
||||
err, rollback_err
|
||||
)));
|
||||
}
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
Ok(Json(RtspConfigResponse::from(&new_config)))
|
||||
}
|
||||
@@ -106,6 +106,15 @@ pub async fn update_rustdesk_config(
|
||||
tracing::error!("Failed to apply RustDesk config: {}", e);
|
||||
}
|
||||
|
||||
// Share a non-sensitive summary for frontend UX
|
||||
let constraints = state.stream_manager.codec_constraints().await;
|
||||
if constraints.rustdesk_enabled || constraints.rtsp_enabled {
|
||||
tracing::info!(
|
||||
"Stream codec constraints active after RustDesk update: {}",
|
||||
constraints.reason
|
||||
);
|
||||
}
|
||||
|
||||
Ok(Json(RustDeskConfigResponse::from(&new_config)))
|
||||
}
|
||||
|
||||
@@ -139,7 +148,7 @@ pub async fn regenerate_device_password(
|
||||
Ok(Json(RustDeskConfigResponse::from(&new_config)))
|
||||
}
|
||||
|
||||
/// 获取设备密码(管理员专用)
|
||||
/// 获取设备密码(已认证用户)
|
||||
pub async fn get_device_password(State(state): State<Arc<AppState>>) -> Json<serde_json::Value> {
|
||||
let config = state.config.get().rustdesk.clone();
|
||||
Json(serde_json::json!({
|
||||
|
||||
@@ -42,5 +42,10 @@ pub async fn update_stream_config(
|
||||
tracing::error!("Failed to apply stream config: {}", e);
|
||||
}
|
||||
|
||||
// 6. Enforce codec constraints after any stream config update
|
||||
if let Err(e) = super::apply::enforce_stream_codec_constraints(&state).await {
|
||||
tracing::error!("Failed to enforce stream codec constraints: {}", e);
|
||||
}
|
||||
|
||||
Ok(Json(StreamConfigResponse::from(&new_stream_config)))
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::config::*;
|
||||
use crate::error::AppError;
|
||||
use crate::rtsp::RtspServiceStatus;
|
||||
use crate::rustdesk::config::RustDeskConfig;
|
||||
use crate::video::encoder::BitratePreset;
|
||||
use serde::Deserialize;
|
||||
@@ -604,6 +605,124 @@ impl RustDeskConfigUpdate {
|
||||
}
|
||||
}
|
||||
|
||||
// ===== RTSP Config =====
|
||||
#[typeshare]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
pub struct RtspConfigResponse {
|
||||
pub enabled: bool,
|
||||
pub bind: String,
|
||||
pub port: u16,
|
||||
pub path: String,
|
||||
pub allow_one_client: bool,
|
||||
pub codec: RtspCodec,
|
||||
pub username: Option<String>,
|
||||
pub has_password: bool,
|
||||
}
|
||||
|
||||
impl From<&RtspConfig> for RtspConfigResponse {
|
||||
fn from(config: &RtspConfig) -> Self {
|
||||
Self {
|
||||
enabled: config.enabled,
|
||||
bind: config.bind.clone(),
|
||||
port: config.port,
|
||||
path: config.path.clone(),
|
||||
allow_one_client: config.allow_one_client,
|
||||
codec: config.codec.clone(),
|
||||
username: config.username.clone(),
|
||||
has_password: config.password.is_some(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
pub struct RtspStatusResponse {
|
||||
pub config: RtspConfigResponse,
|
||||
pub service_status: String,
|
||||
}
|
||||
|
||||
impl RtspStatusResponse {
|
||||
pub fn new(config: &RtspConfig, status: RtspServiceStatus) -> Self {
|
||||
Self {
|
||||
config: RtspConfigResponse::from(config),
|
||||
service_status: status.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct RtspConfigUpdate {
|
||||
pub enabled: Option<bool>,
|
||||
pub bind: Option<String>,
|
||||
pub port: Option<u16>,
|
||||
pub path: Option<String>,
|
||||
pub allow_one_client: Option<bool>,
|
||||
pub codec: Option<RtspCodec>,
|
||||
pub username: Option<String>,
|
||||
pub password: Option<String>,
|
||||
}
|
||||
|
||||
impl RtspConfigUpdate {
|
||||
pub fn validate(&self) -> crate::error::Result<()> {
|
||||
if let Some(port) = self.port {
|
||||
if port == 0 {
|
||||
return Err(AppError::BadRequest("RTSP port cannot be 0".into()));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref bind) = self.bind {
|
||||
if bind.parse::<std::net::IpAddr>().is_err() {
|
||||
return Err(AppError::BadRequest("RTSP bind must be a valid IP".into()));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref path) = self.path {
|
||||
let normalized = path.trim_matches('/');
|
||||
if normalized.is_empty() {
|
||||
return Err(AppError::BadRequest("RTSP path cannot be empty".into()));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn apply_to(&self, config: &mut RtspConfig) {
|
||||
if let Some(enabled) = self.enabled {
|
||||
config.enabled = enabled;
|
||||
}
|
||||
if let Some(ref bind) = self.bind {
|
||||
config.bind = bind.clone();
|
||||
}
|
||||
if let Some(port) = self.port {
|
||||
config.port = port;
|
||||
}
|
||||
if let Some(ref path) = self.path {
|
||||
config.path = path.trim_matches('/').to_string();
|
||||
}
|
||||
if let Some(allow_one_client) = self.allow_one_client {
|
||||
config.allow_one_client = allow_one_client;
|
||||
}
|
||||
if let Some(codec) = self.codec.clone() {
|
||||
config.codec = codec;
|
||||
}
|
||||
if let Some(ref username) = self.username {
|
||||
config.username = if username.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(username.clone())
|
||||
};
|
||||
}
|
||||
if let Some(ref password) = self.password {
|
||||
config.password = if password.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(password.clone())
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Web Config =====
|
||||
#[typeshare]
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
||||
@@ -86,7 +86,7 @@ pub async fn start_extension(
|
||||
// Start the extension
|
||||
mgr.start(ext_id, &config.extensions)
|
||||
.await
|
||||
.map_err(|e| AppError::Internal(e))?;
|
||||
.map_err(AppError::Internal)?;
|
||||
|
||||
// Return updated status
|
||||
Ok(Json(ExtensionInfo {
|
||||
@@ -108,7 +108,7 @@ pub async fn stop_extension(
|
||||
let mgr = &state.extensions;
|
||||
|
||||
// Stop the extension
|
||||
mgr.stop(ext_id).await.map_err(|e| AppError::Internal(e))?;
|
||||
mgr.stop(ext_id).await.map_err(AppError::Internal)?;
|
||||
|
||||
// Return updated status
|
||||
Ok(Json(ExtensionInfo {
|
||||
@@ -156,7 +156,6 @@ pub struct TtydConfigUpdate {
|
||||
pub enabled: Option<bool>,
|
||||
pub port: Option<u16>,
|
||||
pub shell: Option<String>,
|
||||
pub credential: Option<String>,
|
||||
}
|
||||
|
||||
/// Update gostc config
|
||||
@@ -203,9 +202,6 @@ pub async fn update_ttyd_config(
|
||||
if let Some(ref shell) = req.shell {
|
||||
ttyd.shell = shell.clone();
|
||||
}
|
||||
if req.credential.is_some() {
|
||||
ttyd.credential = req.credential.clone();
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -263,14 +259,16 @@ pub async fn update_gostc_config(
|
||||
|
||||
if was_enabled && !is_enabled {
|
||||
state.extensions.stop(ExtensionId::Gostc).await.ok();
|
||||
} else if !was_enabled && is_enabled && has_key {
|
||||
if state.extensions.check_available(ExtensionId::Gostc) {
|
||||
state
|
||||
.extensions
|
||||
.start(ExtensionId::Gostc, &new_config.extensions)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
} else if !was_enabled
|
||||
&& is_enabled
|
||||
&& has_key
|
||||
&& state.extensions.check_available(ExtensionId::Gostc)
|
||||
{
|
||||
state
|
||||
.extensions
|
||||
.start(ExtensionId::Gostc, &new_config.extensions)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
|
||||
Ok(Json(new_config.extensions.gostc.clone()))
|
||||
@@ -312,14 +310,16 @@ pub async fn update_easytier_config(
|
||||
|
||||
if was_enabled && !is_enabled {
|
||||
state.extensions.stop(ExtensionId::Easytier).await.ok();
|
||||
} else if !was_enabled && is_enabled && has_name {
|
||||
if state.extensions.check_available(ExtensionId::Easytier) {
|
||||
state
|
||||
.extensions
|
||||
.start(ExtensionId::Easytier, &new_config.extensions)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
} else if !was_enabled
|
||||
&& is_enabled
|
||||
&& has_name
|
||||
&& state.extensions.check_available(ExtensionId::Easytier)
|
||||
{
|
||||
state
|
||||
.extensions
|
||||
.start(ExtensionId::Easytier, &new_config.extensions)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
|
||||
Ok(Json(new_config.extensions.easytier.clone()))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -50,6 +50,7 @@ pub fn create_router(state: Arc<AppState>) -> Router {
|
||||
.route("/stream/mode", post(handlers::stream_mode_set))
|
||||
.route("/stream/bitrate", post(handlers::stream_set_bitrate))
|
||||
.route("/stream/codecs", get(handlers::stream_codecs_list))
|
||||
.route("/stream/constraints", get(handlers::stream_constraints_get))
|
||||
// WebRTC endpoints
|
||||
.route("/webrtc/session", post(handlers::webrtc_create_session))
|
||||
.route("/webrtc/offer", post(handlers::webrtc_offer))
|
||||
@@ -59,6 +60,7 @@ pub fn create_router(state: Arc<AppState>) -> Router {
|
||||
.route("/webrtc/close", post(handlers::webrtc_close_session))
|
||||
// HID endpoints
|
||||
.route("/hid/status", get(handlers::hid_status))
|
||||
.route("/hid/otg/self-check", get(handlers::hid_otg_self_check))
|
||||
.route("/hid/reset", post(handlers::hid_reset))
|
||||
// WebSocket HID endpoint (for MJPEG mode)
|
||||
.route("/ws/hid", any(ws_hid_handler))
|
||||
@@ -120,6 +122,13 @@ pub fn create_router(state: Arc<AppState>) -> Router {
|
||||
"/config/rustdesk/regenerate-password",
|
||||
post(handlers::config::regenerate_device_password),
|
||||
)
|
||||
// RTSP configuration endpoints
|
||||
.route("/config/rtsp", get(handlers::config::get_rtsp_config))
|
||||
.route("/config/rtsp", patch(handlers::config::update_rtsp_config))
|
||||
.route(
|
||||
"/config/rtsp/status",
|
||||
get(handlers::config::get_rtsp_status),
|
||||
)
|
||||
// Web server configuration
|
||||
.route("/config/web", get(handlers::config::get_web_config))
|
||||
.route("/config/web", patch(handlers::config::update_web_config))
|
||||
@@ -128,6 +137,9 @@ pub fn create_router(state: Arc<AppState>) -> Router {
|
||||
.route("/config/auth", patch(handlers::config::update_auth_config))
|
||||
// System control
|
||||
.route("/system/restart", post(handlers::system_restart))
|
||||
.route("/update/overview", get(handlers::update_overview))
|
||||
.route("/update/upgrade", post(handlers::update_upgrade))
|
||||
.route("/update/status", get(handlers::update_status))
|
||||
// MSD (Mass Storage Device) endpoints
|
||||
.route("/msd/status", get(handlers::msd_status))
|
||||
.route("/msd/images", get(handlers::msd_images_list))
|
||||
@@ -158,6 +170,7 @@ pub fn create_router(state: Arc<AppState>) -> Router {
|
||||
.route("/atx/status", get(handlers::atx_status))
|
||||
.route("/atx/power", post(handlers::atx_power))
|
||||
.route("/atx/wol", post(handlers::atx_wol))
|
||||
.route("/atx/wol/history", get(handlers::atx_wol_history))
|
||||
// Device discovery endpoints
|
||||
.route("/devices/atx", get(handlers::devices::list_atx_devices))
|
||||
// Extension management endpoints
|
||||
|
||||
@@ -127,14 +127,14 @@ fn try_serve_file(path: &str) -> Option<Response<Body>> {
|
||||
.first_or_octet_stream()
|
||||
.to_string();
|
||||
|
||||
return Some(
|
||||
Some(
|
||||
Response::builder()
|
||||
.status(StatusCode::OK)
|
||||
.header(header::CONTENT_TYPE, mime)
|
||||
.header(header::CACHE_CONTROL, "public, max-age=86400")
|
||||
.body(Body::from(data))
|
||||
.unwrap(),
|
||||
);
|
||||
)
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::debug!(
|
||||
@@ -143,7 +143,7 @@ fn try_serve_file(path: &str) -> Option<Response<Body>> {
|
||||
file_path.display(),
|
||||
e
|
||||
);
|
||||
return None;
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user