state recovery, polling optimizations, error rendering
widget states cached to SQLite, loaded on startup to seed DataProjection so server restart preserves last-known data for reconnecting clients. polling: first poll runs immediately, widget list cached per-task with 30s refresh, static text polled once inline instead of looping. poll failures propagate WidgetError::SourceUnavailable to clients. render engine prepends [offline] prefix in accent color, stale data preserved below.
This commit is contained in:
@@ -5,12 +5,13 @@ mod polling;
|
||||
use anyhow::Result;
|
||||
use application::DataProjection;
|
||||
use config_sqlite::SqliteConfigStore;
|
||||
use domain::ConfigRepository;
|
||||
use http_api::AppState;
|
||||
use kframe_auth::{Argon2Hasher, AuthConfig, JwtAuthService};
|
||||
use secret_store::AesSecretStore;
|
||||
use std::sync::Arc;
|
||||
use tcp_server::{ClientTracker, TcpBroadcaster, TcpEventBus, run_tcp_server};
|
||||
use tracing::{error, info};
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
@@ -40,6 +41,15 @@ async fn main() -> Result<()> {
|
||||
let auth = Arc::new(JwtAuthService::new(auth_config));
|
||||
let hasher = Arc::new(Argon2Hasher);
|
||||
|
||||
match config_store.load_widget_states().await {
|
||||
Ok(states) if !states.is_empty() => {
|
||||
info!(count = states.len(), "loaded cached widget states");
|
||||
projection.seed(states).await;
|
||||
}
|
||||
Ok(_) => {}
|
||||
Err(e) => warn!(error = %e, "failed to load cached widget states"),
|
||||
}
|
||||
|
||||
let tcp_addr = cfg.tcp_addr.clone();
|
||||
let tcp_bc = broadcaster.clone();
|
||||
let tcp_tracker = tracker.clone();
|
||||
|
||||
Reference in New Issue
Block a user