init: 本地静态页面托管服务
- 初始化
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
use std::path::PathBuf;
|
||||
use clap::Parser;
|
||||
use colored::*;
|
||||
use stash_core::{Config, start};
|
||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(
|
||||
name = "stash",
|
||||
author = "stash developers",
|
||||
version,
|
||||
about = "🚀 本地静态页面托管与多项目管理工具",
|
||||
long_about = "支持本地静态页面按目录隔离托管,支持前端直接调用同源接口,提供清爽的项目导航列表。"
|
||||
)]
|
||||
struct Cli {
|
||||
/// 静态页面托管根目录(支持相对路径和绝对路径)
|
||||
#[arg(short, long, default_value = "./public", value_name = "DIR")]
|
||||
root: PathBuf,
|
||||
|
||||
/// 监听端口
|
||||
#[arg(short, long, default_value_t = 8080, value_name = "PORT")]
|
||||
port: u16,
|
||||
|
||||
/// 绑定监听地址(默认 127.0.0.1,如需局域网访问可传 0.0.0.0)
|
||||
#[arg(long, default_value = "127.0.0.1", value_name = "HOST")]
|
||||
host: String,
|
||||
|
||||
/// 关闭根目录项目导航列表功能(仅托管静态文件)
|
||||
#[arg(long, default_value_t = false)]
|
||||
no_listing: bool,
|
||||
|
||||
/// 关闭 CORS 跨域标头注入
|
||||
#[arg(long, default_value_t = false)]
|
||||
no_cors: bool,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
// 1. 初始化 tracing 日志
|
||||
tracing_subscriber::registry()
|
||||
.with(
|
||||
tracing_subscriber::EnvFilter::try_from_default_env()
|
||||
.unwrap_or_else(|_| "info".into()),
|
||||
)
|
||||
.with(tracing_subscriber::fmt::layer())
|
||||
.init();
|
||||
|
||||
// 2. 解析命令行输入
|
||||
let cli = Cli::parse();
|
||||
|
||||
// 检查目录是否存在,不存在则友好提示并自动创建默认 public
|
||||
if !cli.root.exists() {
|
||||
if cli.root == PathBuf::from("./public") {
|
||||
println!(
|
||||
"{} 默认托管目录 {} 不存在,正在自动创建...",
|
||||
"[INFO]".cyan(),
|
||||
"./public".yellow()
|
||||
);
|
||||
std::fs::create_dir_all(&cli.root)?;
|
||||
} else {
|
||||
eprintln!(
|
||||
"{} 指定的托管目录不存在: {}",
|
||||
"[ERROR]".red().bold(),
|
||||
cli.root.display().to_string().yellow()
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
let abs_root = cli.root.canonicalize().unwrap_or_else(|_| cli.root.clone());
|
||||
|
||||
// 3. 构造 stash-core 配置并启动
|
||||
let config = Config {
|
||||
root: abs_root.clone(),
|
||||
port: cli.port,
|
||||
host: cli.host,
|
||||
directory_listing: !cli.no_listing,
|
||||
enable_cors: !cli.no_cors,
|
||||
};
|
||||
|
||||
let handle = match start(config).await {
|
||||
Ok(h) => h,
|
||||
Err(err) => {
|
||||
eprintln!("{} 启动失败: {}", "[FATAL]".red().bold(), err);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
// 4. 打印启动成功 Banner
|
||||
println!();
|
||||
println!("{}", "==================================================".bright_blue());
|
||||
println!(" ✨ {} {}", "Stash Server".green().bold(), format!("v{}", env!("CARGO_PKG_VERSION")).dimmed());
|
||||
println!("{}", "==================================================".bright_blue());
|
||||
println!(" ➜ 本地访问: {}", format!("http://{}", handle.local_addr).cyan().bold().underline());
|
||||
println!(" ➜ 托管路径: {}", abs_root.display().to_string().white());
|
||||
println!(" ➜ 目录导航页: {}", if !cli.no_listing { "开启".green() } else { "关闭".red() });
|
||||
println!(" ➜ 接口列表: {}", format!("http://{}/api/pages", handle.local_addr).dimmed());
|
||||
println!("{}", "--------------------------------------------------".bright_blue());
|
||||
println!(" 按 {} 停止服务", "Ctrl+C".yellow().bold());
|
||||
println!();
|
||||
|
||||
// 5. 跨平台捕获退出信号
|
||||
wait_for_shutdown_signal().await;
|
||||
|
||||
println!("\n{} 正在停止服务并释放端口...", "[SYS]".yellow());
|
||||
handle.stop();
|
||||
|
||||
// 留出时间让 tokio 优雅关闭 socket 句柄
|
||||
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
|
||||
println!("{} 服务已安全退出。\n", "[DONE]".green().bold());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 监听停机信号(支持 Windows Ctrl+C 以及 Linux/macOS SIGINT/SIGTERM)
|
||||
async fn wait_for_shutdown_signal() {
|
||||
let ctrl_c = async {
|
||||
tokio::signal::ctrl_c()
|
||||
.await
|
||||
.expect("无法注册 Ctrl+C 信号处理器");
|
||||
};
|
||||
|
||||
#[cfg(unix)]
|
||||
let terminate = async {
|
||||
tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
|
||||
.expect("无法注册 SIGTERM 信号处理器")
|
||||
.recv()
|
||||
.await;
|
||||
};
|
||||
|
||||
#[cfg(not(unix))]
|
||||
let terminate = std::future::pending::<()>();
|
||||
|
||||
tokio::select! {
|
||||
_ = ctrl_c => {},
|
||||
_ = terminate => {},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user