refactor(db): move relay invite store ownership

Signed-off-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
OpenAI Codex
2026-08-26 14:53:48 +00:00
committed by Luke Tornquist
parent cbc9c4136c
commit eacd5739e6
2 changed files with 49 additions and 49 deletions

View File

@@ -1335,54 +1335,6 @@ impl Db {
Ok(result.rows_affected())
}
/// Mints a v2 use-limited relay invite. The plaintext code is returned
/// exactly once; only its SHA-256 hash is persisted.
///
/// `max_uses` is `None` for unlimited or `Some(1..=10000)`.
/// `ttl_secs` must be in the shared invite lifetime range.
#[datastore_span(name = "mint_relay_invite", system = "postgresql")]
pub async fn mint_relay_invite(
&self,
community: CommunityId,
created_by: &str,
ttl_secs: u64,
max_uses: Option<i32>,
) -> Result<relay_invite::MintedInvite> {
relay_invite::mint_relay_invite(&self.pool, community, created_by, ttl_secs, max_uses).await
}
/// Delete one bounded batch of invites expired before `cutoff`.
#[datastore_span(name = "reap_expired_relay_invites", system = "postgresql")]
pub async fn reap_expired_relay_invites(
&self,
cutoff: chrono::DateTime<chrono::Utc>,
) -> Result<u64> {
relay_invite::reap_expired_relay_invites(&self.pool, cutoff).await
}
/// Atomically claims a v2 relay invite. The full redemption (membership
/// insert, policy evidence, use_count increment) runs in one PostgreSQL
/// transaction with `FOR UPDATE` on the invite row.
///
/// `token_hash` is the SHA-256 of the presented v2 code (32 bytes).
#[datastore_span(name = "claim_relay_invite", system = "postgresql")]
pub async fn claim_relay_invite(
&self,
community: CommunityId,
token_hash: &[u8; 32],
claimer_pubkey: &str,
policy_version: Option<&str>,
) -> Result<relay_invite::ClaimOutcome> {
relay_invite::claim_relay_invite(
&self.pool,
community,
token_hash,
claimer_pubkey,
policy_version,
)
.await
}
/// Sidecar an accepted product-feedback event, idempotent by event id.
#[datastore_span(name = "insert_product_feedback", system = "postgresql")]
pub async fn insert_product_feedback(

View File

@@ -21,11 +21,12 @@ use buzz_core::invite::{
encode_v2_code, hash_v2_code, MAX_INVITE_TTL_SECS, MAX_INVITE_USES, MIN_INVITE_TTL_SECS,
V2_SECRET_LEN,
};
use buzz_datastore_tracing::datastore_span;
use chrono::{DateTime, Utc};
use sqlx::{PgPool, Row as _};
use crate::error::Result;
use crate::CommunityId;
use crate::{CommunityId, Db};
/// Outcome of a v2 invite claim. Expected invalid/expired/exhausted states are
/// typed variants so the relay layer can map them to distinct HTTP responses
@@ -380,6 +381,53 @@ pub async fn claim_relay_invite(
})
}
impl Db {
/// Mints a v2 use-limited relay invite. The plaintext code is returned
/// exactly once; only its SHA-256 hash is persisted.
///
/// `max_uses` is `None` for unlimited or `Some(1..=10000)`.
/// `ttl_secs` must be in the shared invite lifetime range.
#[datastore_span(name = "mint_relay_invite", system = "postgresql")]
pub async fn mint_relay_invite(
&self,
community: CommunityId,
created_by: &str,
ttl_secs: u64,
max_uses: Option<i32>,
) -> Result<MintedInvite> {
mint_relay_invite(&self.pool, community, created_by, ttl_secs, max_uses).await
}
/// Delete one bounded batch of invites expired before `cutoff`.
#[datastore_span(name = "reap_expired_relay_invites", system = "postgresql")]
pub async fn reap_expired_relay_invites(&self, cutoff: DateTime<Utc>) -> Result<u64> {
reap_expired_relay_invites(&self.pool, cutoff).await
}
/// Atomically claims a v2 relay invite. The full redemption (membership
/// insert, policy evidence, use_count increment) runs in one PostgreSQL
/// transaction with `FOR UPDATE` on the invite row.
///
/// `token_hash` is the SHA-256 of the presented v2 code (32 bytes).
#[datastore_span(name = "claim_relay_invite", system = "postgresql")]
pub async fn claim_relay_invite(
&self,
community: CommunityId,
token_hash: &[u8; 32],
claimer_pubkey: &str,
policy_version: Option<&str>,
) -> Result<ClaimOutcome> {
claim_relay_invite(
&self.pool,
community,
token_hash,
claimer_pubkey,
policy_version,
)
.await
}
}
#[cfg(test)]
mod tests {
use super::*;