cleanup: strip comments, extract constants, DRY shared helpers across adapters + infra-wiring
- strip all comments except WHY workaround notes (3 remain) - remove all #[allow(dead_code)]; fix via _prefix rename - extract named constants: JWT time units, token types, default config values, jellyfin fallback bitrate - DRY: move serialize_enum_as_string, content_type_str, parse_content_type, parse_genres_blob to adapter-common - sqlite+postgres library.rs use shared helpers instead of local copies - sqlite+postgres channel.rs use shared serialize_enum_as_string - remove dead `let _ = ext` in scanner.rs
This commit is contained in:
@@ -1,16 +1,18 @@
|
||||
//! Infra-wiring crate — shared `DbPool` enum and `Config` struct.
|
||||
//!
|
||||
//! Breaks dependency cycles between adapter crates and the presentation layer
|
||||
//! by owning pool creation, migration running, and env-var config loading.
|
||||
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Errors
|
||||
// ---------------------------------------------------------------------------
|
||||
const DEFAULT_HOST: &str = "0.0.0.0";
|
||||
const DEFAULT_PORT: u16 = 3000;
|
||||
const DEFAULT_DATABASE_URL: &str = "sqlite:data.db?mode=rwc";
|
||||
const DEFAULT_LOG_LEVEL: &str = "info";
|
||||
const DEFAULT_COOKIE_SECRET: &str = "k-template-cookie-secret-key-must-be-at-least-64-bytes-long!!";
|
||||
const DEFAULT_CORS_ORIGIN: &str = "http://localhost:5173";
|
||||
const DEFAULT_MAX_CONNECTIONS: u32 = 5;
|
||||
const DEFAULT_MIN_CONNECTIONS: u32 = 1;
|
||||
const DEFAULT_JWT_EXPIRY_HOURS: u64 = 24;
|
||||
const DEFAULT_JWT_REFRESH_EXPIRY_DAYS: u64 = 30;
|
||||
const DEFAULT_TRANSCODE_CLEANUP_TTL_HOURS: u32 = 24;
|
||||
|
||||
/// Errors that can occur when building a [`Config`].
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ConfigError {
|
||||
#[error("missing required env var: {0}")]
|
||||
@@ -23,7 +25,6 @@ pub enum ConfigError {
|
||||
},
|
||||
}
|
||||
|
||||
/// Errors from pool creation or migration.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum DbError {
|
||||
#[error("unsupported database URL scheme: {0}")]
|
||||
@@ -36,11 +37,6 @@ pub enum DbError {
|
||||
Migrate(#[from] sqlx::migrate::MigrateError),
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// DbPool
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Feature-gated database pool — one variant per supported backend.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum DbPool {
|
||||
#[cfg(feature = "sqlite")]
|
||||
@@ -51,10 +47,6 @@ pub enum DbPool {
|
||||
}
|
||||
|
||||
impl DbPool {
|
||||
/// Create a pool by auto-detecting the scheme of `database_url`.
|
||||
///
|
||||
/// * URLs starting with `sqlite:` → `SqlitePool`
|
||||
/// * URLs starting with `postgres:` / `postgresql:` → `PgPool`
|
||||
pub async fn connect(database_url: &str) -> Result<Self, DbError> {
|
||||
let scheme = database_url
|
||||
.split(':')
|
||||
@@ -77,7 +69,6 @@ impl DbPool {
|
||||
}
|
||||
}
|
||||
|
||||
/// Run the embedded migrations for the detected backend.
|
||||
pub async fn run_migrations(&self) -> Result<(), DbError> {
|
||||
match self {
|
||||
#[cfg(feature = "sqlite")]
|
||||
@@ -88,7 +79,6 @@ impl DbPool {
|
||||
}
|
||||
#[cfg(feature = "postgres")]
|
||||
Self::Postgres(_pool) => {
|
||||
// TODO: add postgres migrations directory and enable
|
||||
tracing::warn!("postgres migrations not yet available");
|
||||
}
|
||||
}
|
||||
@@ -96,93 +86,66 @@ impl DbPool {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Config
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Source of dynamic config values (env-only vs database-backed overrides).
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ConfigSource {
|
||||
Env,
|
||||
Db,
|
||||
}
|
||||
|
||||
/// Application configuration loaded from environment variables.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Config {
|
||||
pub config_source: ConfigSource,
|
||||
|
||||
// Core
|
||||
pub database_url: String,
|
||||
pub host: String,
|
||||
pub port: u16,
|
||||
pub log_level: String,
|
||||
pub is_production: bool,
|
||||
pub base_url: String,
|
||||
|
||||
// HTTP
|
||||
pub cors_origins: Vec<String>,
|
||||
pub cookie_secret: String,
|
||||
pub secure_cookie: bool,
|
||||
|
||||
// Connection pool
|
||||
pub db_max_connections: u32,
|
||||
pub db_min_connections: u32,
|
||||
|
||||
// Auth – JWT
|
||||
pub jwt_secret: Option<String>,
|
||||
pub jwt_issuer: Option<String>,
|
||||
pub jwt_audience: Option<String>,
|
||||
pub jwt_expiry_hours: u64,
|
||||
pub jwt_refresh_expiry_days: u64,
|
||||
pub allow_registration: bool,
|
||||
|
||||
// Auth – OIDC
|
||||
pub oidc_issuer_url: Option<String>,
|
||||
pub oidc_client_id: Option<String>,
|
||||
pub oidc_client_secret: Option<String>,
|
||||
pub oidc_redirect_url: Option<String>,
|
||||
pub oidc_resource_id: Option<String>,
|
||||
|
||||
// Jellyfin media provider
|
||||
pub jellyfin_url: Option<String>,
|
||||
pub jellyfin_api_key: Option<String>,
|
||||
pub jellyfin_user_id: Option<String>,
|
||||
|
||||
// Local-files provider
|
||||
pub local_files_dir: Option<PathBuf>,
|
||||
|
||||
// Transcoding
|
||||
pub transcode_dir: Option<PathBuf>,
|
||||
pub transcode_cleanup_ttl_hours: u32,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Build config from environment variables.
|
||||
///
|
||||
/// Uses sensible defaults where possible; returns `ConfigError` only when
|
||||
/// a required variable is missing and has no default.
|
||||
pub fn from_env() -> Result<Self, ConfigError> {
|
||||
let host = env::var("HOST").unwrap_or_else(|_| "0.0.0.0".to_string());
|
||||
let host = env::var("HOST").unwrap_or_else(|_| DEFAULT_HOST.to_string());
|
||||
|
||||
let port: u16 = env::var("PORT")
|
||||
.ok()
|
||||
.and_then(|p| p.parse().ok())
|
||||
.unwrap_or(3000);
|
||||
.unwrap_or(DEFAULT_PORT);
|
||||
|
||||
let database_url =
|
||||
env::var("DATABASE_URL").unwrap_or_else(|_| "sqlite:data.db?mode=rwc".to_string());
|
||||
env::var("DATABASE_URL").unwrap_or_else(|_| DEFAULT_DATABASE_URL.to_string());
|
||||
|
||||
let log_level = env::var("LOG_LEVEL")
|
||||
.or_else(|_| env::var("RUST_LOG"))
|
||||
.unwrap_or_else(|_| "info".to_string());
|
||||
.unwrap_or_else(|_| DEFAULT_LOG_LEVEL.to_string());
|
||||
|
||||
let cookie_secret = env::var("COOKIE_SECRET").unwrap_or_else(|_| {
|
||||
"k-template-cookie-secret-key-must-be-at-least-64-bytes-long!!".to_string()
|
||||
});
|
||||
let cookie_secret = env::var("COOKIE_SECRET")
|
||||
.unwrap_or_else(|_| DEFAULT_COOKIE_SECRET.to_string());
|
||||
|
||||
let cors_origins: Vec<String> = env::var("CORS_ALLOWED_ORIGINS")
|
||||
.unwrap_or_else(|_| "http://localhost:5173".to_string())
|
||||
.unwrap_or_else(|_| DEFAULT_CORS_ORIGIN.to_string())
|
||||
.split(',')
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
@@ -196,27 +159,25 @@ impl Config {
|
||||
let db_max_connections = env::var("DB_MAX_CONNECTIONS")
|
||||
.ok()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.unwrap_or(5);
|
||||
.unwrap_or(DEFAULT_MAX_CONNECTIONS);
|
||||
|
||||
let db_min_connections = env::var("DB_MIN_CONNECTIONS")
|
||||
.ok()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.unwrap_or(1);
|
||||
.unwrap_or(DEFAULT_MIN_CONNECTIONS);
|
||||
|
||||
// JWT
|
||||
let jwt_secret = env::var("JWT_SECRET").ok();
|
||||
let jwt_issuer = env::var("JWT_ISSUER").ok();
|
||||
let jwt_audience = env::var("JWT_AUDIENCE").ok();
|
||||
let jwt_expiry_hours = env::var("JWT_EXPIRY_HOURS")
|
||||
.ok()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.unwrap_or(24);
|
||||
.unwrap_or(DEFAULT_JWT_EXPIRY_HOURS);
|
||||
let jwt_refresh_expiry_days = env::var("JWT_REFRESH_EXPIRY_DAYS")
|
||||
.ok()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.unwrap_or(30);
|
||||
.unwrap_or(DEFAULT_JWT_REFRESH_EXPIRY_DAYS);
|
||||
|
||||
// OIDC
|
||||
let oidc_issuer_url = env::var("OIDC_ISSUER").ok();
|
||||
let oidc_client_id = env::var("OIDC_CLIENT_ID").ok();
|
||||
let oidc_client_secret = env::var("OIDC_CLIENT_SECRET").ok();
|
||||
@@ -235,20 +196,16 @@ impl Config {
|
||||
.map(|v| !(v == "false" || v == "0"))
|
||||
.unwrap_or(true);
|
||||
|
||||
// Jellyfin
|
||||
let jellyfin_url = env::var("JELLYFIN_BASE_URL").ok();
|
||||
let jellyfin_api_key = env::var("JELLYFIN_API_KEY").ok();
|
||||
let jellyfin_user_id = env::var("JELLYFIN_USER_ID").ok();
|
||||
|
||||
// Local files
|
||||
let local_files_dir = env::var("LOCAL_FILES_DIR").ok().map(PathBuf::from);
|
||||
|
||||
// Transcoding
|
||||
let transcode_dir = env::var("TRANSCODE_DIR").ok().map(PathBuf::from);
|
||||
let transcode_cleanup_ttl_hours = env::var("TRANSCODE_CLEANUP_TTL_HOURS")
|
||||
.ok()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.unwrap_or(24);
|
||||
.unwrap_or(DEFAULT_TRANSCODE_CLEANUP_TTL_HOURS);
|
||||
|
||||
let base_url =
|
||||
env::var("BASE_URL").unwrap_or_else(|_| format!("http://localhost:{}", port));
|
||||
|
||||
Reference in New Issue
Block a user