111 lines
3.8 KiB
Rust
111 lines
3.8 KiB
Rust
use anyhow::{Context, Result};
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct Settings {
|
|
pub port: u16,
|
|
pub database_url: String,
|
|
pub oidc_client_id: String,
|
|
pub oidc_client_secret: String,
|
|
pub oidc_authorize_url: String,
|
|
pub oidc_token_url: String,
|
|
pub oidc_userinfo_url: String,
|
|
pub oidc_redirect_url: String,
|
|
pub oidc_scopes: String,
|
|
pub session_secret: String,
|
|
pub cookie_secure: bool,
|
|
pub stun_urls: Vec<String>,
|
|
pub turn_urls: Vec<String>,
|
|
pub turn_username: Option<String>,
|
|
pub turn_password: Option<String>,
|
|
pub media: Option<MediaSettings>,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct MediaSettings {
|
|
pub account_id: String,
|
|
pub access_key_id: String,
|
|
pub secret_access_key: String,
|
|
pub bucket: String,
|
|
pub public_base_url: String,
|
|
}
|
|
|
|
impl Settings {
|
|
pub fn from_env() -> Result<Self> {
|
|
Ok(Self {
|
|
port: std::env::var("PORT")
|
|
.unwrap_or_else(|_| "3000".into())
|
|
.parse()
|
|
.context("PORT must be a valid u16")?,
|
|
database_url: required("DATABASE_URL")?,
|
|
oidc_client_id: required("OIDC_CLIENT_ID")?,
|
|
oidc_client_secret: required("OIDC_CLIENT_SECRET")?,
|
|
oidc_authorize_url: required("OIDC_AUTHORIZE_URL")?,
|
|
oidc_token_url: required("OIDC_TOKEN_URL")?,
|
|
oidc_userinfo_url: required("OIDC_USERINFO_URL")?,
|
|
oidc_redirect_url: required("OIDC_REDIRECT_URL")?,
|
|
oidc_scopes: std::env::var("OIDC_SCOPES")
|
|
.unwrap_or_else(|_| "openid profile email".to_string()),
|
|
session_secret: required("SESSION_SECRET")?,
|
|
cookie_secure: std::env::var("COOKIE_SECURE")
|
|
.unwrap_or_else(|_| "false".into())
|
|
.parse()
|
|
.context("COOKIE_SECURE must be true/false")?,
|
|
stun_urls: parse_csv_env("STUN_URLS", "stun:stun.l.google.com:19302"),
|
|
turn_urls: parse_csv_env("TURN_URLS", ""),
|
|
turn_username: optional("TURN_USERNAME"),
|
|
turn_password: optional("TURN_PASSWORD"),
|
|
media: MediaSettings::from_env()?,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl MediaSettings {
|
|
fn from_env() -> Result<Option<Self>> {
|
|
let account_id = optional("R2_ACCOUNT_ID");
|
|
let access_key_id = optional("R2_ACCESS_KEY_ID");
|
|
let secret_access_key = optional("R2_SECRET_ACCESS_KEY");
|
|
let bucket = optional("R2_BUCKET");
|
|
let public_base_url = optional("R2_PUBLIC_BASE_URL");
|
|
|
|
if account_id.is_none()
|
|
&& access_key_id.is_none()
|
|
&& secret_access_key.is_none()
|
|
&& bucket.is_none()
|
|
&& public_base_url.is_none()
|
|
{
|
|
return Ok(None);
|
|
}
|
|
|
|
Ok(Some(Self {
|
|
account_id: account_id.context("missing env var R2_ACCOUNT_ID")?,
|
|
access_key_id: access_key_id.context("missing env var R2_ACCESS_KEY_ID")?,
|
|
secret_access_key: secret_access_key.context("missing env var R2_SECRET_ACCESS_KEY")?,
|
|
bucket: bucket.context("missing env var R2_BUCKET")?,
|
|
public_base_url: public_base_url.context("missing env var R2_PUBLIC_BASE_URL")?,
|
|
}))
|
|
}
|
|
|
|
pub fn endpoint_url(&self) -> String {
|
|
format!("https://{}.r2.cloudflarestorage.com", self.account_id)
|
|
}
|
|
}
|
|
|
|
fn required(name: &str) -> Result<String> {
|
|
std::env::var(name).with_context(|| format!("missing env var {name}"))
|
|
}
|
|
|
|
fn optional(name: &str) -> Option<String> {
|
|
std::env::var(name)
|
|
.ok()
|
|
.map(|v| v.trim().to_string())
|
|
.filter(|v| !v.is_empty())
|
|
}
|
|
|
|
fn parse_csv_env(name: &str, default_value: &str) -> Vec<String> {
|
|
let raw = std::env::var(name).unwrap_or_else(|_| default_value.to_string());
|
|
raw.split(',')
|
|
.map(str::trim)
|
|
.filter(|s| !s.is_empty())
|
|
.map(ToString::to_string)
|
|
.collect()
|
|
}
|