pubky_homeserver/client_server/
app.rs1use super::AppState;
2
3#[cfg(any(test, feature = "testing"))]
4use crate::MockDataDir;
5
6use crate::{
7 app_context::{AppContext, AppContextConversionError},
8 PersistentDataDir,
9};
10use anyhow::Result;
11use futures_util::TryFutureExt;
12use pubky_common::auth::AuthVerifier;
13use std::net::TcpListener;
14use std::path::PathBuf;
15use std::time::Duration;
16
17use axum::{
18 routing::{get, post},
19 Router,
20};
21use axum_server::{
22 tls_rustls::{RustlsAcceptor, RustlsConfig},
23 Handle,
24};
25use std::{net::SocketAddr, sync::Arc};
26use tower_cookies::CookieManagerLayer;
27use tower_http::cors::CorsLayer;
28
29use super::layers::{
30 pubky_host::PubkyHostLayer,
31 rate_limiter::{BandwidthQuotaLimitLayer, RequestRateLimitLayer},
32 trace::with_trace_layer,
33};
34use super::routes::{auth, events, root, signup_tokens, tenants};
35
36#[derive(Debug, thiserror::Error)]
38pub enum ClientServerBuildError {
39 #[error("ICANN web server error: {0}")]
41 IcannWebServer(anyhow::Error),
42 #[error("Pubky TLS web server error: {0}")]
44 PubkyTlsServer(anyhow::Error),
45 #[error("AppContext conversion error: {0}")]
47 AppContext(#[from] AppContextConversionError),
48 #[error("Request-count rate limit configuration error: {0}")]
50 RequestRateLimits(String),
51}
52
53pub struct ClientServer {
55 context: AppContext,
57
58 pub(crate) icann_http_handle: Handle<SocketAddr>,
59 pub(crate) icann_http_socket: SocketAddr,
60
61 pub(crate) pubky_tls_handle: Handle<SocketAddr>,
62 pub(crate) pubky_tls_socket: SocketAddr,
63}
64
65impl ClientServer {
66 pub async fn start_with_persistent_data_dir_path(
68 dir_path: PathBuf,
69 ) -> Result<Self, ClientServerBuildError> {
70 let data_dir = PersistentDataDir::new(dir_path);
71 let context = AppContext::read_from(data_dir).await?;
72 Self::start(context).await
73 }
74
75 pub async fn start_with_persistent_data_dir(
77 dir: PersistentDataDir,
78 ) -> Result<Self, ClientServerBuildError> {
79 let context = AppContext::read_from(dir).await?;
80 Self::start(context).await
81 }
82
83 #[cfg(any(test, feature = "testing"))]
85 pub async fn start_with_mock_data_dir(
86 dir: MockDataDir,
87 ) -> Result<Self, ClientServerBuildError> {
88 let context = AppContext::read_from(dir).await?;
89 Self::start(context).await
90 }
91
92 pub async fn start(context: AppContext) -> std::result::Result<Self, ClientServerBuildError> {
94 let router = Self::create_router(&context)?;
95
96 let (icann_http_handle, icann_http_socket) =
97 Self::start_icann_http_server(&context, router.clone())
98 .await
99 .map_err(ClientServerBuildError::IcannWebServer)?;
100 let (pubky_tls_handle, pubky_tls_socket) = Self::start_pubky_tls_server(&context, router)
101 .await
102 .map_err(ClientServerBuildError::PubkyTlsServer)?;
103
104 Ok(Self {
105 context,
106 icann_http_handle,
107 pubky_tls_handle,
108 icann_http_socket,
109 pubky_tls_socket,
110 })
111 }
112
113 pub(crate) fn create_router(
114 context: &AppContext,
115 ) -> std::result::Result<Router, ClientServerBuildError> {
116 let state = AppState {
117 verifier: AuthVerifier::default(),
118 sql_db: context.sql_db.clone(),
119 file_service: context.file_service.clone(),
120 signup_mode: context.config_toml.general.signup_mode.clone(),
121 metrics: context.metrics.clone(),
122 events_service: context.events_service.clone(),
123 user_service: context.user_service.clone(),
124 default_storage_mb: context.config_toml.storage.default_quota_mb,
125 };
126 super::create_app(state.clone(), context)
127 }
128
129 async fn start_icann_http_server(
131 context: &AppContext,
132 router: Router,
133 ) -> Result<(Handle<SocketAddr>, SocketAddr)> {
134 let http_listener = TcpListener::bind(context.config_toml.drive.icann_listen_socket)?;
136 http_listener.set_nonblocking(true)?;
137 let http_socket = http_listener.local_addr()?;
138 let http_handle = Handle::new();
139 let server = axum_server::from_tcp(http_listener)?;
140 tokio::spawn(
141 server
142 .handle(http_handle.clone())
143 .serve(router.into_make_service_with_connect_info::<SocketAddr>())
144 .map_err(|error| {
145 tracing::error!(?error, "Homeserver icann http server error");
146 println!("Homeserver icann http server error: {:?}", error);
147 }),
148 );
149
150 Ok((http_handle, http_socket))
151 }
152
153 async fn start_pubky_tls_server(
155 context: &AppContext,
156 router: Router,
157 ) -> Result<(Handle<SocketAddr>, SocketAddr)> {
158 let https_listener = TcpListener::bind(context.config_toml.drive.pubky_listen_socket)?;
160 https_listener.set_nonblocking(true)?;
161 let https_socket = https_listener.local_addr()?;
162 let https_handle = Handle::new();
163 let server = axum_server::from_tcp(https_listener)?;
164 tokio::spawn(
165 server
166 .acceptor(RustlsAcceptor::new(RustlsConfig::from_config(Arc::new(
167 context.keypair.to_rpk_rustls_server_config(),
168 ))))
169 .handle(https_handle.clone())
170 .serve(router.into_make_service_with_connect_info::<SocketAddr>())
171 .map_err(|error| {
172 tracing::error!(?error, "Homeserver pubky tls server error");
173 println!("Homeserver pubky tls server error: {:?}", error);
174 }),
175 );
176
177 Ok((https_handle, https_socket))
178 }
179 pub fn icann_http_url_string(&self) -> String {
181 format!("http://{}", self.icann_http_socket)
182 }
183
184 pub fn pubky_tls_dns_url_string(&self) -> String {
186 format!("https://{}", self.context.keypair.public_key().z32())
187 }
188
189 pub fn pubky_tls_ip_url_ring(&self) -> String {
191 format!("https://{}", self.pubky_tls_socket)
192 }
193
194 pub fn shutdown(&self) {
196 self.icann_http_handle
197 .graceful_shutdown(Some(Duration::from_secs(5)));
198 self.pubky_tls_handle
199 .graceful_shutdown(Some(Duration::from_secs(5)));
200 }
201}
202
203impl Drop for ClientServer {
204 fn drop(&mut self) {
205 self.shutdown();
206 }
207}
208
209fn base() -> Router<AppState> {
210 Router::new()
211 .route("/", get(root::handler))
212 .route("/signup", post(auth::signup))
213 .route("/signup_tokens/{token}", get(signup_tokens::get))
214 .route("/session", post(auth::signin))
215 .route("/events/", get(events::feed))
217 .route("/events-stream", get(events::feed_stream))
218
219 }
223
224pub fn create_app(
225 state: AppState,
226 context: &AppContext,
227) -> std::result::Result<Router, ClientServerBuildError> {
228 let request_rate_limit_layer =
229 RequestRateLimitLayer::from_path_limits(context.config_toml.drive.rate_limits.clone())
230 .map_err(ClientServerBuildError::RequestRateLimits)?;
231
232 let app = base()
233 .merge(tenants::router(state.clone()))
234 .layer(CorsLayer::very_permissive())
235 .layer(BandwidthQuotaLimitLayer::new(
236 context.user_service.clone(),
237 context.config_toml.default_quotas.clone(),
238 ))
239 .layer(request_rate_limit_layer)
240 .layer(CookieManagerLayer::new())
241 .layer(PubkyHostLayer)
242 .with_state(state);
243
244 Ok(with_trace_layer(app))
246}