diff --git a/crates/buzz-db/src/channel.rs b/crates/buzz-db/src/channel.rs index bdbe812d4..8ffc5866f 100644 --- a/crates/buzz-db/src/channel.rs +++ b/crates/buzz-db/src/channel.rs @@ -733,10 +733,12 @@ pub struct BotChannelEntry { } /// A channel archived by the ephemeral-channel reaper. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct ReapedEphemeralChannel { /// Community that owns the archived channel. pub community_id: CommunityId, + /// Normalized host mapped to that community. + pub host: String, /// Archived channel UUID. pub channel_id: Uuid, } @@ -1325,17 +1327,19 @@ pub async fn bump_ttl_deadline( /// Archive ephemeral channels whose TTL deadline has passed. /// -/// Returns the `(community_id, channel_id)` list that was archived. Idempotent — the +/// Returns the `(community_id, host, channel_id)` list that was archived. Idempotent — the /// `archived_at IS NULL` guard prevents double-archiving even if called /// concurrently from multiple relay pods. pub async fn reap_expired_ephemeral_channels(pool: &PgPool) -> Result> { let rows = sqlx::query( - "UPDATE channels SET archived_at = NOW() \ - WHERE ttl_seconds IS NOT NULL \ - AND ttl_deadline < NOW() \ - AND archived_at IS NULL \ - AND deleted_at IS NULL \ - RETURNING community_id, id", + "UPDATE channels AS ch SET archived_at = NOW() \ + FROM communities AS c \ + WHERE ch.community_id = c.id \ + AND ch.ttl_seconds IS NOT NULL \ + AND ch.ttl_deadline < NOW() \ + AND ch.archived_at IS NULL \ + AND ch.deleted_at IS NULL \ + RETURNING ch.community_id, c.host, ch.id", ) .fetch_all(pool) .await?; @@ -1343,9 +1347,11 @@ pub async fn reap_expired_ephemeral_channels(pool: &PgPool) -> Result, } +impl EventQuery { + /// Construct an unconstrained query inside a server-resolved community. + /// + /// `community_id` has no safe default. This keeps call sites concise while + /// making tenant provenance explicit at construction. + #[must_use] + pub const fn for_community(community_id: CommunityId) -> Self { + Self { + community_id, + channel_id: None, + kinds: None, + pubkey: None, + since: None, + until: None, + limit: None, + offset: None, + p_tag_hex: None, + d_tag: None, + d_tags: None, + before_id: None, + global_only: false, + authors: None, + ids: None, + e_tags: None, + channel_ids: None, + max_limit: None, + } + } +} + /// Maximum length for a `d_tag` value (bytes). NIP-33 d-tags are short identifiers; /// anything beyond this is either a bug or abuse. pub const D_TAG_MAX_LEN: usize = 1024;