1//! Configuration loading from environment variables. 2 3use anyhow::anyhow; 4use std::env; 5 6/// Service configuration loaded from environment. 7pub struct Config { 8 pub host: String, 9 pub port: u16, 10 pub auth_token: Option<String>, 11 pub audd_api_token: String, 12 pub audd_api_url: String, 13 pub database_url: Option<String>, 14 pub labeler_did: Option<String>, 15 pub labeler_signing_key: Option<String>, 16 /// Anthropic API key for Claude image moderation 17 pub claude_api_key: Option<String>, 18 /// Claude model to use (default: claude-sonnet-4-5-20250929) 19 pub claude_model: String, 20 /// Minimum AuDD score to flag as potential copyright violation (default: 85) 21 pub copyright_score_threshold: i32, 22} 23 24impl Config { 25 /// Load configuration from environment variables. 26 pub fn from_env() -> anyhow::Result<Self> { 27 Ok(Self { 28 host: env::var("MODERATION_HOST").unwrap_or_else(|_| "0.0.0.0".to_string()), 29 port: env::var("MODERATION_PORT") 30 .ok() 31 .and_then(|v| v.parse().ok()) 32 .unwrap_or(8083), 33 auth_token: env::var("MODERATION_AUTH_TOKEN").ok(), 34 audd_api_token: env::var("MODERATION_AUDD_API_TOKEN") 35 .map_err(|_| anyhow!("MODERATION_AUDD_API_TOKEN is required"))?, 36 audd_api_url: env::var("MODERATION_AUDD_API_URL") 37 .unwrap_or_else(|_| "https://enterprise.audd.io/".to_string()), 38 database_url: env::var("MODERATION_DATABASE_URL").ok(), 39 labeler_did: env::var("MODERATION_LABELER_DID").ok(), 40 labeler_signing_key: env::var("MODERATION_LABELER_SIGNING_KEY").ok(), 41 claude_api_key: env::var("ANTHROPIC_API_KEY").ok(), 42 claude_model: env::var("MODERATION_CLAUDE_MODEL") 43 .unwrap_or_else(|_| "claude-sonnet-4-5-20250929".to_string()), 44 copyright_score_threshold: env::var("MODERATION_COPYRIGHT_SCORE_THRESHOLD") 45 .ok() 46 .and_then(|v| v.parse().ok()) 47 .unwrap_or(85), 48 }) 49 } 50 51 /// Check if Claude image moderation is enabled. 52 pub fn claude_enabled(&self) -> bool { 53 self.claude_api_key.is_some() && self.database_url.is_some() 54 } 55 56 /// Check if labeler is fully configured. 57 pub fn labeler_enabled(&self) -> bool { 58 self.database_url.is_some() 59 && self.labeler_did.is_some() 60 && self.labeler_signing_key.is_some() 61 } 62}