Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions src/infrastructure/http/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
sql::{DbError, SqlDb},
},
ln_verification,
shared::HomeserverObserver,
shared::HomeserverAdminAPI,
sms_verification::http::router,
};

Expand Down Expand Up @@ -54,7 +54,7 @@ impl HttpServer {
}

pub async fn start(config: EnvConfig) -> Result<Self, HttpServerError> {
HomeserverObserver::spawn_crash_on_unresponsive_homeserver(&config).await;
Self::err_on_homeserver_admin_api_failure(&config).await;

let db = SqlDb::connect(&config.database_url)
.await
Expand Down Expand Up @@ -105,6 +105,22 @@ impl HttpServer {
self.http_handle
.graceful_shutdown(Some(Duration::from_secs(5)));
}

// Verify homeserver is available at startup and credentials are correct, exit process if not.
async fn err_on_homeserver_admin_api_failure(config: &EnvConfig) {
let homeserver_admin_api = HomeserverAdminAPI::new(
&config.homeserver_admin_api_url,
&config.homeserver_admin_password,
&config.homeserver_pubky,
);
if let Err(e) = homeserver_admin_api.verify_password().await {
tracing::error!(
"Homeserver connection failed: {:?}. Stopping server because credentials are incorrect or homeserver is unavailable.",
e
);
std::process::exit(1);
}
}
}

impl Drop for HttpServer {
Expand Down
7 changes: 3 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use infrastructure::{EnvConfig, http::HttpServer};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Load .env file.
// Init tracing after the .env file is loaded so that the logging is configured.
// Init tracing after the .env file is loaded so that the logging is configured.
// If the .env loading fails, log it after tracing is actually initialized.
let dot_env_load_result = dotenvy::dotenv();
tracing_subscriber::fmt::init();
Expand All @@ -24,10 +24,9 @@ async fn main() -> anyhow::Result<()> {

let http_server = HttpServer::start(config).await?;

tracing::info!("Homeserver HTTP listening on {}", http_server.url_string());
tracing::info!("Homegate HTTP listening on {}", http_server.url_string());

tracing::info!("Press Ctrl+C to stop the Homeserver");
tracing::info!("Press Ctrl+C to stop Homegate");
tokio::signal::ctrl_c().await?;
tracing::info!("Shutting down Homeserver");
Ok(())
}
9 changes: 0 additions & 9 deletions src/shared/homeserver_admin_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,4 @@ impl HomeserverAdminAPI {
response.error_for_status()?;
Ok(())
}

/// Checks if the homeserver is responsive by making a GET request to the root URL
/// This is NOT password protected so it won't validate the admin password.
pub async fn health_check(&self) -> Result<(), reqwest::Error> {
let url = self.base_url.join("/").expect("Failed to join URL path");
let response = self.http_client.get(url).send().await?;
response.error_for_status()?;
Ok(())
}
}
63 changes: 0 additions & 63 deletions src/shared/homeserver_observer.rs

This file was deleted.

2 changes: 0 additions & 2 deletions src/shared/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
mod homeserver_admin_api;
mod homeserver_observer;

pub use homeserver_admin_api::HomeserverAdminAPI;
pub use homeserver_observer::HomeserverObserver;
2 changes: 1 addition & 1 deletion src/sms_verification/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ impl AppState {
);
let sms_verification = SmsVerificationService::new(
prelude_api,
homeserver_admin_api,
homeserver_admin_api.clone(),
config.max_sms_verifications_per_week,
config.max_sms_verifications_per_year,
);
Expand Down
3 changes: 3 additions & 0 deletions src/sms_verification/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ pub enum SmsVerificationError {
#[error("External service rate limit exceeded")]
RateLimited { retry_after: Option<u64> },

#[error("Homeserver temporarily unavailable, please retry")]
HomeserverUnavailable,

#[error("HTTP request failed: {0}")]
RequestFailed(#[from] reqwest::Error),

Expand Down
4 changes: 4 additions & 0 deletions src/sms_verification/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ impl IntoResponse for SmsVerificationError {
}
return response;
}
SmsVerificationError::HomeserverUnavailable => {
tracing::error!("Homeserver unavailable during SMS verification");
StatusCode::INTERNAL_SERVER_ERROR
}
SmsVerificationError::RequestFailed(ref err) => {
tracing::error!(error = %err, "Failed to communicate with SMS provider");
StatusCode::INTERNAL_SERVER_ERROR
Expand Down
4 changes: 2 additions & 2 deletions src/sms_verification/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ impl SmsVerificationService {
PreludeCheckCodeResponse::Success { id, .. } => {
let code = match self.homeserver_admin_api.generate_signup_token().await {
Ok(code) => code,
Err(e) => {
Err(_e) => {
if let Err(e) = SmsVerificationRepository::mark_failed(
&mut executor,
&id,
Expand All @@ -169,7 +169,7 @@ impl SmsVerificationService {
{
tracing::error!("{}", e);
}
return Err(e.into());
return Err(SmsVerificationError::HomeserverUnavailable);
}
};

Expand Down
14 changes: 14 additions & 0 deletions src/sms_verification/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,20 @@ async fn test_service_success_but_homeserver_fails_marks_failed(pool: PgPool) {
record.failure_reason,
Some("homeserver_signup_token_generation_failed".to_string())
);

// Verify that failed verification does NOT count towards quota
let phone_hash = test_phone_hasher().hash_phone_number(phone.as_str());
let failed_count = SmsVerificationRepository::count_verified_sessions_in_last_days(
&mut executor,
&phone_hash,
7,
)
.await
.unwrap();
assert_eq!(
failed_count, 0,
"Failed verification should NOT count towards weekly quota"
);
}

// This circumstance happens if validate_code() is called before send_code() - Prelude has no prelude_id for the verification session yet so it returns a dummy value which doesnt match to anything in our db.
Expand Down
Loading