Files
buzz/desktop/src-tauri/src/relay/submit.rs
T
2026-07-23 12:45:00 -07:00

61 lines
2.0 KiB
Rust

use super::*;
/// Response from `POST /events`.
#[derive(Debug, Deserialize, serde::Serialize)]
pub struct SubmitEventResponse {
pub event_id: String,
pub accepted: bool,
pub message: String,
}
/// Sign with an explicit identity and POST the event to an explicit relay.
///
/// The caller owns the signer lifetime. This is important for deferred work:
/// an in-process identity swap cannot retarget the event or its NIP-98 auth
/// after the caller has validated which identity the operation belongs to.
pub async fn submit_event_at_with_keys(
builder: nostr::EventBuilder,
state: &AppState,
api_base_url: &str,
keys: &nostr::Keys,
) -> Result<SubmitEventResponse, String> {
crate::relay_admission::wait_for_rate_limit().await;
let url = format!("{}/events", api_base_url.trim_end_matches('/'));
let event = builder
.sign_with_keys(keys)
.map_err(|e| format!("failed to sign event: {e}"))?;
let body_bytes = event.as_json().into_bytes();
let auth_header = build_nip98_auth_header_for_keys(keys, &Method::POST, &url, &body_bytes)?;
let response = state
.http_client
.post(&url)
.header("Authorization", auth_header)
.header("Content-Type", "application/json")
.body(body_bytes)
.send()
.await
.map_err(|e| classify_request_error(&e))?;
if !response.status().is_success() {
return Err(relay_error_message(response).await);
}
let result: SubmitEventResponse = parse_json_response(response).await?;
if !result.accepted {
return Err(format!("relay rejected event: {}", result.message));
}
Ok(result)
}
/// Build and submit an event to the currently active workspace relay.
pub async fn submit_event(
builder: nostr::EventBuilder,
state: &AppState,
) -> Result<SubmitEventResponse, String> {
let api_base_url = relay_api_base_url_with_override(state);
let keys = state.signing_keys()?;
submit_event_at_with_keys(builder, state, &api_base_url, &keys).await
}