diff --git a/blahd/docs/webapi.yaml b/blahd/docs/webapi.yaml index 81f8383..6788f65 100644 --- a/blahd/docs/webapi.yaml +++ b/blahd/docs/webapi.yaml @@ -179,7 +179,7 @@ paths: 200: content: application/json: - type: integer + type: string description: Created chat id (cid). 400: description: Body is invalid or fails the verification. diff --git a/blahd/schema.sql b/blahd/schema.sql index 7ebc5cf..9fe5ea0 100644 --- a/blahd/schema.sql +++ b/blahd/schema.sql @@ -23,7 +23,7 @@ CREATE TABLE IF NOT EXISTS `room_member` ( CREATE INDEX IF NOT EXISTS `member_room` ON `room_member` (`uid` ASC, `rid` ASC); CREATE TABLE IF NOT EXISTS `room_item` ( - `cid` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + `cid` INTEGER NOT NULL PRIMARY KEY, `rid` INTEGER NOT NULL REFERENCES `room` ON DELETE CASCADE, `uid` INTEGER NOT NULL REFERENCES `user` ON DELETE RESTRICT, `timestamp` INTEGER NOT NULL, diff --git a/blahd/src/database.rs b/blahd/src/database.rs index 8c646ae..40a6c6d 100644 --- a/blahd/src/database.rs +++ b/blahd/src/database.rs @@ -10,7 +10,7 @@ static INIT_SQL: &str = include_str!("../schema.sql"); // Simple and stupid version check for now. // `echo -n 'blahd-database-0' | sha256sum | head -c5` || version -const APPLICATION_ID: i32 = 0xd9e_8401; +const APPLICATION_ID: i32 = 0xd9e_8402; #[derive(Debug)] pub struct Database { diff --git a/blahd/src/id.rs b/blahd/src/id.rs new file mode 100644 index 0000000..8618711 --- /dev/null +++ b/blahd/src/id.rs @@ -0,0 +1,21 @@ +/// Id generation. +/// Ref: https://en.wikipedia.org/wiki/Snowflake_ID +/// FIXME: Currently we assume no more than one request in a single millisecond. +use std::time::SystemTime; + +use blah::types::Id; + +pub trait IdExt { + fn gen() -> Self; +} + +impl IdExt for Id { + fn gen() -> Self { + let timestamp = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .expect("after UNIX epoch"); + let timestamp_ms = timestamp.as_millis() as i64; + assert!(timestamp_ms > 0); + Id(timestamp_ms << 16) + } +} diff --git a/blahd/src/main.rs b/blahd/src/main.rs index 97bc3aa..f17a892 100644 --- a/blahd/src/main.rs +++ b/blahd/src/main.rs @@ -18,6 +18,7 @@ use blah::types::{ use config::Config; use database::Database; use ed25519_dalek::SIGNATURE_LENGTH; +use id::IdExt; use middleware::{ApiError, MaybeAuth, ResultExt as _, SignedJson}; use parking_lot::Mutex; use rusqlite::{named_params, params, Connection, OptionalExtension, Row, ToSql}; @@ -30,6 +31,7 @@ mod middleware; mod config; mod database; mod event; +mod id; mod utils; /// Blah Chat Server @@ -356,17 +358,18 @@ async fn room_create( }; let txn = conn.transaction()?; + let rid = Id::gen(); txn.execute( r" - INSERT INTO `room` (`title`, `attrs`) - VALUES (:title, :attrs) + INSERT INTO `room` (`rid`, `title`, `attrs`) + VALUES (:rid, :title, :attrs) ", named_params! { + ":rid": rid, ":title": params.signee.payload.title, ":attrs": params.signee.payload.attrs, }, )?; - let rid = Id(txn.last_insert_rowid()); let mut insert_user = txn.prepare( r" INSERT INTO `user` (`userkey`) @@ -664,7 +667,7 @@ async fn room_post_item( st: ArcState, R(Path(rid), _): RE>, SignedJson(chat): SignedJson, -) -> Result, ApiError> { +) -> Result, ApiError> { if rid != chat.signee.payload.room { return Err(error_response!( StatusCode::BAD_REQUEST, @@ -705,13 +708,14 @@ async fn room_post_item( )); }; - let cid = conn.query_row( + let cid = Id::gen(); + conn.execute( r" - INSERT INTO `room_item` (`rid`, `uid`, `timestamp`, `nonce`, `sig`, `rich_text`) - VALUES (:rid, :uid, :timestamp, :nonce, :sig, :rich_text) - RETURNING `cid` + INSERT INTO `room_item` (`cid`, `rid`, `uid`, `timestamp`, `nonce`, `sig`, `rich_text`) + VALUES (:cid, :rid, :uid, :timestamp, :nonce, :sig, :rich_text) ", named_params! { + ":cid": cid, ":rid": rid, ":uid": uid, ":timestamp": chat.signee.timestamp, @@ -719,7 +723,6 @@ async fn room_post_item( ":rich_text": &chat.signee.payload.rich_text, ":sig": chat.sig, }, - |row| row.get::<_, u64>(0), )?; // FIXME: Optimize this to not traverses over all members.