#[cfg(not(feature = "cloud-server"))]
use anyhow::bail;
use anyhow::Result;
use serde::{Deserialize, Serialize};
use vault::fact::MemoFact;
#[derive(Debug, Serialize, Deserialize)]
pub struct SyncPushPayload {
pub project_hash: String,
pub facts: Vec<MemoFact>,
pub client_version: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SyncPullResponse {
pub facts: Vec<MemoFact>,
pub server_version: u64,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SyncPushResponse {
pub accepted: usize,
pub merged: usize,
pub server_version: u64,
}
#[cfg(feature = "cloud-server")]
pub async fn push_vault(
endpoint: &str,
token: &str,
project_hash: &str,
facts: Vec<MemoFact>,
) -> Result<SyncPushResponse> {
let payload = SyncPushPayload {
project_hash: project_hash.to_string(),
facts,
client_version: env!("CARGO_PKG_VERSION").to_string(),
};
let resp = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(15))
.connect_timeout(std::time::Duration::from_secs(8))
.build()?
.post(format!("{endpoint}/sync/vault"))
.bearer_auth(token)
.json(&payload)
.send()
.await?
.error_for_status()?
.json::<SyncPushResponse>()
.await?;
Ok(resp)
}
#[cfg(not(feature = "cloud-server"))]
pub async fn push_vault(
_endpoint: &str,
_token: &str,
_project_hash: &str,
_facts: Vec<MemoFact>,
) -> Result<SyncPushResponse> {
bail!("cloud-server feature not enabled")
}
#[cfg(feature = "cloud-server")]
pub async fn pull_vault(
endpoint: &str,
token: &str,
project_hash: &str,
) -> Result<SyncPullResponse> {
let resp = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(15))
.connect_timeout(std::time::Duration::from_secs(8))
.build()?
.get(format!("{endpoint}/sync/vault/{project_hash}"))
.bearer_auth(token)
.send()
.await?
.error_for_status()?
.json::<SyncPullResponse>()
.await?;
Ok(resp)
}
#[cfg(not(feature = "cloud-server"))]
pub async fn pull_vault(
_endpoint: &str,
_token: &str,
_project_hash: &str,
) -> Result<SyncPullResponse> {
bail!("cloud-server feature not enabled")
}
#[cfg(feature = "cloud-server")]
pub fn push_usage_snapshot(
endpoint: &str,
token: &str,
snapshot: &crate::usage::UsageSnapshot,
) -> anyhow::Result<()> {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?;
rt.block_on(async {
let client = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(10))
.connect_timeout(std::time::Duration::from_secs(5))
.build()?;
let resp: serde_json::Value = client
.post(format!("{endpoint}/sync/usage"))
.bearer_auth(token)
.json(snapshot)
.send()
.await?
.error_for_status()?
.json()
.await?;
if resp["ok"] == serde_json::json!(true) {
Ok(())
} else {
anyhow::bail!("snapshot rejected: {}", resp["reason"])
}
})
}
#[cfg(not(feature = "cloud-server"))]
pub fn push_usage_snapshot(
_endpoint: &str,
_token: &str,
_snapshot: &crate::usage::UsageSnapshot,
) -> anyhow::Result<()> {
bail!("cloud-server feature not enabled")
}
#[cfg(all(test, not(feature = "cloud-server")))]
mod stub_tests {
#[test]
fn without_the_cloud_client_a_snapshot_is_never_reported_as_sent() {
let snapshot = crate::usage::UsageSnapshot {
device_id: "11111111-1111-4111-8111-111111111111".into(),
client_version: "test".into(),
rows: vec![],
};
assert!(super::push_usage_snapshot("http://127.0.0.1:9", "t", &snapshot).is_err());
}
}