mirror of
https://github.com/Blah-IM/blahrs.git
synced 2025-05-01 08:41:09 +00:00
Include cid in item responses and update docs
This commit is contained in:
parent
a7f260027d
commit
51e2c8418b
3 changed files with 53 additions and 49 deletions
|
@ -164,7 +164,7 @@ paths:
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: WithSig<ChatPayload>
|
$ref: WithItemId<WithSig<ChatPayload>>
|
||||||
example:
|
example:
|
||||||
sig: 99a77e836538268839ed3419c649eefb043cb51d448f641cc2a1c523811aab4aacd09f92e7c0688ffd659bfc6acb764fea79979a491e132bf6a56dd23adc1d09
|
sig: 99a77e836538268839ed3419c649eefb043cb51d448f641cc2a1c523811aab4aacd09f92e7c0688ffd659bfc6acb764fea79979a491e132bf6a56dd23adc1d09
|
||||||
signee:
|
signee:
|
||||||
|
@ -200,7 +200,7 @@ paths:
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: WithSig<ChatPayload>
|
$ref: WithSig<AdminPayload>
|
||||||
example:
|
example:
|
||||||
sig: 99a77e836538268839ed3419c649eefb043cb51d448f641cc2a1c523811aab4aacd09f92e7c0688ffd659bfc6acb764fea79979a491e132bf6a56dd23adc1d09
|
sig: 99a77e836538268839ed3419c649eefb043cb51d448f641cc2a1c523811aab4aacd09f92e7c0688ffd659bfc6acb764fea79979a491e132bf6a56dd23adc1d09
|
||||||
signee:
|
signee:
|
||||||
|
@ -260,7 +260,7 @@ components:
|
||||||
attrs:
|
attrs:
|
||||||
type: int64
|
type: int64
|
||||||
last_chat:
|
last_chat:
|
||||||
$ref: 'WithSig<ChatPayload>'
|
$ref: 'WithItemId<WithSig<ChatPayload>>'
|
||||||
|
|
||||||
RoomMetadata:
|
RoomMetadata:
|
||||||
type: object
|
type: object
|
||||||
|
@ -280,6 +280,6 @@ components:
|
||||||
items:
|
items:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: 'WithSig<ChatPayload>'
|
$ref: 'WithItemId<WithSig<ChatPayload>>'
|
||||||
skip_token:
|
skip_token:
|
||||||
type: string
|
type: string
|
||||||
|
|
|
@ -13,7 +13,7 @@ use axum::{Json, Router};
|
||||||
use axum_extra::extract::WithRejection as R;
|
use axum_extra::extract::WithRejection as R;
|
||||||
use blah::types::{
|
use blah::types::{
|
||||||
ChatItem, ChatPayload, CreateRoomPayload, Id, MemberPermission, RoomAdminOp, RoomAdminPayload,
|
ChatItem, ChatPayload, CreateRoomPayload, Id, MemberPermission, RoomAdminOp, RoomAdminPayload,
|
||||||
RoomAttrs, ServerPermission, Signee, UserKey, WithSig,
|
RoomAttrs, ServerPermission, Signee, UserKey, WithItemId, WithSig,
|
||||||
};
|
};
|
||||||
use config::Config;
|
use config::Config;
|
||||||
use database::Database;
|
use database::Database;
|
||||||
|
@ -205,7 +205,7 @@ struct ListRoomParams {
|
||||||
filter: ListRoomFilter,
|
filter: ListRoomFilter,
|
||||||
// Workaround: serde(flatten) breaks deserialization
|
// Workaround: serde(flatten) breaks deserialization
|
||||||
// See: https://github.com/nox/serde_urlencoded/issues/33
|
// See: https://github.com/nox/serde_urlencoded/issues/33
|
||||||
skip_token: Option<u64>,
|
skip_token: Option<Id>,
|
||||||
top: Option<NonZeroUsize>,
|
top: Option<NonZeroUsize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,10 +226,9 @@ async fn room_list(
|
||||||
top: params.top,
|
top: params.top,
|
||||||
};
|
};
|
||||||
let page_len = pagination.effective_page_len(&st);
|
let page_len = pagination.effective_page_len(&st);
|
||||||
let start_rid = pagination.skip_token.unwrap_or(0);
|
let start_rid = pagination.skip_token.unwrap_or(Id(0));
|
||||||
|
|
||||||
let query = |sql: &str, params: &[(&str, &dyn ToSql)]| -> Result<RoomList, ApiError> {
|
let query = |sql: &str, params: &[(&str, &dyn ToSql)]| -> Result<RoomList, ApiError> {
|
||||||
let mut last_rid = None;
|
|
||||||
let rooms = st
|
let rooms = st
|
||||||
.db
|
.db
|
||||||
.get()
|
.get()
|
||||||
|
@ -237,21 +236,23 @@ async fn room_list(
|
||||||
.query_map(params, |row| {
|
.query_map(params, |row| {
|
||||||
// TODO: Extract this into a function.
|
// TODO: Extract this into a function.
|
||||||
let rid = row.get("rid")?;
|
let rid = row.get("rid")?;
|
||||||
last_rid = Some(rid);
|
|
||||||
let title = row.get("title")?;
|
let title = row.get("title")?;
|
||||||
let attrs = row.get("attrs")?;
|
let attrs = row.get("attrs")?;
|
||||||
let last_chat = row
|
let last_chat = row
|
||||||
.get::<_, Option<UserKey>>("userkey")?
|
.get::<_, Option<Id>>("cid")?
|
||||||
.map(|user| {
|
.map(|cid| {
|
||||||
Ok::<_, rusqlite::Error>(ChatItem {
|
Ok::<_, rusqlite::Error>(WithItemId {
|
||||||
sig: row.get("sig")?,
|
cid,
|
||||||
signee: Signee {
|
item: ChatItem {
|
||||||
nonce: row.get("nonce")?,
|
sig: row.get("sig")?,
|
||||||
timestamp: row.get("timestamp")?,
|
signee: Signee {
|
||||||
user,
|
nonce: row.get("nonce")?,
|
||||||
payload: ChatPayload {
|
timestamp: row.get("timestamp")?,
|
||||||
rich_text: row.get("rich_text")?,
|
user: row.get("userkey")?,
|
||||||
room: rid,
|
payload: ChatPayload {
|
||||||
|
rich_text: row.get("rich_text")?,
|
||||||
|
room: rid,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -265,7 +266,8 @@ async fn room_list(
|
||||||
})
|
})
|
||||||
})?
|
})?
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
let skip_token = (rooms.len() == page_len).then_some(()).and(last_rid);
|
let skip_token =
|
||||||
|
(rooms.len() == page_len).then(|| rooms.last().expect("page must not be empty").rid);
|
||||||
Ok(RoomList { rooms, skip_token })
|
Ok(RoomList { rooms, skip_token })
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -273,7 +275,7 @@ async fn room_list(
|
||||||
ListRoomFilter::Public => query(
|
ListRoomFilter::Public => query(
|
||||||
r"
|
r"
|
||||||
SELECT `rid`, `title`, `attrs`,
|
SELECT `rid`, `title`, `attrs`,
|
||||||
`last_author`.`userkey` AS `userkey`, `timestamp`, `nonce`, `sig`, `rich_text`
|
`cid`, `last_author`.`userkey`, `timestamp`, `nonce`, `sig`, `rich_text`
|
||||||
FROM `room`
|
FROM `room`
|
||||||
LEFT JOIN `room_item` USING (`rid`)
|
LEFT JOIN `room_item` USING (`rid`)
|
||||||
LEFT JOIN `user` AS `last_author` USING (`uid`)
|
LEFT JOIN `user` AS `last_author` USING (`uid`)
|
||||||
|
@ -295,7 +297,7 @@ async fn room_list(
|
||||||
r"
|
r"
|
||||||
SELECT
|
SELECT
|
||||||
`rid`, `title`, `attrs`,
|
`rid`, `title`, `attrs`,
|
||||||
`last_author`.`userkey`, `timestamp`, `nonce`, `sig`, `rich_text`
|
`cid`, `last_author`.`userkey`, `timestamp`, `nonce`, `sig`, `rich_text`
|
||||||
FROM `user`
|
FROM `user`
|
||||||
JOIN `room_member` USING (`uid`)
|
JOIN `room_member` USING (`uid`)
|
||||||
JOIN `room` USING (`rid`)
|
JOIN `room` USING (`rid`)
|
||||||
|
@ -408,7 +410,7 @@ async fn room_create(
|
||||||
#[serde(deny_unknown_fields, rename_all = "camelCase")]
|
#[serde(deny_unknown_fields, rename_all = "camelCase")]
|
||||||
struct Pagination {
|
struct Pagination {
|
||||||
/// A opaque token from previous response to fetch the next page.
|
/// A opaque token from previous response to fetch the next page.
|
||||||
skip_token: Option<u64>,
|
skip_token: Option<Id>,
|
||||||
/// Maximum page size.
|
/// Maximum page size.
|
||||||
top: Option<NonZeroUsize>,
|
top: Option<NonZeroUsize>,
|
||||||
}
|
}
|
||||||
|
@ -424,9 +426,9 @@ impl Pagination {
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
struct RoomItems {
|
struct RoomItems {
|
||||||
items: Vec<ChatItem>,
|
items: Vec<WithItemId<ChatItem>>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
skip_token: Option<String>,
|
skip_token: Option<Id>,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn room_get_item(
|
async fn room_get_item(
|
||||||
|
@ -440,11 +442,7 @@ async fn room_get_item(
|
||||||
get_room_if_readable(&conn, rid, auth.into_optional()?.as_ref(), |_row| Ok(()))?;
|
get_room_if_readable(&conn, rid, auth.into_optional()?.as_ref(), |_row| Ok(()))?;
|
||||||
query_room_items(&st, &conn, rid, pagination)?
|
query_room_items(&st, &conn, rid, pagination)?
|
||||||
};
|
};
|
||||||
let items = items.into_iter().map(|(_, item)| item).collect();
|
Ok(Json(RoomItems { items, skip_token }))
|
||||||
Ok(Json(RoomItems {
|
|
||||||
items,
|
|
||||||
skip_token: skip_token.map(|x| x.to_string()),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn room_get_metadata(
|
async fn room_get_metadata(
|
||||||
|
@ -482,7 +480,7 @@ async fn room_get_feed(
|
||||||
|
|
||||||
let items = items
|
let items = items
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(cid, item)| {
|
.map(|WithItemId { cid, item }| {
|
||||||
let time = SystemTime::UNIX_EPOCH + Duration::from_secs(item.signee.timestamp);
|
let time = SystemTime::UNIX_EPOCH + Duration::from_secs(item.signee.timestamp);
|
||||||
let author = FeedAuthor {
|
let author = FeedAuthor {
|
||||||
name: item.signee.user.to_string(),
|
name: item.signee.user.to_string(),
|
||||||
|
@ -578,7 +576,7 @@ pub struct RoomMetadata {
|
||||||
|
|
||||||
/// Optional extra information. Only included by the global room list response.
|
/// Optional extra information. Only included by the global room list response.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub last_chat: Option<ChatItem>,
|
pub last_chat: Option<WithItemId<ChatItem>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_room_if_readable<T>(
|
fn get_room_if_readable<T>(
|
||||||
|
@ -610,8 +608,6 @@ fn get_room_if_readable<T>(
|
||||||
.ok_or_else(|| error_response!(StatusCode::NOT_FOUND, "not_found", "room not found"))
|
.ok_or_else(|| error_response!(StatusCode::NOT_FOUND, "not_found", "room not found"))
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChatItemWithId = (u64, ChatItem);
|
|
||||||
|
|
||||||
/// Get room items with pagination parameters,
|
/// Get room items with pagination parameters,
|
||||||
/// return a page of items and the next skip_token if this is not the last page.
|
/// return a page of items and the next skip_token if this is not the last page.
|
||||||
fn query_room_items(
|
fn query_room_items(
|
||||||
|
@ -619,7 +615,7 @@ fn query_room_items(
|
||||||
conn: &Connection,
|
conn: &Connection,
|
||||||
rid: Id,
|
rid: Id,
|
||||||
pagination: Pagination,
|
pagination: Pagination,
|
||||||
) -> Result<(Vec<ChatItemWithId>, Option<u64>), ApiError> {
|
) -> Result<(Vec<WithItemId<ChatItem>>, Option<Id>), ApiError> {
|
||||||
let page_len = pagination.effective_page_len(st);
|
let page_len = pagination.effective_page_len(st);
|
||||||
let mut stmt = conn.prepare(
|
let mut stmt = conn.prepare(
|
||||||
r"
|
r"
|
||||||
|
@ -640,25 +636,26 @@ fn query_room_items(
|
||||||
":limit": page_len,
|
":limit": page_len,
|
||||||
},
|
},
|
||||||
|row| {
|
|row| {
|
||||||
let cid = row.get::<_, u64>("cid")?;
|
Ok(WithItemId {
|
||||||
let item = ChatItem {
|
cid: row.get("cid")?,
|
||||||
sig: row.get("sig")?,
|
item: ChatItem {
|
||||||
signee: Signee {
|
sig: row.get("sig")?,
|
||||||
nonce: row.get("nonce")?,
|
signee: Signee {
|
||||||
timestamp: row.get("timestamp")?,
|
nonce: row.get("nonce")?,
|
||||||
user: row.get("userkey")?,
|
timestamp: row.get("timestamp")?,
|
||||||
payload: ChatPayload {
|
user: row.get("userkey")?,
|
||||||
room: rid,
|
payload: ChatPayload {
|
||||||
rich_text: row.get("rich_text")?,
|
room: rid,
|
||||||
|
rich_text: row.get("rich_text")?,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
})
|
||||||
Ok((cid, item))
|
|
||||||
},
|
},
|
||||||
)?
|
)?
|
||||||
.collect::<rusqlite::Result<Vec<_>>>()?;
|
.collect::<rusqlite::Result<Vec<_>>>()?;
|
||||||
let skip_token =
|
let skip_token =
|
||||||
(items.len() == page_len).then(|| items.last().expect("page must not be empty").0);
|
(items.len() == page_len).then(|| items.last().expect("page must not be empty").cid);
|
||||||
|
|
||||||
Ok((items, skip_token))
|
Ok((items, skip_token))
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,13 @@ impl fmt::Display for Id {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WithItemId<T> {
|
||||||
|
pub cid: Id,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub item: T,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct UserKey(#[serde(with = "hex::serde")] pub [u8; PUBLIC_KEY_LENGTH]);
|
pub struct UserKey(#[serde(with = "hex::serde")] pub [u8; PUBLIC_KEY_LENGTH]);
|
||||||
|
|
Loading…
Add table
Reference in a new issue