mod config;
mod crypto;
mod encoding;
mod error;
mod proxy;
mod server;
use camo::{CamoUrl, Encoding};
use clap::Parser;
use config::{Cli, Command};
use proxy::ProxyClient;
use server::{create_router, AppState};
use std::sync::Arc;
use tracing::info;
use tracing_subscriber::EnvFilter;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let cli = Cli::parse();
match &cli.command {
Some(Command::Sign { url, base, base64 }) => {
let key = cli
.key
.as_ref()
.ok_or_else(|| anyhow::anyhow!("CAMO_KEY is required for signing"))?;
let camo = CamoUrl::new(key).with_encoding(if *base64 {
Encoding::Base64
} else {
Encoding::Hex
});
let signed = camo.sign(url);
if base.is_empty() {
println!("Digest: {}", signed.digest);
println!("Encoded URL: {}", signed.encoded_url);
println!("Path: {}", signed.to_path());
} else {
println!("{}", signed.to_url(base));
}
}
Some(Command::Serve) | None => {
let key = cli
.key
.clone()
.ok_or_else(|| anyhow::anyhow!("CAMO_KEY is required"))?;
tracing_subscriber::fmt()
.with_env_filter(
EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new(&cli.log_level)),
)
.init();
if cli.metrics {
let builder = metrics_exporter_prometheus::PrometheusBuilder::new();
builder
.install()
.expect("Failed to install Prometheus recorder");
}
let listen = cli.listen.clone();
let config = Arc::new(Cli {
key: Some(key),
..cli
});
let proxy = ProxyClient::new(config.clone());
let state = Arc::new(AppState { config, proxy });
let app = create_router(state);
let listener = tokio::net::TcpListener::bind(&listen).await?;
info!("camo-rs listening on {}", listen);
axum::serve(listener, app).await?;
}
}
Ok(())
}