Files
k-frame/crates/adapters/config-sqlite/src/lib.rs
Gabriel Kaszewski 13497dd53c 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.
2026-06-19 12:56:12 +02:00

119 lines
3.2 KiB
Rust

pub mod error;
mod repository;
mod serialization;
use domain::SecretStore;
use sqlx::SqlitePool;
use std::sync::Arc;
pub use error::SqliteConfigError;
pub struct SqliteConfigStore {
pool: SqlitePool,
secrets: Option<Arc<dyn SecretStore + Send + Sync>>,
}
impl SqliteConfigStore {
pub async fn new(database_url: &str) -> Result<Self, sqlx::Error> {
Self::with_secrets(database_url, None).await
}
pub async fn with_secrets(
database_url: &str,
secrets: Option<Arc<dyn SecretStore + Send + Sync>>,
) -> Result<Self, sqlx::Error> {
let pool = SqlitePool::connect(database_url).await?;
let store = Self { pool, secrets };
store.migrate().await?;
Ok(store)
}
pub(crate) fn secrets(&self) -> Option<&(dyn SecretStore + Send + Sync)> {
self.secrets.as_deref()
}
async fn migrate(&self) -> Result<(), sqlx::Error> {
sqlx::query(
"CREATE TABLE IF NOT EXISTS widgets (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
display_hint TEXT NOT NULL,
data_source_id INTEGER NOT NULL,
mappings TEXT NOT NULL,
max_data_size INTEGER NOT NULL
)",
)
.execute(&self.pool)
.await?;
sqlx::query(
"CREATE TABLE IF NOT EXISTS data_sources (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
source_type TEXT NOT NULL,
poll_interval_secs INTEGER NOT NULL,
config TEXT NOT NULL
)",
)
.execute(&self.pool)
.await?;
sqlx::query(
"CREATE TABLE IF NOT EXISTS layout (
id INTEGER PRIMARY KEY CHECK (id = 1),
data TEXT NOT NULL
)",
)
.execute(&self.pool)
.await?;
sqlx::query(
"CREATE TABLE IF NOT EXISTS presets (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
layout_data TEXT NOT NULL
)",
)
.execute(&self.pool)
.await?;
sqlx::query(
"CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL
)",
)
.execute(&self.pool)
.await?;
sqlx::query(
"CREATE TABLE IF NOT EXISTS theme (
id INTEGER PRIMARY KEY CHECK (id = 1),
data TEXT NOT NULL
)",
)
.execute(&self.pool)
.await?;
sqlx::query(
"CREATE TABLE IF NOT EXISTS widget_state_cache (
widget_id INTEGER PRIMARY KEY,
state_json TEXT NOT NULL
)",
)
.execute(&self.pool)
.await?;
// Add alignment columns to widgets (idempotent)
let _ = sqlx::query("ALTER TABLE widgets ADD COLUMN h_align TEXT NOT NULL DEFAULT 'left'")
.execute(&self.pool)
.await;
let _ = sqlx::query("ALTER TABLE widgets ADD COLUMN v_align TEXT NOT NULL DEFAULT 'top'")
.execute(&self.pool)
.await;
Ok(())
}
}