TLS 1.3, channel persistence, membership signaling

- TLS 1.3 on TCP (rustls + tokio-rustls, optional self-signed cert)
- Server-side channel CRUD with SQLite persistence
- Gateway → voice-node membership bridge via broadcast channel
This commit is contained in:
loki5512344 2026-07-09 12:48:18 +02:00
parent 523680e05d
commit 5534cd01f7
Signed by: boba
GPG key ID: 253067914055423B
37 changed files with 771 additions and 160 deletions

View file

@ -35,7 +35,13 @@ impl Storage {
CREATE TABLE IF NOT EXISTS users (
pubkey TEXT PRIMARY KEY, nickname TEXT NOT NULL, first_seen INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS bans (
CREATE TABLE IF NOT EXISTS channels (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
kind TEXT NOT NULL DEFAULT 'text',
created_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS bans (
pubkey TEXT PRIMARY KEY, reason TEXT, banned_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS direct_messages (
@ -215,6 +221,68 @@ pub(crate) fn generate_invite_code() -> String {
format!("{:08x}", hash % 0x100000000u64)
}
use crate::domain::channels::{ChannelKind, ChannelStore};
#[derive(Debug, Clone)]
pub struct ChannelRecord {
pub id: String,
pub name: String,
pub kind: String,
pub created_at: i64,
}
impl Storage {
pub async fn create_channel(&self, id: &str, name: &str, kind: &str) -> Result<bool> {
let result = sqlx::query(
"INSERT OR IGNORE INTO channels (id, name, kind, created_at) VALUES (?, ?, ?, ?)",
)
.bind(id)
.bind(name)
.bind(kind)
.bind(now_ms())
.execute(&self.pool)
.await?;
Ok(result.rows_affected() > 0)
}
pub async fn delete_channel(&self, id: &str) -> Result<bool> {
let result = sqlx::query("DELETE FROM channels WHERE id=?")
.bind(id)
.execute(&self.pool)
.await?;
Ok(result.rows_affected() > 0)
}
pub async fn list_channels(&self) -> Result<Vec<ChannelRecord>> {
let rows = sqlx::query_as::<_, (String, String, String, i64)>(
"SELECT id, name, kind, created_at FROM channels",
)
.fetch_all(&self.pool)
.await?
.into_iter()
.map(|(id, name, kind, created_at)| ChannelRecord {
id,
name,
kind,
created_at,
})
.collect();
Ok(rows)
}
pub async fn load_channels_to_cache(&self, channel_store: &ChannelStore) -> Result<()> {
let channels = self.list_channels().await?;
for ch in channels {
let kind = match ch.kind.as_str() {
"voice" => ChannelKind::Voice,
_ => ChannelKind::Text,
};
crate::domain::channels::create(channel_store, &ch.id, &ch.name, kind).await;
}
Ok(())
}
}
pub(crate) fn now_ms() -> i64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)