feat(daemon,tui): visible secured state — status response + TUI notice
Issue #183 Part B: show node secured/unsecured state so operators can tell the difference between a broken node and one waiting for first-boot password setup. daemon: add "secured": bool to status response (true iff ${data_dir}/.secured exists) TUI: fetch secured from daemon status each refresh cycle render [UNSECURED — set root password to activate agent] in red bold when node is unsecured Part A (rc.d gate gating autospawn on .secured) was already complete.
This commit is contained in:
parent
32f47a75f5
commit
e29b9c10e1
2 changed files with 18 additions and 0 deletions
|
|
@ -346,10 +346,14 @@ async fn cmd_status(state: &SharedState) -> ColibriResponse {
|
||||||
let last_warm_hit = *state.last_warm_cache_hit.read().await;
|
let last_warm_hit = *state.last_warm_cache_hit.read().await;
|
||||||
let last_warm_tokens = *state.last_warm_hit_tokens.read().await;
|
let last_warm_tokens = *state.last_warm_hit_tokens.read().await;
|
||||||
|
|
||||||
|
let secured_path = state.config.data_dir.join(".secured");
|
||||||
|
let secured = secured_path.exists();
|
||||||
|
|
||||||
ColibriResponse::ok(serde_json::json!({
|
ColibriResponse::ok(serde_json::json!({
|
||||||
"daemon": "colibri-daemon",
|
"daemon": "colibri-daemon",
|
||||||
"version": env!("CARGO_PKG_VERSION"),
|
"version": env!("CARGO_PKG_VERSION"),
|
||||||
"host": state.config.host,
|
"host": state.config.host,
|
||||||
|
"secured": secured,
|
||||||
"paths": {
|
"paths": {
|
||||||
"data_dir": state.config.data_dir,
|
"data_dir": state.config.data_dir,
|
||||||
"db_path": state.config.db_path,
|
"db_path": state.config.db_path,
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,7 @@ struct App {
|
||||||
sessions: Vec<String>, // all known session IDs
|
sessions: Vec<String>, // all known session IDs
|
||||||
session_idx: usize, // which session is selected
|
session_idx: usize, // which session is selected
|
||||||
attention_only: bool, // 'a' filter: show only attention panes
|
attention_only: bool, // 'a' filter: show only attention panes
|
||||||
|
secured: Option<bool>, // node secured state (None = unknown yet)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
|
@ -97,6 +98,7 @@ impl App {
|
||||||
sessions: Vec::new(),
|
sessions: Vec::new(),
|
||||||
session_idx: 0,
|
session_idx: 0,
|
||||||
attention_only: false,
|
attention_only: false,
|
||||||
|
secured: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -234,6 +236,10 @@ impl App {
|
||||||
self.error = Some(format!("daemon error: {e}"));
|
self.error = Some(format!("daemon error: {e}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// check secured state on each refresh (lightweight status call)
|
||||||
|
if let Ok(st) = self.client.status().await {
|
||||||
|
self.secured = st.get("secured").and_then(|v| v.as_bool());
|
||||||
|
}
|
||||||
// decay status message
|
// decay status message
|
||||||
if let Some((_, ref mut ttl)) = &mut self.status_msg {
|
if let Some((_, ref mut ttl)) = &mut self.status_msg {
|
||||||
*ttl = ttl.saturating_sub(1);
|
*ttl = ttl.saturating_sub(1);
|
||||||
|
|
@ -392,6 +398,14 @@ impl App {
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unsecured node notice
|
||||||
|
if self.secured == Some(false) {
|
||||||
|
lines.push(Line::from(Span::styled(
|
||||||
|
"[UNSECURED — set root password to activate agent]",
|
||||||
|
Style::default().fg(Color::Red).add_modifier(Modifier::BOLD),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
f.render_widget(
|
f.render_widget(
|
||||||
Paragraph::new(lines).block(Block::default().borders(Borders::NONE)),
|
Paragraph::new(lines).block(Block::default().borders(Borders::NONE)),
|
||||||
area,
|
area,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue