wasmer_cli/commands/app/cdn/
status.rs1use bytesize::ByteSize;
2use comfy_table::{Cell, Table};
3use time::{Duration, OffsetDateTime};
4
5use super::{AppIdentOpts, WasmerEnv};
6use crate::{
7 commands::AsyncCliCommand,
8 opts::ItemFormatOpts,
9 utils::render::{CliRender, ListFormat},
10};
11
12#[derive(clap::Parser, Debug)]
14pub struct CmdAppCdnStatus {
15 #[clap(flatten)]
16 pub env: WasmerEnv,
17
18 #[clap(flatten)]
19 pub fmt: ItemFormatOpts,
20
21 #[clap(flatten)]
22 pub ident: AppIdentOpts,
23
24 #[clap(long)]
26 pub with_metrics: bool,
27}
28
29#[async_trait::async_trait]
30impl AsyncCliCommand for CmdAppCdnStatus {
31 type Output = ();
32
33 async fn run_async(self) -> Result<(), anyhow::Error> {
34 let client = self.env.client()?;
35 let (_ident, app) = self.ident.load_app(&client).await?;
36 let cdn_status = wasmer_backend_api::query::app_cdn_cache_status(
37 &client,
38 wasmer_backend_api::types::GetAppCdnCacheStatusVars {
39 app: app.id.clone(),
40 },
41 )
42 .await?;
43
44 let metrics = if self.with_metrics {
45 let end_at = OffsetDateTime::now_utc();
46 let start_at = end_at - Duration::days(30);
47 let metrics = wasmer_backend_api::query::app_cdn_cache_metrics(
48 &client,
49 wasmer_backend_api::types::GetAppCdnCacheMetricsVars {
50 app: app.id.clone(),
51 start_at: start_at.try_into()?,
52 end_at: end_at.try_into()?,
53 grouped_by: wasmer_backend_api::types::MetricGrouping::ByDay,
54 },
55 )
56 .await?;
57 Some(CdnCacheMetricsStatus::from(metrics))
58 } else {
59 None
60 };
61
62 let status = CdnCacheStatus {
63 app: app.name,
64 owner: app.owner.global_name,
65 enabled: cdn_status.cdn_cache_enabled,
66 last_purged_at: cdn_status.cdn_cache_purged_at.map(|dt| dt.0),
67 metrics,
68 };
69
70 println!("{}", self.fmt.get().render(&status));
71
72 Ok(())
73 }
74}
75
76#[derive(Debug, serde::Serialize)]
77struct CdnCacheStatus {
78 app: String,
79 owner: String,
80 enabled: bool,
81 last_purged_at: Option<String>,
82 #[serde(skip_serializing_if = "Option::is_none")]
83 metrics: Option<CdnCacheMetricsStatus>,
84}
85
86#[derive(Debug, serde::Serialize)]
87struct CdnCacheMetricsStatus {
88 cached_requests: i64,
89 total_requests: i64,
90 cached_network_egress_bytes: i64,
91 total_network_egress_bytes: i64,
92}
93
94impl From<wasmer_backend_api::types::AppCdnCacheMetrics> for CdnCacheMetricsStatus {
95 fn from(value: wasmer_backend_api::types::AppCdnCacheMetrics) -> Self {
96 let requests = value.grouped_metrics.totals.requests;
97 Self {
98 cached_requests: requests.cached_requests.0,
99 total_requests: requests.total_requests.0,
100 cached_network_egress_bytes: requests.data_cached_bytes.0,
101 total_network_egress_bytes: requests.data_served_bytes.0,
102 }
103 }
104}
105
106impl CliRender for CdnCacheStatus {
107 fn render_item_table(&self) -> String {
108 let mut table = Table::new();
109 table.load_preset(comfy_table::presets::NOTHING);
110 table.set_content_arrangement(comfy_table::ContentArrangement::Dynamic);
111 table.set_header(vec![
112 Cell::new("App"),
113 Cell::new("Owner"),
114 Cell::new("Status"),
115 Cell::new("Last purge"),
116 Cell::new("Cached requests"),
117 Cell::new("Cached egress"),
118 ]);
119
120 let (cached_requests, cached_egress) = match &self.metrics {
121 Some(metrics) => (
122 format!("{} / {}", metrics.cached_requests, metrics.total_requests),
123 format!(
124 "{} / {}",
125 ByteSize(metrics.cached_network_egress_bytes as u64),
126 ByteSize(metrics.total_network_egress_bytes as u64)
127 ),
128 ),
129 None => ("-".to_string(), "-".to_string()),
130 };
131
132 table.add_row(vec![
133 Cell::new(&self.app),
134 Cell::new(&self.owner),
135 Cell::new(if self.enabled { "enabled" } else { "disabled" }),
136 Cell::new(self.last_purged_at.as_deref().unwrap_or("-")),
137 Cell::new(cached_requests),
138 Cell::new(cached_egress),
139 ]);
140
141 table.to_string()
142 }
143
144 fn render_list(_items: &[Self], _format: ListFormat) -> String {
145 unreachable!("CDN cache status is rendered as a single item")
146 }
147
148 fn render_list_table(items: &[Self]) -> String {
149 items
150 .iter()
151 .map(Self::render_item_table)
152 .collect::<Vec<_>>()
153 .join("\n\n")
154 }
155}