feat: 接入 mnote web tree shell 与主页链路整理

- 增加 mnote-web tree/command 支持与前端 MnoteWebTreeShell 集成
- 调整 sidebar、documents、runtime config 与 dev/prod server 配套逻辑
- 补充 homepage/tree shell smoke 脚本并更新 harness 进度文件
This commit is contained in:
lix-2026
2026-04-17 23:36:24 +08:00
parent cfc3af8984
commit d8de820d93
40 changed files with 4668 additions and 4143 deletions
+57 -3
View File
@@ -2,6 +2,7 @@ use crate::middleware::request_context::inject_request_context;
use crate::routes::build_router;
use axum::Router;
use std::env;
use std::fs;
use std::sync::Arc;
use tower_http::trace::TraceLayer;
@@ -15,6 +16,8 @@ pub struct AppConfig {
pub convex_url: Option<String>,
pub convex_admin_key: Option<String>,
pub allow_dev_fixtures: bool,
pub query_fixtures_json: Option<String>,
pub mutation_fixtures_json: Option<String>,
pub dev_user_id: String,
pub dev_user_name: String,
pub dev_user_email: String,
@@ -34,23 +37,74 @@ impl AppConfig {
convex_url: env::var("CONVEX_SELF_HOSTED_URL")
.ok()
.or_else(|| env::var("NEXT_PUBLIC_CONVEX_URL").ok())
.or_else(|| read_env_or_dotenv("CONVEX_SELF_HOSTED_URL"))
.or_else(|| read_env_or_dotenv("NEXT_PUBLIC_CONVEX_URL"))
.map(|value| value.trim().trim_end_matches('/').to_string())
.filter(|value| !value.is_empty()),
convex_admin_key: env::var("CONVEX_SELF_HOSTED_ADMIN_KEY")
.ok()
.or_else(|| read_env_or_dotenv("CONVEX_SELF_HOSTED_ADMIN_KEY"))
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty()),
allow_dev_fixtures: env::var("MNOTE_WEB_ALLOW_DEV_FIXTURES")
.ok()
.map(|value| matches!(value.trim(), "1" | "true" | "TRUE" | "yes" | "YES"))
.unwrap_or(false),
dev_user_id: env::var("DEV_USER_ID").unwrap_or_else(|_| "dev-user".into()),
dev_user_name: env::var("DEV_USER_NAME").unwrap_or_else(|_| "开发用户".into()),
dev_user_email: env::var("DEV_USER_EMAIL").unwrap_or_else(|_| "dev@mnote.local".into()),
query_fixtures_json: env::var("MNOTE_WEB_QUERY_FIXTURES_JSON")
.ok()
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty()),
mutation_fixtures_json: env::var("MNOTE_WEB_MUTATION_FIXTURES_JSON")
.ok()
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty()),
dev_user_id: env::var("DEV_USER_ID")
.ok()
.or_else(|| read_env_or_dotenv("DEV_USER_ID"))
.unwrap_or_else(|| "dev-user".into()),
dev_user_name: env::var("DEV_USER_NAME")
.ok()
.or_else(|| read_env_or_dotenv("DEV_USER_NAME"))
.unwrap_or_else(|| "开发用户".into()),
dev_user_email: env::var("DEV_USER_EMAIL")
.ok()
.or_else(|| read_env_or_dotenv("DEV_USER_EMAIL"))
.unwrap_or_else(|| "dev@mnote.local".into()),
}
}
}
fn read_env_or_dotenv(key: &str) -> Option<String> {
if let Ok(value) = env::var(key) {
let trimmed = value.trim().trim_matches('"').to_string();
if !trimmed.is_empty() {
return Some(trimmed);
}
}
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../../..")
.join(".env.all");
let content = fs::read_to_string(root).ok()?;
for line in content.lines() {
let line = line.trim_end_matches('\r');
if line.starts_with('#') || line.trim().is_empty() {
continue;
}
let Some((k, v)) = line.split_once('=') else {
continue;
};
if k.trim() != key {
continue;
}
let trimmed = v.trim().trim_matches('"').to_string();
if !trimmed.is_empty() {
return Some(trimmed);
}
}
None
}
#[derive(Debug, Clone)]
pub struct AppState {
config: Arc<AppConfig>,