From 12bd98cb6be18954f857960a4b3e82f2307a1916 Mon Sep 17 00:00:00 2001 From: mikoto Date: Fri, 29 Mar 2024 02:51:42 +0000 Subject: [PATCH] wip: space support --- crates/core/src/direct.rs | 4 + crates/core/src/direct/create.rs | 37 +++++++++ crates/core/src/error.rs | 3 + crates/core/src/helpers.rs | 22 ++++++ crates/core/src/lib.rs | 4 +- crates/core/src/membership.rs | 2 + crates/core/src/membership/join.rs | 19 +++++ crates/core/src/membership/leave.rs | 38 ++++++++++ crates/core/src/rooms/create.rs | 52 ------------- crates/core/src/spaces.rs | 1 + crates/core/src/spaces/channels.rs | 1 + crates/core/src/spaces/channels/create.rs | 76 +++++++++++++++++++ crates/core/src/spaces/create.rs | 1 + crates/core/src/util.rs | 1 + crates/core/src/util/opaque_id.rs | 34 +++++++++ crates/matrix/src/admin.rs | 2 +- crates/matrix/src/admin/{room.rs => rooms.rs} | 11 +-- .../{room/delete_room.rs => rooms/delete.rs} | 0 .../forward_extremities/delete.rs | 0 .../forward_extremities/get.rs | 0 .../admin/{room/get_room.rs => rooms/get.rs} | 6 ++ .../{room/get_rooms.rs => rooms/get_many.rs} | 0 crates/matrix/src/admin/rooms/members.rs | 1 + .../get_members.rs => rooms/members/get.rs} | 0 crates/matrix/src/admin/rooms/state.rs | 1 + .../{room/get_state.rs => rooms/state/get.rs} | 0 crates/matrix/src/client.rs | 7 +- crates/matrix/src/client/create_room.rs | 2 +- crates/matrix/src/client/directory.rs | 1 + crates/matrix/src/client/directory/room.rs | 32 ++++++++ crates/matrix/src/client/membership.rs | 2 + crates/matrix/src/client/membership/join.rs | 44 +++++++++++ crates/matrix/src/client/membership/leave.rs | 34 +++++++++ crates/matrix/src/client/rooms/messages.rs | 0 crates/matrix/src/client/rooms/state.rs | 30 +------- .../matrix/src/client/rooms/state/create.rs | 56 ++++++++++++++ crates/matrix/src/client/rooms/state/get.rs | 53 +++++++++++++ .../matrix/src/client/rooms/state/get_many.rs | 38 ++++++++++ crates/router/src/api.rs | 5 +- crates/router/src/api/direct.rs | 1 + crates/router/src/api/direct/create.rs | 40 ++++++++++ crates/router/src/api/membership.rs | 2 + crates/router/src/api/membership/join.rs | 26 +++++++ crates/router/src/api/membership/leave.rs | 0 crates/router/src/api/rooms.rs | 2 +- .../src/api/rooms/{root.rs => create.rs} | 9 ++- crates/router/src/api/spaces.rs | 3 +- crates/router/src/api/spaces/channels.rs | 1 + .../router/src/api/spaces/channels/create.rs | 34 +++++++++ .../src/api/spaces/{root.rs => create.rs} | 0 crates/router/src/lib.rs | 11 ++- crates/test/src/api.rs | 1 + crates/test/src/api/rooms.rs | 0 crates/test/src/api/rooms/create.rs | 1 + crates/test/src/api/rooms/create/direct.rs | 35 +++++++++ crates/test/src/api/rooms/direct.rs | 0 crates/test/src/api/spaces/create.rs | 2 +- 57 files changed, 687 insertions(+), 101 deletions(-) create mode 100644 crates/core/src/direct.rs create mode 100644 crates/core/src/direct/create.rs create mode 100644 crates/core/src/helpers.rs create mode 100644 crates/core/src/membership.rs create mode 100644 crates/core/src/membership/join.rs create mode 100644 crates/core/src/membership/leave.rs delete mode 100644 crates/core/src/rooms/create.rs create mode 100644 crates/core/src/spaces/channels.rs create mode 100644 crates/core/src/spaces/channels/create.rs create mode 100644 crates/core/src/util/opaque_id.rs rename crates/matrix/src/admin/{room.rs => rooms.rs} (92%) rename crates/matrix/src/admin/{room/delete_room.rs => rooms/delete.rs} (100%) rename crates/matrix/src/admin/{room => rooms}/forward_extremities/delete.rs (100%) rename crates/matrix/src/admin/{room => rooms}/forward_extremities/get.rs (100%) rename crates/matrix/src/admin/{room/get_room.rs => rooms/get.rs} (84%) rename crates/matrix/src/admin/{room/get_rooms.rs => rooms/get_many.rs} (100%) create mode 100644 crates/matrix/src/admin/rooms/members.rs rename crates/matrix/src/admin/{room/get_members.rs => rooms/members/get.rs} (100%) create mode 100644 crates/matrix/src/admin/rooms/state.rs rename crates/matrix/src/admin/{room/get_state.rs => rooms/state/get.rs} (100%) create mode 100644 crates/matrix/src/client/directory.rs create mode 100644 crates/matrix/src/client/directory/room.rs create mode 100644 crates/matrix/src/client/membership.rs create mode 100644 crates/matrix/src/client/membership/join.rs create mode 100644 crates/matrix/src/client/membership/leave.rs create mode 100644 crates/matrix/src/client/rooms/messages.rs create mode 100644 crates/matrix/src/client/rooms/state/create.rs create mode 100644 crates/matrix/src/client/rooms/state/get.rs create mode 100644 crates/matrix/src/client/rooms/state/get_many.rs create mode 100644 crates/router/src/api/direct.rs create mode 100644 crates/router/src/api/direct/create.rs create mode 100644 crates/router/src/api/membership.rs create mode 100644 crates/router/src/api/membership/join.rs create mode 100644 crates/router/src/api/membership/leave.rs rename crates/router/src/api/rooms/{root.rs => create.rs} (78%) create mode 100644 crates/router/src/api/spaces/channels.rs create mode 100644 crates/router/src/api/spaces/channels/create.rs rename crates/router/src/api/spaces/{root.rs => create.rs} (100%) create mode 100644 crates/test/src/api/rooms.rs create mode 100644 crates/test/src/api/rooms/create.rs create mode 100644 crates/test/src/api/rooms/create/direct.rs create mode 100644 crates/test/src/api/rooms/direct.rs diff --git a/crates/core/src/direct.rs b/crates/core/src/direct.rs new file mode 100644 index 0000000..a7d59d5 --- /dev/null +++ b/crates/core/src/direct.rs @@ -0,0 +1,4 @@ +//! This library deals with personal messages, enabling `m.direct` on all rooms created through +//! this endpoint. The name `direct` was chosen to avoid confusion with regular Matrix rooms. + +pub mod create; diff --git a/crates/core/src/direct/create.rs b/crates/core/src/direct/create.rs new file mode 100644 index 0000000..9c1d35e --- /dev/null +++ b/crates/core/src/direct/create.rs @@ -0,0 +1,37 @@ +use matrix::{ + client::create_room::{Request, Response, RoomCreationContent, RoomVisibility}, + ruma_common::{OwnedUserId, RoomVersionId}, +}; + +use crate::{commune, error::Result}; + +pub async fn service( + access_token: impl AsRef, + name: Option, + topic: Option, + invite: Vec, +) -> Result { + let creation_content = Some(RoomCreationContent { + kind: None, + federate: true, + room_version: RoomVersionId::V11, + predecessor: None, + }); + + let req = Request::new( + creation_content, + Vec::new(), + invite, + true, + name, + None, + None, + topic, + RoomVisibility::Private, + ); + + commune() + .send_matrix_request(req, Some(access_token.as_ref())) + .await + .map_err(Into::into) +} diff --git a/crates/core/src/error.rs b/crates/core/src/error.rs index 7134727..70e4f65 100644 --- a/crates/core/src/error.rs +++ b/crates/core/src/error.rs @@ -9,6 +9,9 @@ pub enum Error { #[error("forwarding a Matrix request failed: {0}")] Matrix(#[from] matrix::HandleError), + #[error("(de)serializing type failed: {0}")] + Serde(#[from] serde_json::Error), + #[error("instance does not allow email address originating from this domain")] EmailDomain, diff --git a/crates/core/src/helpers.rs b/crates/core/src/helpers.rs new file mode 100644 index 0000000..6641419 --- /dev/null +++ b/crates/core/src/helpers.rs @@ -0,0 +1,22 @@ +use matrix::{ + client::directory::room::Request, + ruma_common::{OwnedRoomId, OwnedRoomOrAliasId}, +}; + +use crate::{commune, error::Error}; + +pub async fn get_room_id( + room_or_alias_id: impl Into, +) -> Result { + // this cannot error, `Result` is just provided in place of an enum + // https://github.com/ruma/ruma/issues/1761 + + match room_or_alias_id.into().try_into() { + Ok(room_id) => Ok(room_id), + Err(room_alias) => commune() + .send_matrix_request(Request::new(room_alias), None) + .await + .map(|resp| resp.room_id) + .map_err(Into::into), + } +} diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 4a758cf..68847e3 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -3,12 +3,14 @@ pub mod config; pub mod error; +pub mod helpers; pub mod util; pub mod account; +pub mod direct; pub mod profile; pub mod spaces; -pub mod rooms; +pub mod membership; use std::sync::RwLock; diff --git a/crates/core/src/membership.rs b/crates/core/src/membership.rs new file mode 100644 index 0000000..2c0fcca --- /dev/null +++ b/crates/core/src/membership.rs @@ -0,0 +1,2 @@ +pub mod join; +pub mod leave; diff --git a/crates/core/src/membership/join.rs b/crates/core/src/membership/join.rs new file mode 100644 index 0000000..05ae2c0 --- /dev/null +++ b/crates/core/src/membership/join.rs @@ -0,0 +1,19 @@ +use matrix::{ + client::membership::join::{Request, Response}, + ruma_common::OwnedRoomOrAliasId, +}; + +use crate::{commune, error::Result}; + +pub async fn service( + access_token: impl AsRef, + room_id: impl Into, + reason: Option, +) -> Result { + let req = Request::new(room_id.into(), reason); + + commune() + .send_matrix_request(req, Some(access_token.as_ref())) + .await + .map_err(Into::into) +} diff --git a/crates/core/src/membership/leave.rs b/crates/core/src/membership/leave.rs new file mode 100644 index 0000000..c5327f5 --- /dev/null +++ b/crates/core/src/membership/leave.rs @@ -0,0 +1,38 @@ +use matrix::{ + client::{ + self, + membership::leave::{Request, Response}, + }, + ruma_common::OwnedRoomOrAliasId, +}; + +use crate::{commune, error::Result}; + +pub async fn service( + access_token: impl AsRef, + room_or_alias_id: impl Into, + reason: Option, +) -> Result { + let room_or_alias_id: OwnedRoomOrAliasId = room_or_alias_id.into(); + + // this cannot error, `Result` is just provided in place of an enum + // https://github.com/ruma/ruma/issues/1761 + let room_id = match room_or_alias_id.try_into() { + Ok(room_id) => room_id, + Err(room_alias) => { + let req = client::directory::room::Request::new(room_alias); + + commune() + .send_matrix_request(req, None) + .await + .map(|resp| resp.room_id)? + } + }; + + let req = Request::new(room_id, reason); + + commune() + .send_matrix_request(req, Some(access_token.as_ref())) + .await + .map_err(Into::into) +} diff --git a/crates/core/src/rooms/create.rs b/crates/core/src/rooms/create.rs deleted file mode 100644 index 81ede67..0000000 --- a/crates/core/src/rooms/create.rs +++ /dev/null @@ -1,52 +0,0 @@ -use matrix::{ - client::create_room::{Request, Response, RoomCreationContent, RoomVisibility}, - ruma_common::{room::RoomType, OwnedRoomOrAliasId, RoomVersionId}, - ruma_events::{ - room::power_levels::RoomPowerLevelsEventContent, - space::parent::{InitialSpaceParentEvent, SpaceParentEventContent}, - }, -}; - -use crate::{commune, error::Result}; - -pub async fn service( - access_token: impl AsRef, - parent: OwnedRoomOrAliasId, - alias: Option, - name: Option, - topic: Option, -) -> Result { - let creation_content = Some(RoomCreationContent { - kind: None, - federate: true, - room_version: RoomVersionId::V11, - predecessor: None, - }); - - let req = Request::new( - creation_content, - Vec::new(), - Vec::new(), - false, - name, - None, - alias, - topic, - RoomVisibility::Public, - ); - - let resp = commune() - .send_matrix_request(req, Some(access_token.as_ref())) - .await?; - - let mut content = - SpaceParentEventContent::new(vec![commune().config.matrix.server_name.clone()]); - content.canonical = true; - - let state_event = InitialSpaceParentEvent { - content, - state_key: resp.room_id.clone(), - }; - - Ok(resp) -} diff --git a/crates/core/src/spaces.rs b/crates/core/src/spaces.rs index c5fb369..70cc1d1 100644 --- a/crates/core/src/spaces.rs +++ b/crates/core/src/spaces.rs @@ -1 +1,2 @@ pub mod create; +pub mod channels; diff --git a/crates/core/src/spaces/channels.rs b/crates/core/src/spaces/channels.rs new file mode 100644 index 0000000..c5fb369 --- /dev/null +++ b/crates/core/src/spaces/channels.rs @@ -0,0 +1 @@ +pub mod create; diff --git a/crates/core/src/spaces/channels/create.rs b/crates/core/src/spaces/channels/create.rs new file mode 100644 index 0000000..9a6a590 --- /dev/null +++ b/crates/core/src/spaces/channels/create.rs @@ -0,0 +1,76 @@ +use matrix::{ + client::{ + create_room::{Request, Response, RoomCreationContent, RoomVisibility}, + rooms, + }, + ruma_common::{OwnedRoomId, RoomVersionId}, + ruma_events::{ + space::{child::SpaceChildEventContent, parent::SpaceParentEventContent}, + EventContent, + }, +}; + +use crate::{commune, error::Result, util::opaque_id::OpaqueId}; + +pub async fn service( + access_token: impl AsRef, + space_id: OpaqueId, + name: Option, + topic: Option, +) -> Result { + let server_name = &commune().config.matrix.server_name; + // this should never panic + let space_id = OwnedRoomId::try_from(format!("!{space_id}:{server_name}")) + .expect("failed to parse room ID"); + + let req = Request::new( + Some(RoomCreationContent { + kind: None, + federate: true, + room_version: RoomVersionId::V11, + predecessor: None, + }), + Vec::new(), + Vec::new(), + false, + name, + None, + None, + topic, + RoomVisibility::Public, + ); + + let resp = commune() + .send_matrix_request(req, Some(access_token.as_ref())) + .await?; + + let mut parent_content = SpaceParentEventContent::new(vec![server_name.to_owned()]); + parent_content.canonical = true; + + let req = rooms::state::create::Request::new( + parent_content.event_type(), + resp.room_id.clone(), + Some(resp.room_id.to_string()), + parent_content, + )?; + + commune() + .send_matrix_request(req, Some(access_token.as_ref())) + .await?; + + let mut child_content = SpaceChildEventContent::new(vec![server_name.to_owned()]); + child_content.suggested = true; + + let req = rooms::state::create::Request::new( + child_content.event_type(), + space_id.clone(), + Some(space_id.to_string()), + child_content, + )?; + + commune() + .send_matrix_request(req, Some(access_token.as_ref())) + .await?; + + Ok(resp) +} diff --git a/crates/core/src/spaces/create.rs b/crates/core/src/spaces/create.rs index 6a1c6a7..1bc43b1 100644 --- a/crates/core/src/spaces/create.rs +++ b/crates/core/src/spaces/create.rs @@ -12,6 +12,7 @@ pub async fn service( name: Option, topic: Option, ) -> Result { + // Only state events should be allowed in spaces let mut power_levels = RoomPowerLevelsEventContent::new(); power_levels.events_default = 100.into(); diff --git a/crates/core/src/util.rs b/crates/core/src/util.rs index 73b12db..4a4154d 100644 --- a/crates/core/src/util.rs +++ b/crates/core/src/util.rs @@ -1 +1,2 @@ pub mod secret; +pub mod opaque_id; diff --git a/crates/core/src/util/opaque_id.rs b/crates/core/src/util/opaque_id.rs new file mode 100644 index 0000000..9ebee5d --- /dev/null +++ b/crates/core/src/util/opaque_id.rs @@ -0,0 +1,34 @@ +use std::{fmt::Display, str::FromStr}; + +use matrix::ruma_identifiers_validation::Error; +use serde::{de, Deserialize, Deserializer}; + +// helper type to validate the opaque part of a room ID separately. +pub struct OpaqueId(String); + +impl Display for OpaqueId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.0.as_str()) + } +} + +impl FromStr for OpaqueId { + type Err = Error; + + fn from_str(s: &str) -> Result { + match s.chars().all(char::is_alphanumeric) { + true => Ok(OpaqueId(s.to_owned())), + false => Err(Error::InvalidCharacters), + } + } +} + +impl<'de> Deserialize<'de> for OpaqueId { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + s.parse().map_err(de::Error::custom) + } +} diff --git a/crates/matrix/src/admin.rs b/crates/matrix/src/admin.rs index 2608ca2..917bfd9 100644 --- a/crates/matrix/src/admin.rs +++ b/crates/matrix/src/admin.rs @@ -3,6 +3,6 @@ //! reference: https://matrix-org.github.io/synapse/latest/usage/administration/admin_api/index.html pub mod registration_tokens; -// pub mod room; +pub mod rooms; // pub mod session; // pub mod user; diff --git a/crates/matrix/src/admin/room.rs b/crates/matrix/src/admin/rooms.rs similarity index 92% rename from crates/matrix/src/admin/room.rs rename to crates/matrix/src/admin/rooms.rs index 38a3738..72ed8f6 100644 --- a/crates/matrix/src/admin/room.rs +++ b/crates/matrix/src/admin/rooms.rs @@ -9,11 +9,12 @@ use ruma_common::{ use ruma_events::room::{history_visibility::HistoryVisibility, join_rules::JoinRule}; use serde::Deserialize; -pub mod delete_room; -pub mod get_members; -pub mod get_room; -pub mod get_rooms; -pub mod get_state; +pub mod members; +pub mod state; + +pub mod get; +pub mod get_many; +pub mod delete; #[derive(Clone, Debug, Deserialize)] pub struct Room { diff --git a/crates/matrix/src/admin/room/delete_room.rs b/crates/matrix/src/admin/rooms/delete.rs similarity index 100% rename from crates/matrix/src/admin/room/delete_room.rs rename to crates/matrix/src/admin/rooms/delete.rs diff --git a/crates/matrix/src/admin/room/forward_extremities/delete.rs b/crates/matrix/src/admin/rooms/forward_extremities/delete.rs similarity index 100% rename from crates/matrix/src/admin/room/forward_extremities/delete.rs rename to crates/matrix/src/admin/rooms/forward_extremities/delete.rs diff --git a/crates/matrix/src/admin/room/forward_extremities/get.rs b/crates/matrix/src/admin/rooms/forward_extremities/get.rs similarity index 100% rename from crates/matrix/src/admin/room/forward_extremities/get.rs rename to crates/matrix/src/admin/rooms/forward_extremities/get.rs diff --git a/crates/matrix/src/admin/room/get_room.rs b/crates/matrix/src/admin/rooms/get.rs similarity index 84% rename from crates/matrix/src/admin/room/get_room.rs rename to crates/matrix/src/admin/rooms/get.rs index 967f577..0feea91 100644 --- a/crates/matrix/src/admin/room/get_room.rs +++ b/crates/matrix/src/admin/rooms/get.rs @@ -20,6 +20,12 @@ pub struct Request { pub room_id: OwnedRoomId, } +impl Request { + pub fn new(room_id: OwnedRoomId) -> Self { + Self { room_id } + } +} + #[response(error = crate::Error)] pub struct Response { #[ruma_api(body)] diff --git a/crates/matrix/src/admin/room/get_rooms.rs b/crates/matrix/src/admin/rooms/get_many.rs similarity index 100% rename from crates/matrix/src/admin/room/get_rooms.rs rename to crates/matrix/src/admin/rooms/get_many.rs diff --git a/crates/matrix/src/admin/rooms/members.rs b/crates/matrix/src/admin/rooms/members.rs new file mode 100644 index 0000000..125ca70 --- /dev/null +++ b/crates/matrix/src/admin/rooms/members.rs @@ -0,0 +1 @@ +pub mod get; diff --git a/crates/matrix/src/admin/room/get_members.rs b/crates/matrix/src/admin/rooms/members/get.rs similarity index 100% rename from crates/matrix/src/admin/room/get_members.rs rename to crates/matrix/src/admin/rooms/members/get.rs diff --git a/crates/matrix/src/admin/rooms/state.rs b/crates/matrix/src/admin/rooms/state.rs new file mode 100644 index 0000000..125ca70 --- /dev/null +++ b/crates/matrix/src/admin/rooms/state.rs @@ -0,0 +1 @@ +pub mod get; diff --git a/crates/matrix/src/admin/room/get_state.rs b/crates/matrix/src/admin/rooms/state/get.rs similarity index 100% rename from crates/matrix/src/admin/room/get_state.rs rename to crates/matrix/src/admin/rooms/state/get.rs diff --git a/crates/matrix/src/client.rs b/crates/matrix/src/client.rs index 3e6f5d1..feb9cbc 100644 --- a/crates/matrix/src/client.rs +++ b/crates/matrix/src/client.rs @@ -5,9 +5,12 @@ pub mod login; pub mod logout; pub mod register; +pub mod account; +pub mod profile; + pub mod create_room; pub mod rooms; +pub mod directory; +pub mod membership; -pub mod account; -pub mod profile; pub mod uiaa; diff --git a/crates/matrix/src/client/create_room.rs b/crates/matrix/src/client/create_room.rs index 1683742..7bc4794 100644 --- a/crates/matrix/src/client/create_room.rs +++ b/crates/matrix/src/client/create_room.rs @@ -3,7 +3,7 @@ use ruma_common::{ metadata, room::RoomType, serde::Raw, - OwnedRoomAliasId, OwnedRoomId, OwnedUserId, RoomVersionId, + OwnedRoomId, OwnedUserId, RoomVersionId, }; use ruma_events::{ room::{create::PreviousRoom, power_levels::RoomPowerLevelsEventContent}, diff --git a/crates/matrix/src/client/directory.rs b/crates/matrix/src/client/directory.rs new file mode 100644 index 0000000..addf7a5 --- /dev/null +++ b/crates/matrix/src/client/directory.rs @@ -0,0 +1 @@ +pub mod room; diff --git a/crates/matrix/src/client/directory/room.rs b/crates/matrix/src/client/directory/room.rs new file mode 100644 index 0000000..82c789a --- /dev/null +++ b/crates/matrix/src/client/directory/room.rs @@ -0,0 +1,32 @@ +use ruma_common::{ + api::{request, response, Metadata}, + metadata, OwnedRoomAliasId, OwnedServerName, OwnedRoomId, +}; + +#[allow(dead_code)] +const METADATA: Metadata = metadata! { + method: GET, + rate_limited: false, + authentication: None, + history: { + unstable => "/_matrix/client/v3/directory/room/{room_alias}", + } +}; + +#[request(error = crate::Error)] +pub struct Request { + #[ruma_api(path)] + pub room_alias: OwnedRoomAliasId, +} + +impl Request { + pub fn new(room_alias: OwnedRoomAliasId) -> Self { + Self { room_alias } + } +} + +#[response(error = crate::Error)] +pub struct Response { + pub room_id: OwnedRoomId, + pub servers: Vec, +} diff --git a/crates/matrix/src/client/membership.rs b/crates/matrix/src/client/membership.rs new file mode 100644 index 0000000..2c0fcca --- /dev/null +++ b/crates/matrix/src/client/membership.rs @@ -0,0 +1,2 @@ +pub mod join; +pub mod leave; diff --git a/crates/matrix/src/client/membership/join.rs b/crates/matrix/src/client/membership/join.rs new file mode 100644 index 0000000..9892603 --- /dev/null +++ b/crates/matrix/src/client/membership/join.rs @@ -0,0 +1,44 @@ +use ruma_common::{ + api::{request, response, Metadata}, + metadata, OwnedRoomId, OwnedRoomOrAliasId, OwnedServerName, +}; +use serde::Serialize; + +#[allow(dead_code)] +const METADATA: Metadata = metadata! { + method: POST, + rate_limited: true, + authentication: AccessToken, + history: { + unstable => "/_matrix/client/v3/join/:room_id_or_alias", + } +}; + +#[request(error = crate::Error)] +pub struct Request { + #[ruma_api(path)] + pub room_id_or_alias: OwnedRoomOrAliasId, + + #[ruma_api(query)] + #[serde(skip_serializing_if = "<[_]>::is_empty")] + pub server_name: Vec, + + #[serde(skip_serializing_if = "Option::is_none")] + pub reason: Option, +} + +impl Request { + pub fn new(room_id_or_alias: OwnedRoomOrAliasId, reason: Option) -> Self { + Self { + room_id_or_alias, + reason, + server_name: Vec::new(), + } + } +} + +#[response(error = crate::Error)] +#[derive(Serialize)] +pub struct Response { + pub room_id: OwnedRoomId, +} diff --git a/crates/matrix/src/client/membership/leave.rs b/crates/matrix/src/client/membership/leave.rs new file mode 100644 index 0000000..51470ef --- /dev/null +++ b/crates/matrix/src/client/membership/leave.rs @@ -0,0 +1,34 @@ +use ruma_common::{ + api::{request, response, Metadata}, + metadata, OwnedRoomId, +}; + +#[allow(dead_code)] +const METADATA: Metadata = metadata! { + method: POST, + rate_limited: true, + authentication: AccessToken, + history: { + unstable => "/_matrix/client/v3/rooms/:room_id/leave", + } +}; + +#[request(error = crate::Error)] +pub struct Request { + #[ruma_api(path)] + pub room_id: OwnedRoomId, + + #[serde(skip_serializing_if = "Option::is_none")] + pub reason: Option, +} + +impl Request { + pub fn new(room_id: OwnedRoomId, reason: Option) -> Self { + Self { room_id, reason } + } +} + +#[response(error = crate::Error)] +pub struct Response { + pub room_id: OwnedRoomId, +} diff --git a/crates/matrix/src/client/rooms/messages.rs b/crates/matrix/src/client/rooms/messages.rs new file mode 100644 index 0000000..e69de29 diff --git a/crates/matrix/src/client/rooms/state.rs b/crates/matrix/src/client/rooms/state.rs index f715df0..c59b5b4 100644 --- a/crates/matrix/src/client/rooms/state.rs +++ b/crates/matrix/src/client/rooms/state.rs @@ -1,27 +1,3 @@ -use ruma_common::{ - api::{request, response, Metadata}, - metadata, -}; - -#[allow(dead_code)] -const METADATA: Metadata = metadata! { - method: POST, - rate_limited: false, - authentication: AccessToken, - history: { - unstable => "/_matrix/client/v3/logout", - } -}; - -#[request(error = crate::Error)] -pub struct Request {} - -#[allow(clippy::new_without_default)] -impl Request { - pub fn new() -> Self { - Self {} - } -} - -#[response(error = crate::Error)] -pub struct Response {} +pub mod get; +pub mod get_many; +pub mod create; diff --git a/crates/matrix/src/client/rooms/state/create.rs b/crates/matrix/src/client/rooms/state/create.rs new file mode 100644 index 0000000..9534657 --- /dev/null +++ b/crates/matrix/src/client/rooms/state/create.rs @@ -0,0 +1,56 @@ +use ruma_common::{ + api::{request, response, Metadata}, + metadata, + serde::Raw, + OwnedEventId, OwnedRoomId, +}; +use ruma_events::{AnyStateEventContent, StateEventType}; +use serde::{Serialize, Deserialize}; + +#[allow(dead_code)] +const METADATA: Metadata = metadata! { + method: PUT, + rate_limited: true, + authentication: AccessToken, + history: { + unstable => "/_matrix/client/v3/rooms/{room_id}/state/{event_type}/{state_key}", + } +}; + +#[request(error = crate::Error)] +#[derive(Serialize)] +pub struct Request { + #[ruma_api(path)] + pub event_type: StateEventType, + + #[ruma_api(path)] + pub room_id: OwnedRoomId, + + #[ruma_api(path)] + #[serde(skip_serializing_if = "String::is_empty")] + pub state_key: String, + + pub content: Raw, +} + +impl Request { + pub fn new( + event_type: StateEventType, + room_id: OwnedRoomId, + state_key: Option, + content: impl Into, + ) -> serde_json::Result { + Ok(Self { + event_type, + room_id, + state_key: state_key.unwrap_or_default(), + content: Raw::new(&content.into())?, + }) + } +} + +#[response(error = crate::Error)] +#[derive(Deserialize)] +pub struct Response { + pub event_id: OwnedEventId, +} diff --git a/crates/matrix/src/client/rooms/state/get.rs b/crates/matrix/src/client/rooms/state/get.rs new file mode 100644 index 0000000..c312455 --- /dev/null +++ b/crates/matrix/src/client/rooms/state/get.rs @@ -0,0 +1,53 @@ +use ruma_common::{ + api::{request, response, Metadata}, + metadata, + serde::Raw, + OwnedRoomId, +}; +use ruma_events::{AnyStateEventContent, StateEventType}; +use serde::{Deserialize, Serialize}; + +#[allow(dead_code)] +const METADATA: Metadata = metadata! { + method: GET, + rate_limited: false, + authentication: AccessToken, + history: { + unstable => "/_matrix/client/v3/rooms/{room_id}/state/{event_type}/{state_key}", + } +}; + +#[request(error = crate::Error)] +#[derive(Serialize)] +pub struct Request { + #[ruma_api(path)] + pub event_type: StateEventType, + + #[ruma_api(path)] + pub room_id: OwnedRoomId, + + #[ruma_api(path)] + #[serde(skip_serializing_if = "String::is_empty")] + pub state_key: String, +} + +impl Request { + pub fn new( + event_type: StateEventType, + room_id: OwnedRoomId, + state_key: Option, + ) -> Self { + Self { + event_type, + room_id, + state_key: state_key.unwrap_or_default(), + } + } +} + +#[response(error = crate::Error)] +#[derive(Deserialize)] +#[serde(transparent)] +pub struct Response { + pub content: Raw, +} diff --git a/crates/matrix/src/client/rooms/state/get_many.rs b/crates/matrix/src/client/rooms/state/get_many.rs new file mode 100644 index 0000000..18cfdf4 --- /dev/null +++ b/crates/matrix/src/client/rooms/state/get_many.rs @@ -0,0 +1,38 @@ +use ruma_common::{ + api::{request, response, Metadata}, + metadata, + serde::Raw, + OwnedRoomId, +}; +use ruma_events::AnyStateEvent; +use serde::{Deserialize, Serialize}; + +#[allow(dead_code)] +const METADATA: Metadata = metadata! { + method: GET, + rate_limited: true, + authentication: AccessToken, + history: { + unstable => "/_matrix/client/v3/rooms/{room_id}/state", + } +}; + +#[request(error = crate::Error)] +#[derive(Serialize)] +pub struct Request { + #[ruma_api(path)] + pub room_id: OwnedRoomId, +} + +impl Request { + pub fn new(room_id: OwnedRoomId) -> Self { + Self { room_id } + } +} + +#[response(error = crate::Error)] +#[derive(Deserialize)] +#[serde(transparent)] +pub struct Response { + pub event_id: Vec>, +} diff --git a/crates/router/src/api.rs b/crates/router/src/api.rs index 4117da4..21a185e 100644 --- a/crates/router/src/api.rs +++ b/crates/router/src/api.rs @@ -3,7 +3,8 @@ //! reference: https://spec.matrix.org/unstable/client-server-api pub mod account; -pub mod relative; +pub mod direct; +pub mod membership; pub mod register; +pub mod relative; pub mod spaces; -pub mod rooms; diff --git a/crates/router/src/api/direct.rs b/crates/router/src/api/direct.rs new file mode 100644 index 0000000..c5fb369 --- /dev/null +++ b/crates/router/src/api/direct.rs @@ -0,0 +1 @@ +pub mod create; diff --git a/crates/router/src/api/direct/create.rs b/crates/router/src/api/direct/create.rs new file mode 100644 index 0000000..aa0d192 --- /dev/null +++ b/crates/router/src/api/direct/create.rs @@ -0,0 +1,40 @@ +use axum::{ + response::{IntoResponse, Response}, + Json, +}; +use axum_extra::{ + headers::{authorization::Bearer, Authorization}, + TypedHeader, +}; +use matrix::ruma_common::OwnedUserId; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Payload { + pub name: Option, + pub topic: Option, + pub invite: Vec, +} + +pub async fn handler( + TypedHeader(access_token): TypedHeader>, + Json(payload): Json, +) -> Response { + use commune::direct::create::service; + + match service( + access_token.token(), + payload.name, + payload.topic, + payload.invite + ) + .await + { + Ok(resp) => Json(resp).into_response(), + Err(e) => { + tracing::warn!(?e, "failed to create room"); + + e.into_response() + } + } +} diff --git a/crates/router/src/api/membership.rs b/crates/router/src/api/membership.rs new file mode 100644 index 0000000..2c0fcca --- /dev/null +++ b/crates/router/src/api/membership.rs @@ -0,0 +1,2 @@ +pub mod join; +pub mod leave; diff --git a/crates/router/src/api/membership/join.rs b/crates/router/src/api/membership/join.rs new file mode 100644 index 0000000..1b38b71 --- /dev/null +++ b/crates/router/src/api/membership/join.rs @@ -0,0 +1,26 @@ +use axum::{ + extract::Path, + response::{IntoResponse, Response}, + Json, +}; +use axum_extra::{ + headers::{authorization::Bearer, Authorization}, + TypedHeader, +}; +use matrix::ruma_common::OwnedRoomOrAliasId; + +pub async fn handler( + TypedHeader(access_token): TypedHeader>, + Path(room_or_alias_id): Path, +) -> Response { + use commune::membership::join::service; + + match service(access_token.token(), room_or_alias_id, None).await { + Ok(resp) => Json(resp).into_response(), + Err(e) => { + tracing::warn!(?e, "failed to join space"); + + e.into_response() + } + } +} diff --git a/crates/router/src/api/membership/leave.rs b/crates/router/src/api/membership/leave.rs new file mode 100644 index 0000000..e69de29 diff --git a/crates/router/src/api/rooms.rs b/crates/router/src/api/rooms.rs index dec16f3..c5fb369 100644 --- a/crates/router/src/api/rooms.rs +++ b/crates/router/src/api/rooms.rs @@ -1 +1 @@ -pub mod root; +pub mod create; diff --git a/crates/router/src/api/rooms/root.rs b/crates/router/src/api/rooms/create.rs similarity index 78% rename from crates/router/src/api/rooms/root.rs rename to crates/router/src/api/rooms/create.rs index d2d2388..e55e691 100644 --- a/crates/router/src/api/rooms/root.rs +++ b/crates/router/src/api/rooms/create.rs @@ -6,7 +6,7 @@ use axum_extra::{ headers::{authorization::Bearer, Authorization}, TypedHeader, }; -use matrix::ruma_common::OwnedRoomOrAliasId; +use matrix::ruma_common::OwnedUserId; use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] @@ -14,26 +14,27 @@ pub struct Payload { pub alias: Option, pub name: Option, pub topic: Option, - pub parent: OwnedRoomOrAliasId, + pub invite: Vec, } pub async fn handler( TypedHeader(access_token): TypedHeader>, Json(payload): Json, ) -> Response { - use commune::rooms::create::service; + use commune::rooms::create::direct::service; match service( access_token.token(), payload.alias, payload.name, payload.topic, + payload.invite ) .await { Ok(resp) => Json(resp).into_response(), Err(e) => { - tracing::warn!(?e, "failed to create space"); + tracing::warn!(?e, "failed to create room"); e.into_response() } diff --git a/crates/router/src/api/spaces.rs b/crates/router/src/api/spaces.rs index dec16f3..3c29188 100644 --- a/crates/router/src/api/spaces.rs +++ b/crates/router/src/api/spaces.rs @@ -1 +1,2 @@ -pub mod root; +pub mod channels; +pub mod create; diff --git a/crates/router/src/api/spaces/channels.rs b/crates/router/src/api/spaces/channels.rs new file mode 100644 index 0000000..c5fb369 --- /dev/null +++ b/crates/router/src/api/spaces/channels.rs @@ -0,0 +1 @@ +pub mod create; diff --git a/crates/router/src/api/spaces/channels/create.rs b/crates/router/src/api/spaces/channels/create.rs new file mode 100644 index 0000000..3f22e8b --- /dev/null +++ b/crates/router/src/api/spaces/channels/create.rs @@ -0,0 +1,34 @@ +use axum::{ + extract::Path, + response::{IntoResponse, Response}, + Json, +}; +use axum_extra::{ + headers::{authorization::Bearer, Authorization}, + TypedHeader, +}; +use commune::util::opaque_id::OpaqueId; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Payload { + pub name: Option, + pub topic: Option, +} + +pub async fn handler( + TypedHeader(access_token): TypedHeader>, + Path(space_id): Path, + Json(payload): Json, +) -> Response { + use commune::spaces::channels::create::service; + + match service(access_token.token(), space_id, payload.name, payload.topic).await { + Ok(resp) => Json(resp).into_response(), + Err(e) => { + tracing::warn!(?e, "failed to create channel for space"); + + e.into_response() + } + } +} diff --git a/crates/router/src/api/spaces/root.rs b/crates/router/src/api/spaces/create.rs similarity index 100% rename from crates/router/src/api/spaces/root.rs rename to crates/router/src/api/spaces/create.rs diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs index 6200b12..ea12e34 100644 --- a/crates/router/src/lib.rs +++ b/crates/router/src/lib.rs @@ -10,6 +10,8 @@ pub mod api; pub async fn routes() -> Router { let router = Router::new() + .route("/login", post(api::relative::login::handler)) + .route("/logout", post(api::relative::logout::handler)) .nest( "/register", Router::new() @@ -20,8 +22,6 @@ pub async fn routes() -> Router { ) .route("/email/:email", get(api::register::email::handler)), ) - .route("/login", post(api::relative::login::handler)) - .route("/logout", post(api::relative::logout::handler)) .nest( "/account", Router::new() @@ -30,10 +30,15 @@ pub async fn routes() -> Router { .route("/display_name", put(api::account::display_name::handler)) .route("/avatar", put(api::account::avatar::handler)), ) + .nest( + "/direct", + Router::new().route("/", post(api::direct::create::handler)), + ) .nest( "/spaces", Router::new() - .route("/", post(api::spaces::root::handler)) + .route("/", post(api::spaces::create::handler)) + .route("/:space_id/channels", post(api::spaces::channels::create::handler)), ); Router::new().nest("/_commune/client/r0", router) diff --git a/crates/test/src/api.rs b/crates/test/src/api.rs index b8bbac1..2e5b066 100644 --- a/crates/test/src/api.rs +++ b/crates/test/src/api.rs @@ -5,3 +5,4 @@ pub mod account; pub mod relative; pub mod spaces; +pub mod rooms; diff --git a/crates/test/src/api/rooms.rs b/crates/test/src/api/rooms.rs new file mode 100644 index 0000000..e69de29 diff --git a/crates/test/src/api/rooms/create.rs b/crates/test/src/api/rooms/create.rs new file mode 100644 index 0000000..afdae97 --- /dev/null +++ b/crates/test/src/api/rooms/create.rs @@ -0,0 +1 @@ +pub mod direct; diff --git a/crates/test/src/api/rooms/create/direct.rs b/crates/test/src/api/rooms/create/direct.rs new file mode 100644 index 0000000..a9771db --- /dev/null +++ b/crates/test/src/api/rooms/create/direct.rs @@ -0,0 +1,35 @@ +use commune::util::secret::Secret; + +use matrix::client::register::root::*; +use router::api::register::root as register; + +use crate::{env::Env, util::generate_comforming_localpart}; + +pub async fn register(client: &Env) -> Result { + let username = generate_comforming_localpart(); + + tracing::info!(?username); + + let resp = client + .post("/_commune/client/r0/rooms") + .json(®ister::Payload { + username, + password: Secret::new("verysecure"), + registration_token: None, + }) + .send() + .await?; + + resp.json().await +} + +#[tokio::test] +async fn register_test() { + let client = Env::new().await; + + let resp = register(&client).await.unwrap(); + + tracing::info!(?resp); + + assert!(resp.access_token.is_some() && resp.access_token.map(|at| !at.is_empty()).unwrap()); +} diff --git a/crates/test/src/api/rooms/direct.rs b/crates/test/src/api/rooms/direct.rs new file mode 100644 index 0000000..e69de29 diff --git a/crates/test/src/api/spaces/create.rs b/crates/test/src/api/spaces/create.rs index c77371b..a578b3b 100644 --- a/crates/test/src/api/spaces/create.rs +++ b/crates/test/src/api/spaces/create.rs @@ -1,5 +1,5 @@ use matrix::client::create_room::*; -use router::api::spaces::root::Payload; +use router::api::spaces::create::Payload; use crate::{api::relative::login, env::Env};