mz_environmentd/http/
console.rs1use std::collections::BTreeMap;
13use std::sync::{Arc, LazyLock};
14
15use axum::Extension;
16use axum::Json;
17use axum::body::Body;
18use axum::http::{Request, StatusCode};
19use axum::response::{IntoResponse, Response};
20use http::HeaderValue;
21use http::header::HOST;
22use hyper::Uri;
23use hyper_tls::HttpsConnector;
24use hyper_util::client::legacy::Client;
25use hyper_util::client::legacy::connect::HttpConnector;
26use hyper_util::rt::TokioExecutor;
27use mz_adapter_types::dyncfgs::{CONSOLE_OIDC_CLIENT_ID, CONSOLE_OIDC_SCOPES, OIDC_ISSUER};
28
29use crate::http::Delayed;
30
31pub(crate) struct ConsoleProxyConfig {
32 client: Client<HttpsConnector<HttpConnector>, Body>,
34
35 url: String,
37
38 route_prefix: String,
40}
41
42impl ConsoleProxyConfig {
43 pub(crate) fn new(proxy_url: Option<String>, route_prefix: String) -> Self {
44 let mut url = proxy_url.unwrap_or_else(|| "https://console.materialize.com".to_string());
45 if let Some(new) = url.strip_suffix('/') {
46 url = new.to_string();
47 }
48 Self {
49 client: Client::builder(TokioExecutor::new()).build(HttpsConnector::new()),
50 url,
51 route_prefix,
52 }
53 }
54}
55
56static CONSOLE_CONFIG_VAR_NAMES: LazyLock<[&'static str; 3]> = LazyLock::new(|| {
58 [
59 OIDC_ISSUER.name(),
60 CONSOLE_OIDC_CLIENT_ID.name(),
61 CONSOLE_OIDC_SCOPES.name(),
62 ]
63});
64
65pub async fn handle_console_config(
68 Extension(adapter_client_rx): Extension<Delayed<mz_adapter::Client>>,
69) -> Result<Response, (StatusCode, String)> {
70 let adapter_client = adapter_client_rx.await.map_err(|_| {
71 (
72 StatusCode::INTERNAL_SERVER_ERROR,
73 "Adapter client unavailable".to_string(),
74 )
75 })?;
76
77 let system_vars = adapter_client.get_system_vars().await;
78 let mut config: BTreeMap<&str, String> = BTreeMap::new();
79 for var_name in CONSOLE_CONFIG_VAR_NAMES.iter() {
80 let value = system_vars.get(var_name).map(|v| v.value()).map_err(|_| {
81 (
82 StatusCode::INTERNAL_SERVER_ERROR,
83 format!("failed to retrieve system variable {var_name}"),
84 )
85 })?;
86 config.insert(var_name, value);
87 }
88
89 Ok((StatusCode::OK, Json(config)).into_response())
90}
91
92pub(crate) async fn handle_internal_console(
99 console_config: Extension<Arc<ConsoleProxyConfig>>,
100 mut req: Request<Body>,
101) -> Result<Response, StatusCode> {
102 let path = req.uri().path();
103 let mut path_query = req
104 .uri()
105 .path_and_query()
106 .map(|v| v.as_str())
107 .unwrap_or(path);
108 if let Some(stripped_path_query) = path_query.strip_prefix(&console_config.route_prefix) {
109 path_query = stripped_path_query;
110 }
111
112 let uri = Uri::try_from(format!("{}{}", &console_config.url, path_query)).unwrap();
113 let host = uri.host().unwrap().to_string();
114 *req.uri_mut() = uri;
116
117 req.headers_mut()
119 .insert(HOST, HeaderValue::from_str(&host).unwrap());
120
121 Ok(console_config
123 .client
124 .request(req)
125 .await
126 .map_err(|err| {
127 tracing::warn!("Error retrieving console url: {}", err);
128 StatusCode::BAD_REQUEST
129 })?
130 .into_response())
131}