diff options
| author | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2026-07-09 21:43:23 +0200 |
|---|---|---|
| committer | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2026-07-09 21:43:23 +0200 |
| commit | 3223d93cb3c77ab02aa0a35f2a8314e447cee9a4 (patch) | |
| tree | 3971a1f37f5fe3cadb747c5229a0d54e868e45e3 /crates/server/src/database/mod.rs | |
| parent | fix(client/sync): Pass through precise error on `SyncError::WrongKey` (diff) | |
| download | atuin-3223d93cb3c77ab02aa0a35f2a8314e447cee9a4.zip | |
chore: Separate daemon, client, server, and lib into crates
Diffstat (limited to 'crates/server/src/database/mod.rs')
| -rw-r--r-- | crates/server/src/database/mod.rs | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/crates/server/src/database/mod.rs b/crates/server/src/database/mod.rs new file mode 100644 index 00000000..43fe5c3b --- /dev/null +++ b/crates/server/src/database/mod.rs @@ -0,0 +1,99 @@ +pub(crate) mod db; +pub(crate) mod models; + +use std::fmt::{Debug, Display}; + +use serde::{Deserialize, Serialize}; + +#[derive(Debug)] +pub(crate) enum DbError { + NotFound, + Other(eyre::Report), +} + +impl Display for DbError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::NotFound => write!(f, "Not found"), + Self::Other(report) => write!(f, "Other: {report}"), + } + } +} + +impl From<time::error::ComponentRange> for DbError { + fn from(error: time::error::ComponentRange) -> Self { + Self::Other(error.into()) + } +} + +impl From<time::error::Error> for DbError { + fn from(error: time::error::Error) -> Self { + Self::Other(error.into()) + } +} + +impl From<sqlx::Error> for DbError { + fn from(error: sqlx::Error) -> Self { + match error { + sqlx::Error::RowNotFound => Self::NotFound, + error => Self::Other(error.into()), + } + } +} + +impl std::error::Error for DbError {} + +pub(crate) type DbResult<T> = Result<T, DbError>; + +#[derive(Debug, PartialEq)] +pub(crate) enum DbType { + Postgres, + Unknown, +} + +#[derive(Clone, Deserialize, Serialize)] +pub(crate) struct DbSettings { + pub(crate) db_uri: String, + + /// Optional URI for read replicas. If set, read-only queries will use this connection. + pub(crate) read_db_uri: Option<String>, +} + +impl DbSettings { + pub(crate) fn db_type(&self) -> DbType { + if self.db_uri.starts_with("postgres://") || self.db_uri.starts_with("postgresql://") { + DbType::Postgres + } else { + DbType::Unknown + } + } +} + +fn redact_db_uri(uri: &str) -> String { + url::Url::parse(uri).map_or_else( + |_| uri.to_string(), + |mut url| { + url.set_password(Some("****")).expect("should be possible"); + url.to_string() + }, + ) +} + +// Do our best to redact passwords so they're not logged in the event of an error. +impl Debug for DbSettings { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.db_type() == DbType::Postgres { + let redacted_uri = redact_db_uri(&self.db_uri); + let redacted_read_uri = self.read_db_uri.as_ref().map(|uri| redact_db_uri(uri)); + f.debug_struct("DbSettings") + .field("db_uri", &redacted_uri) + .field("read_db_uri", &redacted_read_uri) + .finish() + } else { + f.debug_struct("DbSettings") + .field("db_uri", &self.db_uri) + .field("read_db_uri", &self.read_db_uri) + .finish() + } + } +} |
