mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-07-29 15:31:46 +08:00
feat: 新增 frp 远程访问扩展
This commit is contained in:
@@ -4,12 +4,14 @@ use axum::{
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use std::sync::Arc;
|
||||
use toml_edit::DocumentMut;
|
||||
use typeshare::typeshare;
|
||||
|
||||
use crate::error::{AppError, Result};
|
||||
use crate::extensions::{
|
||||
EasytierConfig, EasytierInfo, ExtensionId, ExtensionInfo, ExtensionLogs, ExtensionsStatus,
|
||||
GostcConfig, GostcInfo, TtydConfig, TtydInfo,
|
||||
FrpProxyType, FrpcConfig, FrpcConfigMode, FrpcInfo, GostcConfig, GostcInfo, TtydConfig,
|
||||
TtydInfo,
|
||||
};
|
||||
use crate::state::AppState;
|
||||
|
||||
@@ -34,6 +36,46 @@ fn validate_easytier_enabled(config: &EasytierConfig) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_frpc_enabled(config: &FrpcConfig) -> Result<()> {
|
||||
match config.config_mode {
|
||||
FrpcConfigMode::Quick => {
|
||||
if config.proxy_name.trim().is_empty() {
|
||||
return Err(AppError::BadRequest("FRPC proxy name is required".into()));
|
||||
}
|
||||
if config.server_addr.trim().is_empty() {
|
||||
return Err(AppError::BadRequest(
|
||||
"FRPC server address is required".into(),
|
||||
));
|
||||
}
|
||||
if config.token.is_empty() {
|
||||
return Err(AppError::BadRequest("FRPC token is required".into()));
|
||||
}
|
||||
if config.local_ip.trim().is_empty() {
|
||||
return Err(AppError::BadRequest("FRPC local IP is required".into()));
|
||||
}
|
||||
if matches!(config.proxy_type, FrpProxyType::Tcp | FrpProxyType::Udp)
|
||||
&& config.remote_port.is_none()
|
||||
{
|
||||
return Err(AppError::BadRequest(
|
||||
"FRPC remote port is required for TCP/UDP proxies".into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
FrpcConfigMode::Full => {
|
||||
let toml = config.custom_toml.trim();
|
||||
if toml.is_empty() {
|
||||
return Err(AppError::BadRequest(
|
||||
"FRPC full configuration is required".into(),
|
||||
));
|
||||
}
|
||||
toml.parse::<DocumentMut>().map_err(|e| {
|
||||
AppError::BadRequest(format!("FRPC full configuration is not valid TOML: {}", e))
|
||||
})?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn list_extensions(State(state): State<Arc<AppState>>) -> Json<ExtensionsStatus> {
|
||||
let config = state.config.get();
|
||||
let mgr = &state.extensions;
|
||||
@@ -54,6 +96,11 @@ pub async fn list_extensions(State(state): State<Arc<AppState>>) -> Json<Extensi
|
||||
status: mgr.status(ExtensionId::Easytier).await,
|
||||
config: config.extensions.easytier.clone(),
|
||||
},
|
||||
frpc: FrpcInfo {
|
||||
available: mgr.check_available(ExtensionId::Frpc),
|
||||
status: mgr.status(ExtensionId::Frpc).await,
|
||||
config: config.extensions.frpc.clone(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -159,6 +206,25 @@ pub struct EasytierConfigUpdate {
|
||||
pub virtual_ip: Option<String>,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct FrpcConfigUpdate {
|
||||
pub enabled: Option<bool>,
|
||||
pub config_mode: Option<FrpcConfigMode>,
|
||||
pub proxy_name: Option<String>,
|
||||
pub proxy_type: Option<FrpProxyType>,
|
||||
pub server_addr: Option<String>,
|
||||
pub server_port: Option<u16>,
|
||||
pub token: Option<String>,
|
||||
pub local_ip: Option<String>,
|
||||
pub local_port: Option<u16>,
|
||||
pub remote_port: Option<Option<u16>>,
|
||||
pub custom_domain: Option<Option<String>>,
|
||||
pub secret_key: Option<String>,
|
||||
pub tls: Option<bool>,
|
||||
pub custom_toml: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn update_ttyd_config(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Json(req): Json<TtydConfigUpdate>,
|
||||
@@ -295,3 +361,81 @@ pub async fn update_easytier_config(
|
||||
|
||||
Ok(Json(new_config.extensions.easytier.clone()))
|
||||
}
|
||||
|
||||
pub async fn update_frpc_config(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Json(req): Json<FrpcConfigUpdate>,
|
||||
) -> Result<Json<FrpcConfig>> {
|
||||
let current_config = state.config.get();
|
||||
let was_enabled = current_config.extensions.frpc.enabled;
|
||||
let mut next_frpc = current_config.extensions.frpc.clone();
|
||||
|
||||
if let Some(enabled) = req.enabled {
|
||||
next_frpc.enabled = enabled;
|
||||
}
|
||||
if let Some(config_mode) = req.config_mode {
|
||||
next_frpc.config_mode = config_mode;
|
||||
}
|
||||
if let Some(ref proxy_name) = req.proxy_name {
|
||||
next_frpc.proxy_name = proxy_name.clone();
|
||||
}
|
||||
if let Some(proxy_type) = req.proxy_type {
|
||||
next_frpc.proxy_type = proxy_type;
|
||||
}
|
||||
if let Some(ref addr) = req.server_addr {
|
||||
next_frpc.server_addr = addr.clone();
|
||||
}
|
||||
if let Some(port) = req.server_port {
|
||||
next_frpc.server_port = port;
|
||||
}
|
||||
if let Some(ref token) = req.token {
|
||||
next_frpc.token = token.clone();
|
||||
}
|
||||
if let Some(ref local_ip) = req.local_ip {
|
||||
next_frpc.local_ip = local_ip.clone();
|
||||
}
|
||||
if let Some(local_port) = req.local_port {
|
||||
next_frpc.local_port = local_port;
|
||||
}
|
||||
if let Some(remote_port) = req.remote_port {
|
||||
next_frpc.remote_port = remote_port;
|
||||
}
|
||||
if let Some(custom_domain) = req.custom_domain {
|
||||
next_frpc.custom_domain = custom_domain;
|
||||
}
|
||||
if let Some(ref secret_key) = req.secret_key {
|
||||
next_frpc.secret_key = secret_key.clone();
|
||||
}
|
||||
if let Some(tls) = req.tls {
|
||||
next_frpc.tls = tls;
|
||||
}
|
||||
if let Some(ref custom_toml) = req.custom_toml {
|
||||
next_frpc.custom_toml = custom_toml.clone();
|
||||
}
|
||||
|
||||
if next_frpc.enabled || matches!(next_frpc.config_mode, FrpcConfigMode::Full) {
|
||||
validate_frpc_enabled(&next_frpc)?;
|
||||
}
|
||||
|
||||
state
|
||||
.config
|
||||
.update(|config| {
|
||||
config.extensions.frpc = next_frpc.clone();
|
||||
})
|
||||
.await?;
|
||||
|
||||
let new_config = state.config.get();
|
||||
let is_enabled = new_config.extensions.frpc.enabled;
|
||||
|
||||
if was_enabled && !is_enabled {
|
||||
state.extensions.stop(ExtensionId::Frpc).await.ok();
|
||||
} else if !was_enabled && is_enabled && state.extensions.check_available(ExtensionId::Frpc) {
|
||||
state
|
||||
.extensions
|
||||
.start(ExtensionId::Frpc, &new_config.extensions)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
|
||||
Ok(Json(new_config.extensions.frpc.clone()))
|
||||
}
|
||||
|
||||
@@ -205,6 +205,10 @@ pub fn create_router(state: Arc<AppState>) -> Router {
|
||||
"/extensions/easytier/config",
|
||||
patch(handlers::extensions::update_easytier_config),
|
||||
)
|
||||
.route(
|
||||
"/extensions/frpc/config",
|
||||
patch(handlers::extensions::update_frpc_config),
|
||||
)
|
||||
// Terminal (ttyd) reverse proxy - WebSocket and HTTP
|
||||
.route("/terminal", get(handlers::terminal::terminal_index))
|
||||
.route("/terminal/", get(handlers::terminal::terminal_index))
|
||||
|
||||
Reference in New Issue
Block a user