1pub mod whoami;
12
13use reqwest::blocking::{Client, RequestBuilder, Response};
14use reqwest::header::AUTHORIZATION;
15use std::time::Duration;
16
17use crate::auth::ApiKey;
18use crate::error::{Result, ToriiError};
19
20const UA: &str = concat!("torii/", env!("CARGO_PKG_VERSION"));
21
22pub struct CloudClient {
23 http: Client,
24 key: ApiKey,
25}
26
27impl CloudClient {
28 pub fn new(key: ApiKey) -> Self {
29 let http = Client::builder()
30 .user_agent(UA)
31 .timeout(Duration::from_secs(15))
32 .build()
33 .expect("reqwest client builds");
34 Self { http, key }
35 }
36
37 pub fn endpoint(&self) -> &str {
38 &self.key.endpoint
39 }
40
41 fn url(&self, path: &str) -> String {
42 format!("{}{}", self.key.endpoint.trim_end_matches('/'), path)
43 }
44
45 fn get(&self, path: &str) -> RequestBuilder {
46 self.http
47 .get(self.url(path))
48 .header(AUTHORIZATION, format!("Bearer {}", self.key.key))
49 }
50
51 #[allow(dead_code)] fn post(&self, path: &str) -> RequestBuilder {
53 self.http
54 .post(self.url(path))
55 .header(AUTHORIZATION, format!("Bearer {}", self.key.key))
56 }
57}
58
59pub(crate) fn check_status(resp: Response) -> Result<Response> {
62 let status = resp.status();
63 if status.is_success() {
64 return Ok(resp);
65 }
66 let body = resp.text().unwrap_or_default();
67 let msg = match status.as_u16() {
68 401 => "API key rejected. Run `torii auth login` to refresh.".to_string(),
69 402 => format!(
70 "your plan does not include this feature. Upgrade at https://gitorii.com/upgrade ({})",
71 short_body(&body)
72 ),
73 403 => "organization suspended. Contact support@gitorii.com.".to_string(),
74 404 => "endpoint not found — CLI may be outdated.".to_string(),
75 s if (500..=599).contains(&s) => format!("server error {}: {}", s, short_body(&body)),
76 s => format!("unexpected HTTP {}: {}", s, short_body(&body)),
77 };
78 Err(ToriiError::PlatformApi { provider: "gitorii.com".into(), status: status.as_u16(), message: msg })
79}
80
81fn short_body(body: &str) -> String {
82 let trimmed = body.trim();
83 if trimmed.chars().count() > 200 {
86 let head: String = trimmed.chars().take(200).collect();
87 format!("{}…", head)
88 } else {
89 trimmed.to_string()
90 }
91}