use std::collections::VecDeque; use std::net::{IpAddr, SocketAddr}; use std::ops::Deref; use std::path::{Path, PathBuf}; use std::sync::Arc; use bollard::Docker; use patch_db::{PatchDb, Revision}; use reqwest::Url; use rpc_toolkit::url::Host; use rpc_toolkit::Context; use serde::Deserialize; use sqlx::migrate::MigrateDatabase; use sqlx::{Sqlite, SqlitePool}; use tokio::fs::File; use tokio::sync::RwLock; use crate::manager::ManagerMap; use crate::net::NetController; use crate::util::{from_toml_async_reader, AsyncFileExt}; use crate::{Error, ResultExt}; #[derive(Debug, Default, Deserialize)] #[serde(rename_all = "kebab-case")] pub struct RpcContextConfig { pub bind_rpc: Option, pub bind_ws: Option, pub tor_control: Option, pub db: Option, pub secret_store: Option, pub revision_cache_size: Option, } pub struct RpcContextSeed { pub bind_rpc: SocketAddr, pub bind_ws: SocketAddr, pub db: PatchDb, pub secret_store: SqlitePool, pub docker: Docker, pub net_controller: Arc, pub managers: ManagerMap, pub revision_cache_size: usize, pub revision_cache: RwLock>>, pub metrics_cache: RwLock>, } #[derive(Clone)] pub struct RpcContext(Arc); impl RpcContext { pub async fn init>(cfg_path: Option

) -> Result { let cfg_path = cfg_path .as_ref() .map(|p| p.as_ref()) .unwrap_or(Path::new(crate::CONFIG_PATH)); let base = if let Some(f) = File::maybe_open(cfg_path) .await .with_ctx(|_| (crate::ErrorKind::Filesystem, cfg_path.display().to_string()))? { from_toml_async_reader(f).await? } else { RpcContextConfig::default() }; let db = PatchDb::open( base.db .unwrap_or_else(|| Path::new("/mnt/embassy-os/embassy.db").to_owned()), ) .await?; let secret_store_url = format!( "sqlite://{}", base.secret_store .unwrap_or_else(|| Path::new("/mnt/embassy-os/secrets.db").to_owned()) .display() ); if !Sqlite::database_exists(&secret_store_url).await? { Sqlite::create_database(&secret_store_url).await?; } let secret_store = SqlitePool::connect(&secret_store_url).await?; sqlx::migrate!() .run(&secret_store) .await .with_kind(crate::ErrorKind::Database)?; let docker = Docker::connect_with_unix_defaults()?; let net_controller = Arc::new( NetController::init( ([127, 0, 0, 1], 80).into(), todo!("Grab Key from Database, Generate if it doesn't exist"), base.tor_control .unwrap_or(SocketAddr::from(([127, 0, 0, 1], 9051))), ) .await?, ); let managers = ManagerMap::init( &mut db.handle(), &mut secret_store.acquire().await?, docker.clone(), net_controller.clone(), ) .await?; let seed = Arc::new(RpcContextSeed { bind_rpc: base.bind_rpc.unwrap_or(([127, 0, 0, 1], 5959).into()), bind_ws: base.bind_ws.unwrap_or(([127, 0, 0, 1], 5960).into()), db, secret_store, docker, net_controller, managers, revision_cache_size: base.revision_cache_size.unwrap_or(512), revision_cache: RwLock::new(VecDeque::new()), metrics_cache: RwLock::new(None), }); // TODO: handle apps in bad / transient state Ok(Self(seed)) } pub async fn package_registry_url(&self) -> Result { Ok( if let Some(market) = crate::db::DatabaseModel::new() .server_info() .package_marketplace() .get(&mut self.db.handle(), false) .await? .to_owned() { market } else { crate::db::DatabaseModel::new() .server_info() .eos_marketplace() .get(&mut self.db.handle(), false) .await? .to_owned() }, ) } } impl Context for RpcContext { fn host(&self) -> Host<&str> { match self.0.bind_rpc.ip() { IpAddr::V4(a) => Host::Ipv4(a), IpAddr::V6(a) => Host::Ipv6(a), } } fn port(&self) -> u16 { self.0.bind_rpc.port() } } impl Deref for RpcContext { type Target = RpcContextSeed; fn deref(&self) -> &Self::Target { &*self.0 } }