feat(db): port SQLite community helpers

Signed-off-by: npub13n66s06epmqf2kc3v373ez8hj65cuzyvxzjf93vwpervxqn2u7jq2qd9je <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz>
This commit is contained in:
npub13n66s06epmqf2kc3v373ez8hj65cuzyvxzjf93vwpervxqn2u7jq2qd9je
2026-08-10 12:12:07 -04:00
committed by Brother Darryl
parent b4f619f82c
commit 3cf000f865
2 changed files with 103 additions and 0 deletions
+9
View File
@@ -1437,6 +1437,9 @@ impl Db {
/// Set by relay admins/owners via the kind:9033 command; the value is
/// validated and size-capped at that write path.
pub async fn get_community_icon(&self, community_id: CommunityId) -> Result<Option<String>> {
if let DbBackend::SQLite(pool) = &self.backend {
return sqlite::lookup_community_icon(pool, community_id).await;
}
let row = sqlx::query(
r#"
SELECT icon
@@ -1461,6 +1464,9 @@ impl Db {
community_id: CommunityId,
icon: Option<&str>,
) -> Result<()> {
if let DbBackend::SQLite(pool) = &self.backend {
return sqlite::set_community_icon(pool, community_id, icon).await;
}
sqlx::query(
r#"
UPDATE communities
@@ -1666,6 +1672,9 @@ impl Db {
/// Internal relay producers use this to derive tenant context from the row
/// they are acting on, rather than falling back to an implicit default.
pub async fn community_of_channel(&self, channel_id: Uuid) -> Result<Option<CommunityId>> {
if let DbBackend::SQLite(pool) = &self.backend {
return sqlite::community_of_channel(pool, channel_id).await;
}
let row = sqlx::query(
r#"
SELECT community_id
+94
View File
@@ -479,6 +479,52 @@ pub(crate) async fn is_community_active(
Ok(count != 0)
}
pub(crate) async fn lookup_community_icon(
pool: &SqlitePool,
community: CommunityId,
) -> Result<Option<String>> {
Ok(
sqlx::query_scalar::<_, Option<String>>("SELECT icon FROM communities WHERE id = ?1")
.bind(community.as_uuid().to_string())
.fetch_optional(pool)
.await?
.flatten()
.filter(|icon| !icon.is_empty()),
)
}
pub(crate) async fn set_community_icon(
pool: &SqlitePool,
community: CommunityId,
icon: Option<&str>,
) -> Result<()> {
sqlx::query("UPDATE communities SET icon = ?2 WHERE id = ?1")
.bind(community.as_uuid().to_string())
.bind(icon)
.execute(pool)
.await?;
Ok(())
}
pub(crate) async fn community_of_channel(
pool: &SqlitePool,
channel_id: Uuid,
) -> Result<Option<CommunityId>> {
let community: Option<String> = sqlx::query_scalar(
"SELECT community_id FROM channels WHERE id = ?1 AND deleted_at IS NULL",
)
.bind(channel_id.to_string())
.fetch_optional(pool)
.await?;
community
.map(|id| {
Uuid::parse_str(&id)
.map(CommunityId::from_uuid)
.map_err(|e| crate::DbError::InvalidData(e.to_string()))
})
.transpose()
}
pub(crate) async fn ensure_configured_community(
pool: &SqlitePool,
normalized_host: &str,
@@ -4582,6 +4628,54 @@ mod tests {
std::fs::remove_file(path).unwrap();
}
#[tokio::test]
async fn community_icon_and_channel_owner_helpers_match_lifecycle_contracts() {
let pool = connect("sqlite::memory:").await.unwrap();
let community = ensure_configured_community(&pool, "community-helpers.example")
.await
.unwrap()
.id;
assert_eq!(lookup_community_icon(&pool, community).await.unwrap(), None);
set_community_icon(&pool, community, Some("https://example.test/icon.png"))
.await
.unwrap();
assert_eq!(
lookup_community_icon(&pool, community)
.await
.unwrap()
.as_deref(),
Some("https://example.test/icon.png")
);
set_community_icon(&pool, community, Some(""))
.await
.unwrap();
assert_eq!(lookup_community_icon(&pool, community).await.unwrap(), None);
set_community_icon(&pool, community, None).await.unwrap();
let channel = Uuid::new_v4();
sqlx::query("INSERT INTO channels (id,community_id,name,channel_type,visibility,created_by) VALUES (?1,?2,'helper','public','public',?3)")
.bind(channel.to_string())
.bind(community.as_uuid().to_string())
.bind(vec![1_u8; 32])
.execute(&pool)
.await
.unwrap();
assert_eq!(
community_of_channel(&pool, channel).await.unwrap(),
Some(community)
);
sqlx::query("UPDATE channels SET deleted_at = unixepoch() WHERE id = ?1")
.bind(channel.to_string())
.execute(&pool)
.await
.unwrap();
assert_eq!(community_of_channel(&pool, channel).await.unwrap(), None);
assert_eq!(
community_of_channel(&pool, Uuid::new_v4()).await.unwrap(),
None
);
}
#[tokio::test]
async fn allowlist_is_idempotent_and_community_scoped() {
let pool = connect("sqlite::memory:").await.unwrap();