use crate::admin_server::{AdminServer, AdminServerBuildError};
use crate::client_server::{ClientServer, ClientServerBuildError};
use crate::metrics_server::{MetricsServer, MetricsServerBuildError};
use crate::republishers::{
HomeserverKeyRepublisher, KeyRepublisherBuildError, UserKeysRepublisher,
};
use crate::tracing::init_tracing_logs_with_config_if_set;
#[cfg(any(test, feature = "testing"))]
use crate::MockDataDir;
use crate::{app_context::AppContext, data_directory::PersistentDataDir};
use anyhow::Result;
use pubky_common::crypto::PublicKey;
use std::path::PathBuf;
use std::time::Duration;
const INITIAL_DELAY_BEFORE_REPUBLISH: Duration = Duration::from_secs(60);
#[derive(thiserror::Error, Debug)]
pub enum HomeserverAppBuildError {
#[error("Failed to build homeserver: {0}")]
Homeserver(ClientServerBuildError),
#[error("Failed to build admin server: {0}")]
Admin(AdminServerBuildError),
#[error("Failed to build metrics server: {0}")]
Metrics(MetricsServerBuildError),
}
pub struct HomeserverApp {
context: AppContext,
#[allow(dead_code)] client_server: ClientServer,
#[allow(dead_code)]
pub(crate) user_keys_republisher: UserKeysRepublisher,
#[allow(dead_code)]
pub(crate) key_republisher: HomeserverKeyRepublisher,
#[allow(dead_code)] admin_server: Option<AdminServer>,
#[allow(dead_code)] metrics_server: Option<MetricsServer>,
}
impl HomeserverApp {
pub async fn start_with_persistent_data_dir_path(dir_path: PathBuf) -> Result<Self> {
let data_dir = PersistentDataDir::new(dir_path);
let context = AppContext::read_from(data_dir).await?;
Self::start(context).await
}
pub async fn start_with_persistent_data_dir(dir: PersistentDataDir) -> Result<Self> {
let context = AppContext::read_from(dir).await?;
Self::start(context).await
}
#[cfg(any(test, feature = "testing"))]
pub async fn start_with_mock_data_dir(dir: MockDataDir) -> Result<Self> {
let context = AppContext::read_from(dir).await?;
Self::start(context).await
}
pub async fn start(context: AppContext) -> Result<Self> {
let _ = init_tracing_logs_with_config_if_set(&context.config_toml);
tracing::debug!("Homeserver data dir: {}", context.data_dir.path().display());
let user_keys_republisher =
UserKeysRepublisher::start_delayed(&context, INITIAL_DELAY_BEFORE_REPUBLISH);
let admin_server = if context.config_toml.admin.enabled {
Some(AdminServer::start(&context).await?)
} else {
None
};
let metrics_server = if context.config_toml.metrics.enabled {
Some(MetricsServer::start(&context).await?)
} else {
None
};
let client_server = ClientServer::start(context.clone()).await?;
let key_republisher = HomeserverKeyRepublisher::start(
&context,
client_server.icann_http_socket.port(),
client_server.pubky_tls_socket.port(),
)
.await
.map_err(KeyRepublisherBuildError::KeyRepublisher)?;
Ok(Self {
context,
client_server,
admin_server,
metrics_server,
user_keys_republisher,
key_republisher,
})
}
pub fn client_server(&self) -> &ClientServer {
&self.client_server
}
pub fn admin_server(&self) -> Option<&AdminServer> {
self.admin_server.as_ref()
}
pub fn metrics_server(&self) -> Option<&MetricsServer> {
self.metrics_server.as_ref()
}
pub fn public_key(&self) -> PublicKey {
self.context.keypair.public_key()
}
pub fn pubky_url(&self) -> url::Url {
url::Url::parse(&format!("https://{}", self.public_key().z32())).expect("valid url")
}
pub fn icann_http_url(&self) -> url::Url {
url::Url::parse(&self.client_server.icann_http_url_string()).expect("valid url")
}
}