mint_rpc_cli.rs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. use std::path::PathBuf;
  2. use anyhow::{anyhow, Result};
  3. use cdk_mint_rpc::cdk_mint_client::CdkMintClient;
  4. use cdk_mint_rpc::mint_rpc_cli::subcommands;
  5. use cdk_mint_rpc::GetInfoRequest;
  6. use clap::{Parser, Subcommand};
  7. use tonic::transport::{Certificate, Channel, ClientTlsConfig, Identity};
  8. use tonic::Request;
  9. use tracing_subscriber::EnvFilter;
  10. /// Common CLI arguments for CDK binaries
  11. #[derive(Parser, Debug)]
  12. pub struct CommonArgs {
  13. /// Enable logging (default is false)
  14. #[arg(long, default_value_t = false)]
  15. pub enable_logging: bool,
  16. /// Logging level when enabled (default is debug)
  17. #[arg(long, default_value = "debug")]
  18. pub log_level: tracing::Level,
  19. }
  20. /// Initialize logging based on CLI arguments
  21. pub fn init_logging(enable_logging: bool, log_level: tracing::Level) {
  22. if enable_logging {
  23. let default_filter = log_level.to_string();
  24. // Common filters to reduce noise
  25. let sqlx_filter = "sqlx=warn";
  26. let hyper_filter = "hyper=warn";
  27. let h2_filter = "h2=warn";
  28. let rustls_filter = "rustls=warn";
  29. let reqwest_filter = "reqwest=warn";
  30. let env_filter = EnvFilter::new(format!(
  31. "{default_filter},{sqlx_filter},{hyper_filter},{h2_filter},{rustls_filter},{reqwest_filter}"
  32. ));
  33. // Ok if successful, Err if already initialized
  34. let _ = tracing_subscriber::fmt()
  35. .with_env_filter(env_filter)
  36. .try_init();
  37. }
  38. }
  39. const DEFAULT_WORK_DIR: &str = ".cdk-mint-rpc-cli";
  40. #[derive(Parser)]
  41. #[command(version, about, long_about = None)]
  42. struct Cli {
  43. #[command(flatten)]
  44. common: CommonArgs,
  45. /// Address of RPC server
  46. #[arg(short, long, default_value = "https://127.0.0.1:8086")]
  47. addr: String,
  48. /// Path to working dir
  49. #[arg(short, long)]
  50. work_dir: Option<PathBuf>,
  51. #[command(subcommand)]
  52. command: Commands,
  53. }
  54. #[derive(Subcommand)]
  55. enum Commands {
  56. /// Get info
  57. GetInfo,
  58. /// Update motd
  59. UpdateMotd(subcommands::UpdateMotdCommand),
  60. /// Update short description
  61. UpdateShortDescription(subcommands::UpdateShortDescriptionCommand),
  62. /// Update long description
  63. UpdateLongDescription(subcommands::UpdateLongDescriptionCommand),
  64. /// Update name
  65. UpdateName(subcommands::UpdateNameCommand),
  66. /// Update icon url
  67. UpdateIconUrl(subcommands::UpdateIconUrlCommand),
  68. /// Add Url
  69. AddUrl(subcommands::AddUrlCommand),
  70. /// Remove Url
  71. RemoveUrl(subcommands::RemoveUrlCommand),
  72. /// Add contact
  73. AddContact(subcommands::AddContactCommand),
  74. /// Remove contact
  75. RemoveContact(subcommands::RemoveContactCommand),
  76. /// Update nut04
  77. UpdateNut04(subcommands::UpdateNut04Command),
  78. /// Update nut05
  79. UpdateNut05(subcommands::UpdateNut05Command),
  80. /// Update quote ttl
  81. UpdateQuoteTtl(subcommands::UpdateQuoteTtlCommand),
  82. /// Get quote ttl
  83. GetQuoteTtl,
  84. /// Update Nut04 quote
  85. UpdateNut04QuoteState(subcommands::UpdateNut04QuoteCommand),
  86. /// Rotate next keyset
  87. RotateNextKeyset(subcommands::RotateNextKeysetCommand),
  88. }
  89. #[tokio::main]
  90. async fn main() -> Result<()> {
  91. let args: Cli = Cli::parse();
  92. // Initialize logging based on CLI arguments
  93. init_logging(args.common.enable_logging, args.common.log_level);
  94. let cli = Cli::parse();
  95. let work_dir = match &args.work_dir {
  96. Some(work_dir) => work_dir.clone(),
  97. None => {
  98. let home_dir = home::home_dir().ok_or(anyhow!("Could not find home dir"))?;
  99. home_dir.join(DEFAULT_WORK_DIR)
  100. }
  101. };
  102. std::fs::create_dir_all(&work_dir)?;
  103. tracing::debug!("Using work dir: {}", work_dir.display());
  104. let channel = if work_dir.join("tls").is_dir() {
  105. if rustls::crypto::CryptoProvider::get_default().is_none() {
  106. let _ = rustls::crypto::ring::default_provider().install_default();
  107. }
  108. // TLS directory exists, configure TLS
  109. let server_root_ca_cert = std::fs::read_to_string(work_dir.join("tls/ca.pem")).unwrap();
  110. let server_root_ca_cert = Certificate::from_pem(server_root_ca_cert);
  111. let client_cert = std::fs::read_to_string(work_dir.join("tls/client.pem"))?;
  112. let client_key = std::fs::read_to_string(work_dir.join("tls/client.key"))?;
  113. let client_identity = Identity::from_pem(client_cert, client_key);
  114. let tls = ClientTlsConfig::new()
  115. .ca_certificate(server_root_ca_cert)
  116. .identity(client_identity);
  117. Channel::from_shared(cli.addr.to_string())?
  118. .tls_config(tls)?
  119. .connect()
  120. .await?
  121. } else {
  122. // No TLS directory, skip TLS configuration
  123. Channel::from_shared(cli.addr.to_string())?
  124. .connect()
  125. .await?
  126. };
  127. let mut client = CdkMintClient::new(channel);
  128. match cli.command {
  129. Commands::GetInfo => {
  130. let response = client.get_info(Request::new(GetInfoRequest {})).await?;
  131. let info = response.into_inner();
  132. println!(
  133. "name: {}",
  134. info.name.unwrap_or("None".to_string())
  135. );
  136. println!(
  137. "version: {}",
  138. info.version.unwrap_or("None".to_string())
  139. );
  140. println!(
  141. "description: {}",
  142. info.description.unwrap_or("None".to_string())
  143. );
  144. println!(
  145. "long description: {}",
  146. info.long_description.unwrap_or("None".to_string())
  147. );
  148. println!("motd: {}", info.motd.unwrap_or("None".to_string()));
  149. println!("icon_url: {}", info.icon_url.unwrap_or("None".to_string()));
  150. for url in info.urls {
  151. println!("mint_url: {url}");
  152. }
  153. for contact in info.contact {
  154. println!("method: {}, info: {}", contact.method, contact.info);
  155. }
  156. println!("total issued: {} sat", info.total_issued);
  157. println!("total redeemed: {} sat", info.total_redeemed);
  158. }
  159. Commands::UpdateMotd(sub_command_args) => {
  160. subcommands::update_motd(&mut client, &sub_command_args).await?;
  161. }
  162. Commands::UpdateShortDescription(sub_command_args) => {
  163. subcommands::update_short_description(&mut client, &sub_command_args).await?;
  164. }
  165. Commands::UpdateLongDescription(sub_command_args) => {
  166. subcommands::update_long_description(&mut client, &sub_command_args).await?;
  167. }
  168. Commands::UpdateName(sub_command_args) => {
  169. subcommands::update_name(&mut client, &sub_command_args).await?;
  170. }
  171. Commands::UpdateIconUrl(sub_command_args) => {
  172. subcommands::update_icon_url(&mut client, &sub_command_args).await?;
  173. }
  174. Commands::AddUrl(sub_command_args) => {
  175. subcommands::add_url(&mut client, &sub_command_args).await?;
  176. }
  177. Commands::RemoveUrl(sub_command_args) => {
  178. subcommands::remove_url(&mut client, &sub_command_args).await?;
  179. }
  180. Commands::AddContact(sub_command_args) => {
  181. subcommands::add_contact(&mut client, &sub_command_args).await?;
  182. }
  183. Commands::RemoveContact(sub_command_args) => {
  184. subcommands::remove_contact(&mut client, &sub_command_args).await?;
  185. }
  186. Commands::UpdateNut04(sub_command_args) => {
  187. subcommands::update_nut04(&mut client, &sub_command_args).await?;
  188. }
  189. Commands::UpdateNut05(sub_command_args) => {
  190. subcommands::update_nut05(&mut client, &sub_command_args).await?;
  191. }
  192. Commands::GetQuoteTtl => {
  193. subcommands::get_quote_ttl(&mut client).await?;
  194. }
  195. Commands::UpdateQuoteTtl(sub_command_args) => {
  196. subcommands::update_quote_ttl(&mut client, &sub_command_args).await?;
  197. }
  198. Commands::UpdateNut04QuoteState(sub_command_args) => {
  199. subcommands::update_nut04_quote_state(&mut client, &sub_command_args).await?;
  200. }
  201. Commands::RotateNextKeyset(sub_command_args) => {
  202. subcommands::rotate_next_keyset(&mut client, &sub_command_args).await?;
  203. }
  204. }
  205. Ok(())
  206. }