1pub mod admin;
4pub mod handler;
5pub mod protocol;
6
7mod prelude;
8
9use std::collections::HashMap;
10use std::sync::Arc;
11use tokio::sync::RwLock;
12use url::Url;
13
14use rustls::sign::CertifiedKey;
15use rustls_pki_types::{pem::PemObject, CertificateDer, PrivateKeyDer};
16
17use crate::prelude::*;
18use cloudillo_types::auth_adapter::ProxySiteConfig;
19
20#[derive(Debug)]
22pub struct ProxySiteEntry {
23 pub site_id: i64,
24 pub domain: Box<str>,
25 pub proxy_type: Box<str>,
26 pub backend_url: Url,
27 pub config: ProxySiteConfig,
28}
29
30pub type ProxySiteCache = Arc<RwLock<HashMap<Box<str>, Arc<ProxySiteEntry>>>>;
32
33pub fn new_proxy_cache() -> ProxySiteCache {
35 Arc::new(RwLock::new(HashMap::new()))
36}
37
38fn build_certified_key(cert_pem: &str, key_pem: &str) -> Option<CertifiedKey> {
40 let certs: Vec<CertificateDer<'static>> = CertificateDer::pem_slice_iter(cert_pem.as_bytes())
41 .filter_map(Result::ok)
42 .collect();
43 let key = PrivateKeyDer::from_pem_slice(key_pem.as_bytes()).ok()?;
44 let provider = rustls::crypto::CryptoProvider::get_default()?;
45 CertifiedKey::from_der(certs, key, provider).ok()
46}
47
48pub async fn reload_proxy_cache(app: &App) -> ClResult<()> {
50 let sites = app.auth_adapter.list_proxy_sites().await?;
51 let proxy_sites = app.ext::<ProxySiteCache>()?;
52 let mut cache = proxy_sites.write().await;
53 cache.clear();
54
55 if let Ok(mut cert_cache) = app.certs.write() {
57 for site in &sites {
58 if site.status.as_ref() == "D" {
59 continue;
60 }
61 if let (Some(cert_pem), Some(key_pem)) = (site.cert.as_ref(), site.cert_key.as_ref()) {
62 if let Some(certified_key) = build_certified_key(cert_pem, key_pem) {
63 cert_cache.insert(site.domain.clone(), Arc::new(certified_key));
64 }
65 }
66 }
67 }
68
69 for site in sites {
70 if site.status.as_ref() == "D" {
71 continue;
72 }
73 let url = Url::parse(&site.backend_url).map_err(|e| {
74 warn!("Invalid backend URL for proxy site {}: {}", site.domain, e);
75 Error::ValidationError(format!("invalid backend URL: {}", e))
76 })?;
77 let entry = Arc::new(ProxySiteEntry {
78 site_id: site.site_id,
79 domain: site.domain.clone(),
80 proxy_type: site.proxy_type,
81 backend_url: url,
82 config: site.config,
83 });
84 cache.insert(site.domain, entry);
85 }
86
87 info!("Proxy cache reloaded: {} active sites", cache.len());
88 Ok(())
89}
90
91