init: 本地静态页面托管服务
- 初始化
This commit is contained in:
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user