Archived
109 lines
3.5 KiB
Rust
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
|
|
}
|
|
}
|