#[cfg(feature = "postgres")]
use crate::hyperspace::registry::postgres::{
PostgresConnectInfo, PostgresPlatform, PostgresRegistry, PostgresRegistryContext,
PostgresRegistryContextHandle,
};
use crate::hyperspace::driver::{DriverAvail, DriversBuilder};
use crate::hyperspace::driver::base::BaseDriverFactory;
use crate::hyperspace::driver::control::ControlDriverFactory;
use crate::hyperspace::driver::root::RootDriverFactory;
use crate::space::artifact::asynch::Artifacts;
use crate::space::kind::StarSub;
use crate::space::loc::{MachineName, StarKey};
use crate::space::point::Point;
use std::fs;
use std::path::Path;
use std::str::FromStr;
use std::sync::Arc;
use crate::hyperspace::database::{Database, LiveDatabase};
use crate::env::{config_path, STARLANE_CONTROL_PORT, STARLANE_DATA_DIR, STARLANE_HOME};
use crate::hyperspace::err::HypErr;
use crate::hyperspace::hyperlane::tcp::{CertGenerator, HyperlaneTcpServer};
use crate::hyperspace::hyperlane::{AnonHyperAuthenticator, HyperGateSelector, LocalHyperwayGateJumper};
use crate::hyperspace::platform::{Platform, PlatformConfig};
use crate::hyperspace::reg::{PgRegistryConfig, Registry, RegistryWrapper};
use crate::hyperspace::registry::err::RegErr;
use crate::hyperspace::registry::postgres::embed::PgEmbedSettings;
use crate::hyperspace::registry::postgres::PostgresDbKey;
use crate::hyperspace::shutdown::panic_shutdown;
use anyhow::anyhow;
use port_check::is_local_ipv4_port_free;
use serde::{Deserialize, Serialize};
use starlane_primitive_macros::{logger, push_loc};
use std::collections::HashSet;
use std::ops::Deref;
use wasmer_wasix::virtual_net::VirtualConnectedSocketExt;
use crate::hyperspace::driver::space::SpaceDriverFactory;
use crate::hyperspace::foundation::{Foundation, StandAloneFoundation};
use crate::hyperspace::machine::MachineTemplate;
#[derive(Clone, Serialize, Deserialize)]
pub struct StarlaneConfig {
pub context: String,
pub home: String,
pub can_nuke: bool,
pub can_scorch: bool,
pub control_port: u16,
pub registry: PgRegistryConfig,
}
impl PlatformConfig for StarlaneConfig {
fn can_scorch(&self) -> bool {
self.can_scorch
}
fn can_nuke(&self) -> bool {
self.can_nuke
}
fn registry(&self) -> &PgRegistryConfig {
&self.registry
}
fn home(&self) -> &String {
&self.home
}
}
impl Default for StarlaneConfig {
fn default() -> StarlaneConfig {
Self {
context: "default".to_string(),
home: STARLANE_HOME.to_string(),
can_nuke: false,
can_scorch: false,
control_port: 4343u16,
registry: PgRegistryConfig::default(),
}
}
}
#[derive(Clone)]
pub struct Starlane {
config: StarlaneConfig,
artifacts: Artifacts,
registry: Registry,
foundation: StandAloneFoundation,
}
#[cfg(feature = "postgres")]
impl Starlane {
pub async fn new(
config: StarlaneConfig,
foundation: StandAloneFoundation,
) -> Result<Starlane, HypErr> {
let artifacts = Artifacts::just_builtins();
let db = match config.clone().registry {
PgRegistryConfig::Embedded(db) => {
let rtn = foundation.provision_registry(&config).await?;
rtn
}
PgRegistryConfig::External(db) => {
let (handle, mut rx) = tokio::sync::mpsc::channel(1);
tokio::spawn(async move {
while let Some(_) = rx.recv().await {
}
});
LiveDatabase::new(db, handle)
}
};
let lookups = PostgresLookups::new(config.registry.clone());
let mut set = HashSet::new();
set.insert(db.database.clone());
let ctx = Arc::new(PostgresRegistryContext::new(set, Box::new(lookups.clone())).await?);
let handle = PostgresRegistryContextHandle::new(&db.database, ctx, db.handle);
let logger = logger!(&Point::global_registry());
let registry = Arc::new(RegistryWrapper::new(Arc::new(
PostgresRegistry::new(handle, Box::new(lookups), logger).await?,
)));
Ok(Self {
config,
registry,
artifacts,
foundation,
})
}
}
impl Drop for Starlane {
fn drop(&mut self) {
match &self.config.registry {
PgRegistryConfig::Embedded(db) => {}
_ => {}
};
}
}
#[async_trait]
impl Platform for Starlane
where
Self: Sync + Send + Sized,
{
type Err = HypErr;
type StarAuth = AnonHyperAuthenticator;
type RemoteStarConnectionFactory = LocalHyperwayGateJumper;
type Foundation = StandAloneFoundation;
type Config = StarlaneConfig;
fn data_dir(&self) -> String {
STARLANE_DATA_DIR.clone()
}
fn star_auth(&self, star: &StarKey) -> Result<Self::StarAuth, Self::Err> {
Ok(AnonHyperAuthenticator::new())
}
fn remote_connection_factory_for_star(
&self,
star: &StarKey,
) -> Result<Self::RemoteStarConnectionFactory, Self::Err> {
todo!()
}
fn machine_template(&self) -> MachineTemplate {
MachineTemplate::default()
}
fn machine_name(&self) -> MachineName {
"singularity".to_string()
}
fn drivers_builder(&self, kind: &StarSub) -> DriversBuilder {
let mut builder = DriversBuilder::new(kind.clone());
if *kind == StarSub::Super {
builder.add_post(Arc::new(BaseDriverFactory::new(DriverAvail::External)));
} else {
builder.add_post(Arc::new(BaseDriverFactory::new(DriverAvail::Internal)));
}
match kind {
StarSub::Central => {
builder.add_post(Arc::new(RootDriverFactory::new()));
}
StarSub::Super => {
builder.add_post(Arc::new(SpaceDriverFactory::new()));
}
StarSub::Nexus => {}
StarSub::Maelstrom => {
}
StarSub::Scribe => {
}
StarSub::Jump => {
}
StarSub::Fold => {}
StarSub::Machine => {
builder.add_post(Arc::new(ControlDriverFactory::new()));
}
}
builder
}
async fn global_registry(&self) -> Result<Registry, Self::Err> {
Ok(self.registry.clone())
}
async fn star_registry(&self, star: &StarKey) -> Result<Registry, Self::Err> {
todo!()
}
fn artifact_hub(&self) -> Artifacts {
self.artifacts.clone()
}
async fn start_services(&self, gate: &Arc<HyperGateSelector>) {
let dir = match dirs::home_dir() {
None => ".starlane/localhost/certs".to_string(),
Some(path) => format!("{}/.starlane/localhost/certs", path.display()),
};
fs::create_dir_all(dir.as_str());
let cert = format!("{}/cert.der", dir.as_str());
let key = format!("{}/key.der", dir.as_str());
let cert_path = Path::new(&cert);
let key_path = Path::new(&key);
if !cert_path.exists() || !key_path.exists() {
CertGenerator::gen(vec!["localhost".to_string()])
.unwrap()
.write_to_dir(dir.clone())
.await
.unwrap();
};
let logger = push_loc!((self.logger(), Point::from_str("control-blah").unwrap()));
if !is_local_ipv4_port_free(STARLANE_CONTROL_PORT.clone()) {
panic_shutdown(format!(
"starlane port '{}' is being used by another process",
STARLANE_CONTROL_PORT.to_string()
));
}
let server =
HyperlaneTcpServer::new(STARLANE_CONTROL_PORT.clone(), dir, gate.clone(), logger)
.await
.unwrap();
server.start().unwrap();
}
fn config(&self) -> &Self::Config {
&self.config
}
async fn scorch(&self) -> Result<(), Self::Err> {
if !self.config().can_scorch() {
Err(anyhow!("in config '{}' can_scorch=false", config_path()))?;
}
self.global_registry().await.unwrap().scorch().await?;
Ok(())
}
}
#[cfg(feature = "postgres")]
#[derive(Clone)]
pub struct PostgresLookups(PgRegistryConfig);
#[cfg(feature = "postgres")]
impl PostgresLookups {
pub fn new(config: PgRegistryConfig) -> Self {
Self(config)
}
}
#[cfg(feature = "postgres")]
impl PostgresPlatform for PostgresLookups {
fn lookup_registry_db(&self) -> Result<Database<PostgresConnectInfo>, RegErr> {
Ok(self.0.clone().into())
}
fn lookup_star_db(&self, star: &StarKey) -> Result<Database<PostgresConnectInfo>, RegErr> {
let mut rtn: Database<PostgresConnectInfo> = self.0.clone().into();
rtn.database = star.to_sql_name();
Ok(rtn)
}
}
pub struct StarlaneContext {
pub context: String,
pub home: String,
pub log_dir: String,
pub config: StarlaneConfig,
}