use crate::client::build_client;
use crate::config::app::AppConfig;
use crate::config::turnkey;
use anyhow::{Context, Result};
use clap::Args as ClapArgs;
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};
use turnkey_client::generated::{CreateTvcAppIntent, TvcOperatorParams, TvcOperatorSetParams};
#[derive(Debug, ClapArgs)]
#[command(about, long_about = None)]
pub struct Args {
pub config_file: PathBuf,
}
pub async fn run(args: Args) -> Result<()> {
let config_content = std::fs::read_to_string(&args.config_file)
.with_context(|| format!("failed to read config file: {}", args.config_file.display()))?;
let app_config: AppConfig = serde_json::from_str(&config_content).with_context(|| {
format!(
"failed to parse config file: {}",
args.config_file.display()
)
})?;
if app_config.has_placeholders() {
anyhow::bail!(
"Config file contains placeholder values (<FILL_IN_...>). \
Please edit {} and fill in all required values.",
args.config_file.display()
);
}
if app_config.manifest_set_id.is_some() && app_config.manifest_set_params.is_some() {
anyhow::bail!("Cannot specify both manifestSetId and manifestSetParams");
}
if app_config.manifest_set_id.is_none() && app_config.manifest_set_params.is_none() {
anyhow::bail!("Must specify either manifestSetId or manifestSetParams");
}
println!("Creating app '{}'...", app_config.name);
let auth = build_client().await?;
let intent = CreateTvcAppIntent {
name: app_config.name.clone(),
quorum_public_key: app_config.quorum_public_key.clone(),
manifest_set_id: app_config.manifest_set_id.clone(),
manifest_set_params: app_config.manifest_set_params.as_ref().map(|p| {
TvcOperatorSetParams {
name: p.name.clone(),
threshold: p.threshold,
new_operators: p
.new_operators
.iter()
.map(|o| TvcOperatorParams {
name: o.name.clone(),
public_key: o.public_key.clone(),
})
.collect(),
existing_operator_ids: p.existing_operator_ids.clone(),
}
}),
share_set_id: None,
share_set_params: {
let p = AppConfig::share_set_params();
Some(TvcOperatorSetParams {
name: p.name,
threshold: p.threshold,
new_operators: p
.new_operators
.into_iter()
.map(|o| TvcOperatorParams {
name: o.name,
public_key: o.public_key,
})
.collect(),
existing_operator_ids: p.existing_operator_ids,
})
},
enable_egress: app_config.external_connectivity,
};
let timestamp_ms = SystemTime::now()
.duration_since(UNIX_EPOCH)
.context("system time before unix epoch")?
.as_millis();
let result = auth
.client
.create_tvc_app(auth.org_id, timestamp_ms, intent)
.await
.context("failed to create TVC app")?;
let app_id = result.result.app_id;
let operator_ids = result.result.manifest_set_operator_ids;
let mut config = turnkey::Config::load().await?;
config.set_last_app_id(&app_id)?;
config.set_last_operator_ids(&operator_ids)?;
config.save().await?;
println!();
println!("App created successfully!");
println!();
println!("App ID: {app_id}");
println!("Name: {}", app_config.name);
println!("Manifest Set ID: {}", result.result.manifest_set_id);
if !operator_ids.is_empty() {
println!("Manifest Set Operator IDs: {}", operator_ids.join(", "));
}
println!("Config: {}", args.config_file.display());
println!();
println!(
"Use one of the Manifest Set Operator IDs above with `tvc deploy approve --operator-id`"
);
Ok(())
}