feat: async SunbeamClient factory with unified auth resolution
SunbeamClient accessors are now async and resolve auth per-client:
- SSO bearer (get_token) for admin APIs, Matrix, La Suite, OpenSearch
- Gitea PAT (get_gitea_token) for VCS
- None for Prometheus, Loki, S3, LiveKit
Fixes client URLs to match deployed routes: hydra→hydra.{domain},
matrix→messages.{domain}, grafana→metrics.{domain},
prometheus→systemmetrics.{domain}, loki→systemlogs.{domain}.
Removes all ad-hoc token helpers from CLI modules (matrix_with_token,
os_client, people_client, etc). Every dispatch just calls
client.service().await?.
This commit is contained in:
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -3469,7 +3469,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sunbeam"
|
name = "sunbeam"
|
||||||
version = "1.0.0"
|
version = "1.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
@@ -3482,7 +3482,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sunbeam-sdk"
|
name = "sunbeam-sdk"
|
||||||
version = "1.0.0"
|
version = "1.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ impl ServiceClient for HydraClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl HydraClient {
|
impl HydraClient {
|
||||||
/// Build a HydraClient from domain (e.g. `https://auth.{domain}`).
|
/// Build a HydraClient from domain (e.g. `https://hydra.{domain}`).
|
||||||
pub fn connect(domain: &str) -> Self {
|
pub fn connect(domain: &str) -> Self {
|
||||||
let base_url = format!("https://auth.{domain}");
|
let base_url = format!("https://hydra.{domain}");
|
||||||
Self::from_parts(base_url, AuthMethod::None)
|
Self::from_parts(base_url, AuthMethod::None)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,7 +467,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_connect_url() {
|
fn test_connect_url() {
|
||||||
let c = HydraClient::connect("sunbeam.pt");
|
let c = HydraClient::connect("sunbeam.pt");
|
||||||
assert_eq!(c.base_url(), "https://auth.sunbeam.pt");
|
assert_eq!(c.base_url(), "https://hydra.sunbeam.pt");
|
||||||
assert_eq!(c.service_name(), "hydra");
|
assert_eq!(c.service_name(), "hydra");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
use crate::error::{Result, ResultExt, SunbeamError};
|
use crate::error::{Result, ResultExt, SunbeamError};
|
||||||
use reqwest::Method;
|
use reqwest::Method;
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
use std::sync::OnceLock;
|
use tokio::sync::OnceCell;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// AuthMethod
|
// AuthMethod
|
||||||
@@ -222,51 +222,51 @@ impl HttpTransport {
|
|||||||
/// Unified entry point for all service clients.
|
/// Unified entry point for all service clients.
|
||||||
///
|
///
|
||||||
/// Lazily constructs and caches per-service clients from the active config
|
/// Lazily constructs and caches per-service clients from the active config
|
||||||
/// context. Each accessor returns a `&Client` reference, constructing on
|
/// context. Each accessor resolves auth and returns a `&Client` reference,
|
||||||
/// first call via [`OnceLock`].
|
/// constructing on first call via [`OnceCell`] (async-aware).
|
||||||
|
///
|
||||||
|
/// Auth is resolved per-client:
|
||||||
|
/// - SSO bearer (`get_token()`) — admin APIs, Matrix, La Suite, OpenSearch
|
||||||
|
/// - Gitea PAT (`get_gitea_token()`) — Gitea
|
||||||
|
/// - None — Prometheus, Loki, S3, LiveKit
|
||||||
pub struct SunbeamClient {
|
pub struct SunbeamClient {
|
||||||
ctx: crate::config::Context,
|
ctx: crate::config::Context,
|
||||||
domain: String,
|
domain: String,
|
||||||
// Phase 1
|
|
||||||
#[cfg(feature = "identity")]
|
#[cfg(feature = "identity")]
|
||||||
kratos: OnceLock<crate::identity::KratosClient>,
|
kratos: OnceCell<crate::identity::KratosClient>,
|
||||||
#[cfg(feature = "identity")]
|
#[cfg(feature = "identity")]
|
||||||
hydra: OnceLock<crate::auth::hydra::HydraClient>,
|
hydra: OnceCell<crate::auth::hydra::HydraClient>,
|
||||||
// Phase 2
|
|
||||||
#[cfg(feature = "gitea")]
|
#[cfg(feature = "gitea")]
|
||||||
gitea: OnceLock<crate::gitea::GiteaClient>,
|
gitea: OnceCell<crate::gitea::GiteaClient>,
|
||||||
// Phase 3
|
|
||||||
#[cfg(feature = "matrix")]
|
#[cfg(feature = "matrix")]
|
||||||
matrix: OnceLock<crate::matrix::MatrixClient>,
|
matrix: OnceCell<crate::matrix::MatrixClient>,
|
||||||
#[cfg(feature = "opensearch")]
|
#[cfg(feature = "opensearch")]
|
||||||
opensearch: OnceLock<crate::search::OpenSearchClient>,
|
opensearch: OnceCell<crate::search::OpenSearchClient>,
|
||||||
#[cfg(feature = "s3")]
|
#[cfg(feature = "s3")]
|
||||||
s3: OnceLock<crate::storage::S3Client>,
|
s3: OnceCell<crate::storage::S3Client>,
|
||||||
#[cfg(feature = "livekit")]
|
#[cfg(feature = "livekit")]
|
||||||
livekit: OnceLock<crate::media::LiveKitClient>,
|
livekit: OnceCell<crate::media::LiveKitClient>,
|
||||||
#[cfg(feature = "monitoring")]
|
#[cfg(feature = "monitoring")]
|
||||||
prometheus: OnceLock<crate::monitoring::PrometheusClient>,
|
prometheus: OnceCell<crate::monitoring::PrometheusClient>,
|
||||||
#[cfg(feature = "monitoring")]
|
#[cfg(feature = "monitoring")]
|
||||||
loki: OnceLock<crate::monitoring::LokiClient>,
|
loki: OnceCell<crate::monitoring::LokiClient>,
|
||||||
#[cfg(feature = "monitoring")]
|
#[cfg(feature = "monitoring")]
|
||||||
grafana: OnceLock<crate::monitoring::GrafanaClient>,
|
grafana: OnceCell<crate::monitoring::GrafanaClient>,
|
||||||
// Phase 4
|
|
||||||
#[cfg(feature = "lasuite")]
|
#[cfg(feature = "lasuite")]
|
||||||
people: OnceLock<crate::lasuite::PeopleClient>,
|
people: OnceCell<crate::lasuite::PeopleClient>,
|
||||||
#[cfg(feature = "lasuite")]
|
#[cfg(feature = "lasuite")]
|
||||||
docs: OnceLock<crate::lasuite::DocsClient>,
|
docs: OnceCell<crate::lasuite::DocsClient>,
|
||||||
#[cfg(feature = "lasuite")]
|
#[cfg(feature = "lasuite")]
|
||||||
meet: OnceLock<crate::lasuite::MeetClient>,
|
meet: OnceCell<crate::lasuite::MeetClient>,
|
||||||
#[cfg(feature = "lasuite")]
|
#[cfg(feature = "lasuite")]
|
||||||
drive: OnceLock<crate::lasuite::DriveClient>,
|
drive: OnceCell<crate::lasuite::DriveClient>,
|
||||||
#[cfg(feature = "lasuite")]
|
#[cfg(feature = "lasuite")]
|
||||||
messages: OnceLock<crate::lasuite::MessagesClient>,
|
messages: OnceCell<crate::lasuite::MessagesClient>,
|
||||||
#[cfg(feature = "lasuite")]
|
#[cfg(feature = "lasuite")]
|
||||||
calendars: OnceLock<crate::lasuite::CalendarsClient>,
|
calendars: OnceCell<crate::lasuite::CalendarsClient>,
|
||||||
#[cfg(feature = "lasuite")]
|
#[cfg(feature = "lasuite")]
|
||||||
find: OnceLock<crate::lasuite::FindClient>,
|
find: OnceCell<crate::lasuite::FindClient>,
|
||||||
// Bao/Planka stay in their existing modules
|
bao: OnceCell<crate::openbao::BaoClient>,
|
||||||
bao: OnceLock<crate::openbao::BaoClient>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SunbeamClient {
|
impl SunbeamClient {
|
||||||
@@ -276,40 +276,40 @@ impl SunbeamClient {
|
|||||||
domain: ctx.domain.clone(),
|
domain: ctx.domain.clone(),
|
||||||
ctx: ctx.clone(),
|
ctx: ctx.clone(),
|
||||||
#[cfg(feature = "identity")]
|
#[cfg(feature = "identity")]
|
||||||
kratos: OnceLock::new(),
|
kratos: OnceCell::new(),
|
||||||
#[cfg(feature = "identity")]
|
#[cfg(feature = "identity")]
|
||||||
hydra: OnceLock::new(),
|
hydra: OnceCell::new(),
|
||||||
#[cfg(feature = "gitea")]
|
#[cfg(feature = "gitea")]
|
||||||
gitea: OnceLock::new(),
|
gitea: OnceCell::new(),
|
||||||
#[cfg(feature = "matrix")]
|
#[cfg(feature = "matrix")]
|
||||||
matrix: OnceLock::new(),
|
matrix: OnceCell::new(),
|
||||||
#[cfg(feature = "opensearch")]
|
#[cfg(feature = "opensearch")]
|
||||||
opensearch: OnceLock::new(),
|
opensearch: OnceCell::new(),
|
||||||
#[cfg(feature = "s3")]
|
#[cfg(feature = "s3")]
|
||||||
s3: OnceLock::new(),
|
s3: OnceCell::new(),
|
||||||
#[cfg(feature = "livekit")]
|
#[cfg(feature = "livekit")]
|
||||||
livekit: OnceLock::new(),
|
livekit: OnceCell::new(),
|
||||||
#[cfg(feature = "monitoring")]
|
#[cfg(feature = "monitoring")]
|
||||||
prometheus: OnceLock::new(),
|
prometheus: OnceCell::new(),
|
||||||
#[cfg(feature = "monitoring")]
|
#[cfg(feature = "monitoring")]
|
||||||
loki: OnceLock::new(),
|
loki: OnceCell::new(),
|
||||||
#[cfg(feature = "monitoring")]
|
#[cfg(feature = "monitoring")]
|
||||||
grafana: OnceLock::new(),
|
grafana: OnceCell::new(),
|
||||||
#[cfg(feature = "lasuite")]
|
#[cfg(feature = "lasuite")]
|
||||||
people: OnceLock::new(),
|
people: OnceCell::new(),
|
||||||
#[cfg(feature = "lasuite")]
|
#[cfg(feature = "lasuite")]
|
||||||
docs: OnceLock::new(),
|
docs: OnceCell::new(),
|
||||||
#[cfg(feature = "lasuite")]
|
#[cfg(feature = "lasuite")]
|
||||||
meet: OnceLock::new(),
|
meet: OnceCell::new(),
|
||||||
#[cfg(feature = "lasuite")]
|
#[cfg(feature = "lasuite")]
|
||||||
drive: OnceLock::new(),
|
drive: OnceCell::new(),
|
||||||
#[cfg(feature = "lasuite")]
|
#[cfg(feature = "lasuite")]
|
||||||
messages: OnceLock::new(),
|
messages: OnceCell::new(),
|
||||||
#[cfg(feature = "lasuite")]
|
#[cfg(feature = "lasuite")]
|
||||||
calendars: OnceLock::new(),
|
calendars: OnceCell::new(),
|
||||||
#[cfg(feature = "lasuite")]
|
#[cfg(feature = "lasuite")]
|
||||||
find: OnceLock::new(),
|
find: OnceCell::new(),
|
||||||
bao: OnceLock::new(),
|
bao: OnceCell::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,131 +323,172 @@ impl SunbeamClient {
|
|||||||
&self.ctx
|
&self.ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Lazy accessors (each feature-gated) --------------------------------
|
// -- Auth helpers --------------------------------------------------------
|
||||||
|
|
||||||
|
/// Get cached SSO bearer token (from `sunbeam auth sso`).
|
||||||
|
async fn sso_token(&self) -> Result<String> {
|
||||||
|
crate::auth::get_token().await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get cached Gitea PAT (from `sunbeam auth git`).
|
||||||
|
fn gitea_token(&self) -> Result<String> {
|
||||||
|
crate::auth::get_gitea_token()
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- Lazy async accessors (each feature-gated) ---------------------------
|
||||||
|
//
|
||||||
|
// Each accessor resolves the appropriate auth and constructs the client
|
||||||
|
// with from_parts(url, auth). Cached after first call.
|
||||||
|
|
||||||
#[cfg(feature = "identity")]
|
#[cfg(feature = "identity")]
|
||||||
pub fn kratos(&self) -> &crate::identity::KratosClient {
|
pub async fn kratos(&self) -> Result<&crate::identity::KratosClient> {
|
||||||
self.kratos.get_or_init(|| {
|
self.kratos.get_or_try_init(|| async {
|
||||||
crate::identity::KratosClient::connect(&self.domain)
|
let token = self.sso_token().await?;
|
||||||
})
|
let url = format!("https://id.{}", self.domain);
|
||||||
|
Ok(crate::identity::KratosClient::from_parts(url, AuthMethod::Bearer(token)))
|
||||||
|
}).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "identity")]
|
#[cfg(feature = "identity")]
|
||||||
pub fn hydra(&self) -> &crate::auth::hydra::HydraClient {
|
pub async fn hydra(&self) -> Result<&crate::auth::hydra::HydraClient> {
|
||||||
self.hydra.get_or_init(|| {
|
self.hydra.get_or_try_init(|| async {
|
||||||
crate::auth::hydra::HydraClient::connect(&self.domain)
|
let token = self.sso_token().await?;
|
||||||
})
|
let url = format!("https://hydra.{}", self.domain);
|
||||||
|
Ok(crate::auth::hydra::HydraClient::from_parts(url, AuthMethod::Bearer(token)))
|
||||||
|
}).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "gitea")]
|
#[cfg(feature = "gitea")]
|
||||||
pub fn gitea(&self) -> &crate::gitea::GiteaClient {
|
pub async fn gitea(&self) -> Result<&crate::gitea::GiteaClient> {
|
||||||
self.gitea.get_or_init(|| {
|
self.gitea.get_or_try_init(|| async {
|
||||||
crate::gitea::GiteaClient::connect(&self.domain)
|
let token = self.gitea_token()?;
|
||||||
})
|
let url = format!("https://src.{}/api/v1", self.domain);
|
||||||
|
Ok(crate::gitea::GiteaClient::from_parts(url, AuthMethod::Token(token)))
|
||||||
|
}).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "matrix")]
|
#[cfg(feature = "matrix")]
|
||||||
pub fn matrix(&self) -> &crate::matrix::MatrixClient {
|
pub async fn matrix(&self) -> Result<&crate::matrix::MatrixClient> {
|
||||||
self.matrix.get_or_init(|| {
|
self.matrix.get_or_try_init(|| async {
|
||||||
crate::matrix::MatrixClient::connect(&self.domain)
|
let token = self.sso_token().await?;
|
||||||
})
|
let url = format!("https://messages.{}/_matrix", self.domain);
|
||||||
|
Ok(crate::matrix::MatrixClient::from_parts(url, AuthMethod::Bearer(token)))
|
||||||
|
}).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "opensearch")]
|
#[cfg(feature = "opensearch")]
|
||||||
pub fn opensearch(&self) -> &crate::search::OpenSearchClient {
|
pub async fn opensearch(&self) -> Result<&crate::search::OpenSearchClient> {
|
||||||
self.opensearch.get_or_init(|| {
|
self.opensearch.get_or_try_init(|| async {
|
||||||
crate::search::OpenSearchClient::connect(&self.domain)
|
let token = self.sso_token().await?;
|
||||||
})
|
let url = format!("https://search.{}", self.domain);
|
||||||
|
Ok(crate::search::OpenSearchClient::from_parts(url, AuthMethod::Bearer(token)))
|
||||||
|
}).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "s3")]
|
#[cfg(feature = "s3")]
|
||||||
pub fn s3(&self) -> &crate::storage::S3Client {
|
pub async fn s3(&self) -> Result<&crate::storage::S3Client> {
|
||||||
self.s3.get_or_init(|| {
|
self.s3.get_or_try_init(|| async {
|
||||||
crate::storage::S3Client::connect(&self.domain)
|
Ok(crate::storage::S3Client::connect(&self.domain))
|
||||||
})
|
}).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "livekit")]
|
#[cfg(feature = "livekit")]
|
||||||
pub fn livekit(&self) -> &crate::media::LiveKitClient {
|
pub async fn livekit(&self) -> Result<&crate::media::LiveKitClient> {
|
||||||
self.livekit.get_or_init(|| {
|
self.livekit.get_or_try_init(|| async {
|
||||||
crate::media::LiveKitClient::connect(&self.domain)
|
Ok(crate::media::LiveKitClient::connect(&self.domain))
|
||||||
})
|
}).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "monitoring")]
|
#[cfg(feature = "monitoring")]
|
||||||
pub fn prometheus(&self) -> &crate::monitoring::PrometheusClient {
|
pub async fn prometheus(&self) -> Result<&crate::monitoring::PrometheusClient> {
|
||||||
self.prometheus.get_or_init(|| {
|
self.prometheus.get_or_try_init(|| async {
|
||||||
crate::monitoring::PrometheusClient::connect(&self.domain)
|
Ok(crate::monitoring::PrometheusClient::connect(&self.domain))
|
||||||
})
|
}).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "monitoring")]
|
#[cfg(feature = "monitoring")]
|
||||||
pub fn loki(&self) -> &crate::monitoring::LokiClient {
|
pub async fn loki(&self) -> Result<&crate::monitoring::LokiClient> {
|
||||||
self.loki.get_or_init(|| {
|
self.loki.get_or_try_init(|| async {
|
||||||
crate::monitoring::LokiClient::connect(&self.domain)
|
Ok(crate::monitoring::LokiClient::connect(&self.domain))
|
||||||
})
|
}).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "monitoring")]
|
#[cfg(feature = "monitoring")]
|
||||||
pub fn grafana(&self) -> &crate::monitoring::GrafanaClient {
|
pub async fn grafana(&self) -> Result<&crate::monitoring::GrafanaClient> {
|
||||||
self.grafana.get_or_init(|| {
|
self.grafana.get_or_try_init(|| async {
|
||||||
crate::monitoring::GrafanaClient::connect(&self.domain)
|
Ok(crate::monitoring::GrafanaClient::connect(&self.domain))
|
||||||
})
|
}).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "lasuite")]
|
#[cfg(feature = "lasuite")]
|
||||||
pub fn people(&self) -> &crate::lasuite::PeopleClient {
|
pub async fn people(&self) -> Result<&crate::lasuite::PeopleClient> {
|
||||||
self.people.get_or_init(|| {
|
self.people.get_or_try_init(|| async {
|
||||||
crate::lasuite::PeopleClient::connect(&self.domain)
|
let token = self.sso_token().await?;
|
||||||
})
|
let url = format!("https://people.{}/api/v1.0", self.domain);
|
||||||
|
Ok(crate::lasuite::PeopleClient::from_parts(url, AuthMethod::Bearer(token)))
|
||||||
|
}).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "lasuite")]
|
#[cfg(feature = "lasuite")]
|
||||||
pub fn docs(&self) -> &crate::lasuite::DocsClient {
|
pub async fn docs(&self) -> Result<&crate::lasuite::DocsClient> {
|
||||||
self.docs.get_or_init(|| {
|
self.docs.get_or_try_init(|| async {
|
||||||
crate::lasuite::DocsClient::connect(&self.domain)
|
let token = self.sso_token().await?;
|
||||||
})
|
let url = format!("https://docs.{}/api/v1.0", self.domain);
|
||||||
|
Ok(crate::lasuite::DocsClient::from_parts(url, AuthMethod::Bearer(token)))
|
||||||
|
}).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "lasuite")]
|
#[cfg(feature = "lasuite")]
|
||||||
pub fn meet(&self) -> &crate::lasuite::MeetClient {
|
pub async fn meet(&self) -> Result<&crate::lasuite::MeetClient> {
|
||||||
self.meet.get_or_init(|| {
|
self.meet.get_or_try_init(|| async {
|
||||||
crate::lasuite::MeetClient::connect(&self.domain)
|
let token = self.sso_token().await?;
|
||||||
})
|
let url = format!("https://meet.{}/api/v1.0", self.domain);
|
||||||
|
Ok(crate::lasuite::MeetClient::from_parts(url, AuthMethod::Bearer(token)))
|
||||||
|
}).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "lasuite")]
|
#[cfg(feature = "lasuite")]
|
||||||
pub fn drive(&self) -> &crate::lasuite::DriveClient {
|
pub async fn drive(&self) -> Result<&crate::lasuite::DriveClient> {
|
||||||
self.drive.get_or_init(|| {
|
self.drive.get_or_try_init(|| async {
|
||||||
crate::lasuite::DriveClient::connect(&self.domain)
|
let token = self.sso_token().await?;
|
||||||
})
|
let url = format!("https://drive.{}/api/v1.0", self.domain);
|
||||||
|
Ok(crate::lasuite::DriveClient::from_parts(url, AuthMethod::Bearer(token)))
|
||||||
|
}).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "lasuite")]
|
#[cfg(feature = "lasuite")]
|
||||||
pub fn messages(&self) -> &crate::lasuite::MessagesClient {
|
pub async fn messages(&self) -> Result<&crate::lasuite::MessagesClient> {
|
||||||
self.messages.get_or_init(|| {
|
self.messages.get_or_try_init(|| async {
|
||||||
crate::lasuite::MessagesClient::connect(&self.domain)
|
let token = self.sso_token().await?;
|
||||||
})
|
let url = format!("https://mail.{}/api/v1.0", self.domain);
|
||||||
|
Ok(crate::lasuite::MessagesClient::from_parts(url, AuthMethod::Bearer(token)))
|
||||||
|
}).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "lasuite")]
|
#[cfg(feature = "lasuite")]
|
||||||
pub fn calendars(&self) -> &crate::lasuite::CalendarsClient {
|
pub async fn calendars(&self) -> Result<&crate::lasuite::CalendarsClient> {
|
||||||
self.calendars.get_or_init(|| {
|
self.calendars.get_or_try_init(|| async {
|
||||||
crate::lasuite::CalendarsClient::connect(&self.domain)
|
let token = self.sso_token().await?;
|
||||||
})
|
let url = format!("https://calendar.{}/api/v1.0", self.domain);
|
||||||
|
Ok(crate::lasuite::CalendarsClient::from_parts(url, AuthMethod::Bearer(token)))
|
||||||
|
}).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "lasuite")]
|
#[cfg(feature = "lasuite")]
|
||||||
pub fn find(&self) -> &crate::lasuite::FindClient {
|
pub async fn find(&self) -> Result<&crate::lasuite::FindClient> {
|
||||||
self.find.get_or_init(|| {
|
self.find.get_or_try_init(|| async {
|
||||||
crate::lasuite::FindClient::connect(&self.domain)
|
let token = self.sso_token().await?;
|
||||||
})
|
let url = format!("https://find.{}/api/v1.0", self.domain);
|
||||||
|
Ok(crate::lasuite::FindClient::from_parts(url, AuthMethod::Bearer(token)))
|
||||||
|
}).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bao(&self, base_url: &str) -> &crate::openbao::BaoClient {
|
pub async fn bao(&self) -> Result<&crate::openbao::BaoClient> {
|
||||||
self.bao.get_or_init(|| {
|
self.bao.get_or_try_init(|| async {
|
||||||
crate::openbao::BaoClient::new(base_url)
|
let token = self.sso_token().await?;
|
||||||
})
|
let url = format!("https://vault.{}", self.domain);
|
||||||
|
Ok(crate::openbao::BaoClient::with_token(&url, &token))
|
||||||
|
}).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
|
|
||||||
|
use crate::client::SunbeamClient;
|
||||||
use crate::error::{Result, SunbeamError};
|
use crate::error::{Result, SunbeamError};
|
||||||
use crate::gitea::types::*;
|
use crate::gitea::types::*;
|
||||||
use crate::gitea::GiteaClient;
|
|
||||||
use crate::output::{render, render_list, read_json_input, OutputFormat};
|
use crate::output::{render, render_list, read_json_input, OutputFormat};
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -435,7 +435,8 @@ fn notification_row(n: &Notification) -> Vec<String> {
|
|||||||
// Dispatch
|
// Dispatch
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
pub async fn dispatch(cmd: VcsCommand, client: &GiteaClient, fmt: OutputFormat) -> Result<()> {
|
pub async fn dispatch(cmd: VcsCommand, client: &SunbeamClient, fmt: OutputFormat) -> Result<()> {
|
||||||
|
let client = client.gitea().await?;
|
||||||
match cmd {
|
match cmd {
|
||||||
// -- Repo -----------------------------------------------------------
|
// -- Repo -----------------------------------------------------------
|
||||||
VcsCommand::Repo { action } => match action {
|
VcsCommand::Repo { action } => match action {
|
||||||
|
|||||||
@@ -349,7 +349,7 @@ pub async fn dispatch(
|
|||||||
AuthCommand::Courier { action } => dispatch_courier(action, client, output).await,
|
AuthCommand::Courier { action } => dispatch_courier(action, client, output).await,
|
||||||
// -- Kratos: Health -----------------------------------------------------
|
// -- Kratos: Health -----------------------------------------------------
|
||||||
AuthCommand::Health => {
|
AuthCommand::Health => {
|
||||||
let status = client.kratos().alive().await?;
|
let status = client.kratos().await?.alive().await?;
|
||||||
output::render(&status, output)
|
output::render(&status, output)
|
||||||
}
|
}
|
||||||
// -- Hydra: Client ------------------------------------------------------
|
// -- Hydra: Client ------------------------------------------------------
|
||||||
@@ -384,7 +384,7 @@ async fn dispatch_identity(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let kratos = client.kratos();
|
let kratos = client.kratos().await?;
|
||||||
match action {
|
match action {
|
||||||
IdentityAction::List { page, page_size } => {
|
IdentityAction::List { page, page_size } => {
|
||||||
let items = kratos.list_identities(page, page_size).await?;
|
let items = kratos.list_identities(page, page_size).await?;
|
||||||
@@ -437,7 +437,7 @@ async fn dispatch_session(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let kratos = client.kratos();
|
let kratos = client.kratos().await?;
|
||||||
match action {
|
match action {
|
||||||
SessionAction::List {
|
SessionAction::List {
|
||||||
page_size,
|
page_size,
|
||||||
@@ -486,7 +486,7 @@ async fn dispatch_recovery(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let kratos = client.kratos();
|
let kratos = client.kratos().await?;
|
||||||
match action {
|
match action {
|
||||||
RecoveryAction::CreateCode { id, expires_in } => {
|
RecoveryAction::CreateCode { id, expires_in } => {
|
||||||
let item = kratos
|
let item = kratos
|
||||||
@@ -512,7 +512,7 @@ async fn dispatch_schema(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let kratos = client.kratos();
|
let kratos = client.kratos().await?;
|
||||||
match action {
|
match action {
|
||||||
SchemaAction::List => {
|
SchemaAction::List => {
|
||||||
let items = kratos.list_schemas().await?;
|
let items = kratos.list_schemas().await?;
|
||||||
@@ -539,7 +539,7 @@ async fn dispatch_courier(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let kratos = client.kratos();
|
let kratos = client.kratos().await?;
|
||||||
match action {
|
match action {
|
||||||
CourierAction::List {
|
CourierAction::List {
|
||||||
page_size,
|
page_size,
|
||||||
@@ -579,7 +579,7 @@ async fn dispatch_client(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let hydra = client.hydra();
|
let hydra = client.hydra().await?;
|
||||||
match action {
|
match action {
|
||||||
ClientAction::List { limit, offset } => {
|
ClientAction::List { limit, offset } => {
|
||||||
let items = hydra.list_clients(limit, offset).await?;
|
let items = hydra.list_clients(limit, offset).await?;
|
||||||
@@ -631,7 +631,7 @@ async fn dispatch_jwk(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let hydra = client.hydra();
|
let hydra = client.hydra().await?;
|
||||||
match action {
|
match action {
|
||||||
JwkAction::List { set_name } => {
|
JwkAction::List { set_name } => {
|
||||||
let item = hydra.get_jwk_set(&set_name).await?;
|
let item = hydra.get_jwk_set(&set_name).await?;
|
||||||
@@ -665,7 +665,7 @@ async fn dispatch_issuer(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let hydra = client.hydra();
|
let hydra = client.hydra().await?;
|
||||||
match action {
|
match action {
|
||||||
IssuerAction::List => {
|
IssuerAction::List => {
|
||||||
let items = hydra.list_trusted_issuers().await?;
|
let items = hydra.list_trusted_issuers().await?;
|
||||||
@@ -711,7 +711,7 @@ async fn dispatch_token(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let hydra = client.hydra();
|
let hydra = client.hydra().await?;
|
||||||
match action {
|
match action {
|
||||||
TokenAction::Introspect { token } => {
|
TokenAction::Introspect { token } => {
|
||||||
let item = hydra.introspect_token(&token).await?;
|
let item = hydra.introspect_token(&token).await?;
|
||||||
|
|||||||
@@ -6,44 +6,6 @@ use crate::client::SunbeamClient;
|
|||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::output::{self, OutputFormat};
|
use crate::output::{self, OutputFormat};
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
// Helper: build an authenticated La Suite client
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
async fn people_client(domain: &str) -> Result<super::PeopleClient> {
|
|
||||||
let token = crate::auth::get_token().await?;
|
|
||||||
Ok(super::PeopleClient::connect(domain).with_token(&token))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn docs_client(domain: &str) -> Result<super::DocsClient> {
|
|
||||||
let token = crate::auth::get_token().await?;
|
|
||||||
Ok(super::DocsClient::connect(domain).with_token(&token))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn meet_client(domain: &str) -> Result<super::MeetClient> {
|
|
||||||
let token = crate::auth::get_token().await?;
|
|
||||||
Ok(super::MeetClient::connect(domain).with_token(&token))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn drive_client(domain: &str) -> Result<super::DriveClient> {
|
|
||||||
let token = crate::auth::get_token().await?;
|
|
||||||
Ok(super::DriveClient::connect(domain).with_token(&token))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn messages_client(domain: &str) -> Result<super::MessagesClient> {
|
|
||||||
let token = crate::auth::get_token().await?;
|
|
||||||
Ok(super::MessagesClient::connect(domain).with_token(&token))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn calendars_client(domain: &str) -> Result<super::CalendarsClient> {
|
|
||||||
let token = crate::auth::get_token().await?;
|
|
||||||
Ok(super::CalendarsClient::connect(domain).with_token(&token))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn find_client(domain: &str) -> Result<super::FindClient> {
|
|
||||||
let token = crate::auth::get_token().await?;
|
|
||||||
Ok(super::FindClient::connect(domain).with_token(&token))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
// People
|
// People
|
||||||
@@ -143,7 +105,7 @@ pub async fn dispatch_people(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let people = people_client(client.domain()).await?;
|
let people = client.people().await?;
|
||||||
match cmd {
|
match cmd {
|
||||||
PeopleCommand::Contact { action } => match action {
|
PeopleCommand::Contact { action } => match action {
|
||||||
ContactAction::List { page } => {
|
ContactAction::List { page } => {
|
||||||
@@ -346,7 +308,7 @@ pub async fn dispatch_docs(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let docs = docs_client(client.domain()).await?;
|
let docs = client.docs().await?;
|
||||||
match cmd {
|
match cmd {
|
||||||
DocsCommand::Document { action } => match action {
|
DocsCommand::Document { action } => match action {
|
||||||
DocumentAction::List { page } => {
|
DocumentAction::List { page } => {
|
||||||
@@ -498,7 +460,7 @@ pub async fn dispatch_meet(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let meet = meet_client(client.domain()).await?;
|
let meet = client.meet().await?;
|
||||||
match cmd {
|
match cmd {
|
||||||
MeetCommand::Room { action } => match action {
|
MeetCommand::Room { action } => match action {
|
||||||
RoomAction::List { page } => {
|
RoomAction::List { page } => {
|
||||||
@@ -645,7 +607,7 @@ pub async fn dispatch_drive(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let drive = drive_client(client.domain()).await?;
|
let drive = client.drive().await?;
|
||||||
match cmd {
|
match cmd {
|
||||||
DriveCommand::File { action } => match action {
|
DriveCommand::File { action } => match action {
|
||||||
FileAction::List { page } => {
|
FileAction::List { page } => {
|
||||||
@@ -823,7 +785,7 @@ pub async fn dispatch_mail(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mail = messages_client(client.domain()).await?;
|
let mail = client.messages().await?;
|
||||||
match cmd {
|
match cmd {
|
||||||
MailCommand::Mailbox { action } => match action {
|
MailCommand::Mailbox { action } => match action {
|
||||||
MailboxAction::List => {
|
MailboxAction::List => {
|
||||||
@@ -1013,7 +975,7 @@ pub async fn dispatch_cal(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let cal = calendars_client(client.domain()).await?;
|
let cal = client.calendars().await?;
|
||||||
match cmd {
|
match cmd {
|
||||||
CalCommand::Calendar { action } => match action {
|
CalCommand::Calendar { action } => match action {
|
||||||
CalendarAction::List => {
|
CalendarAction::List => {
|
||||||
@@ -1124,7 +1086,7 @@ pub async fn dispatch_find(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let find = find_client(client.domain()).await?;
|
let find = client.find().await?;
|
||||||
match cmd {
|
match cmd {
|
||||||
FindCommand::Search { query, page } => {
|
FindCommand::Search { query, page } => {
|
||||||
let page_data = find.search(&query, page).await?;
|
let page_data = find.search(&query, page).await?;
|
||||||
|
|||||||
@@ -1,22 +1,10 @@
|
|||||||
//! CLI dispatch for Matrix chat commands.
|
//! CLI dispatch for Matrix chat commands.
|
||||||
|
|
||||||
|
use crate::client::SunbeamClient;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::output::{self, OutputFormat};
|
use crate::output::{self, OutputFormat};
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Auth helper
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/// Construct a [`MatrixClient`] with a valid access token from the credential
|
|
||||||
/// cache. Fails if the user is not logged in.
|
|
||||||
async fn matrix_with_token(domain: &str) -> Result<super::MatrixClient> {
|
|
||||||
let token = crate::auth::get_token().await?;
|
|
||||||
let mut m = super::MatrixClient::connect(domain);
|
|
||||||
m.set_token(&token);
|
|
||||||
Ok(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Command tree
|
// Command tree
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -343,8 +331,8 @@ pub enum UserAction {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
/// Dispatch a parsed [`ChatCommand`] against the Matrix homeserver.
|
/// Dispatch a parsed [`ChatCommand`] against the Matrix homeserver.
|
||||||
pub async fn dispatch(domain: &str, format: OutputFormat, cmd: ChatCommand) -> Result<()> {
|
pub async fn dispatch(client: &SunbeamClient, format: OutputFormat, cmd: ChatCommand) -> Result<()> {
|
||||||
let m = matrix_with_token(domain).await?;
|
let m = client.matrix().await?;
|
||||||
|
|
||||||
match cmd {
|
match cmd {
|
||||||
// -- Whoami ---------------------------------------------------------
|
// -- Whoami ---------------------------------------------------------
|
||||||
|
|||||||
@@ -32,9 +32,9 @@ impl ServiceClient for MatrixClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MatrixClient {
|
impl MatrixClient {
|
||||||
/// Build a MatrixClient from domain (e.g. `https://matrix.{domain}/_matrix`).
|
/// Build a MatrixClient from domain (e.g. `https://messages.{domain}/_matrix`).
|
||||||
pub fn connect(domain: &str) -> Self {
|
pub fn connect(domain: &str) -> Self {
|
||||||
let base_url = format!("https://matrix.{domain}/_matrix");
|
let base_url = format!("https://messages.{domain}/_matrix");
|
||||||
Self::from_parts(base_url, AuthMethod::Bearer(String::new()))
|
Self::from_parts(base_url, AuthMethod::Bearer(String::new()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1204,7 +1204,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_connect_url() {
|
fn test_connect_url() {
|
||||||
let c = MatrixClient::connect("sunbeam.pt");
|
let c = MatrixClient::connect("sunbeam.pt");
|
||||||
assert_eq!(c.base_url(), "https://matrix.sunbeam.pt/_matrix");
|
assert_eq!(c.base_url(), "https://messages.sunbeam.pt/_matrix");
|
||||||
assert_eq!(c.service_name(), "matrix");
|
assert_eq!(c.service_name(), "matrix");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ async fn dispatch_room(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let lk = client.livekit();
|
let lk = client.livekit().await?;
|
||||||
match action {
|
match action {
|
||||||
RoomAction::List => {
|
RoomAction::List => {
|
||||||
let resp = lk.list_rooms().await?;
|
let resp = lk.list_rooms().await?;
|
||||||
@@ -227,7 +227,7 @@ async fn dispatch_participant(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let lk = client.livekit();
|
let lk = client.livekit().await?;
|
||||||
match action {
|
match action {
|
||||||
ParticipantAction::List { room } => {
|
ParticipantAction::List { room } => {
|
||||||
let resp = lk
|
let resp = lk
|
||||||
@@ -278,7 +278,7 @@ async fn dispatch_egress(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let lk = client.livekit();
|
let lk = client.livekit().await?;
|
||||||
match action {
|
match action {
|
||||||
EgressAction::List { room } => {
|
EgressAction::List { room } => {
|
||||||
let resp = lk
|
let resp = lk
|
||||||
|
|||||||
@@ -425,7 +425,7 @@ async fn dispatch_prometheus(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let prom = client.prometheus();
|
let prom = client.prometheus().await?;
|
||||||
match action {
|
match action {
|
||||||
PrometheusAction::Query { query, time } => {
|
PrometheusAction::Query { query, time } => {
|
||||||
let res = prom.query(&query, time.as_deref()).await?;
|
let res = prom.query(&query, time.as_deref()).await?;
|
||||||
@@ -511,7 +511,7 @@ async fn dispatch_loki(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let loki = client.loki();
|
let loki = client.loki().await?;
|
||||||
match action {
|
match action {
|
||||||
LokiAction::Query { query, limit, time } => {
|
LokiAction::Query { query, limit, time } => {
|
||||||
let res = loki.query(&query, limit, time.as_deref()).await?;
|
let res = loki.query(&query, limit, time.as_deref()).await?;
|
||||||
@@ -631,7 +631,7 @@ async fn dispatch_grafana_dashboard(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let grafana = client.grafana();
|
let grafana = client.grafana().await?;
|
||||||
match action {
|
match action {
|
||||||
GrafanaDashboardAction::List => {
|
GrafanaDashboardAction::List => {
|
||||||
let items = grafana.list_dashboards().await?;
|
let items = grafana.list_dashboards().await?;
|
||||||
@@ -696,7 +696,7 @@ async fn dispatch_grafana_datasource(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let grafana = client.grafana();
|
let grafana = client.grafana().await?;
|
||||||
match action {
|
match action {
|
||||||
GrafanaDatasourceAction::List => {
|
GrafanaDatasourceAction::List => {
|
||||||
let items = grafana.list_datasources().await?;
|
let items = grafana.list_datasources().await?;
|
||||||
@@ -746,7 +746,7 @@ async fn dispatch_grafana_folder(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let grafana = client.grafana();
|
let grafana = client.grafana().await?;
|
||||||
match action {
|
match action {
|
||||||
GrafanaFolderAction::List => {
|
GrafanaFolderAction::List => {
|
||||||
let items = grafana.list_folders().await?;
|
let items = grafana.list_folders().await?;
|
||||||
@@ -794,7 +794,7 @@ async fn dispatch_grafana_annotation(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let grafana = client.grafana();
|
let grafana = client.grafana().await?;
|
||||||
match action {
|
match action {
|
||||||
GrafanaAnnotationAction::List { params } => {
|
GrafanaAnnotationAction::List { params } => {
|
||||||
let items = grafana.list_annotations(params.as_deref()).await?;
|
let items = grafana.list_annotations(params.as_deref()).await?;
|
||||||
@@ -833,7 +833,7 @@ async fn dispatch_grafana_alert(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let grafana = client.grafana();
|
let grafana = client.grafana().await?;
|
||||||
match action {
|
match action {
|
||||||
GrafanaAlertAction::List => {
|
GrafanaAlertAction::List => {
|
||||||
let items = grafana.get_alert_rules().await?;
|
let items = grafana.get_alert_rules().await?;
|
||||||
@@ -879,7 +879,7 @@ async fn dispatch_grafana_org(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let grafana = client.grafana();
|
let grafana = client.grafana().await?;
|
||||||
match action {
|
match action {
|
||||||
GrafanaOrgAction::Get => {
|
GrafanaOrgAction::Get => {
|
||||||
let item = grafana.get_current_org().await?;
|
let item = grafana.get_current_org().await?;
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ impl ServiceClient for GrafanaClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl GrafanaClient {
|
impl GrafanaClient {
|
||||||
/// Build a GrafanaClient from domain (e.g. `https://grafana.{domain}/api`).
|
/// Build a GrafanaClient from domain (e.g. `https://metrics.{domain}/api`).
|
||||||
pub fn connect(domain: &str) -> Self {
|
pub fn connect(domain: &str) -> Self {
|
||||||
let base_url = format!("https://grafana.{domain}/api");
|
let base_url = format!("https://metrics.{domain}/api");
|
||||||
Self::from_parts(base_url, AuthMethod::None)
|
Self::from_parts(base_url, AuthMethod::None)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,7 +410,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_connect_url() {
|
fn test_connect_url() {
|
||||||
let c = GrafanaClient::connect("sunbeam.pt");
|
let c = GrafanaClient::connect("sunbeam.pt");
|
||||||
assert_eq!(c.base_url(), "https://grafana.sunbeam.pt/api");
|
assert_eq!(c.base_url(), "https://metrics.sunbeam.pt/api");
|
||||||
assert_eq!(c.service_name(), "grafana");
|
assert_eq!(c.service_name(), "grafana");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ impl ServiceClient for LokiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl LokiClient {
|
impl LokiClient {
|
||||||
/// Build a LokiClient from domain (e.g. `https://loki.{domain}/loki/api/v1`).
|
/// Build a LokiClient from domain (e.g. `https://systemlogs.{domain}/loki/api/v1`).
|
||||||
pub fn connect(domain: &str) -> Self {
|
pub fn connect(domain: &str) -> Self {
|
||||||
let base_url = format!("https://loki.{domain}/loki/api/v1");
|
let base_url = format!("https://systemlogs.{domain}/loki/api/v1");
|
||||||
Self::from_parts(base_url, AuthMethod::None)
|
Self::from_parts(base_url, AuthMethod::None)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,7 +254,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_connect_url() {
|
fn test_connect_url() {
|
||||||
let c = LokiClient::connect("sunbeam.pt");
|
let c = LokiClient::connect("sunbeam.pt");
|
||||||
assert_eq!(c.base_url(), "https://loki.sunbeam.pt/loki/api/v1");
|
assert_eq!(c.base_url(), "https://systemlogs.sunbeam.pt/loki/api/v1");
|
||||||
assert_eq!(c.service_name(), "loki");
|
assert_eq!(c.service_name(), "loki");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ impl ServiceClient for PrometheusClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrometheusClient {
|
impl PrometheusClient {
|
||||||
/// Build a PrometheusClient from domain (e.g. `https://prometheus.{domain}/api/v1`).
|
/// Build a PrometheusClient from domain (e.g. `https://systemmetrics.{domain}/api/v1`).
|
||||||
pub fn connect(domain: &str) -> Self {
|
pub fn connect(domain: &str) -> Self {
|
||||||
let base_url = format!("https://prometheus.{domain}/api/v1");
|
let base_url = format!("https://systemmetrics.{domain}/api/v1");
|
||||||
Self::from_parts(base_url, AuthMethod::None)
|
Self::from_parts(base_url, AuthMethod::None)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,7 +253,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_connect_url() {
|
fn test_connect_url() {
|
||||||
let c = PrometheusClient::connect("sunbeam.pt");
|
let c = PrometheusClient::connect("sunbeam.pt");
|
||||||
assert_eq!(c.base_url(), "https://prometheus.sunbeam.pt/api/v1");
|
assert_eq!(c.base_url(), "https://systemmetrics.sunbeam.pt/api/v1");
|
||||||
assert_eq!(c.service_name(), "prometheus");
|
assert_eq!(c.service_name(), "prometheus");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
|
|
||||||
|
use crate::client::SunbeamClient;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::output::{self, OutputFormat};
|
use crate::output::{self, OutputFormat};
|
||||||
|
|
||||||
@@ -226,9 +227,10 @@ fn read_text_input(flag: Option<&str>) -> Result<String> {
|
|||||||
|
|
||||||
pub async fn dispatch(
|
pub async fn dispatch(
|
||||||
cmd: VaultCommand,
|
cmd: VaultCommand,
|
||||||
bao: &super::BaoClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
let bao = client.bao().await?;
|
||||||
match cmd {
|
match cmd {
|
||||||
// -- Status ---------------------------------------------------------
|
// -- Status ---------------------------------------------------------
|
||||||
VaultCommand::Status => {
|
VaultCommand::Status => {
|
||||||
|
|||||||
@@ -6,17 +6,6 @@ use serde_json::json;
|
|||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::output::{self, OutputFormat};
|
use crate::output::{self, OutputFormat};
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Client helper
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
async fn os_client(domain: &str) -> Result<super::OpenSearchClient> {
|
|
||||||
let token = crate::auth::get_token().await?;
|
|
||||||
let mut c = super::OpenSearchClient::connect(domain);
|
|
||||||
c.set_token(token);
|
|
||||||
Ok(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Top-level command enum
|
// Top-level command enum
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -413,7 +402,7 @@ pub async fn dispatch(
|
|||||||
client: &crate::client::SunbeamClient,
|
client: &crate::client::SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let c = os_client(client.domain()).await?;
|
let c = client.opensearch().await?;
|
||||||
|
|
||||||
match cmd {
|
match cmd {
|
||||||
// -----------------------------------------------------------------
|
// -----------------------------------------------------------------
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ async fn dispatch_bucket(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let s3 = client.s3();
|
let s3 = client.s3().await?;
|
||||||
match action {
|
match action {
|
||||||
BucketAction::List => {
|
BucketAction::List => {
|
||||||
let resp = s3.list_buckets().await?;
|
let resp = s3.list_buckets().await?;
|
||||||
@@ -194,7 +194,7 @@ async fn dispatch_object(
|
|||||||
client: &SunbeamClient,
|
client: &SunbeamClient,
|
||||||
fmt: OutputFormat,
|
fmt: OutputFormat,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let s3 = client.s3();
|
let s3 = client.s3().await?;
|
||||||
match action {
|
match action {
|
||||||
ObjectAction::List {
|
ObjectAction::List {
|
||||||
bucket,
|
bucket,
|
||||||
|
|||||||
@@ -927,12 +927,14 @@ pub async fn dispatch() -> Result<()> {
|
|||||||
let sc = sunbeam_sdk::client::SunbeamClient::from_context(
|
let sc = sunbeam_sdk::client::SunbeamClient::from_context(
|
||||||
&sunbeam_sdk::config::active_context(),
|
&sunbeam_sdk::config::active_context(),
|
||||||
);
|
);
|
||||||
sunbeam_sdk::gitea::cli::dispatch(action, sc.gitea(), cli.output_format).await
|
sunbeam_sdk::gitea::cli::dispatch(action, &sc, cli.output_format).await
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Verb::Chat { action }) => {
|
Some(Verb::Chat { action }) => {
|
||||||
let domain = sunbeam_sdk::config::active_context().domain.clone();
|
let sc = sunbeam_sdk::client::SunbeamClient::from_context(
|
||||||
sunbeam_sdk::matrix::cli::dispatch(&domain, cli.output_format, action).await
|
&sunbeam_sdk::config::active_context(),
|
||||||
|
);
|
||||||
|
sunbeam_sdk::matrix::cli::dispatch(&sc, cli.output_format, action).await
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Verb::Search { action }) => {
|
Some(Verb::Search { action }) => {
|
||||||
@@ -964,8 +966,10 @@ pub async fn dispatch() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Some(Verb::Vault { action }) => {
|
Some(Verb::Vault { action }) => {
|
||||||
let bao = sunbeam_sdk::openbao::BaoClient::new("http://127.0.0.1:8200");
|
let sc = sunbeam_sdk::client::SunbeamClient::from_context(
|
||||||
sunbeam_sdk::openbao::cli::dispatch(action, &bao, cli.output_format).await
|
&sunbeam_sdk::config::active_context(),
|
||||||
|
);
|
||||||
|
sunbeam_sdk::openbao::cli::dispatch(action, &sc, cli.output_format).await
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Verb::People { action }) => {
|
Some(Verb::People { action }) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user