1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
use std::net::{Ipv6Addr, SocketAddr};
use std::path::PathBuf;
use anyhow::{bail, Result};
use aws_config::meta::region::RegionProviderChain;
use aws_sdk_s3::Client;
use aws_types::sdk_config::SdkConfig;
use clap::Parser;
use tracing::info;
use crate::server::resolver::{AwsSecrets, ResolverChain};
use crate::server::{server, state::State, storage::Storage};
pub async fn default_aws_config() -> SdkConfig {
let region_provider = RegionProviderChain::default_provider();
aws_config::from_env().region(region_provider).load().await
}
#[derive(Parser, Debug)]
#[clap(version, about, long_about = None)]
pub struct Args {
#[clap(short, long, default_value_t = 7608, env = "CADRE_PORT")]
port: u16,
#[clap(long, env = "CADRE_SECRET")]
secret: String,
#[clap(long, env = "CADRE_BUCKET")]
bucket: Option<String>,
#[clap(long, parse(from_os_str), env = "CADRE_LOCAL_DIR")]
local_dir: Option<PathBuf>,
#[clap(long, env = "CADRE_DEFAULT_TEMPLATE")]
default_template: Option<String>,
}
impl Args {
pub async fn run(self) -> Result<()> {
let sdk_config = default_aws_config().await;
let mut chain = ResolverChain::new();
chain.add(AwsSecrets::new(&sdk_config));
let storage = match (&self.bucket, &self.local_dir) {
(Some(bucket), None) => Storage::S3(Client::new(&sdk_config), bucket.into()),
(None, Some(local_dir)) => Storage::LocalFS(local_dir.into()),
_ => bail!("must specify exactly one of --bucket or --local-dir"),
};
let logged_secret = self.secret.clone();
let state = State::new(chain, storage, self.default_template.as_deref());
let app = server(state, self.secret);
let addr: SocketAddr = (Ipv6Addr::UNSPECIFIED, self.port).into();
info!(?addr, "running cadre");
info!("visit frontend at {}?secret={}", addr, logged_secret);
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await?;
Ok(())
}
}