init: 本地静态页面托管服务

- 初始化
This commit is contained in:
2026-10-16 13:46:20 +08:00
commit 2e4f0e947b
13 changed files with 1791 additions and 0 deletions
+75
View File
@@ -0,0 +1,75 @@
use serde::Serialize;
use std::path::Path;
const INDEX_TEMPLATE: &str = include_str!("../assets/index.html");
const NOT_FOUND_TEMPLATE: &str = include_str!("../assets/404.html");
#[derive(Debug, Serialize, Clone)]
pub struct AppProject {
pub name: String,
pub route_path: String,
}
pub fn scan_projects(root: &Path) -> Vec<AppProject> {
let mut projects = Vec::new();
let Ok(read_dir) = std::fs::read_dir(root) else {
return projects;
};
for item in read_dir.flatten() {
let path = item.path();
let name = item.file_name().to_string_lossy().to_string();
if name.starts_with('.') {
continue;
}
if path.is_dir() {
let has_index = path.join("index.html").exists() || path.join("index.htm").exists();
if has_index {
let route_path = format!("/{}/", name);
projects.push(AppProject { name, route_path });
}
}
}
projects.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
projects
}
pub fn render_dashboard(projects: &[AppProject]) -> String {
let mut cards_html = String::new();
for proj in projects {
// 提取首字符作为圆形头像字母
let initial = proj.name.chars().next().unwrap_or('A').to_uppercase().to_string();
cards_html.push_str(&format!(
r#"
<a class="site-item" href="{url}" title="{name}">
<div class="site-avatar">{initial}</div>
<div class="site-info">
<span class="site-name">{name}</span>
<span class="site-desc">本地托管应用 • {url}</span>
</div>
</a>
"#,
url = proj.route_path,
name = proj.name,
initial = initial
));
}
let final_cards = if cards_html.is_empty() {
r#"<div class="empty-state">当前目录下暂无含有 index.html 的前端项目</div>"#.to_string()
} else {
cards_html
};
INDEX_TEMPLATE.replace("__CARDS__", &final_cards)
}
pub fn render_not_found(path: &str) -> String {
NOT_FOUND_TEMPLATE.replace("__PATH__", path)
}