Skip to main content

codetether_agent/tui/chat/sync/
config.rs

1//! Configuration parsing from env vars and Vault.
2
3use super::config_types::*;
4use super::env_helpers::{env_bool, env_non_empty, env_non_placeholder};
5
6fn vault_str(s: &crate::secrets::ProviderSecrets, keys: &[&str]) -> Option<String> {
7    keys.iter().find_map(|k| {
8        s.extra
9            .get(*k)
10            .and_then(|v| v.as_str())
11            .map(|v| v.trim().to_string())
12            .filter(|v| !v.is_empty())
13    })
14}
15
16fn vault_bool_val(s: &crate::secrets::ProviderSecrets, keys: &[&str]) -> Option<bool> {
17    keys.iter().find_map(|k| {
18        s.extra
19            .get(*k)
20            .and_then(|v| v.as_str())
21            .and_then(|v| match v {
22                "true" | "1" | "yes" => Some(true),
23                "false" | "0" | "no" => Some(false),
24                _ => None,
25            })
26    })
27}
28
29/// Parse chat sync config. Returns `None` if disabled, `Err` if required but misconfigured.
30pub async fn parse_chat_sync_config(required: bool) -> Result<Option<ChatSyncConfig>, String> {
31    if !env_bool("CODETETHER_CHAT_SYNC_ENABLED").unwrap_or(false) {
32        if required {
33            return Err("CODETETHER_CHAT_SYNC_ENABLED must be true".into());
34        }
35        return Ok(None);
36    }
37    let vault = crate::secrets::get_provider_secrets("chat-sync-minio").await;
38    let ep_raw = env_non_empty("CODETETHER_CHAT_SYNC_MINIO_ENDPOINT")
39        .or_else(|| {
40            vault.as_ref().and_then(|s| {
41                s.base_url
42                    .clone()
43                    .filter(|v| !v.trim().is_empty())
44                    .or_else(|| vault_str(s, &["endpoint", "minio_endpoint"]))
45            })
46        })
47        .ok_or("CODETETHER_CHAT_SYNC_MINIO_ENDPOINT required")?;
48    let endpoint = normalize_minio_endpoint(&ep_raw);
49    let ak = env_non_placeholder("CODETETHER_CHAT_SYNC_MINIO_ACCESS_KEY")
50        .or_else(|| {
51            vault.as_ref().and_then(|s| {
52                vault_str(s, &["access_key", "api_key"]).or_else(|| {
53                    s.api_key
54                        .as_ref()
55                        .map(|v: &String| v.trim().to_string())
56                        .filter(|v: &String| !v.is_empty())
57                })
58            })
59        })
60        .ok_or("CODETETHER_CHAT_SYNC_MINIO_ACCESS_KEY required")?;
61    let sk = env_non_placeholder("CODETETHER_CHAT_SYNC_MINIO_SECRET_KEY")
62        .or_else(|| {
63            vault
64                .as_ref()
65                .and_then(|s| vault_str(s, &["secret_key", "password", "secret"]))
66        })
67        .ok_or("CODETETHER_CHAT_SYNC_MINIO_SECRET_KEY required")?;
68    let bucket = env_non_empty("CODETETHER_CHAT_SYNC_MINIO_BUCKET")
69        .or_else(|| vault.as_ref().and_then(|s| vault_str(s, &["bucket"])))
70        .unwrap_or_else(|| CHAT_SYNC_DEFAULT_BUCKET.into());
71    let prefix = env_non_empty("CODETETHER_CHAT_SYNC_MINIO_PREFIX")
72        .or_else(|| vault.as_ref().and_then(|s| vault_str(s, &["prefix"])))
73        .unwrap_or_else(|| CHAT_SYNC_DEFAULT_PREFIX.into())
74        .trim_matches('/')
75        .to_string();
76    let iv = env_non_empty("CODETETHER_CHAT_SYNC_INTERVAL_SECS")
77        .and_then(|v| v.parse::<u64>().ok())
78        .unwrap_or(CHAT_SYNC_DEFAULT_INTERVAL_SECS)
79        .clamp(1, CHAT_SYNC_MAX_INTERVAL_SECS);
80    let tls = env_bool("CODETETHER_CHAT_SYNC_MINIO_INSECURE_SKIP_TLS_VERIFY")
81        .or_else(|| {
82            vault
83                .as_ref()
84                .and_then(|s| vault_bool_val(s, &["insecure_skip_tls_verify"]))
85        })
86        .unwrap_or(false);
87    Ok(Some(ChatSyncConfig {
88        endpoint: endpoint.clone(),
89        fallback_endpoint: minio_fallback_endpoint(&endpoint),
90        access_key: ak,
91        secret_key: sk,
92        bucket,
93        prefix,
94        interval_secs: iv,
95        ignore_cert_check: tls,
96    }))
97}