fn0-worker 0.3.16

Worker binary for the fn0 FaaS platform
use aes_gcm::{
    Aes256Gcm, Nonce,
    aead::{Aead, KeyInit},
};
use base64::Engine;
use color_eyre::eyre::{Result, eyre};

pub fn decode_key_base64(key_base64: &str) -> Result<[u8; 32]> {
    let bytes = base64::engine::general_purpose::STANDARD
        .decode(key_base64)
        .map_err(|e| eyre!("FN0_ENV_KEY_BASE64 is not valid base64: {e}"))?;
    let arr: [u8; 32] = bytes.try_into().map_err(|v: Vec<u8>| {
        eyre!(
            "FN0_ENV_KEY_BASE64 must decode to 32 bytes, got {}",
            v.len()
        )
    })?;
    Ok(arr)
}

pub fn decrypt(key: &[u8; 32], blob: &[u8]) -> Result<Vec<u8>> {
    if blob.len() < 12 {
        return Err(eyre!("env ciphertext too short"));
    }
    let (nonce_bytes, ciphertext) = blob.split_at(12);
    let cipher = Aes256Gcm::new(key.into());
    let nonce = Nonce::from_slice(nonce_bytes);
    cipher
        .decrypt(nonce, ciphertext)
        .map_err(|e| eyre!("env decryption failed: {e}"))
}

pub fn parse_env_file(content: &str) -> Vec<(String, String)> {
    let mut vars = Vec::new();
    for line in content.lines() {
        let line = line.trim();
        if line.is_empty() || line.starts_with('#') {
            continue;
        }
        if let Some((key, value)) = line.split_once('=') {
            vars.push((key.trim().to_string(), value.trim().to_string()));
        }
    }
    vars
}