diff options
Diffstat (limited to '')
| -rw-r--r-- | crates/server/src/database/db/mod.rs | 10 | ||||
| -rw-r--r-- | crates/server/src/database/db/wrappers.rs | 2 | ||||
| -rw-r--r-- | crates/server/src/database/mod.rs | 18 | ||||
| -rw-r--r-- | crates/server/src/database/models.rs | 4 | ||||
| -rw-r--r-- | crates/server/src/handlers/mod.rs | 4 | ||||
| -rw-r--r-- | crates/server/src/lib.rs | 86 | ||||
| -rw-r--r-- | crates/server/src/main.rs | 88 | ||||
| -rw-r--r-- | crates/server/src/router.rs | 6 | ||||
| -rw-r--r-- | crates/server/src/settings.rs | 30 |
9 files changed, 121 insertions, 127 deletions
diff --git a/crates/server/src/database/db/mod.rs b/crates/server/src/database/db/mod.rs index c95a2ed4..9345ff6b 100644 --- a/crates/server/src/database/db/mod.rs +++ b/crates/server/src/database/db/mod.rs @@ -15,7 +15,7 @@ mod wrappers; const MIN_PG_VERSION: u32 = 14; #[derive(Clone)] -pub struct ServerPostgres { +pub(crate) struct ServerPostgres { pool: sqlx::Pool<sqlx::postgres::Postgres>, /// Optional read replica pool for read-only queries read_pool: Option<sqlx::Pool<sqlx::postgres::Postgres>>, @@ -30,7 +30,7 @@ impl ServerPostgres { } impl ServerPostgres { - pub async fn new(settings: &DbSettings) -> DbResult<Self> { + pub(crate) async fn new(settings: &DbSettings) -> DbResult<Self> { let pool = PgPoolOptions::new() .max_connections(100) .connect(settings.db_uri.as_str()) @@ -91,7 +91,7 @@ impl ServerPostgres { } #[instrument(skip_all)] - pub async fn add_records( + pub(crate) async fn add_records( &self, user: &User, records: &[Record<EncryptedData>], @@ -167,7 +167,7 @@ impl ServerPostgres { } #[instrument(skip_all)] - pub async fn next_records( + pub(crate) async fn next_records( &self, user: &User, host: HostId, @@ -220,7 +220,7 @@ impl ServerPostgres { Ok(ret) } - pub async fn status(&self, user: &User) -> DbResult<RecordStatus> { + pub(crate) async fn status(&self, user: &User) -> DbResult<RecordStatus> { // If IDX_CACHE_ROLLOUT is set, then we // 1. Read the value of the var, use it as a % chance of using the cache // 2. If we use the cache, just read from the cache table diff --git a/crates/server/src/database/db/wrappers.rs b/crates/server/src/database/db/wrappers.rs index 8054289a..6bacf47f 100644 --- a/crates/server/src/database/db/wrappers.rs +++ b/crates/server/src/database/db/wrappers.rs @@ -1,7 +1,7 @@ use turtle_common::record::{EncryptedData, Host, Record}; use sqlx::{Row, postgres::PgRow}; -pub struct DbRecord(pub Record<EncryptedData>); +pub(crate) struct DbRecord(pub(crate) Record<EncryptedData>); impl<'a> ::sqlx::FromRow<'a, PgRow> for DbRecord { fn from_row(row: &'a PgRow) -> ::sqlx::Result<Self> { diff --git a/crates/server/src/database/mod.rs b/crates/server/src/database/mod.rs index c05fa783..43fe5c3b 100644 --- a/crates/server/src/database/mod.rs +++ b/crates/server/src/database/mod.rs @@ -1,12 +1,12 @@ -pub mod db; -pub mod models; +pub(crate) mod db; +pub(crate) mod models; use std::fmt::{Debug, Display}; use serde::{Deserialize, Serialize}; #[derive(Debug)] -pub enum DbError { +pub(crate) enum DbError { NotFound, Other(eyre::Report), } @@ -43,24 +43,24 @@ impl From<sqlx::Error> for DbError { impl std::error::Error for DbError {} -pub type DbResult<T> = Result<T, DbError>; +pub(crate) type DbResult<T> = Result<T, DbError>; #[derive(Debug, PartialEq)] -pub enum DbType { +pub(crate) enum DbType { Postgres, Unknown, } #[derive(Clone, Deserialize, Serialize)] -pub struct DbSettings { - pub db_uri: String, +pub(crate) struct DbSettings { + pub(crate) db_uri: String, /// Optional URI for read replicas. If set, read-only queries will use this connection. - pub read_db_uri: Option<String>, + pub(crate) read_db_uri: Option<String>, } impl DbSettings { - pub fn db_type(&self) -> DbType { + pub(crate) fn db_type(&self) -> DbType { if self.db_uri.starts_with("postgres://") || self.db_uri.starts_with("postgresql://") { DbType::Postgres } else { diff --git a/crates/server/src/database/models.rs b/crates/server/src/database/models.rs index 9f6241ae..3fa6f471 100644 --- a/crates/server/src/database/models.rs +++ b/crates/server/src/database/models.rs @@ -1,5 +1,5 @@ use uuid::Uuid; -pub struct User { - pub id: Uuid, +pub(crate) struct User { + pub(crate) id: Uuid, } diff --git a/crates/server/src/handlers/mod.rs b/crates/server/src/handlers/mod.rs index 1ac5a0c4..5fb5d0f2 100644 --- a/crates/server/src/handlers/mod.rs +++ b/crates/server/src/handlers/mod.rs @@ -29,8 +29,8 @@ impl IntoResponse for ErrorResponseStatus<'_> { } pub(crate) struct ErrorResponseStatus<'a> { - pub error: ErrorResponse<'a>, - pub status: http::StatusCode, + pub(crate) error: ErrorResponse<'a>, + pub(crate) status: http::StatusCode, } pub(crate) trait RespExt<'a> { diff --git a/crates/server/src/lib.rs b/crates/server/src/lib.rs deleted file mode 100644 index 726afeea..00000000 --- a/crates/server/src/lib.rs +++ /dev/null @@ -1,86 +0,0 @@ -use std::future::Future; -use std::net::SocketAddr; - -use axum::{Router, serve}; -use database::db::ServerPostgres; -use eyre::{Context, Result}; - -pub mod database; -mod handlers; -mod metrics; -mod router; - -pub use settings::Settings; - -pub mod settings; - -use tokio::net::TcpListener; -use tokio::signal; - -#[cfg(target_family = "unix")] -async fn shutdown_signal() { - let mut term = signal::unix::signal(signal::unix::SignalKind::terminate()) - .expect("failed to register signal handler"); - let mut interrupt = signal::unix::signal(signal::unix::SignalKind::interrupt()) - .expect("failed to register signal handler"); - - tokio::select! { - _ = term.recv() => {}, - _ = interrupt.recv() => {}, - }; - eprintln!("Shutting down gracefully..."); -} - -pub async fn launch(settings: Settings, addr: SocketAddr) -> Result<()> { - launch_with_tcp_listener( - settings, - TcpListener::bind(addr) - .await - .context("could not connect to socket")?, - shutdown_signal(), - ) - .await -} - -pub async fn launch_with_tcp_listener( - settings: Settings, - listener: TcpListener, - shutdown: impl Future<Output = ()> + Send + 'static, -) -> Result<()> { - let r = make_router(settings).await?; - - serve(listener, r.into_make_service()) - .with_graceful_shutdown(shutdown) - .await?; - - Ok(()) -} - -// The separate listener means it's much easier to ensure metrics are not accidentally exposed to -// the public. -pub async fn launch_metrics_server(host: String, port: u16) -> Result<()> { - let listener = TcpListener::bind((host, port)) - .await - .context("failed to bind metrics tcp")?; - - let recorder_handle = metrics::setup_metrics_recorder(); - - let router = Router::new().route( - "/metrics", - axum::routing::get(move || std::future::ready(recorder_handle.render())), - ); - - serve(listener, router.into_make_service()) - .with_graceful_shutdown(shutdown_signal()) - .await?; - - Ok(()) -} - -async fn make_router(settings: Settings) -> Result<Router, eyre::Error> { - let db = ServerPostgres::new(&settings.db_settings) - .await - .wrap_err_with(|| format!("failed to connect to db: {:?}", settings.db_settings))?; - let r = router::router(db, settings); - Ok(r) -} diff --git a/crates/server/src/main.rs b/crates/server/src/main.rs index 94d6d143..fb587754 100644 --- a/crates/server/src/main.rs +++ b/crates/server/src/main.rs @@ -1,13 +1,25 @@ +use std::future::Future; use std::net::SocketAddr; -use turtle_server::{Settings, database::DbType, launch, launch_metrics_server}; - +use axum::{Router, serve}; use clap::Parser; +use database::db::ServerPostgres; use eyre::{Context, Result, eyre}; +use tokio::net::TcpListener; +use tokio::signal; + +use crate::database::DbType; +use crate::settings::Settings; + +mod database; +mod handlers; +mod metrics; +mod router; +mod settings; #[derive(Parser, Clone, Debug)] #[command(infer_subcommands = true)] -pub enum Cmd { +pub(crate) enum Cmd { /// Start the server Start { /// The host address to bind @@ -24,7 +36,7 @@ pub enum Cmd { } impl Cmd { - pub async fn run(self) -> Result<()> { + async fn run(self) -> Result<()> { match self { Self::Start { host, port } => { let settings = Settings::new().wrap_err("could not load server settings")?; @@ -59,3 +71,71 @@ impl Cmd { async fn main() -> Result<()> { Cmd::parse().run().await } + +#[cfg(target_family = "unix")] +async fn shutdown_signal() { + let mut term = signal::unix::signal(signal::unix::SignalKind::terminate()) + .expect("failed to register signal handler"); + let mut interrupt = signal::unix::signal(signal::unix::SignalKind::interrupt()) + .expect("failed to register signal handler"); + + tokio::select! { + _ = term.recv() => {}, + _ = interrupt.recv() => {}, + }; + eprintln!("Shutting down gracefully..."); +} + +async fn launch(settings: Settings, addr: SocketAddr) -> Result<()> { + launch_with_tcp_listener( + settings, + TcpListener::bind(addr) + .await + .context("could not connect to socket")?, + shutdown_signal(), + ) + .await +} + +async fn launch_with_tcp_listener( + settings: Settings, + listener: TcpListener, + shutdown: impl Future<Output = ()> + Send + 'static, +) -> Result<()> { + let r = make_router(settings).await?; + + serve(listener, r.into_make_service()) + .with_graceful_shutdown(shutdown) + .await?; + + Ok(()) +} + +// The separate listener means it's much easier to ensure metrics are not accidentally exposed to +// the public. +async fn launch_metrics_server(host: String, port: u16) -> Result<()> { + let listener = TcpListener::bind((host, port)) + .await + .context("failed to bind metrics tcp")?; + + let recorder_handle = metrics::setup_metrics_recorder(); + + let router = Router::new().route( + "/metrics", + axum::routing::get(move || std::future::ready(recorder_handle.render())), + ); + + serve(listener, router.into_make_service()) + .with_graceful_shutdown(shutdown_signal()) + .await?; + + Ok(()) +} + +async fn make_router(settings: Settings) -> Result<Router, eyre::Error> { + let db = ServerPostgres::new(&settings.db_settings) + .await + .wrap_err_with(|| format!("failed to connect to db: {:?}", settings.db_settings))?; + let r = router::router(db, settings); + Ok(r) +} diff --git a/crates/server/src/router.rs b/crates/server/src/router.rs index ce12f5b6..5ba15e62 100644 --- a/crates/server/src/router.rs +++ b/crates/server/src/router.rs @@ -21,7 +21,7 @@ use crate::{ settings::Settings, }; -pub(crate) struct UserAuth(pub User); +pub(crate) struct UserAuth(pub(crate) User); impl FromRequestParts<AppState> for UserAuth { type Rejection = ErrorResponseStatus<'static>; @@ -66,8 +66,8 @@ async fn semver(request: Request, next: Next) -> Response { #[derive(Clone)] pub(crate) struct AppState { - pub database: ServerPostgres, - pub settings: Settings, + pub(crate) database: ServerPostgres, + pub(crate) settings: Settings, } pub(crate) fn router(database: ServerPostgres, settings: Settings) -> Router { diff --git a/crates/server/src/settings.rs b/crates/server/src/settings.rs index 256aa661..60837525 100644 --- a/crates/server/src/settings.rs +++ b/crates/server/src/settings.rs @@ -9,11 +9,11 @@ use tracing::info; use crate::database::DbSettings; #[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Metrics { +pub(crate) struct Metrics { #[serde(alias = "enabled")] - pub enable: bool, - pub host: String, - pub port: u16, + pub(crate) enable: bool, + pub(crate) host: String, + pub(crate) port: u16, } impl Default for Metrics { @@ -27,29 +27,29 @@ impl Default for Metrics { } #[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Settings { - pub host: String, - pub port: u16, - pub path: String, - pub max_history_length: usize, - pub max_record_size: usize, - pub page_size: i64, - pub metrics: Metrics, +pub(crate) struct Settings { + pub(crate) host: String, + pub(crate) port: u16, + pub(crate) path: String, + pub(crate) max_history_length: usize, + pub(crate) max_record_size: usize, + pub(crate) page_size: i64, + pub(crate) metrics: Metrics, /// Advertise a version that is not what we are _actually_ running /// Many clients compare their version with api.atuin.sh, and if they differ, notify the user /// that an update is available. /// Now that we take beta releases, we should be able to advertise a different version to avoid /// notifying users when the server runs something that is not a stable release. - pub fake_version: Option<String>, + pub(crate) fake_version: Option<String>, #[serde(flatten)] #[expect(clippy::struct_field_names)] - pub db_settings: DbSettings, + pub(crate) db_settings: DbSettings, } impl Settings { - pub fn new() -> Result<Self> { + pub(crate) fn new() -> Result<Self> { // create the config file if it does not exist let mut config_builder = Config::builder() .set_default("host", "127.0.0.1")? |
