Message store
This commit is contained in:
Generated
+102
@@ -2,6 +2,18 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.8.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"version_check",
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.104"
|
version = "1.0.104"
|
||||||
@@ -111,6 +123,16 @@ version = "1.12.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
|
checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d"
|
||||||
|
dependencies = [
|
||||||
|
"find-msvc-tools",
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
@@ -229,18 +251,37 @@ dependencies = [
|
|||||||
"bs58",
|
"bs58",
|
||||||
"ed25519-dalek",
|
"ed25519-dalek",
|
||||||
"rand 0.8.7",
|
"rand 0.8.7",
|
||||||
|
"rusqlite",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fallible-iterator"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fallible-streaming-iterator"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fiat-crypto"
|
name = "fiat-crypto"
|
||||||
version = "0.2.9"
|
version = "0.2.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
|
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "find-msvc-tools"
|
||||||
|
version = "0.1.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "form_urlencoded"
|
name = "form_urlencoded"
|
||||||
version = "1.2.2"
|
version = "1.2.2"
|
||||||
@@ -323,6 +364,24 @@ dependencies = [
|
|||||||
"wasip2",
|
"wasip2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashlink"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -421,6 +480,17 @@ version = "0.2.189"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libsqlite3-sys"
|
||||||
|
version = "0.28.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.33"
|
version = "0.4.33"
|
||||||
@@ -494,6 +564,12 @@ dependencies = [
|
|||||||
"spki",
|
"spki",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.21"
|
version = "0.2.21"
|
||||||
@@ -586,6 +662,20 @@ dependencies = [
|
|||||||
"getrandom 0.3.4",
|
"getrandom 0.3.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rusqlite"
|
||||||
|
version = "0.31.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"fallible-iterator",
|
||||||
|
"fallible-streaming-iterator",
|
||||||
|
"hashlink",
|
||||||
|
"libsqlite3-sys",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@@ -695,6 +785,12 @@ dependencies = [
|
|||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signature"
|
name = "signature"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
@@ -963,6 +1059,12 @@ version = "1.0.24"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcpkg"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.5"
|
version = "0.9.5"
|
||||||
|
|||||||
@@ -13,3 +13,4 @@ ed25519-dalek = { version = "2", features = ["rand_core"] }
|
|||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
bs58 = "0.5.1"
|
bs58 = "0.5.1"
|
||||||
tower-http = { version = "0.7.0", features = ["fs", "cors"] }
|
tower-http = { version = "0.7.0", features = ["fs", "cors"] }
|
||||||
|
rusqlite = { version = "0.31", features = ["bundled"] }
|
||||||
|
|||||||
@@ -0,0 +1,112 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use rusqlite::{params, Connection};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
pub struct MessageStore {
|
||||||
|
data_dir: PathBuf,
|
||||||
|
connections: Mutex<HashMap<String, Connection>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct StoredMessage {
|
||||||
|
pub id: String,
|
||||||
|
pub author_pubkey: String,
|
||||||
|
pub content: String,
|
||||||
|
pub timestamp: i64,
|
||||||
|
pub signature: String,
|
||||||
|
pub channel_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageStore {
|
||||||
|
pub fn new(data_dir: PathBuf) -> Result<Self> {
|
||||||
|
std::fs::create_dir_all(&data_dir)?;
|
||||||
|
Ok(Self {
|
||||||
|
data_dir,
|
||||||
|
connections: Mutex::new(HashMap::new()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Opens (or reuses an already-open) connection for a channel,
|
||||||
|
/// creating the schema if this is the first time.
|
||||||
|
fn with_channel<T>(
|
||||||
|
&self,
|
||||||
|
channel_id: &str,
|
||||||
|
f: impl FnOnce(&Connection) -> Result<T>,
|
||||||
|
) -> Result<T> {
|
||||||
|
let mut conns = self.connections.lock().unwrap();
|
||||||
|
|
||||||
|
if !conns.contains_key(channel_id) {
|
||||||
|
let path = self.data_dir.join(format!("{channel_id}.db"));
|
||||||
|
let conn = Connection::open(&path)?;
|
||||||
|
|
||||||
|
conn.execute(
|
||||||
|
"CREATE TABLE IF NOT EXISTS messages (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
author_pubkey TEXT NOT NULL,
|
||||||
|
content TEXT NOT NULL,
|
||||||
|
timestamp INTEGER NOT NULL,
|
||||||
|
signature TEXT NOT NULL,
|
||||||
|
channel_id TEXT NOT NULL
|
||||||
|
)",
|
||||||
|
[],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
conn.execute(
|
||||||
|
"CREATE INDEX IF NOT EXISTS idx_messages_timestamp ON messages(timestamp)",
|
||||||
|
[],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
conns.insert(channel_id.to_string(), conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
let conn = conns.get(channel_id).unwrap();
|
||||||
|
f(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_message(&self, channel_id: &str, msg: &StoredMessage) -> Result<()> {
|
||||||
|
self.with_channel(channel_id, |conn| {
|
||||||
|
conn.execute(
|
||||||
|
"INSERT INTO messages (id, author_pubkey, content, timestamp, signature, channel_id)
|
||||||
|
VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
|
||||||
|
params![
|
||||||
|
msg.id,
|
||||||
|
msg.author_pubkey,
|
||||||
|
msg.content,
|
||||||
|
msg.timestamp,
|
||||||
|
msg.signature,
|
||||||
|
msg.channel_id,
|
||||||
|
],
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetches the most recent `limit` messages, oldest-first (ready to render top-to-bottom).
|
||||||
|
pub fn get_recent_messages(&self, channel_id: &str, limit: u32) -> Result<Vec<StoredMessage>> {
|
||||||
|
self.with_channel(channel_id, |conn| {
|
||||||
|
let mut stmt = conn.prepare(
|
||||||
|
"SELECT id, author_pubkey, content, timestamp, signature, channel_id
|
||||||
|
FROM messages
|
||||||
|
ORDER BY timestamp DESC
|
||||||
|
LIMIT ?1",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let rows = stmt.query_map(params![limit], |row| {
|
||||||
|
Ok(StoredMessage {
|
||||||
|
id: row.get(0)?,
|
||||||
|
author_pubkey: row.get(1)?,
|
||||||
|
content: row.get(2)?,
|
||||||
|
timestamp: row.get(3)?,
|
||||||
|
signature: row.get(4)?,
|
||||||
|
channel_id: row.get(5)?,
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut messages: Vec<StoredMessage> = rows.collect::<Result<_, _>>()?;
|
||||||
|
messages.reverse(); // DESC query, then flip to oldest-first for display
|
||||||
|
Ok(messages)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1 +1,2 @@
|
|||||||
pub mod config;
|
pub mod config;
|
||||||
|
pub mod messages;
|
||||||
|
|||||||
+4
-1
@@ -1,5 +1,6 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
path::PathBuf,
|
||||||
sync::{Arc, atomic::AtomicU16},
|
sync::{Arc, atomic::AtomicU16},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -11,7 +12,7 @@ use ed25519_dalek::{SigningKey, VerifyingKey};
|
|||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
data::config::Config,
|
data::{config::Config, messages::MessageStore},
|
||||||
protocol::{ClientMethod, read_loop, send_socket},
|
protocol::{ClientMethod, read_loop, send_socket},
|
||||||
types::ClientMeta,
|
types::ClientMeta,
|
||||||
};
|
};
|
||||||
@@ -27,6 +28,7 @@ pub struct Server {
|
|||||||
pub key: SigningKey,
|
pub key: SigningKey,
|
||||||
pub config: Config,
|
pub config: Config,
|
||||||
pub clients: Mutex<HashMap<VerifyingKey, UserConnections>>,
|
pub clients: Mutex<HashMap<VerifyingKey, UserConnections>>,
|
||||||
|
pub message_store: MessageStore,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
@@ -35,6 +37,7 @@ impl Server {
|
|||||||
key: crate::signature::get().await?,
|
key: crate::signature::get().await?,
|
||||||
config: Config::get().await?,
|
config: Config::get().await?,
|
||||||
clients: Mutex::new(HashMap::new()),
|
clients: Mutex::new(HashMap::new()),
|
||||||
|
message_store: MessageStore::new(PathBuf::from("item"))?,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user