mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-06-14 03:32:00 +08:00
feat: 允许通过环境变量手动指定前端资源路径,删除 debug 分支默认资源路径
This commit is contained in:
@@ -49,7 +49,7 @@ reqwest = { version = "0.13", features = ["stream", "rustls", "json"], default-f
|
|||||||
urlencoding = "2"
|
urlencoding = "2"
|
||||||
|
|
||||||
# Static file embedding
|
# Static file embedding
|
||||||
rust-embed = { version = "8", features = ["compression"] }
|
rust-embed = { version = "8", features = ["compression", "debug-embed"] }
|
||||||
mime_guess = "2"
|
mime_guess = "2"
|
||||||
|
|
||||||
# TLS/HTTPS
|
# TLS/HTTPS
|
||||||
|
|||||||
@@ -4,31 +4,40 @@ use axum::{
|
|||||||
routing::get,
|
routing::get,
|
||||||
Router,
|
Router,
|
||||||
};
|
};
|
||||||
#[cfg(debug_assertions)]
|
use rust_embed::Embed;
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
const FRONTEND_DIR_ENV: &str = "ONE_KVM_FRONTEND_DIR";
|
||||||
use rust_embed::Embed;
|
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
#[derive(Embed)]
|
#[derive(Embed)]
|
||||||
#[folder = "web/dist"]
|
#[folder = "web/dist"]
|
||||||
#[prefix = ""]
|
#[prefix = ""]
|
||||||
pub struct StaticAssets;
|
pub struct StaticAssets;
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
fn frontend_dir_override() -> Option<PathBuf> {
|
||||||
fn get_static_base_dir() -> PathBuf {
|
static FRONTEND_DIR: OnceLock<Option<PathBuf>> = OnceLock::new();
|
||||||
static BASE_DIR: OnceLock<PathBuf> = OnceLock::new();
|
FRONTEND_DIR
|
||||||
BASE_DIR
|
|
||||||
.get_or_init(|| {
|
.get_or_init(|| {
|
||||||
if let Ok(exe_path) = std::env::current_exe() {
|
let value = std::env::var_os(FRONTEND_DIR_ENV)?;
|
||||||
if let Some(exe_dir) = exe_path.parent() {
|
let path = PathBuf::from(value);
|
||||||
return exe_dir.join("web").join("dist");
|
|
||||||
|
if path.as_os_str().is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
match path.canonicalize() {
|
||||||
|
Ok(path) => Some(path),
|
||||||
|
Err(e) => {
|
||||||
|
tracing::warn!(
|
||||||
|
"{}='{}' is not accessible: {}",
|
||||||
|
FRONTEND_DIR_ENV,
|
||||||
|
path.display(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PathBuf::from("web/dist")
|
|
||||||
})
|
})
|
||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
@@ -84,69 +93,60 @@ fn serve_file(path: &str) -> Response<Body> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn try_serve_file(path: &str) -> Option<Response<Body>> {
|
fn try_serve_file(path: &str) -> Option<Response<Body>> {
|
||||||
#[cfg(debug_assertions)]
|
if let Some(base_dir) = frontend_dir_override() {
|
||||||
{
|
return try_serve_file_from_dir(&base_dir, path);
|
||||||
let base_dir = get_static_base_dir();
|
}
|
||||||
|
|
||||||
|
let asset = StaticAssets::get(path)?;
|
||||||
|
Some(static_response(path, asset.data.to_vec()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_serve_file_from_dir(base_dir: &Path, path: &str) -> Option<Response<Body>> {
|
||||||
let file_path = base_dir.join(path);
|
let file_path = base_dir.join(path);
|
||||||
|
|
||||||
if !file_path.starts_with(&base_dir) {
|
let normalized_path = match file_path.canonicalize() {
|
||||||
|
Ok(path) => path,
|
||||||
|
Err(e) => {
|
||||||
|
tracing::debug!(
|
||||||
|
"Failed to resolve static file '{}' from '{}': {}",
|
||||||
|
path,
|
||||||
|
file_path.display(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !normalized_path.starts_with(base_dir) {
|
||||||
tracing::warn!("Path traversal attempt blocked: {}", path);
|
tracing::warn!("Path traversal attempt blocked: {}", path);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (Ok(normalized_path), Ok(normalized_base)) =
|
match std::fs::read(&normalized_path) {
|
||||||
(file_path.canonicalize(), base_dir.canonicalize())
|
Ok(data) => Some(static_response(path, data)),
|
||||||
{
|
|
||||||
if !normalized_path.starts_with(&normalized_base) {
|
|
||||||
tracing::warn!("Path traversal attempt blocked (canonicalized): {}", path);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match std::fs::read(&file_path) {
|
|
||||||
Ok(data) => {
|
|
||||||
let mime = mime_guess::from_path(path)
|
|
||||||
.first_or_octet_stream()
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
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) => {
|
Err(e) => {
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"Failed to read static file '{}' from '{}': {}",
|
"Failed to read static file '{}' from '{}': {}",
|
||||||
path,
|
path,
|
||||||
file_path.display(),
|
normalized_path.display(),
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
{
|
|
||||||
let asset = StaticAssets::get(path)?;
|
|
||||||
|
|
||||||
|
fn static_response(path: &str, data: Vec<u8>) -> Response<Body> {
|
||||||
let mime = mime_guess::from_path(path)
|
let mime = mime_guess::from_path(path)
|
||||||
.first_or_octet_stream()
|
.first_or_octet_stream()
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
Some(
|
|
||||||
Response::builder()
|
Response::builder()
|
||||||
.status(StatusCode::OK)
|
.status(StatusCode::OK)
|
||||||
.header(header::CONTENT_TYPE, mime)
|
.header(header::CONTENT_TYPE, mime)
|
||||||
.header(header::CACHE_CONTROL, "public, max-age=86400")
|
.header(header::CACHE_CONTROL, "public, max-age=86400")
|
||||||
.body(Body::from(asset.data.to_vec()))
|
.body(Body::from(data))
|
||||||
.unwrap(),
|
.unwrap()
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn placeholder_html() -> &'static str {
|
pub fn placeholder_html() -> &'static str {
|
||||||
|
|||||||
Reference in New Issue
Block a user