auths_cli/commands/
account.rs1use anyhow::Result;
2use clap::{Parser, Subcommand};
3use serde::Deserialize;
4
5use super::executable::ExecutableCommand;
6use crate::config::CliConfig;
7
8#[derive(Parser, Debug, Clone)]
10pub struct AccountCommand {
11 #[clap(subcommand)]
12 pub subcommand: AccountSubcommand,
13}
14
15#[derive(Subcommand, Debug, Clone)]
16pub enum AccountSubcommand {
17 Status {
19 #[arg(long, default_value = "https://registry.auths.dev")]
21 registry_url: String,
22 },
23 Usage {
25 #[arg(long, default_value = "https://registry.auths.dev")]
27 registry_url: String,
28 #[arg(long, default_value = "7")]
30 days: u32,
31 },
32}
33
34#[derive(Debug, Deserialize)]
35struct AccountStatusResponse {
36 did: String,
37 tier: String,
38 daily_limit: i32,
39 daily_used: i32,
40 expires_at: Option<String>,
41}
42
43#[derive(Debug, Deserialize)]
44struct UsageEntry {
45 date: String,
46 request_count: i32,
47}
48
49fn handle_status(registry_url: &str) -> Result<()> {
50 let url = registry_url.trim_end_matches('/');
51
52 println!("Fetching account status...");
53
54 let client = reqwest::blocking::Client::new();
55 let resp = client
56 .get(format!("{url}/v1/account/status"))
57 .send()
58 .map_err(|e| anyhow::anyhow!("Failed to fetch account status: {e}"))?;
59
60 if !resp.status().is_success() {
61 return Err(anyhow::anyhow!("Registry returned {}", resp.status()));
62 }
63
64 let status: AccountStatusResponse = resp
65 .json()
66 .map_err(|e| anyhow::anyhow!("Failed to parse response: {e}"))?;
67
68 println!("\nAccount Status:");
69 println!(" DID: {}", status.did);
70 println!(" Tier: {}", status.tier);
71 println!(" Daily Limit: {}", status.daily_limit);
72 println!(" Daily Used: {}", status.daily_used);
73 if let Some(expires) = status.expires_at {
74 println!(" Expires: {expires}");
75 }
76
77 Ok(())
78}
79
80fn handle_usage(registry_url: &str, days: u32) -> Result<()> {
81 let url = registry_url.trim_end_matches('/');
82
83 println!("Fetching usage history ({days} days)...");
84
85 let client = reqwest::blocking::Client::new();
86 let resp = client
87 .get(format!("{url}/v1/account/usage?days={days}"))
88 .send()
89 .map_err(|e| anyhow::anyhow!("Failed to fetch usage: {e}"))?;
90
91 if !resp.status().is_success() {
92 return Err(anyhow::anyhow!("Registry returned {}", resp.status()));
93 }
94
95 let entries: Vec<UsageEntry> = resp
96 .json()
97 .map_err(|e| anyhow::anyhow!("Failed to parse response: {e}"))?;
98
99 if entries.is_empty() {
100 println!("\nNo usage data found.");
101 return Ok(());
102 }
103
104 println!("\nUsage History:");
105 for entry in &entries {
106 println!(" {} -- {} requests", entry.date, entry.request_count);
107 }
108
109 Ok(())
110}
111
112impl ExecutableCommand for AccountCommand {
113 fn execute(&self, _ctx: &CliConfig) -> Result<()> {
114 match &self.subcommand {
115 AccountSubcommand::Status { registry_url } => handle_status(registry_url),
116 AccountSubcommand::Usage { registry_url, days } => handle_usage(registry_url, *days),
117 }
118 }
119}