use serde::Deserialize; #[derive(Deserialize)] pub struct Config { #[serde(default = "default_name")] pub name: String, pub tg: TgConfig, pub auth: AuthConfig, pub session: SessionConfig, #[serde(default)] pub backend: BackendConfig, #[serde(default)] pub whisper_url: Option, #[serde(default)] pub gitea: Option, } #[derive(Deserialize, Clone)] pub struct GiteaConfig { pub url: String, /// Direct token or read from token_file at startup #[serde(default)] pub token: String, #[serde(default)] pub token_file: Option, #[serde(default = "default_webhook_port")] pub webhook_port: u16, #[serde(default)] pub webhook_secret: Option, } impl GiteaConfig { /// Resolve token: if token_file is set and token is empty, read from file pub fn resolve_token(&mut self) { if self.token.is_empty() { if let Some(path) = &self.token_file { match std::fs::read_to_string(path) { Ok(t) => self.token = t.trim().to_string(), Err(e) => tracing::error!("failed to read gitea token_file {path}: {e}"), } } } } } fn default_webhook_port() -> u16 { 9800 } fn default_name() -> String { "noc".to_string() } #[derive(Deserialize, Clone, Default)] #[serde(tag = "type")] pub enum BackendConfig { #[serde(rename = "claude")] #[default] Claude, #[serde(rename = "openai")] OpenAI { endpoint: String, model: String, #[serde(default = "default_api_key")] api_key: String, }, } fn default_api_key() -> String { "unused".to_string() } #[derive(Deserialize)] pub struct TgConfig { pub key: String, } #[derive(Deserialize)] pub struct AuthConfig { pub passphrase: String, } #[derive(Deserialize)] pub struct SessionConfig { pub refresh_hour: u32, }