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