mirror of
https://github.com/Blah-IM/blahrs.git
synced 2025-04-30 16:21:10 +00:00
save: add utoipa for OAPI generation
This commit is contained in:
parent
71c5f038fa
commit
acaf0f955a
11 changed files with 212 additions and 610 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -5,6 +5,7 @@
|
|||
*.profraw
|
||||
result
|
||||
result-*
|
||||
/docs/types.json
|
||||
|
||||
# Test configurations.
|
||||
config.toml
|
||||
|
|
25
Cargo.lock
generated
25
Cargo.lock
generated
|
@ -280,6 +280,7 @@ dependencies = [
|
|||
"sha2",
|
||||
"thiserror",
|
||||
"url",
|
||||
"utoipa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2692,6 +2693,30 @@ version = "0.2.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "utoipa"
|
||||
version = "5.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "845494cea2113cf53dbb60904638afc8cb6c36fe39be7bcbb0eca1cfa49c3c1a"
|
||||
dependencies = [
|
||||
"indexmap 2.6.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"utoipa-gen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utoipa-gen"
|
||||
version = "5.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1223ed4a64c622737615a02d062c20813b978d9d39ceced627337449b195771"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.0"
|
||||
|
|
|
@ -7,6 +7,9 @@ members = [
|
|||
]
|
||||
default-members = ["blahd"]
|
||||
|
||||
[workspace.dependencies]
|
||||
utoipa = "5"
|
||||
|
||||
[workspace.lints.clippy]
|
||||
allow_attributes_without_reason = "warn"
|
||||
dbg_macro = "warn"
|
||||
|
|
|
@ -6,6 +6,11 @@ edition = "2021"
|
|||
[features]
|
||||
default = []
|
||||
unsafe_use_mock_instant_for_testing = ["dep:mock_instant"]
|
||||
utoipa = ["dep:utoipa"]
|
||||
|
||||
[[example]]
|
||||
name = "openapi"
|
||||
required-features = ["utoipa"]
|
||||
|
||||
[[bench]]
|
||||
name = "crypto_ops"
|
||||
|
@ -26,6 +31,7 @@ serde_json = "1"
|
|||
serde_with = "3"
|
||||
thiserror = "1"
|
||||
url = { version = "2", features = ["serde"] }
|
||||
utoipa = { workspace = true, optional = true, features = ["url"] } # Generics support.
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.5"
|
||||
|
|
8
blah-types/examples/openapi.rs
Normal file
8
blah-types/examples/openapi.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
#![expect(clippy::print_stdout, reason = "allowed to dump OAPI")]
|
||||
|
||||
fn main() {
|
||||
let json = blah_types::openapi()
|
||||
.to_pretty_json()
|
||||
.expect("serialization cannot fail");
|
||||
println!("{json}");
|
||||
}
|
|
@ -11,6 +11,7 @@ use rand::RngCore;
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct UserKey {
|
||||
pub id_key: PubKey,
|
||||
pub act_key: PubKey,
|
||||
|
@ -18,6 +19,7 @@ pub struct UserKey {
|
|||
|
||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema), schema(value_type = String))]
|
||||
pub struct PubKey(#[serde(with = "hex::serde")] pub [u8; PUBLIC_KEY_LENGTH]);
|
||||
|
||||
impl FromStr for PubKey {
|
||||
|
@ -55,14 +57,17 @@ impl From<&VerifyingKey> for PubKey {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Signed<T> {
|
||||
#[serde(with = "hex::serde")]
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String))]
|
||||
pub sig: [u8; SIGNATURE_LENGTH],
|
||||
pub signee: Signee<T>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Signee<T> {
|
||||
pub nonce: u32,
|
||||
|
|
|
@ -12,6 +12,7 @@ use crate::{PubKey, Signed};
|
|||
/// User identity description structure.
|
||||
// TODO: Revise and shrink duplicates (pubkey fields).
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct UserIdentityDesc {
|
||||
/// User primary identity key, only for signing action keys.
|
||||
pub id_key: PubKey,
|
||||
|
@ -90,6 +91,7 @@ impl UserIdentityDesc {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(tag = "typ", rename = "user_act_key")]
|
||||
pub struct UserActKeyDesc {
|
||||
pub act_key: PubKey,
|
||||
|
@ -98,6 +100,7 @@ pub struct UserActKeyDesc {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(tag = "typ", rename = "user_profile")]
|
||||
pub struct UserProfile {
|
||||
pub preferred_chat_server_urls: Vec<Url>,
|
||||
|
@ -105,6 +108,7 @@ pub struct UserProfile {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema), schema(value_type = Url))]
|
||||
#[serde(try_from = "Url")]
|
||||
pub struct IdUrl(Url);
|
||||
|
||||
|
|
|
@ -10,3 +10,46 @@ pub mod crypto;
|
|||
pub mod identity;
|
||||
pub mod msg;
|
||||
pub mod server;
|
||||
|
||||
#[cfg(feature = "utoipa")]
|
||||
pub fn openapi() -> utoipa::openapi::OpenApi {
|
||||
use utoipa::OpenApi;
|
||||
|
||||
#[derive(OpenApi)]
|
||||
#[openapi(components(schemas(
|
||||
crypto::Signed::<msg::AuthPayload>,
|
||||
crypto::Signed::<msg::ChatPayload>,
|
||||
crypto::Signed::<msg::CreateRoomPayload>,
|
||||
crypto::Signed::<msg::DeleteRoomPayload>,
|
||||
crypto::Signed::<msg::RoomAdminPayload>,
|
||||
crypto::Signed::<msg::UserRegisterPayload>,
|
||||
identity::UserIdentityDesc,
|
||||
identity::UserProfile,
|
||||
msg::AuthPayload,
|
||||
msg::ChatPayload,
|
||||
msg::DeleteRoomPayload,
|
||||
msg::RichText,
|
||||
msg::RoomAdminPayload,
|
||||
msg::UserRegisterPayload,
|
||||
server::ClientEvent,
|
||||
server::ErrorResponse,
|
||||
server::ErrorResponseWithChallenge,
|
||||
server::RoomList,
|
||||
server::RoomMetadata,
|
||||
server::RoomMsgs,
|
||||
server::ServerCapabilities,
|
||||
server::ServerEvent,
|
||||
server::ServerMetadata,
|
||||
)))]
|
||||
struct ApiDoc;
|
||||
|
||||
ApiDoc::openapi()
|
||||
}
|
||||
|
||||
#[cfg(feature = "utoipa")]
|
||||
#[test]
|
||||
#[expect(clippy::print_stdout, reason = "allowed in tests")]
|
||||
fn test_openapi() {
|
||||
let json = crate::openapi().to_pretty_json().unwrap();
|
||||
println!("{json}");
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ use crate::{PubKey, Signed};
|
|||
/// It's currently serialized as a string for JavaScript's convenience.
|
||||
#[serde_as]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema), schema(value_type = String))]
|
||||
#[serde(transparent)]
|
||||
pub struct Id(#[serde_as(as = "DisplayFromStr")] pub i64);
|
||||
|
||||
|
@ -39,6 +40,7 @@ impl Id {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct WithMsgId<T> {
|
||||
pub cid: Id,
|
||||
#[serde(flatten)]
|
||||
|
@ -53,17 +55,20 @@ impl<T> WithMsgId<T> {
|
|||
|
||||
/// Register a user on a chat server.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(tag = "typ", rename = "user_register")]
|
||||
pub struct UserRegisterPayload {
|
||||
pub server_url: Url,
|
||||
pub id_url: IdUrl,
|
||||
pub id_key: PubKey,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
|
||||
pub challenge: Option<UserRegisterChallengeResponse>,
|
||||
}
|
||||
|
||||
/// The server-specific challenge data for registration.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum UserRegisterChallengeResponse {
|
||||
/// Proof of work challenge containing the same nonce from server challenge request.
|
||||
|
@ -73,6 +78,7 @@ pub enum UserRegisterChallengeResponse {
|
|||
|
||||
// FIXME: `deny_unknown_fields` breaks this.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(tag = "typ", rename = "chat")]
|
||||
pub struct ChatPayload {
|
||||
pub rich_text: RichText,
|
||||
|
@ -81,6 +87,7 @@ pub struct ChatPayload {
|
|||
|
||||
/// Ref: <https://github.com/Blah-IM/Weblah/blob/a3fa0f265af54c846f8d65f42aa4409c8dba9dd9/src/lib/richText.ts>
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema), schema(value_type = Vec<RichTextPieceRaw>))]
|
||||
#[serde(transparent)]
|
||||
pub struct RichText(pub Vec<RichTextPiece>);
|
||||
|
||||
|
@ -103,8 +110,9 @@ impl Serialize for RichTextPiece {
|
|||
}
|
||||
}
|
||||
|
||||
/// The protocol representation of `RichTextPiece`.
|
||||
/// The representation on wire of `RichTextPiece`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(untagged)]
|
||||
enum RichTextPieceRaw {
|
||||
Text(String),
|
||||
|
@ -148,6 +156,7 @@ impl<'de> Deserialize<'de> for RichText {
|
|||
|
||||
// TODO: This protocol format is quite large. Could use bitflags for database.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct TextAttrs {
|
||||
#[serde(default, rename = "b", skip_serializing_if = "is_default")]
|
||||
pub bold: bool,
|
||||
|
@ -159,6 +168,7 @@ pub struct TextAttrs {
|
|||
pub italic: bool,
|
||||
// TODO: Should we validate and/or filter the URL.
|
||||
#[serde(default, skip_serializing_if = "is_default")]
|
||||
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
|
||||
pub link: Option<String>,
|
||||
#[serde(default, rename = "s", skip_serializing_if = "is_default")]
|
||||
pub strike: bool,
|
||||
|
@ -271,6 +281,7 @@ pub type SignedChatMsg = Signed<ChatPayload>;
|
|||
pub type SignedChatMsgWithId = WithMsgId<SignedChatMsg>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(tag = "typ")]
|
||||
pub enum CreateRoomPayload {
|
||||
#[serde(rename = "create_room")]
|
||||
|
@ -281,6 +292,7 @@ pub enum CreateRoomPayload {
|
|||
|
||||
/// Multi-user room.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct CreateGroup {
|
||||
pub attrs: RoomAttrs,
|
||||
pub title: String,
|
||||
|
@ -288,11 +300,13 @@ pub struct CreateGroup {
|
|||
|
||||
/// Peer-to-peer chat room with exactly two symmetric users.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct CreatePeerChat {
|
||||
pub peer: PubKey,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(tag = "typ", rename = "delete_room")]
|
||||
pub struct DeleteRoomPayload {
|
||||
pub room: Id,
|
||||
|
@ -302,6 +316,7 @@ pub struct DeleteRoomPayload {
|
|||
/// 1. Sorted by userkeys.
|
||||
/// 2. No duplicated users.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(try_from = "Vec<RoomMember>")]
|
||||
pub struct RoomMemberList(pub Vec<RoomMember>);
|
||||
|
||||
|
@ -327,6 +342,7 @@ impl TryFrom<Vec<RoomMember>> for RoomMemberList {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct RoomMember {
|
||||
pub permission: MemberPermission,
|
||||
pub user: PubKey,
|
||||
|
@ -336,11 +352,13 @@ pub struct RoomMember {
|
|||
///
|
||||
/// TODO: Should we use JWT here instead?
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(tag = "typ", rename = "auth")]
|
||||
pub struct AuthPayload {}
|
||||
|
||||
// FIXME: Remove this.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
// `typ` is provided by `RoomAdminOp`.
|
||||
pub struct RoomAdminPayload {
|
||||
#[serde(flatten)]
|
||||
|
@ -348,6 +366,7 @@ pub struct RoomAdminPayload {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(tag = "typ", rename_all = "snake_case", rename = "remove_member")]
|
||||
pub struct RemoveMemberPayload {
|
||||
pub room: Id,
|
||||
|
@ -357,6 +376,7 @@ pub struct RemoveMemberPayload {
|
|||
|
||||
// TODO: Maybe disallow adding other user without consent?
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(tag = "typ", rename_all = "snake_case", rename = "add_member")]
|
||||
pub struct AddMemberPayload {
|
||||
pub room: Id,
|
||||
|
@ -365,6 +385,7 @@ pub struct AddMemberPayload {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(tag = "typ", rename_all = "snake_case", rename = "update_member")]
|
||||
pub struct UpdateMemberPayload {
|
||||
pub room: Id,
|
||||
|
@ -373,6 +394,7 @@ pub struct UpdateMemberPayload {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(untagged)]
|
||||
pub enum RoomAdminOp {
|
||||
AddMember(AddMemberPayload),
|
||||
|
@ -382,6 +404,7 @@ pub enum RoomAdminOp {
|
|||
bitflags::bitflags! {
|
||||
/// TODO: Is this a really all about permission, or is a generic `UserFlags`?
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema), schema(value_type = i32))]
|
||||
pub struct ServerPermission: i32 {
|
||||
const CREATE_ROOM = 1 << 0;
|
||||
|
||||
|
@ -391,6 +414,7 @@ bitflags::bitflags! {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema), schema(value_type = i32))]
|
||||
pub struct MemberPermission: i32 {
|
||||
const POST_CHAT = 1 << 0;
|
||||
const ADD_MEMBER = 1 << 1;
|
||||
|
@ -410,6 +434,7 @@ bitflags::bitflags! {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema), schema(value_type = i32))]
|
||||
pub struct RoomAttrs: i32 {
|
||||
// NB. Used by schema.
|
||||
const PUBLIC_READABLE = 1 << 0;
|
||||
|
|
|
@ -10,6 +10,7 @@ use crate::PubKey;
|
|||
|
||||
/// The response object returned as body on HTTP error status.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct ErrorResponse<S = String> {
|
||||
/// The error object.
|
||||
pub error: ErrorObject<S>,
|
||||
|
@ -18,21 +19,27 @@ pub struct ErrorResponse<S = String> {
|
|||
/// The response object of `/_blah/user/me` endpoint on HTTP error status.
|
||||
/// It contains additional registration information.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct ErrorResponseWithChallenge<S = String> {
|
||||
/// The error object.
|
||||
pub error: ErrorObject<S>,
|
||||
|
||||
/// The challenge metadata returned by the `/_blah/user/me` endpoint for registration.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
|
||||
pub register_challenge: Option<UserRegisterChallenge>,
|
||||
}
|
||||
|
||||
/// The error object.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct ErrorObject<S = String> {
|
||||
/// A machine-readable error code string.
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String, example = "user_not_found"))]
|
||||
pub code: S,
|
||||
|
||||
/// A human-readable error message.
|
||||
#[cfg_attr(feature = "utoipa", schema(value_type = String, example = "the user does not exist"))]
|
||||
pub message: S,
|
||||
}
|
||||
|
||||
|
@ -50,16 +57,20 @@ impl<S: fmt::Display + fmt::Debug> std::error::Error for ErrorObject<S> {}
|
|||
/// It may contains extra fields and clients should ignore them for future compatibility.
|
||||
/// Chat Servers can also include any custom fields here as long they have a `_` prefix.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct ServerMetadata {
|
||||
/// A server-defined version string indicating its implementation name and the version.
|
||||
///
|
||||
/// It is expected to be in form `<server-name>/<server-version>` but not mandatory.
|
||||
#[cfg_attr(feature = "utoipa", schema(example = "blahd/0.0.1"))]
|
||||
pub server: String,
|
||||
|
||||
/// The URL to the source code of the Chat Server.
|
||||
///
|
||||
/// It is expected to be a public accessible maybe-compressed tarball link without
|
||||
/// access control.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
|
||||
pub src_url: Option<Url>,
|
||||
|
||||
/// The server capabilities set.
|
||||
|
@ -67,6 +78,7 @@ pub struct ServerMetadata {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct ServerCapabilities {
|
||||
/// Whether registration is open to public.
|
||||
pub allow_public_register: bool,
|
||||
|
@ -74,6 +86,7 @@ pub struct ServerCapabilities {
|
|||
|
||||
/// Registration challenge information.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum UserRegisterChallenge {
|
||||
/// Proof-of-work (PoW) challenge.
|
||||
|
@ -86,67 +99,82 @@ pub enum UserRegisterChallenge {
|
|||
|
||||
/// Response to list rooms.
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct RoomList {
|
||||
/// Result list of rooms.
|
||||
pub rooms: Vec<RoomMetadata>,
|
||||
/// The skip-token to fetch the next page.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
|
||||
pub skip_token: Option<Id>,
|
||||
}
|
||||
|
||||
/// The metadata of a room.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct RoomMetadata {
|
||||
/// Room id.
|
||||
pub rid: Id,
|
||||
/// Plain text room title. None for peer chat.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
|
||||
pub title: Option<String>,
|
||||
/// Room attributes.
|
||||
pub attrs: RoomAttrs,
|
||||
|
||||
// Extra information is only available for some APIs.
|
||||
/// The last message in the room.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
|
||||
pub last_msg: Option<SignedChatMsgWithId>,
|
||||
/// The current user's last seen message's `cid`.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
|
||||
pub last_seen_cid: Option<Id>,
|
||||
/// The number of unseen messages, ie. the number of messages from `last_seen_cid` to
|
||||
/// `last_msg.cid`.
|
||||
/// This may or may not be a precise number.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
|
||||
pub unseen_cnt: Option<u32>,
|
||||
/// The member permission of current user in the room, or `None` if it is not a member.
|
||||
/// Only available with authentication.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
|
||||
pub member_permission: Option<MemberPermission>,
|
||||
/// The peer user, if this is a peer chat room.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
|
||||
pub peer_user: Option<PubKey>,
|
||||
}
|
||||
|
||||
/// Response to list room msgs.
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct RoomMsgs {
|
||||
/// Result list of msgs.
|
||||
pub msgs: Vec<SignedChatMsgWithId>,
|
||||
/// The skip-token to fetch the next page.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
|
||||
pub skip_token: Option<Id>,
|
||||
}
|
||||
|
||||
/// Response to list room members.
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct RoomMemberList {
|
||||
/// Result list of members.
|
||||
pub members: Vec<RoomMember>,
|
||||
/// The skip-token to fetch the next page.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
|
||||
pub skip_token: Option<Id>,
|
||||
}
|
||||
|
||||
/// The description of a room member.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct RoomMember {
|
||||
/// The identity key of the member user.
|
||||
pub id_key: PubKey,
|
||||
|
@ -154,11 +182,13 @@ pub struct RoomMember {
|
|||
pub permission: MemberPermission,
|
||||
/// The user's last seen message `cid` in the room.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
|
||||
pub last_seen_cid: Option<Id>,
|
||||
}
|
||||
|
||||
/// A server-to-client event.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ServerEvent {
|
||||
/// A message from a joined room.
|
||||
|
@ -170,5 +200,6 @@ pub enum ServerEvent {
|
|||
|
||||
/// A client-to-server event.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ClientEvent {}
|
||||
|
|
657
docs/webapi.yaml
657
docs/webapi.yaml
|
@ -12,19 +12,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
server:
|
||||
type: string
|
||||
example: 'blah/0.0.0'
|
||||
src_url:
|
||||
type: string
|
||||
example: 'https://github.com/Blah-IM/blahrs'
|
||||
capabilities:
|
||||
type: object
|
||||
properties:
|
||||
allow_public_register:
|
||||
type: boolean
|
||||
$ref: 'types.json#/components/schemas/ServerMetadata'
|
||||
|
||||
# OAPI does not support WebSocket interface definitions.
|
||||
# See: https://github.com/OAI/OpenAPI-Specification/issues/55#issuecomment-929382279
|
||||
|
@ -35,7 +23,7 @@ paths:
|
|||
This endpoint is for server-side-event dispatching.
|
||||
|
||||
Once connected, client must send a JSON text message of type
|
||||
`Signed-Auth` for authentication.
|
||||
`Signed_Auth` for authentication.
|
||||
If server does not close it immediately, it means success.
|
||||
|
||||
Since OAPI does not support WebSocket interface, we use request and
|
||||
|
@ -54,7 +42,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/WSClientToServer'
|
||||
$ref: 'types.json#/components/schemas/ClientEvent'
|
||||
|
||||
responses:
|
||||
101:
|
||||
|
@ -66,7 +54,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/WSServerToClient'
|
||||
$ref: 'types.json#/components/schemas/ServerEvent'
|
||||
|
||||
/_blah/user/me:
|
||||
get:
|
||||
|
@ -76,7 +64,7 @@ paths:
|
|||
in: header
|
||||
description: Optional user authentication token.
|
||||
schema:
|
||||
$ref: '#/components/schemas/Signed-Auth'
|
||||
$ref: 'types.json#/components/schemas/Signed_AuthPayload'
|
||||
|
||||
responses:
|
||||
204:
|
||||
|
@ -88,7 +76,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiErrorWithRegisterChallenge'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponseWithChallenge'
|
||||
|
||||
post:
|
||||
summary: Register or update user identity
|
||||
|
@ -116,7 +104,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Signed-UserRegister'
|
||||
$ref: 'types.json#/components/schemas/Signed_UserRegisterPayload'
|
||||
|
||||
responses:
|
||||
204:
|
||||
|
@ -127,7 +115,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
401:
|
||||
description: |
|
||||
|
@ -136,7 +124,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
403:
|
||||
description: |
|
||||
|
@ -145,7 +133,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
409:
|
||||
description: |
|
||||
|
@ -153,7 +141,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
422:
|
||||
description: |
|
||||
|
@ -163,7 +151,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
/_blah/room:
|
||||
get:
|
||||
|
@ -207,7 +195,7 @@ paths:
|
|||
in: header
|
||||
description: Optional proof of membership for private rooms.
|
||||
schema:
|
||||
$ref: '#/components/schemas/Signed-Auth'
|
||||
$ref: 'types.json#/components/schemas/Signed_AuthPayload'
|
||||
|
||||
responses:
|
||||
200:
|
||||
|
@ -215,14 +203,14 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RoomList'
|
||||
$ref: 'types.json#/components/schemas/RoomList'
|
||||
|
||||
401:
|
||||
description: Missing or invalid Authorization header.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
post:
|
||||
summary: Create a room
|
||||
|
@ -237,7 +225,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Signed-CreateRoom'
|
||||
$ref: 'types.json#/components/schemas/Signed_CreateRoomPayload'
|
||||
|
||||
responses:
|
||||
200:
|
||||
|
@ -253,7 +241,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
404:
|
||||
description: |
|
||||
|
@ -262,14 +250,14 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
409:
|
||||
description: There is already a peer chat room between the user pair.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
/_blah/room/create:
|
||||
post:
|
||||
|
@ -286,7 +274,7 @@ paths:
|
|||
in: header
|
||||
description: Optional proof of membership for private rooms.
|
||||
schema:
|
||||
$ref: '#/components/schemas/Signed-Auth'
|
||||
$ref: 'types.json#/components/schemas/Signed_AuthPayload'
|
||||
|
||||
responses:
|
||||
200:
|
||||
|
@ -294,7 +282,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RoomMetadata'
|
||||
$ref: 'types.json#/components/schemas/RoomMetadata'
|
||||
|
||||
404:
|
||||
description: |
|
||||
|
@ -302,7 +290,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
delete:
|
||||
summary: Delete a room
|
||||
|
@ -310,7 +298,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Signed-DeleteRoom'
|
||||
$ref: 'types.json#/components/schemas/Signed_DeleteRoomPayload'
|
||||
|
||||
responses:
|
||||
204:
|
||||
|
@ -321,7 +309,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
404:
|
||||
description: |
|
||||
|
@ -329,7 +317,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
/_blah/room/{rid}/admin:
|
||||
post:
|
||||
|
@ -343,7 +331,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Signed-RoomAdmin'
|
||||
$ref: 'types.json#/components/schemas/Signed_RoomAdminPayload'
|
||||
|
||||
responses:
|
||||
204:
|
||||
|
@ -356,7 +344,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
409:
|
||||
description:
|
||||
|
@ -364,7 +352,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
/_blah/room/{rid}/feed.json:
|
||||
get:
|
||||
|
@ -385,7 +373,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
/_blah/room/{rid}/feed.atom:
|
||||
get:
|
||||
|
@ -408,8 +396,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
/_blah/room/{rid}/msg:
|
||||
get:
|
||||
|
@ -426,7 +413,7 @@ paths:
|
|||
in: header
|
||||
description: Optional proof of membership for private rooms.
|
||||
schema:
|
||||
$ref: '#/components/schemas/Signed-Auth'
|
||||
$ref: 'types.json#/components/schemas/Signed_AuthPayload'
|
||||
|
||||
- name: top
|
||||
in: query
|
||||
|
@ -450,7 +437,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RoomMsgs'
|
||||
$ref: 'types.json#/components/schemas/RoomMsgs'
|
||||
|
||||
404:
|
||||
description: |
|
||||
|
@ -458,7 +445,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
post:
|
||||
summary: Post a `Msg` into a room
|
||||
|
@ -466,14 +453,14 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Signed-Chat'
|
||||
$ref: 'types.json#/components/schemas/Signed_ChatPayload'
|
||||
|
||||
responses:
|
||||
200:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: string
|
||||
$ref: 'types.json#/components/schemas/Id'
|
||||
description: Newly created message id `cid`.
|
||||
|
||||
403:
|
||||
|
@ -481,14 +468,14 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
404:
|
||||
description: The room does not exist or the user is not a room member.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
/_blah/room/{rid}/msg/{cid}/seen:
|
||||
post:
|
||||
|
@ -507,7 +494,7 @@ paths:
|
|||
required: true
|
||||
description: Proof of membership for private rooms.
|
||||
schema:
|
||||
$ref: '#/components/schemas/Signed-Auth'
|
||||
$ref: 'types.json#/components/schemas/Signed_AuthPayload'
|
||||
|
||||
responses:
|
||||
204:
|
||||
|
@ -519,7 +506,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
/_blah/room/{rid}/member:
|
||||
get:
|
||||
|
@ -530,7 +517,7 @@ paths:
|
|||
required: true
|
||||
description: Proof of membership.
|
||||
schema:
|
||||
$ref: '#/components/schemas/Signed-Auth'
|
||||
$ref: 'types.json#/components/schemas/Signed_AuthPayload'
|
||||
|
||||
- name: top
|
||||
in: query
|
||||
|
@ -553,7 +540,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RoomMemberList'
|
||||
$ref: 'types.json#/components/schemas/RoomMemberList'
|
||||
|
||||
403:
|
||||
description: |
|
||||
|
@ -561,7 +548,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
404:
|
||||
description: |
|
||||
|
@ -569,7 +556,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
post:
|
||||
summary: Join a room
|
||||
|
@ -577,7 +564,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Signed-AddMember'
|
||||
$ref: 'types.json#/components/schemas/Signed_AddMemberPayload'
|
||||
|
||||
responses:
|
||||
204:
|
||||
|
@ -590,7 +577,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
409:
|
||||
description:
|
||||
|
@ -598,7 +585,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
/_blah/room/{rid}/member/{member_id_key}:
|
||||
get:
|
||||
|
@ -616,7 +603,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RoomMember'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
404:
|
||||
description: |
|
||||
|
@ -625,7 +612,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ErrorResponse'
|
||||
|
||||
patch:
|
||||
summary: Update permission of a room member
|
||||
|
@ -634,7 +621,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Signed-UpdateMember'
|
||||
$ref: 'types.json#/components/schemas/Signed_UpdateMember'
|
||||
|
||||
responses:
|
||||
204:
|
||||
|
@ -647,7 +634,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ServerMetadata'
|
||||
|
||||
404:
|
||||
description: |
|
||||
|
@ -656,7 +643,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ServerMetadata'
|
||||
|
||||
delete:
|
||||
summary: Remove a room member.
|
||||
|
@ -665,7 +652,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Signed-RemoveMember'
|
||||
$ref: 'types.json#/components/schemas/Signed_RemoveMember'
|
||||
|
||||
responses:
|
||||
204:
|
||||
|
@ -677,7 +664,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
$ref: 'types.json#/components/schemas/ServerMetadata'
|
||||
|
||||
404:
|
||||
description: |
|
||||
|
@ -686,540 +673,4 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
|
||||
|
||||
# Ideally we should generate these from src, but we need to
|
||||
# WAIT: https://github.com/juhaku/utoipa/pull/1034
|
||||
components:
|
||||
schemas:
|
||||
WSClientToServer:
|
||||
anyOf:
|
||||
- $ref: '#/components/schemas/Signed-Auth'
|
||||
|
||||
WSServerToClient:
|
||||
anyOf:
|
||||
- type: object
|
||||
properties:
|
||||
chat:
|
||||
$ref: '#/components/schemas/WithMsgId-Signed-Chat'
|
||||
|
||||
- type: object
|
||||
properties:
|
||||
lagged:
|
||||
type: object
|
||||
const: {}
|
||||
|
||||
ApiError:
|
||||
type: object
|
||||
properties:
|
||||
error:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
description: A machine-readable error code string.
|
||||
example: invalid_signature
|
||||
message:
|
||||
type: string
|
||||
description: A human-readable error message.
|
||||
example: signature verification failed
|
||||
|
||||
ApiErrorWithRegisterChallenge:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/ApiError'
|
||||
- type: object
|
||||
properties:
|
||||
register_challenge:
|
||||
type: object
|
||||
properties:
|
||||
pow:
|
||||
type: object
|
||||
properties:
|
||||
nonce:
|
||||
type: integer
|
||||
format: uint32
|
||||
difficulty:
|
||||
type: integer
|
||||
format: uint32
|
||||
|
||||
RoomList:
|
||||
type: object
|
||||
required:
|
||||
- rooms
|
||||
properties:
|
||||
rooms:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/RoomMetadataForList'
|
||||
next_token:
|
||||
type: string
|
||||
description: An opaque token to fetch the next page.
|
||||
|
||||
RoomMetadataForList:
|
||||
type: object
|
||||
required: ['rid', 'title', 'attrs']
|
||||
properties:
|
||||
rid:
|
||||
type: string
|
||||
title:
|
||||
type: string
|
||||
attrs:
|
||||
description: Room attributes bitset, see `RoomAttrs`.
|
||||
type: integer
|
||||
format: int32
|
||||
last_msg:
|
||||
$ref: '#/components/schemas/WithMsgId-Signed-Chat'
|
||||
last_seen_cid:
|
||||
description: The `cid` of the last chat being marked as seen.
|
||||
type: string
|
||||
unseen_cnt:
|
||||
description: |
|
||||
The number of unseen messages. Only available for
|
||||
GET `/room?filter=unseen`.
|
||||
type: integer
|
||||
format: uint32
|
||||
member_permission:
|
||||
type: integer
|
||||
format: int32
|
||||
peer_user:
|
||||
type: string
|
||||
description: |
|
||||
For peer chat room, this gives the identity of the peer user.
|
||||
|
||||
RoomMetadata:
|
||||
type: object
|
||||
required: ['rid', 'title', 'attrs']
|
||||
properties:
|
||||
rid:
|
||||
type: string
|
||||
title:
|
||||
type: string
|
||||
attrs:
|
||||
type: integer
|
||||
format: int32
|
||||
|
||||
RoomMsgs:
|
||||
type: object
|
||||
required:
|
||||
- msgs
|
||||
properties:
|
||||
msgs:
|
||||
description: Room messages in reversed server-received time order.
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/WithMsgId-Signed-Chat'
|
||||
skip_token:
|
||||
description: The token for fetching the next page.
|
||||
type: string
|
||||
|
||||
RoomMemberList:
|
||||
type: object
|
||||
required:
|
||||
- members
|
||||
properties:
|
||||
members:
|
||||
description: Room members in server-specified order.
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/RoomMember'
|
||||
skip_token:
|
||||
description: The token for fetching the next page.
|
||||
type: string
|
||||
|
||||
RoomMember:
|
||||
type: object
|
||||
required:
|
||||
- id_key
|
||||
- permission
|
||||
properties:
|
||||
id_key:
|
||||
type: string
|
||||
permission:
|
||||
type: integer
|
||||
format: int32
|
||||
last_seen_cid:
|
||||
type: string
|
||||
|
||||
RichText:
|
||||
type: array
|
||||
items:
|
||||
anyOf:
|
||||
- type: string
|
||||
description: Unstyled text piece.
|
||||
- type: array
|
||||
items: false
|
||||
prefixItems:
|
||||
- type: string
|
||||
description: The text piece to apply styles on.
|
||||
- type: object
|
||||
properties:
|
||||
b:
|
||||
type: boolean
|
||||
description: Bold.
|
||||
m:
|
||||
type: boolean
|
||||
description: Monospace.
|
||||
i:
|
||||
type: boolean
|
||||
description: Italic.
|
||||
s:
|
||||
type: boolean
|
||||
description: Strikethrough.
|
||||
u:
|
||||
type: boolean
|
||||
description: Underline.
|
||||
hashtag:
|
||||
type: boolean
|
||||
description: Hashtag.
|
||||
link:
|
||||
type: string
|
||||
description: Link target.
|
||||
|
||||
|
||||
Signed-Auth:
|
||||
type: object
|
||||
properties:
|
||||
sig:
|
||||
type: string
|
||||
signee:
|
||||
type: object
|
||||
properties:
|
||||
nonce:
|
||||
type: integer
|
||||
format: uint32
|
||||
timestamp:
|
||||
type: integer
|
||||
format: uint64
|
||||
id_key:
|
||||
type: string
|
||||
act_key:
|
||||
type: string
|
||||
payload:
|
||||
type: object
|
||||
properties:
|
||||
typ:
|
||||
type: string
|
||||
const: 'auth'
|
||||
|
||||
Signed-RoomAdmin:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/Signed-AddMember'
|
||||
- $ref: '#/components/schemas/Signed-RemoveMember'
|
||||
|
||||
Signed-AddMember:
|
||||
type: object
|
||||
properties:
|
||||
sig:
|
||||
type: string
|
||||
signee:
|
||||
type: object
|
||||
properties:
|
||||
nonce:
|
||||
type: integer
|
||||
format: uint32
|
||||
timestamp:
|
||||
type: integer
|
||||
format: uint64
|
||||
id_key:
|
||||
type: string
|
||||
act_key:
|
||||
type: string
|
||||
payload:
|
||||
type: object
|
||||
properties:
|
||||
typ:
|
||||
type: string
|
||||
const: 'add_member'
|
||||
room:
|
||||
type: string
|
||||
permission:
|
||||
type: integer
|
||||
format: int32
|
||||
user:
|
||||
type: string
|
||||
|
||||
Signed-UpdateMember:
|
||||
type: object
|
||||
properties:
|
||||
sig:
|
||||
type: string
|
||||
signee:
|
||||
type: object
|
||||
properties:
|
||||
nonce:
|
||||
type: integer
|
||||
format: uint32
|
||||
timestamp:
|
||||
type: integer
|
||||
format: uint64
|
||||
id_key:
|
||||
type: string
|
||||
act_key:
|
||||
type: string
|
||||
payload:
|
||||
type: object
|
||||
properties:
|
||||
typ:
|
||||
type: string
|
||||
const: 'update_member'
|
||||
room:
|
||||
type: string
|
||||
permission:
|
||||
type: integer
|
||||
format: int32
|
||||
user:
|
||||
type: string
|
||||
|
||||
Signed-RemoveMember:
|
||||
type: object
|
||||
properties:
|
||||
sig:
|
||||
type: string
|
||||
signee:
|
||||
type: object
|
||||
properties:
|
||||
nonce:
|
||||
type: integer
|
||||
format: uint32
|
||||
timestamp:
|
||||
type: integer
|
||||
format: uint64
|
||||
id_key:
|
||||
type: string
|
||||
act_key:
|
||||
type: string
|
||||
payload:
|
||||
type: object
|
||||
properties:
|
||||
typ:
|
||||
type: string
|
||||
const: 'remove_member'
|
||||
room:
|
||||
type: string
|
||||
user:
|
||||
type: string
|
||||
|
||||
Signed-Chat:
|
||||
type: object
|
||||
properties:
|
||||
sig:
|
||||
type: string
|
||||
signee:
|
||||
type: object
|
||||
properties:
|
||||
nonce:
|
||||
type: integer
|
||||
format: uint32
|
||||
timestamp:
|
||||
type: integer
|
||||
format: uint64
|
||||
id_key:
|
||||
type: string
|
||||
act_key:
|
||||
type: string
|
||||
payload:
|
||||
type: object
|
||||
properties:
|
||||
typ:
|
||||
type: string
|
||||
const: 'chat'
|
||||
room:
|
||||
type: string
|
||||
rich_text:
|
||||
$ref: '$/components/schemas/RichText'
|
||||
|
||||
WithMsgId-Signed-Chat:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Signed-Chat'
|
||||
- type: object
|
||||
properties:
|
||||
cid:
|
||||
type: string
|
||||
description: An opaque server-specific identifier.
|
||||
|
||||
Signed-CreateRoom:
|
||||
type: object
|
||||
properties:
|
||||
sig:
|
||||
type: string
|
||||
signee:
|
||||
type: object
|
||||
properties:
|
||||
nonce:
|
||||
type: integer
|
||||
format: uint32
|
||||
timestamp:
|
||||
type: integer
|
||||
format: uint64
|
||||
id_key:
|
||||
type: string
|
||||
act_key:
|
||||
type: string
|
||||
payload:
|
||||
oneOf:
|
||||
- type: object
|
||||
properties:
|
||||
typ:
|
||||
type: string
|
||||
const: 'create_room'
|
||||
title:
|
||||
type: string
|
||||
- type: object
|
||||
properties:
|
||||
typ:
|
||||
type: string
|
||||
const: 'create_peer_chat'
|
||||
peer:
|
||||
type: string
|
||||
|
||||
Signed-DeleteRoom:
|
||||
type: object
|
||||
properties:
|
||||
sig:
|
||||
type: string
|
||||
signee:
|
||||
type: object
|
||||
properties:
|
||||
nonce:
|
||||
type: integer
|
||||
format: uint32
|
||||
timestamp:
|
||||
type: integer
|
||||
format: uint64
|
||||
id_key:
|
||||
type: string
|
||||
act_key:
|
||||
type: string
|
||||
payload:
|
||||
type: object
|
||||
properties:
|
||||
typ:
|
||||
type: string
|
||||
const: 'delete_room'
|
||||
room:
|
||||
type: integer
|
||||
format: in64
|
||||
|
||||
Signed-UserRegister:
|
||||
type: object
|
||||
properties:
|
||||
sig:
|
||||
type: string
|
||||
signee:
|
||||
type: object
|
||||
properties:
|
||||
nonce:
|
||||
type: integer
|
||||
format: uint32
|
||||
timestamp:
|
||||
type: integer
|
||||
format: uint64
|
||||
id_key:
|
||||
type: string
|
||||
act_key:
|
||||
type: string
|
||||
payload:
|
||||
type: object
|
||||
properties:
|
||||
typ:
|
||||
type: string
|
||||
const: 'user_register'
|
||||
server_url:
|
||||
type: string
|
||||
description: |
|
||||
The server URL to register on. Must matches chat server's base_url.
|
||||
It's path segment must be normalized, eg. always contains a `/` path for top-level.
|
||||
id_url:
|
||||
type: string
|
||||
description: |
|
||||
The identity server URL. Must be in form `https://<domain>/`.
|
||||
It's path segment must be normalized, eg. always contains a `/` path for top-level.
|
||||
id_key:
|
||||
type: string
|
||||
description: Hex encoded user primary key `id_key`.
|
||||
challenge:
|
||||
type: object
|
||||
properties:
|
||||
pow:
|
||||
type: object
|
||||
properties:
|
||||
nonce:
|
||||
type: integer
|
||||
format: uint32
|
||||
description: The challenge nonce retrieved from a recent GET response of `/user/me`.
|
||||
|
||||
UserIdentityDescription:
|
||||
type: object
|
||||
properties:
|
||||
id_key:
|
||||
type: string
|
||||
|
||||
act_keys:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
sig:
|
||||
type: string
|
||||
signee:
|
||||
type: object
|
||||
properties:
|
||||
nonce:
|
||||
type: integer
|
||||
format: uint32
|
||||
timestamp:
|
||||
type: integer
|
||||
format: uint64
|
||||
id_key:
|
||||
type: string
|
||||
act_key:
|
||||
type: string
|
||||
payload:
|
||||
type: object
|
||||
properties:
|
||||
typ:
|
||||
type: string
|
||||
const: 'user_act_key'
|
||||
act_key:
|
||||
type: string
|
||||
expire_time:
|
||||
type: integer
|
||||
format: uint64
|
||||
comment:
|
||||
type: string
|
||||
|
||||
profile:
|
||||
type: object
|
||||
properties:
|
||||
sig:
|
||||
type: string
|
||||
signee:
|
||||
type: object
|
||||
properties:
|
||||
nonce:
|
||||
type: integer
|
||||
format: uint32
|
||||
timestamp:
|
||||
type: integer
|
||||
format: uint64
|
||||
id_key:
|
||||
type: string
|
||||
act_key:
|
||||
type: string
|
||||
payload:
|
||||
type: object
|
||||
properties:
|
||||
typ:
|
||||
type: string
|
||||
const: 'user_profile'
|
||||
preferred_chat_server_urls:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: url
|
||||
id_urls:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: url
|
||||
$ref: 'types.json#/components/schemas/ServerMetadata'
|
||||
|
|
Loading…
Add table
Reference in a new issue