mint_rpc_cli.rs 8.1 KB

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