chore: document architecture gaps and add dev hot reload
- add npm dev:hot wrapper using cargo-watch and page reload polling - add mnote-web dev hot reload endpoint and coverage - record current architecture review and tracked bug findings across realtime, tree, editor, and AI runtimes Verification: - node scripts/task-dev-hot-plan-test.js - node --check scripts/dev-hot.js - cargo test -p mnote-web dev_hot -- --nocapture
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
use axum::http::{HeaderMap, HeaderValue};
|
||||
use axum::response::IntoResponse;
|
||||
use axum::Json;
|
||||
use serde::Serialize;
|
||||
use std::sync::LazyLock;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
const HEADER_MNOTE_WEB_OWNER: &str = "x-mnote-web-owner";
|
||||
|
||||
static DEV_HOT_BOOT_ID: LazyLock<String> = LazyLock::new(|| {
|
||||
let now = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map(|duration| duration.as_millis())
|
||||
.unwrap_or_default();
|
||||
format!("{}-{now}", std::process::id())
|
||||
});
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DevHotReloadPayload {
|
||||
ok: bool,
|
||||
enabled: bool,
|
||||
boot_id: &'static str,
|
||||
}
|
||||
|
||||
pub fn dev_hot_reload_enabled() -> bool {
|
||||
matches!(
|
||||
std::env::var("MNOTE_WEB_DEV_HOT_RELOAD")
|
||||
.unwrap_or_default()
|
||||
.trim()
|
||||
.to_ascii_lowercase()
|
||||
.as_str(),
|
||||
"1" | "true" | "yes" | "on"
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn hot_reload() -> impl IntoResponse {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
HEADER_MNOTE_WEB_OWNER,
|
||||
HeaderValue::from_static("mnote-web"),
|
||||
);
|
||||
(
|
||||
headers,
|
||||
Json(DevHotReloadPayload {
|
||||
ok: true,
|
||||
enabled: dev_hot_reload_enabled(),
|
||||
boot_id: DEV_HOT_BOOT_ID.as_str(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
mod bridge;
|
||||
pub(crate) mod command_support;
|
||||
mod compat;
|
||||
mod dev_hot;
|
||||
mod documents;
|
||||
mod editor;
|
||||
mod gateway;
|
||||
@@ -80,6 +81,7 @@ pub fn build_router(state: AppState) -> Router {
|
||||
)
|
||||
.route("/api/search/documents", post(search::documents))
|
||||
.route("/api/gateway/health", get(gateway::gateway_health))
|
||||
.route("/api/dev/hot-reload", get(dev_hot::hot_reload))
|
||||
.route("/api/runtime/config", get(session::runtime_config))
|
||||
.route("/api/auth", post(gateway::auth_api))
|
||||
.route("/api/auth/session", get(session::session))
|
||||
@@ -350,4 +352,18 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn dev_hot_reload_endpoint_is_mounted() {
|
||||
let response = app(false)
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.uri("/api/dev/hot-reload")
|
||||
.body(Body::empty())
|
||||
.expect("request"),
|
||||
)
|
||||
.await
|
||||
.expect("response");
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user