From 8c40cd2ddfb7c4dbaaa47ff6e23430c792a42fca Mon Sep 17 00:00:00 2001 From: nazeh Date: Tue, 3 Dec 2024 16:01:44 +0300 Subject: [PATCH] refactor(homeserver): separate the core and io modules --- pubky-homeserver/src/{ => core}/config.rs | 4 + .../src/{ => core}/database/migrations.rs | 0 .../src/{ => core}/database/migrations/m0.rs | 2 +- .../src/{database.rs => core/database/mod.rs} | 4 +- .../src/{ => core}/database/tables.rs | 0 .../src/{ => core}/database/tables/blobs.rs | 2 +- .../src/{ => core}/database/tables/entries.rs | 8 +- .../src/{ => core}/database/tables/events.rs | 2 +- .../{ => core}/database/tables/sessions.rs | 2 +- .../src/{ => core}/database/tables/users.rs | 0 pubky-homeserver/src/{ => core}/error.rs | 0 pubky-homeserver/src/{ => core}/extractors.rs | 2 +- pubky-homeserver/src/{core.rs => core/mod.rs} | 57 +++++---- .../src/{ => core}/routes/auth.rs | 4 +- .../src/{ => core}/routes/feed.rs | 4 +- .../src/{routes.rs => core/routes/mod.rs} | 8 +- .../src/{ => core}/routes/pkarr.rs | 4 +- .../src/{ => core}/routes/public/mod.rs | 4 +- .../src/{ => core}/routes/public/read.rs | 4 +- .../src/{ => core}/routes/public/write.rs | 4 +- .../src/{ => core}/routes/root.rs | 0 pubky-homeserver/src/io/http.rs | 34 ++++++ pubky-homeserver/src/io/mod.rs | 71 +++++++++++ pubky-homeserver/src/io/pkarr.rs | 66 ++++++++++ pubky-homeserver/src/lib.rs | 11 +- pubky-homeserver/src/main.rs | 8 +- pubky-homeserver/src/pkarr.rs | 45 ------- pubky-homeserver/src/server.rs | 115 ------------------ 28 files changed, 243 insertions(+), 222 deletions(-) rename pubky-homeserver/src/{ => core}/config.rs (99%) rename pubky-homeserver/src/{ => core}/database/migrations.rs (100%) rename pubky-homeserver/src/{ => core}/database/migrations/m0.rs (87%) rename pubky-homeserver/src/{database.rs => core/database/mod.rs} (95%) rename pubky-homeserver/src/{ => core}/database/tables.rs (100%) rename pubky-homeserver/src/{ => core}/database/tables/blobs.rs (94%) rename pubky-homeserver/src/{ => core}/database/tables/entries.rs (98%) rename pubky-homeserver/src/{ => core}/database/tables/events.rs (98%) rename pubky-homeserver/src/{ => core}/database/tables/sessions.rs (98%) rename pubky-homeserver/src/{ => core}/database/tables/users.rs (100%) rename pubky-homeserver/src/{ => core}/error.rs (100%) rename pubky-homeserver/src/{ => core}/extractors.rs (98%) rename pubky-homeserver/src/{core.rs => core/mod.rs} (75%) rename pubky-homeserver/src/{ => core}/routes/auth.rs (99%) rename pubky-homeserver/src/{ => core}/routes/feed.rs (96%) rename pubky-homeserver/src/{routes.rs => core/routes/mod.rs} (93%) rename pubky-homeserver/src/{ => core}/routes/pkarr.rs (97%) rename pubky-homeserver/src/{ => core}/routes/public/mod.rs (97%) rename pubky-homeserver/src/{ => core}/routes/public/read.rs (99%) rename pubky-homeserver/src/{ => core}/routes/public/write.rs (97%) rename pubky-homeserver/src/{ => core}/routes/root.rs (100%) create mode 100644 pubky-homeserver/src/io/http.rs create mode 100644 pubky-homeserver/src/io/mod.rs create mode 100644 pubky-homeserver/src/io/pkarr.rs delete mode 100644 pubky-homeserver/src/pkarr.rs delete mode 100644 pubky-homeserver/src/server.rs diff --git a/pubky-homeserver/src/config.rs b/pubky-homeserver/src/core/config.rs similarity index 99% rename from pubky-homeserver/src/config.rs rename to pubky-homeserver/src/core/config.rs index 08e1889..e94a720 100644 --- a/pubky-homeserver/src/config.rs +++ b/pubky-homeserver/src/core/config.rs @@ -162,6 +162,10 @@ impl Config { } } + pub fn is_testnet(&self) -> bool { + self.testnet + } + pub fn port(&self) -> u16 { self.port } diff --git a/pubky-homeserver/src/database/migrations.rs b/pubky-homeserver/src/core/database/migrations.rs similarity index 100% rename from pubky-homeserver/src/database/migrations.rs rename to pubky-homeserver/src/core/database/migrations.rs diff --git a/pubky-homeserver/src/database/migrations/m0.rs b/pubky-homeserver/src/core/database/migrations/m0.rs similarity index 87% rename from pubky-homeserver/src/database/migrations/m0.rs rename to pubky-homeserver/src/core/database/migrations/m0.rs index 11c0e1a..27f4b9e 100644 --- a/pubky-homeserver/src/database/migrations/m0.rs +++ b/pubky-homeserver/src/core/database/migrations/m0.rs @@ -1,6 +1,6 @@ use heed::{Env, RwTxn}; -use crate::database::tables::{blobs, entries, events, sessions, users}; +use crate::core::database::tables::{blobs, entries, events, sessions, users}; pub fn run(env: &Env, wtxn: &mut RwTxn) -> anyhow::Result<()> { let _: users::UsersTable = env.create_database(wtxn, Some(users::USERS_TABLE))?; diff --git a/pubky-homeserver/src/database.rs b/pubky-homeserver/src/core/database/mod.rs similarity index 95% rename from pubky-homeserver/src/database.rs rename to pubky-homeserver/src/core/database/mod.rs index 4560d8d..617b766 100644 --- a/pubky-homeserver/src/database.rs +++ b/pubky-homeserver/src/core/database/mod.rs @@ -1,3 +1,5 @@ +//! Internal database in [crate::HomeserverCore] + use std::{fs, path::PathBuf}; use heed::{Env, EnvOpenOptions}; @@ -5,7 +7,7 @@ use heed::{Env, EnvOpenOptions}; mod migrations; pub mod tables; -use crate::config::Config; +use crate::core::config::Config; use tables::{Tables, TABLES_COUNT}; diff --git a/pubky-homeserver/src/database/tables.rs b/pubky-homeserver/src/core/database/tables.rs similarity index 100% rename from pubky-homeserver/src/database/tables.rs rename to pubky-homeserver/src/core/database/tables.rs diff --git a/pubky-homeserver/src/database/tables/blobs.rs b/pubky-homeserver/src/core/database/tables/blobs.rs similarity index 94% rename from pubky-homeserver/src/database/tables/blobs.rs rename to pubky-homeserver/src/core/database/tables/blobs.rs index 18ec724..1162652 100644 --- a/pubky-homeserver/src/database/tables/blobs.rs +++ b/pubky-homeserver/src/core/database/tables/blobs.rs @@ -1,6 +1,6 @@ use heed::{types::Bytes, Database, RoTxn}; -use crate::database::DB; +use crate::core::database::DB; use super::entries::Entry; diff --git a/pubky-homeserver/src/database/tables/entries.rs b/pubky-homeserver/src/core/database/tables/entries.rs similarity index 98% rename from pubky-homeserver/src/database/tables/entries.rs rename to pubky-homeserver/src/core/database/tables/entries.rs index 0717f77..733833e 100644 --- a/pubky-homeserver/src/database/tables/entries.rs +++ b/pubky-homeserver/src/core/database/tables/entries.rs @@ -18,7 +18,7 @@ use pubky_common::{ timestamp::Timestamp, }; -use crate::database::DB; +use crate::core::database::DB; use super::events::Event; @@ -447,13 +447,13 @@ mod tests { use bytes::Bytes; use pkarr::{mainline::Testnet, Keypair}; - use crate::config::Config; + use crate::Config; use super::DB; #[tokio::test] async fn entries() -> anyhow::Result<()> { - let mut db = DB::open(Config::test(&Testnet::new(0).unwrap())).unwrap(); + let mut db = unsafe { DB::open(Config::test(&Testnet::new(0).unwrap())).unwrap() }; let keypair = Keypair::random(); let public_key = keypair.public_key(); @@ -495,7 +495,7 @@ mod tests { #[tokio::test] async fn chunked_entry() -> anyhow::Result<()> { - let mut db = DB::open(Config::test(&Testnet::new(0).unwrap())).unwrap(); + let mut db = unsafe { DB::open(Config::test(&Testnet::new(0).unwrap())).unwrap() }; let keypair = Keypair::random(); let public_key = keypair.public_key(); diff --git a/pubky-homeserver/src/database/tables/events.rs b/pubky-homeserver/src/core/database/tables/events.rs similarity index 98% rename from pubky-homeserver/src/database/tables/events.rs rename to pubky-homeserver/src/core/database/tables/events.rs index 76a4d46..6895931 100644 --- a/pubky-homeserver/src/database/tables/events.rs +++ b/pubky-homeserver/src/core/database/tables/events.rs @@ -10,7 +10,7 @@ use heed::{ use postcard::{from_bytes, to_allocvec}; use serde::{Deserialize, Serialize}; -use crate::database::DB; +use crate::core::database::DB; /// Event [Timestamp] base32 => Encoded event. pub type EventsTable = Database; diff --git a/pubky-homeserver/src/database/tables/sessions.rs b/pubky-homeserver/src/core/database/tables/sessions.rs similarity index 98% rename from pubky-homeserver/src/database/tables/sessions.rs rename to pubky-homeserver/src/core/database/tables/sessions.rs index 31dc2bc..e2ff58d 100644 --- a/pubky-homeserver/src/database/tables/sessions.rs +++ b/pubky-homeserver/src/core/database/tables/sessions.rs @@ -6,7 +6,7 @@ use pkarr::PublicKey; use pubky_common::session::Session; use tower_cookies::Cookies; -use crate::database::DB; +use crate::core::database::DB; /// session secret => Session. pub type SessionsTable = Database; diff --git a/pubky-homeserver/src/database/tables/users.rs b/pubky-homeserver/src/core/database/tables/users.rs similarity index 100% rename from pubky-homeserver/src/database/tables/users.rs rename to pubky-homeserver/src/core/database/tables/users.rs diff --git a/pubky-homeserver/src/error.rs b/pubky-homeserver/src/core/error.rs similarity index 100% rename from pubky-homeserver/src/error.rs rename to pubky-homeserver/src/core/error.rs diff --git a/pubky-homeserver/src/extractors.rs b/pubky-homeserver/src/core/extractors.rs similarity index 98% rename from pubky-homeserver/src/extractors.rs rename to pubky-homeserver/src/core/extractors.rs index 35fd691..ec799da 100644 --- a/pubky-homeserver/src/extractors.rs +++ b/pubky-homeserver/src/core/extractors.rs @@ -10,7 +10,7 @@ use axum::{ use pkarr::PublicKey; -use crate::error::{Error, Result}; +use crate::core::error::{Error, Result}; #[derive(Debug)] pub struct Pubky(PublicKey); diff --git a/pubky-homeserver/src/core.rs b/pubky-homeserver/src/core/mod.rs similarity index 75% rename from pubky-homeserver/src/core.rs rename to pubky-homeserver/src/core/mod.rs index 58f3eb0..5c325c9 100644 --- a/pubky-homeserver/src/core.rs +++ b/pubky-homeserver/src/core/mod.rs @@ -1,6 +1,6 @@ use anyhow::Result; use axum::{extract::Request, response::Response, Router}; -use pkarr::PublicKey; +use pkarr::{Keypair, PublicKey}; use pubky_common::{ auth::AuthVerifier, capabilities::Capability, crypto::random_bytes, session::Session, timestamp::Timestamp, @@ -8,22 +8,26 @@ use pubky_common::{ use tower::ServiceExt; use tower_cookies::{cookie::SameSite, Cookie}; -use crate::{ - config::Config, - database::{tables::users::User, DB}, -}; +mod config; +mod database; +mod error; +mod extractors; +mod routes; + +use database::{tables::users::User, DB}; + +pub use config::Config; #[derive(Clone, Debug)] pub(crate) struct AppState { pub(crate) verifier: AuthVerifier, pub(crate) db: DB, - pub(crate) pkarr_client: pkarr::Client, - pub(crate) config: Config, } -#[derive(Debug)] +#[derive(Debug, Clone)] /// An I/O-less Core of the [Homeserver]. pub struct HomeserverCore { + pub(crate) config: Config, pub(crate) state: AppState, pub(crate) router: Router, } @@ -32,33 +36,20 @@ impl HomeserverCore { /// # Safety /// HomeserverCore uses LMDB, [opening][heed::EnvOpenOptions::open] which comes with some safety precautions. pub unsafe fn new(config: &Config) -> Result { - tracing::debug!(?config); - let db = unsafe { DB::open(config.clone())? }; - let mut dht_settings = pkarr::mainline::Settings::default(); - - if let Some(bootstrap) = config.bootstrap() { - dht_settings = dht_settings.bootstrap(&bootstrap); - } - if let Some(request_timeout) = config.dht_request_timeout() { - dht_settings = dht_settings.request_timeout(request_timeout); - } - - let pkarr_client = pkarr::Client::builder() - .dht_settings(dht_settings) - .build()?; - let state = AppState { verifier: AuthVerifier::default(), db, - pkarr_client: pkarr_client.clone(), - config: config.clone(), }; - let router = crate::routes::create_app(state.clone()); + let router = routes::create_app(state.clone()); - Ok(Self { state, router }) + Ok(Self { + state, + router, + config: config.clone(), + }) } #[cfg(test)] @@ -69,6 +60,18 @@ impl HomeserverCore { unsafe { HomeserverCore::new(&Config::test(&testnet)) } } + // === Getters === + + pub fn keypair(&self) -> &Keypair { + self.config.keypair() + } + + pub fn public_key(&self) -> PublicKey { + self.config.keypair().public_key() + } + + // === Public Methods === + // TODO: move this logic to a common place. pub fn create_user(&mut self, public_key: &PublicKey) -> Result { let mut wtxn = self.state.db.env.write_txn()?; diff --git a/pubky-homeserver/src/routes/auth.rs b/pubky-homeserver/src/core/routes/auth.rs similarity index 99% rename from pubky-homeserver/src/routes/auth.rs rename to pubky-homeserver/src/core/routes/auth.rs index 8acb7a4..9b143ab 100644 --- a/pubky-homeserver/src/routes/auth.rs +++ b/pubky-homeserver/src/core/routes/auth.rs @@ -9,11 +9,11 @@ use tower_cookies::{cookie::SameSite, Cookie, Cookies}; use pubky_common::{crypto::random_bytes, session::Session, timestamp::Timestamp}; -use crate::{ - core::AppState, +use crate::core::{ database::tables::users::User, error::{Error, Result}, extractors::Pubky, + AppState, }; pub async fn signup( diff --git a/pubky-homeserver/src/routes/feed.rs b/pubky-homeserver/src/core/routes/feed.rs similarity index 96% rename from pubky-homeserver/src/routes/feed.rs rename to pubky-homeserver/src/core/routes/feed.rs index 44e06e5..6b11fa4 100644 --- a/pubky-homeserver/src/routes/feed.rs +++ b/pubky-homeserver/src/core/routes/feed.rs @@ -6,10 +6,10 @@ use axum::{ }; use pubky_common::timestamp::Timestamp; -use crate::{ - core::AppState, +use crate::core::{ error::{Error, Result}, extractors::ListQueryParams, + AppState, }; pub async fn feed( diff --git a/pubky-homeserver/src/routes.rs b/pubky-homeserver/src/core/routes/mod.rs similarity index 93% rename from pubky-homeserver/src/routes.rs rename to pubky-homeserver/src/core/routes/mod.rs index c4ad09f..4edb1f3 100644 --- a/pubky-homeserver/src/routes.rs +++ b/pubky-homeserver/src/core/routes/mod.rs @@ -1,3 +1,5 @@ +//! The controller part of the [crate::HomeserverCore] + use axum::{ extract::DefaultBodyLimit, routing::{delete, get, head, post, put}, @@ -8,11 +10,11 @@ use tower_http::{cors::CorsLayer, trace::TraceLayer}; use crate::core::AppState; -use self::pkarr::pkarr_router; +// use self::pkarr::pkarr_router; mod auth; mod feed; -mod pkarr; +// mod pkarr; mod public; mod root; @@ -56,7 +58,7 @@ fn base(state: AppState) -> Router { pub fn create_app(state: AppState) -> Router { base(state.clone()) // TODO: Only enable this for test environments? - .nest("/pkarr", pkarr_router(state)) + // .nest("/pkarr", pkarr_router(state)) .layer(CorsLayer::very_permissive()) .layer(TraceLayer::new_for_http()) } diff --git a/pubky-homeserver/src/routes/pkarr.rs b/pubky-homeserver/src/core/routes/pkarr.rs similarity index 97% rename from pubky-homeserver/src/routes/pkarr.rs rename to pubky-homeserver/src/core/routes/pkarr.rs index abc67da..1c079dd 100644 --- a/pubky-homeserver/src/routes/pkarr.rs +++ b/pubky-homeserver/src/core/routes/pkarr.rs @@ -10,10 +10,10 @@ use futures_util::stream::StreamExt; use pkarr::SignedPacket; -use crate::{ - core::AppState, +use crate::core::{ error::{Error, Result}, extractors::Pubky, + AppState, }; /// Pkarr relay, helpful for testing. diff --git a/pubky-homeserver/src/routes/public/mod.rs b/pubky-homeserver/src/core/routes/public/mod.rs similarity index 97% rename from pubky-homeserver/src/routes/public/mod.rs rename to pubky-homeserver/src/core/routes/public/mod.rs index e5dd31d..f19abef 100644 --- a/pubky-homeserver/src/routes/public/mod.rs +++ b/pubky-homeserver/src/core/routes/public/mod.rs @@ -2,9 +2,9 @@ use axum::http::StatusCode; use pkarr::PublicKey; use tower_cookies::Cookies; -use crate::{ - core::AppState, +use crate::core::{ error::{Error, Result}, + AppState, }; pub mod read; diff --git a/pubky-homeserver/src/routes/public/read.rs b/pubky-homeserver/src/core/routes/public/read.rs similarity index 99% rename from pubky-homeserver/src/routes/public/read.rs rename to pubky-homeserver/src/core/routes/public/read.rs index 89a8c6f..84fb8e5 100644 --- a/pubky-homeserver/src/routes/public/read.rs +++ b/pubky-homeserver/src/core/routes/public/read.rs @@ -8,11 +8,11 @@ use httpdate::HttpDate; use pkarr::PublicKey; use std::str::FromStr; -use crate::{ - core::AppState, +use crate::core::{ database::tables::entries::Entry, error::{Error, Result}, extractors::{EntryPath, ListQueryParams, Pubky}, + AppState, }; use super::verify; diff --git a/pubky-homeserver/src/routes/public/write.rs b/pubky-homeserver/src/core/routes/public/write.rs similarity index 97% rename from pubky-homeserver/src/routes/public/write.rs rename to pubky-homeserver/src/core/routes/public/write.rs index f87aa22..4c78b0a 100644 --- a/pubky-homeserver/src/routes/public/write.rs +++ b/pubky-homeserver/src/core/routes/public/write.rs @@ -5,10 +5,10 @@ use futures_util::stream::StreamExt; use axum::{body::Body, extract::State, http::StatusCode, response::IntoResponse}; use tower_cookies::Cookies; -use crate::{ - core::AppState, +use crate::core::{ error::{Error, Result}, extractors::{EntryPath, Pubky}, + AppState, }; use super::{authorize, verify}; diff --git a/pubky-homeserver/src/routes/root.rs b/pubky-homeserver/src/core/routes/root.rs similarity index 100% rename from pubky-homeserver/src/routes/root.rs rename to pubky-homeserver/src/core/routes/root.rs diff --git a/pubky-homeserver/src/io/http.rs b/pubky-homeserver/src/io/http.rs new file mode 100644 index 0000000..c6f7e21 --- /dev/null +++ b/pubky-homeserver/src/io/http.rs @@ -0,0 +1,34 @@ +//! Http server around the HomeserverCore + +use std::{ + net::{SocketAddr, TcpListener}, + sync::Arc, +}; + +use anyhow::Result; +use axum_server::{ + tls_rustls::{RustlsAcceptor, RustlsConfig}, + Handle, +}; + +use crate::core::HomeserverCore; + +pub(crate) async fn start(core: HomeserverCore) -> Result { + let listener = TcpListener::bind(SocketAddr::from(([0, 0, 0, 0], core.config.port())))?; + + let acceptor = RustlsAcceptor::new(RustlsConfig::from_config(Arc::new( + core.keypair().to_rpk_rustls_server_config(), + ))); + let server = axum_server::from_tcp(listener).acceptor(acceptor); + + let handle = Handle::new(); + + tokio::spawn( + server.handle(handle.clone()).serve( + core.router + .into_make_service_with_connect_info::(), + ), + ); + + Ok(handle) +} diff --git a/pubky-homeserver/src/io/mod.rs b/pubky-homeserver/src/io/mod.rs new file mode 100644 index 0000000..e1e7713 --- /dev/null +++ b/pubky-homeserver/src/io/mod.rs @@ -0,0 +1,71 @@ +use ::pkarr::{mainline::Testnet, PublicKey}; +use anyhow::Result; +use axum_server::Handle; +use pkarr::PkarrServer; +use tracing::info; + +use crate::{Config, HomeserverCore}; + +mod http; +mod pkarr; + +#[derive(Debug)] +/// Homeserver [Core][HomeserverCore] + I/O (http server and pkarr publishing). +pub struct Homeserver { + handle: Handle, + core: HomeserverCore, +} + +impl Homeserver { + /// # Safety + /// Homeserver uses LMDB, [opening][heed::EnvOpenOptions::open] which comes with some safety precautions. + pub async unsafe fn start(config: Config) -> Result { + tracing::debug!(?config, "Starting homeserver with configurations"); + + let core = unsafe { HomeserverCore::new(&config)? }; + + let handle = http::start(core.clone()).await?; + + let port = handle + .listening() + .await + .expect("Homeserver listening") + .port(); + + info!("Homeserver listening on http://localhost:{port}"); + + info!("Publishing Pkarr packet.."); + + let pkarr_server = PkarrServer::new(config)?; + pkarr_server.publish_server_packet(port).await?; + + info!("Homeserver listening on https://{}", core.public_key()); + + Ok(Self { handle, core }) + } + + /// Test version of [Homeserver::start], using mainline Testnet, and a temporary storage. + pub async fn start_test(testnet: &Testnet) -> Result { + info!("Running testnet.."); + + unsafe { Homeserver::start(Config::test(testnet)).await } + } + + // === Getters === + + pub fn public_key(&self) -> PublicKey { + self.core.public_key() + } + + /// Return the `https://` url + pub fn url(&self) -> url::Url { + url::Url::parse(&format!("https://{}", self.public_key())).expect("valid url") + } + + // === Public Methods === + + /// Send a shutdown signal to all open resources + pub fn shutdown(&self) { + self.handle.shutdown(); + } +} diff --git a/pubky-homeserver/src/io/pkarr.rs b/pubky-homeserver/src/io/pkarr.rs new file mode 100644 index 0000000..7959354 --- /dev/null +++ b/pubky-homeserver/src/io/pkarr.rs @@ -0,0 +1,66 @@ +//! Pkarr related task + +use anyhow::Result; +use pkarr::{dns::rdata::SVCB, SignedPacket}; + +use crate::Config; + +pub struct PkarrServer { + client: pkarr::Client, + config: Config, +} + +impl PkarrServer { + pub fn new(config: Config) -> Result { + let mut dht_settings = pkarr::mainline::Settings::default(); + + if let Some(bootstrap) = config.bootstrap() { + dht_settings = dht_settings.bootstrap(&bootstrap); + } + if let Some(request_timeout) = config.dht_request_timeout() { + dht_settings = dht_settings.request_timeout(request_timeout); + } + + let client = pkarr::Client::builder() + .dht_settings(dht_settings) + .build()?; + + Ok(Self { client, config }) + } + + pub async fn publish_server_packet(&self, port: u16) -> anyhow::Result<()> { + // TODO: Try to resolve first before publishing. + + let default = ".".to_string(); + let target = self.config.domain().unwrap_or(&default); + let mut svcb = SVCB::new(0, target.as_str().try_into()?); + + svcb.priority = 1; + svcb.set_port(port); + + let mut signed_packet_builder = + SignedPacket::builder().https(".".try_into().unwrap(), svcb.clone(), 60 * 60); + + if self.config.domain().is_none() { + // TODO: remove after remvoing Pubky shared/public + // and add local host IP address instead. + svcb.target = "localhost".try_into().unwrap(); + + signed_packet_builder = signed_packet_builder + .https(".".try_into().unwrap(), svcb, 60 * 60) + .address( + ".".try_into().unwrap(), + "127.0.0.1".parse().unwrap(), + 60 * 60, + ); + } + + // TODO: announce A/AAAA records as well for TLS connections? + + let signed_packet = signed_packet_builder.build(self.config.keypair())?; + + self.client.publish(&signed_packet).await?; + + Ok(()) + } +} diff --git a/pubky-homeserver/src/lib.rs b/pubky-homeserver/src/lib.rs index 03de893..ee44b10 100644 --- a/pubky-homeserver/src/lib.rs +++ b/pubky-homeserver/src/lib.rs @@ -1,11 +1,6 @@ -pub mod config; mod core; -mod database; -mod error; -mod extractors; -mod pkarr; -mod routes; -mod server; +mod io; +pub use core::Config; pub use core::HomeserverCore; -pub use server::Homeserver; +pub use io::Homeserver; diff --git a/pubky-homeserver/src/main.rs b/pubky-homeserver/src/main.rs index 3497c1a..9d2363f 100644 --- a/pubky-homeserver/src/main.rs +++ b/pubky-homeserver/src/main.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; use anyhow::Result; -use pubky_homeserver::{config::Config, Homeserver}; +use pubky_homeserver::{Config, Homeserver}; use clap::Parser; @@ -42,7 +42,11 @@ async fn main() -> Result<()> { .await? }; - server.run_until_done().await?; + tokio::signal::ctrl_c().await?; + + tracing::info!("Shutting down Homeserver"); + + server.shutdown(); Ok(()) } diff --git a/pubky-homeserver/src/pkarr.rs b/pubky-homeserver/src/pkarr.rs deleted file mode 100644 index 6c777d7..0000000 --- a/pubky-homeserver/src/pkarr.rs +++ /dev/null @@ -1,45 +0,0 @@ -//! Pkarr related task - -use pkarr::{dns::rdata::SVCB, SignedPacket}; - -use crate::config::Config; - -pub async fn publish_server_packet( - pkarr_client: &pkarr::Client, - config: &Config, - port: u16, -) -> anyhow::Result<()> { - // TODO: Try to resolve first before publishing. - - let default = ".".to_string(); - let target = config.domain().unwrap_or(&default); - let mut svcb = SVCB::new(0, target.as_str().try_into()?); - - svcb.priority = 1; - svcb.set_port(port); - - let mut signed_packet_builder = - SignedPacket::builder().https(".".try_into().unwrap(), svcb.clone(), 60 * 60); - - if config.domain().is_none() { - // TODO: remove after remvoing Pubky shared/public - // and add local host IP address instead. - svcb.target = "localhost".try_into().unwrap(); - - signed_packet_builder = signed_packet_builder - .https(".".try_into().unwrap(), svcb, 60 * 60) - .address( - ".".try_into().unwrap(), - "127.0.0.1".parse().unwrap(), - 60 * 60, - ); - } - - // TODO: announce A/AAAA records as well for TLS connections? - - let signed_packet = signed_packet_builder.build(config.keypair())?; - - pkarr_client.publish(&signed_packet).await?; - - Ok(()) -} diff --git a/pubky-homeserver/src/server.rs b/pubky-homeserver/src/server.rs deleted file mode 100644 index 424d5a1..0000000 --- a/pubky-homeserver/src/server.rs +++ /dev/null @@ -1,115 +0,0 @@ -use std::{ - net::{SocketAddr, TcpListener}, - sync::Arc, -}; - -use anyhow::{Error, Result}; -use axum_server::tls_rustls::{RustlsAcceptor, RustlsConfig}; -use tokio::task::JoinSet; -use tracing::{info, warn}; - -use pkarr::{mainline::Testnet, PublicKey}; - -use crate::{ - config::Config, - core::{AppState, HomeserverCore}, - pkarr::publish_server_packet, -}; - -#[derive(Debug)] -/// Homeserver [Core][HomeserverCore] + http server. -pub struct Homeserver { - state: AppState, - tasks: JoinSet>, -} - -impl Homeserver { - /// # Safety - /// Homeserver uses LMDB, [opening][heed::EnvOpenOptions::open] which comes with some safety precautions. - pub async unsafe fn start(config: Config) -> Result { - let mut tasks = JoinSet::new(); - - let listener = TcpListener::bind(SocketAddr::from(([0, 0, 0, 0], config.port())))?; - - let port = listener.local_addr()?.port(); - - let keypair = config.keypair().clone(); - - let acceptor = RustlsAcceptor::new(RustlsConfig::from_config(Arc::new( - keypair.to_rpk_rustls_server_config(), - ))); - let server = axum_server::from_tcp(listener).acceptor(acceptor); - - let core = unsafe { HomeserverCore::new(&config)? }; - - // Spawn http server task - tasks.spawn( - server.serve( - core.router - .into_make_service_with_connect_info::(), - ), - ); - - info!("Homeserver listening on http://localhost:{port}"); - - info!("Publishing Pkarr packet.."); - - publish_server_packet(&core.state.pkarr_client, &config, port).await?; - - info!("Homeserver listening on https://{}", keypair.public_key()); - - Ok(Self { - tasks, - state: core.state, - }) - } - - /// Test version of [Homeserver::start], using mainline Testnet, and a temporary storage. - pub async fn start_test(testnet: &Testnet) -> Result { - info!("Running testnet.."); - - unsafe { Homeserver::start(Config::test(testnet)).await } - } - - // === Getters === - - pub fn public_key(&self) -> PublicKey { - self.state.config.keypair().public_key() - } - - /// Return the `https://` url - pub fn url(&self) -> url::Url { - url::Url::parse(&format!("https://{}", self.public_key())).expect("valid url") - } - - // === Public Methods === - - /// Shutdown the server and wait for all tasks to complete. - pub async fn shutdown(mut self) -> Result<()> { - self.tasks.abort_all(); - self.run_until_done().await?; - Ok(()) - } - - /// Wait for all tasks to complete. - /// - /// Runs forever unless tasks fail. - pub async fn run_until_done(mut self) -> Result<()> { - let mut final_res: Result<()> = Ok(()); - while let Some(res) = self.tasks.join_next().await { - match res { - Ok(Ok(())) => {} - Err(err) if err.is_cancelled() => {} - Ok(Err(err)) => { - warn!(?err, "task failed"); - final_res = Err(Error::from(err)); - } - Err(err) => { - warn!(?err, "task panicked"); - final_res = Err(err.into()); - } - } - } - final_res - } -}