lib.rs

  1pub mod api;
  2pub mod auth;
  3pub mod db;
  4pub mod env;
  5pub mod executor;
  6pub mod rpc;
  7
  8#[cfg(test)]
  9mod tests;
 10
 11use anyhow::anyhow;
 12use aws_config::{BehaviorVersion, Region};
 13use axum::{http::StatusCode, response::IntoResponse};
 14use db::Database;
 15use executor::Executor;
 16use serde::Deserialize;
 17use std::{path::PathBuf, sync::Arc};
 18use util::ResultExt;
 19
 20pub type Result<T, E = Error> = std::result::Result<T, E>;
 21
 22pub enum Error {
 23    Http(StatusCode, String),
 24    Database(sea_orm::error::DbErr),
 25    Internal(anyhow::Error),
 26}
 27
 28impl From<anyhow::Error> for Error {
 29    fn from(error: anyhow::Error) -> Self {
 30        Self::Internal(error)
 31    }
 32}
 33
 34impl From<sea_orm::error::DbErr> for Error {
 35    fn from(error: sea_orm::error::DbErr) -> Self {
 36        Self::Database(error)
 37    }
 38}
 39
 40impl From<axum::Error> for Error {
 41    fn from(error: axum::Error) -> Self {
 42        Self::Internal(error.into())
 43    }
 44}
 45
 46impl From<hyper::Error> for Error {
 47    fn from(error: hyper::Error) -> Self {
 48        Self::Internal(error.into())
 49    }
 50}
 51
 52impl From<serde_json::Error> for Error {
 53    fn from(error: serde_json::Error) -> Self {
 54        Self::Internal(error.into())
 55    }
 56}
 57
 58impl IntoResponse for Error {
 59    fn into_response(self) -> axum::response::Response {
 60        match self {
 61            Error::Http(code, message) => (code, message).into_response(),
 62            Error::Database(error) => {
 63                (StatusCode::INTERNAL_SERVER_ERROR, format!("{}", &error)).into_response()
 64            }
 65            Error::Internal(error) => {
 66                (StatusCode::INTERNAL_SERVER_ERROR, format!("{}", &error)).into_response()
 67            }
 68        }
 69    }
 70}
 71
 72impl std::fmt::Debug for Error {
 73    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 74        match self {
 75            Error::Http(code, message) => (code, message).fmt(f),
 76            Error::Database(error) => error.fmt(f),
 77            Error::Internal(error) => error.fmt(f),
 78        }
 79    }
 80}
 81
 82impl std::fmt::Display for Error {
 83    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 84        match self {
 85            Error::Http(code, message) => write!(f, "{code}: {message}"),
 86            Error::Database(error) => error.fmt(f),
 87            Error::Internal(error) => error.fmt(f),
 88        }
 89    }
 90}
 91
 92impl std::error::Error for Error {}
 93
 94#[derive(Deserialize)]
 95pub struct Config {
 96    pub http_port: u16,
 97    pub database_url: String,
 98    pub database_max_connections: u32,
 99    pub api_token: String,
100    pub invite_link_prefix: String,
101    pub live_kit_server: Option<String>,
102    pub live_kit_key: Option<String>,
103    pub live_kit_secret: Option<String>,
104    pub rust_log: Option<String>,
105    pub log_json: Option<bool>,
106    pub blob_store_url: Option<String>,
107    pub blob_store_region: Option<String>,
108    pub blob_store_access_key: Option<String>,
109    pub blob_store_secret_key: Option<String>,
110    pub blob_store_bucket: Option<String>,
111    pub zed_environment: Arc<str>,
112}
113
114impl Config {
115    pub fn is_development(&self) -> bool {
116        self.zed_environment == "development".into()
117    }
118}
119
120#[derive(Default, Deserialize)]
121pub struct MigrateConfig {
122    pub database_url: String,
123    pub migrations_path: Option<PathBuf>,
124}
125
126pub struct AppState {
127    pub db: Arc<Database>,
128    pub live_kit_client: Option<Arc<dyn live_kit_server::api::Client>>,
129    pub blob_store_client: Option<aws_sdk_s3::Client>,
130    pub config: Config,
131}
132
133impl AppState {
134    pub async fn new(config: Config) -> Result<Arc<Self>> {
135        let mut db_options = db::ConnectOptions::new(config.database_url.clone());
136        db_options.max_connections(config.database_max_connections);
137        let mut db = Database::new(db_options, Executor::Production).await?;
138        db.initialize_notification_kinds().await?;
139
140        let live_kit_client = if let Some(((server, key), secret)) = config
141            .live_kit_server
142            .as_ref()
143            .zip(config.live_kit_key.as_ref())
144            .zip(config.live_kit_secret.as_ref())
145        {
146            Some(Arc::new(live_kit_server::api::LiveKitClient::new(
147                server.clone(),
148                key.clone(),
149                secret.clone(),
150            )) as Arc<dyn live_kit_server::api::Client>)
151        } else {
152            None
153        };
154
155        let this = Self {
156            db: Arc::new(db),
157            live_kit_client,
158            blob_store_client: build_blob_store_client(&config).await.log_err(),
159            config,
160        };
161        Ok(Arc::new(this))
162    }
163}
164
165async fn build_blob_store_client(config: &Config) -> anyhow::Result<aws_sdk_s3::Client> {
166    let keys = aws_sdk_s3::config::Credentials::new(
167        config
168            .blob_store_access_key
169            .clone()
170            .ok_or_else(|| anyhow!("missing blob_store_access_key"))?,
171        config
172            .blob_store_secret_key
173            .clone()
174            .ok_or_else(|| anyhow!("missing blob_store_secret_key"))?,
175        None,
176        None,
177        "env",
178    );
179
180    let s3_config = aws_config::defaults(BehaviorVersion::latest())
181        .endpoint_url(
182            config
183                .blob_store_url
184                .as_ref()
185                .ok_or_else(|| anyhow!("missing blob_store_url"))?,
186        )
187        .region(Region::new(
188            config
189                .blob_store_region
190                .clone()
191                .ok_or_else(|| anyhow!("missing blob_store_region"))?,
192        ))
193        .credentials_provider(keys)
194        .load()
195        .await;
196
197    Ok(aws_sdk_s3::Client::new(&s3_config))
198}