This repository has been archived on 2026-08-20. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
el-retired/ui/crates/el-services/src/config.rs
T

109 lines
3.5 KiB
Rust

//! Service configuration — reflects `[services.*]` in `el.toml`.
use crate::binding::BindingKind;
/// Authentication configuration for a service binding.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AuthConfig {
/// No authentication.
None,
/// Bearer token (set in environment variable or injected at runtime).
Bearer { token_env: String },
/// Basic auth.
Basic { username_env: String, password_env: String },
/// API key in header.
ApiKey { header: String, key_env: String },
}
impl AuthConfig {
/// Produce the HTTP Authorization header value for this auth config.
/// Returns `None` if auth is `None` or the env var is not set.
pub fn authorization_header(&self) -> Option<String> {
match self {
Self::None => None,
Self::Bearer { token_env } => {
let token = std::env::var(token_env).ok()?;
Some(format!("Bearer {}", token))
}
Self::Basic { username_env, password_env } => {
let user = std::env::var(username_env).ok()?;
let pass = std::env::var(password_env).ok()?;
// Base64 encode user:pass
let raw = format!("{}:{}", user, pass);
let encoded = base64_encode(raw.as_bytes());
Some(format!("Basic {}", encoded))
}
Self::ApiKey { .. } => None,
}
}
/// Produce custom header key/value for API key auth.
pub fn api_key_header(&self) -> Option<(String, String)> {
match self {
Self::ApiKey { header, key_env } => {
let key = std::env::var(key_env).ok()?;
Some((header.clone(), key))
}
_ => None,
}
}
}
fn base64_encode(input: &[u8]) -> String {
const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let mut out = String::new();
for chunk in input.chunks(3) {
let b0 = chunk[0] as u32;
let b1 = if chunk.len() > 1 { chunk[1] as u32 } else { 0 };
let b2 = if chunk.len() > 2 { chunk[2] as u32 } else { 0 };
let n = (b0 << 16) | (b1 << 8) | b2;
out.push(CHARS[((n >> 18) & 63) as usize] as char);
out.push(CHARS[((n >> 12) & 63) as usize] as char);
out.push(if chunk.len() > 1 { CHARS[((n >> 6) & 63) as usize] as char } else { '=' });
out.push(if chunk.len() > 2 { CHARS[(n & 63) as usize] as char } else { '=' });
}
out
}
/// Full configuration for one service, from `[services.ServiceName]`.
#[derive(Debug, Clone)]
pub struct ServiceConfig {
/// Service name (matches the `service Name { ... }` declaration).
pub name: String,
/// The binding protocol.
pub binding: BindingKind,
/// Base URL (for REST/WebSocket/gRPC).
pub base_url: Option<String>,
/// Auth configuration.
pub auth: AuthConfig,
/// Connection timeout in milliseconds.
pub timeout_ms: u64,
}
impl ServiceConfig {
pub fn new(name: impl Into<String>, binding: BindingKind) -> Self {
Self {
name: name.into(),
binding,
base_url: None,
auth: AuthConfig::None,
timeout_ms: 30_000,
}
}
pub fn with_base_url(mut self, url: impl Into<String>) -> Self {
self.base_url = Some(url.into());
self
}
pub fn with_auth(mut self, auth: AuthConfig) -> Self {
self.auth = auth;
self
}
pub fn with_timeout(mut self, ms: u64) -> Self {
self.timeout_ms = ms;
self
}
}