mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
Merge remote-tracking branch 'origin/rewrite/db-community-scope' into rewrite/relay-wiring
* origin/rewrite/db-community-scope: feat(db): add community host lookup seam
This commit is contained in:
commit
830c04bfb0
@@ -47,7 +47,7 @@ use sqlx::{PgPool, QueryBuilder, Row};
|
||||
use std::time::Duration;
|
||||
use uuid::Uuid;
|
||||
|
||||
use buzz_core::StoredEvent;
|
||||
use buzz_core::{CommunityId, StoredEvent};
|
||||
|
||||
/// Extract p-tag mentions from an event and insert into the `event_mentions` table.
|
||||
///
|
||||
@@ -162,6 +162,15 @@ impl Default for DbConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// Community host-map row returned by [`Db::lookup_community_by_host`].
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CommunityRecord {
|
||||
/// Stable server-resolved community id.
|
||||
pub id: CommunityId,
|
||||
/// Normalized host that maps to this community.
|
||||
pub host: String,
|
||||
}
|
||||
|
||||
/// Token summary returned by [`Db::list_active_tokens`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TokenSummary {
|
||||
@@ -216,6 +225,67 @@ impl Db {
|
||||
self.pool.begin().await.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Returns the community mapped to a normalized request host, if one exists.
|
||||
///
|
||||
/// The caller owns host normalization and turns `None` into the fail-closed
|
||||
/// request/connection error. buzz-db only reads the durable host map.
|
||||
pub async fn lookup_community_by_host(
|
||||
&self,
|
||||
normalized_host: &str,
|
||||
) -> Result<Option<CommunityRecord>> {
|
||||
let row = sqlx::query(
|
||||
r#"
|
||||
SELECT id, host
|
||||
FROM communities
|
||||
WHERE host = $1
|
||||
"#,
|
||||
)
|
||||
.bind(normalized_host)
|
||||
.fetch_optional(&self.pool)
|
||||
.await?;
|
||||
|
||||
row.map(|row| {
|
||||
let id: Uuid = row.try_get("id")?;
|
||||
let host: String = row.try_get("host")?;
|
||||
|
||||
Ok(CommunityRecord {
|
||||
id: CommunityId::from_uuid(id),
|
||||
host,
|
||||
})
|
||||
})
|
||||
.transpose()
|
||||
}
|
||||
|
||||
/// Ensure a configured community host exists and return its row.
|
||||
///
|
||||
/// This is the startup/config seeding path for N=1 deployments. Migrations
|
||||
/// create the schema only; deployment-specific hosts are not hardcoded into
|
||||
/// schema history.
|
||||
pub async fn ensure_configured_community(
|
||||
&self,
|
||||
normalized_host: &str,
|
||||
) -> Result<CommunityRecord> {
|
||||
let row = sqlx::query(
|
||||
r#"
|
||||
INSERT INTO communities (host)
|
||||
VALUES ($1)
|
||||
ON CONFLICT (host) DO UPDATE SET host = EXCLUDED.host
|
||||
RETURNING id, host
|
||||
"#,
|
||||
)
|
||||
.bind(normalized_host)
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
|
||||
let id: Uuid = row.try_get("id")?;
|
||||
let host: String = row.try_get("host")?;
|
||||
|
||||
Ok(CommunityRecord {
|
||||
id: CommunityId::from_uuid(id),
|
||||
host,
|
||||
})
|
||||
}
|
||||
|
||||
/// Inserts an event. Returns `(StoredEvent, was_inserted)` — `false` on duplicate.
|
||||
pub async fn insert_event(
|
||||
&self,
|
||||
|
||||
@@ -131,6 +131,13 @@ mod tests {
|
||||
assert_eq!(migrations.len(), 3);
|
||||
assert_eq!(migrations[0].version, 1);
|
||||
assert_eq!(&*migrations[0].description, "initial schema");
|
||||
assert!(
|
||||
migrations[0]
|
||||
.sql
|
||||
.as_str()
|
||||
.contains("CREATE TABLE communities"),
|
||||
"initial schema migration should include tenant host map"
|
||||
);
|
||||
assert!(
|
||||
migrations[0].sql.as_str().contains("CREATE TABLE channels"),
|
||||
"initial schema migration should include Buzz core tables"
|
||||
|
||||
@@ -17,6 +17,17 @@ CREATE TYPE subscription_status AS ENUM ('active', 'paused', 'deleted');
|
||||
CREATE TYPE pause_reason AS ENUM ('user', 'system', 'rate_limit');
|
||||
CREATE TYPE channel_add_policy AS ENUM ('anyone', 'owner_only', 'nobody');
|
||||
|
||||
-- ── Communities ───────────────────────────────────────────────────────────────
|
||||
|
||||
CREATE TABLE communities (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
host VARCHAR(255) NOT NULL UNIQUE,
|
||||
signing_key BYTEA,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT chk_communities_host_not_empty CHECK (LENGTH(TRIM(host)) > 0),
|
||||
CONSTRAINT chk_communities_host_normalized CHECK (host = LOWER(host))
|
||||
);
|
||||
|
||||
-- ── Channels ──────────────────────────────────────────────────────────────────
|
||||
|
||||
CREATE TABLE channels (
|
||||
|
||||
@@ -18,6 +18,17 @@ CREATE TYPE subscription_status AS ENUM ('active', 'paused', 'deleted');
|
||||
CREATE TYPE pause_reason AS ENUM ('user', 'system', 'rate_limit');
|
||||
CREATE TYPE channel_add_policy AS ENUM ('anyone', 'owner_only', 'nobody');
|
||||
|
||||
-- ── Communities ───────────────────────────────────────────────────────────────
|
||||
|
||||
CREATE TABLE communities (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
host VARCHAR(255) NOT NULL UNIQUE,
|
||||
signing_key BYTEA,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT chk_communities_host_not_empty CHECK (LENGTH(TRIM(host)) > 0),
|
||||
CONSTRAINT chk_communities_host_normalized CHECK (host = LOWER(host))
|
||||
);
|
||||
|
||||
-- ── Channels ──────────────────────────────────────────────────────────────────
|
||||
|
||||
CREATE TABLE channels (
|
||||
|
||||
Reference in New Issue
Block a user