feat(console): add runtime management and WIT Registry
- embed wasmeld-runtime behind an Axum management API - persist service metadata, metrics, and events with Toasty and libSQL - register immutable component artifacts with platform-owned limits - publish, list, inspect, and download binary WIT package versions - verify Runtime lifecycle, persistence, Registry immutability, and transitive fetch
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
//! Toasty models and transactional libSQL snapshots for Console control data.
|
||||
//!
|
||||
//! Component bytes and WIT packages remain filesystem artifacts. This module
|
||||
//! persists only service manifests, metrics, and the bounded event history.
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use toasty::Db;
|
||||
use toasty_driver_turso::Turso;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
#[derive(Clone, Debug, toasty::Model)]
|
||||
pub(crate) struct StoredService {
|
||||
#[key]
|
||||
pub service_key: String,
|
||||
pub manifest_toml: String,
|
||||
pub updated_at_ms: u64,
|
||||
pub calls: u64,
|
||||
pub errors: u64,
|
||||
pub last_latency_ms: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, toasty::Model)]
|
||||
pub(crate) struct StoredEvent {
|
||||
#[key]
|
||||
pub id: u64,
|
||||
pub timestamp_ms: u64,
|
||||
pub kind: String,
|
||||
pub service_id: Option<String>,
|
||||
pub revision: Option<String>,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
/// Serialized access to the Toasty database connection.
|
||||
pub(crate) struct Persistence {
|
||||
db: Mutex<Db>,
|
||||
}
|
||||
|
||||
impl Persistence {
|
||||
/// Opens the database and installs the schema for a new file.
|
||||
pub async fn open(path: &Path) -> toasty::Result<Self> {
|
||||
let is_new_database = !path.exists();
|
||||
let db = Db::builder()
|
||||
.models(toasty::models!(StoredService, StoredEvent))
|
||||
.build(Turso::file(path))
|
||||
.await?;
|
||||
if is_new_database {
|
||||
db.push_schema().await?;
|
||||
}
|
||||
Ok(Self { db: Mutex::new(db) })
|
||||
}
|
||||
|
||||
/// Loads the complete service set and retained event history.
|
||||
pub async fn load(&self) -> toasty::Result<(Vec<StoredService>, Vec<StoredEvent>)> {
|
||||
let mut db = self.db.lock().await;
|
||||
let services = StoredService::all().exec(&mut *db).await?;
|
||||
let events = StoredEvent::all().exec(&mut *db).await?;
|
||||
Ok((services, events))
|
||||
}
|
||||
|
||||
/// Atomically upserts a Console snapshot and prunes expired events.
|
||||
pub async fn sync(
|
||||
&self,
|
||||
services: Vec<StoredService>,
|
||||
events: Vec<StoredEvent>,
|
||||
) -> toasty::Result<()> {
|
||||
let mut db = self.db.lock().await;
|
||||
let mut tx = db.transaction().await?;
|
||||
|
||||
for service in services {
|
||||
StoredService::upsert_by_service_key(service.service_key)
|
||||
.manifest_toml(service.manifest_toml)
|
||||
.updated_at_ms(service.updated_at_ms)
|
||||
.calls(service.calls)
|
||||
.errors(service.errors)
|
||||
.last_latency_ms(service.last_latency_ms)
|
||||
.exec(&mut tx)
|
||||
.await?;
|
||||
}
|
||||
|
||||
if let Some(oldest_event) = events.last() {
|
||||
StoredEvent::filter(StoredEvent::fields().id().lt(oldest_event.id))
|
||||
.delete()
|
||||
.exec(&mut tx)
|
||||
.await?;
|
||||
}
|
||||
|
||||
for event in events {
|
||||
StoredEvent::upsert_by_id(event.id)
|
||||
.timestamp_ms(event.timestamp_ms)
|
||||
.kind(event.kind)
|
||||
.service_id(event.service_id)
|
||||
.revision(event.revision)
|
||||
.message(event.message)
|
||||
.exec(&mut tx)
|
||||
.await?;
|
||||
}
|
||||
|
||||
tx.commit().await
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user