bctx_cloud_core/client/
sync.rs1#[cfg(not(feature = "cloud-server"))]
2use anyhow::bail;
3use anyhow::Result;
4use serde::{Deserialize, Serialize};
5use vault::fact::MemoFact;
6
7#[derive(Debug, Serialize, Deserialize)]
8pub struct SyncPushPayload {
9 pub project_hash: String,
10 pub facts: Vec<MemoFact>,
11 pub client_version: String,
12}
13
14#[derive(Debug, Serialize, Deserialize)]
15pub struct SyncPullResponse {
16 pub facts: Vec<MemoFact>,
17 pub server_version: u64,
18}
19
20#[derive(Debug, Serialize, Deserialize)]
21pub struct SyncPushResponse {
22 pub accepted: usize,
23 pub merged: usize,
24 pub server_version: u64,
25}
26
27#[cfg(feature = "cloud-server")]
29pub async fn push_vault(
30 endpoint: &str,
31 token: &str,
32 project_hash: &str,
33 facts: Vec<MemoFact>,
34) -> Result<SyncPushResponse> {
35 let payload = SyncPushPayload {
36 project_hash: project_hash.to_string(),
37 facts,
38 client_version: env!("CARGO_PKG_VERSION").to_string(),
39 };
40 let resp = reqwest::Client::builder()
41 .timeout(std::time::Duration::from_secs(15))
42 .connect_timeout(std::time::Duration::from_secs(8))
43 .build()?
44 .post(format!("{endpoint}/sync/vault"))
45 .bearer_auth(token)
46 .json(&payload)
47 .send()
48 .await?
49 .error_for_status()?
50 .json::<SyncPushResponse>()
51 .await?;
52 Ok(resp)
53}
54
55#[cfg(not(feature = "cloud-server"))]
56pub async fn push_vault(
57 _endpoint: &str,
58 _token: &str,
59 _project_hash: &str,
60 _facts: Vec<MemoFact>,
61) -> Result<SyncPushResponse> {
62 bail!("cloud-server feature not enabled")
63}
64
65#[cfg(feature = "cloud-server")]
67pub async fn pull_vault(
68 endpoint: &str,
69 token: &str,
70 project_hash: &str,
71) -> Result<SyncPullResponse> {
72 let resp = reqwest::Client::builder()
73 .timeout(std::time::Duration::from_secs(15))
74 .connect_timeout(std::time::Duration::from_secs(8))
75 .build()?
76 .get(format!("{endpoint}/sync/vault/{project_hash}"))
77 .bearer_auth(token)
78 .send()
79 .await?
80 .error_for_status()?
81 .json::<SyncPullResponse>()
82 .await?;
83 Ok(resp)
84}
85
86#[cfg(not(feature = "cloud-server"))]
87pub async fn pull_vault(
88 _endpoint: &str,
89 _token: &str,
90 _project_hash: &str,
91) -> Result<SyncPullResponse> {
92 bail!("cloud-server feature not enabled")
93}
94
95#[cfg(feature = "cloud-server")]
98pub fn push_usage_snapshot(
99 endpoint: &str,
100 token: &str,
101 snapshot: &crate::usage::UsageSnapshot,
102) -> anyhow::Result<()> {
103 let rt = tokio::runtime::Builder::new_current_thread()
104 .enable_all()
105 .build()?;
106 rt.block_on(async {
107 let client = reqwest::Client::builder()
108 .timeout(std::time::Duration::from_secs(10))
109 .connect_timeout(std::time::Duration::from_secs(5))
110 .build()?;
111 let resp: serde_json::Value = client
112 .post(format!("{endpoint}/sync/usage"))
113 .bearer_auth(token)
114 .json(snapshot)
115 .send()
116 .await?
117 .error_for_status()?
118 .json()
119 .await?;
120 if resp["ok"] == serde_json::json!(true) {
121 Ok(())
122 } else {
123 anyhow::bail!("snapshot rejected: {}", resp["reason"])
124 }
125 })
126}
127
128#[cfg(not(feature = "cloud-server"))]
129pub fn push_usage_snapshot(
130 _endpoint: &str,
131 _token: &str,
132 _snapshot: &crate::usage::UsageSnapshot,
133) -> anyhow::Result<()> {
134 Ok(())
135}