pull in ruma so I do not have to write all requests and responses myself
Some checks failed
continuous-integration/drone Build is failing
Some checks failed
continuous-integration/drone Build is failing
This commit is contained in:
parent
ba84efd384
commit
71590d6c60
152
Cargo.lock
generated
152
Cargo.lock
generated
@ -39,6 +39,12 @@ dependencies = [
|
||||
"password-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "assign"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f093eed78becd229346bf859eec0aa4dd7ddde0757287b2b4107a1f09c80002"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.53"
|
||||
@ -110,6 +116,18 @@ dependencies = [
|
||||
"mime",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-macros"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cae774e664fd50bf80c9a7132d64ff70be3232a192ff4ba5619c3f72b9b4711f"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.0"
|
||||
@ -419,9 +437,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.6"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03"
|
||||
checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
@ -499,6 +517,7 @@ checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -534,6 +553,24 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js_int"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d937f95470b270ce8b8950207715d71aa8e153c0d44c6684d59397ed4949160a"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js_option"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68421373957a1593a767013698dbf206e2b221eefe97a44d98d18672ff38423c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
@ -576,6 +613,12 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maplit"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.1.0"
|
||||
@ -604,8 +647,12 @@ dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
"axum",
|
||||
"axum-macros",
|
||||
"http",
|
||||
"rand",
|
||||
"ruma",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
@ -815,6 +862,16 @@ version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.37"
|
||||
@ -911,6 +968,82 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruma"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6602cb2ef70629013d1bfade5aeb775d971a5d87f008dd6a8c99566235fa1933"
|
||||
dependencies = [
|
||||
"assign",
|
||||
"js_int",
|
||||
"ruma-client-api",
|
||||
"ruma-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruma-client-api"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4339827423dbb3b4f86cb191a38621f12daef73cb304ffd4e050c9ee553ecbd6"
|
||||
dependencies = [
|
||||
"assign",
|
||||
"bytes",
|
||||
"http",
|
||||
"js_int",
|
||||
"maplit",
|
||||
"percent-encoding",
|
||||
"ruma-common",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruma-common"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ec5360fd23ff56310f9eb571927614feeecceba91fe2d4937f031c236c0e86e"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
"form_urlencoded",
|
||||
"http",
|
||||
"indexmap",
|
||||
"itoa",
|
||||
"js_int",
|
||||
"js_option",
|
||||
"percent-encoding",
|
||||
"ruma-identifiers-validation",
|
||||
"ruma-macros",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
"url",
|
||||
"wildmatch",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruma-identifiers-validation"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74c3b1d01b5ddd8746f25d5971bc1cac5d7f1f455de839a2f817b9e04953a139"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruma-macros"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee1a4faf04110071ce7ca438ad0763bdaa5514395593596320c0ca0936519656"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"ruma-identifiers-validation",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.19.1"
|
||||
@ -1298,6 +1431,15 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.4.12"
|
||||
@ -1605,6 +1747,12 @@ dependencies = [
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wildmatch"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6c48bd20df7e4ced539c12f570f937c6b4884928a87fee70a479d72f031d4e0"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
@ -11,10 +11,14 @@ axum = "0.5"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
serde = {version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
tower-http = { version = "0.2", features = ["cors", "trace"]}
|
||||
sqlx = { version = "0.5", features = ["sqlite", "macros", "runtime-tokio-rustls", "offline"] }
|
||||
anyhow = "1.0"
|
||||
thiserror = "1.0"
|
||||
argon2 = { version = "0.4", features = ["std"] }
|
||||
rand = { version = "0.8.5", features = ["std"] }
|
||||
uuid = { version = "1.0", features = ["v4"] }
|
||||
uuid = { version = "1.0", features = ["v4"] }
|
||||
ruma = { version = "0.6.4", features = ["client-api"] }
|
||||
axum-macros = "0.2.2"
|
||||
http = "0.2.8"
|
||||
|
8
migrations/20220507162217_add_rooms.sql
Normal file
8
migrations/20220507162217_add_rooms.sql
Normal file
@ -0,0 +1,8 @@
|
||||
-- Add migration script here
|
||||
|
||||
CREATE TABLE rooms(
|
||||
uuid TEXT PRIMARY KEY NOT NULL,
|
||||
name TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX name_index ON rooms(name);
|
13
migrations/20220507162532_add_events.sql
Normal file
13
migrations/20220507162532_add_events.sql
Normal file
@ -0,0 +1,13 @@
|
||||
-- Add migration script here
|
||||
|
||||
CREATE TABLE events(
|
||||
uuid TEXT PRIMARY KEY NOT NULL,
|
||||
room_uuid TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
state_key TEXT,
|
||||
sender_uuid TEXT NOT NULL,
|
||||
origin_server_ts INT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
FOREIGN KEY(room_uuid) REFERENCES rooms(uuid),
|
||||
FOREIGN KEY(sender_uuid) REFERENCES users(uuid)
|
||||
);
|
@ -1,9 +1,7 @@
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use argon2::{password_hash::SaltString, Argon2, PasswordHash, PasswordHasher};
|
||||
use axum::{
|
||||
extract::Query,
|
||||
http::StatusCode,
|
||||
routing::{get, post},
|
||||
Extension, Json,
|
||||
};
|
||||
@ -19,19 +17,19 @@ use crate::{
|
||||
authentication::{AuthenticationResponse, AuthenticationSuccess},
|
||||
registration::RegistrationResponse,
|
||||
},
|
||||
types::uuid::Uuid,
|
||||
};
|
||||
use crate::{
|
||||
models::devices::Device,
|
||||
responses::{flow::Flows, registration::RegistrationSuccess},
|
||||
ruma_wrapper::RumaResponse,
|
||||
};
|
||||
use crate::{models::devices::Device, responses::registration::RegistrationSuccess};
|
||||
use crate::{
|
||||
models::users::User,
|
||||
requests::registration::RegistrationRequest,
|
||||
responses::username_available::UsernameAvailable,
|
||||
types::{authentication_data::AuthenticationData, user_id::UserId},
|
||||
Config,
|
||||
};
|
||||
use ruma::api::client::{
|
||||
account,
|
||||
session::get_login_types::v3::{LoginType, PasswordLoginType},
|
||||
};
|
||||
|
||||
pub fn routes() -> axum::Router {
|
||||
axum::Router::new()
|
||||
@ -40,9 +38,12 @@ pub fn routes() -> axum::Router {
|
||||
.route("/r0/register/available", get(get_username_available))
|
||||
}
|
||||
|
||||
use ruma::api::client::session;
|
||||
#[tracing::instrument]
|
||||
async fn get_login() -> Result<Json<Flows>, ApiError> {
|
||||
Ok(Json(Flows::new()))
|
||||
async fn get_login() -> Result<RumaResponse<session::get_login_types::v3::Response>, ApiError> {
|
||||
Ok(RumaResponse(session::get_login_types::v3::Response::new(
|
||||
vec![LoginType::Password(PasswordLoginType::new())],
|
||||
)))
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
@ -92,7 +93,7 @@ async fn get_username_available(
|
||||
Extension(config): Extension<Arc<Config>>,
|
||||
Extension(db): Extension<SqlitePool>,
|
||||
Query(params): Query<HashMap<String, String>>,
|
||||
) -> Result<Json<UsernameAvailable>, ApiError> {
|
||||
) -> Result<RumaResponse<account::get_username_availability::v3::Response>, ApiError> {
|
||||
let username = params
|
||||
.get("username")
|
||||
.ok_or(RegistrationError::MissingUserId)?;
|
||||
@ -101,7 +102,9 @@ async fn get_username_available(
|
||||
.ok_or(RegistrationError::InvalidUserId)?;
|
||||
let exists = User::exists(&db, &user_id).await?;
|
||||
|
||||
Ok(Json(UsernameAvailable::new(!exists)))
|
||||
Ok(RumaResponse(
|
||||
account::get_username_availability::v3::Response::new(!exists),
|
||||
))
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
@ -110,7 +113,10 @@ async fn post_register(
|
||||
Extension(db): Extension<SqlitePool>,
|
||||
Json(body): Json<RegistrationRequest>,
|
||||
) -> Result<Json<RegistrationResponse>, ApiError> {
|
||||
config.enable_registration().then(|| true).ok_or(RegistrationError::RegistrationDisabled)?;
|
||||
config
|
||||
.enable_registration()
|
||||
.then(|| true)
|
||||
.ok_or(RegistrationError::RegistrationDisabled)?;
|
||||
body.auth()
|
||||
.ok_or(RegistrationError::AdditionalAuthenticationInformation)?;
|
||||
|
||||
|
17
src/api/client_server/r0/create_room.rs
Normal file
17
src/api/client_server/r0/create_room.rs
Normal file
@ -0,0 +1,17 @@
|
||||
use axum::routing::post;
|
||||
|
||||
use crate::api::client_server::errors::api_error::ApiError;
|
||||
use crate::ruma_wrapper::RumaRequest;
|
||||
|
||||
use ruma::api::client::room;
|
||||
|
||||
pub fn routes() -> axum::Router {
|
||||
axum::Router::new()
|
||||
.route("/r0/createRoom", post(post_create_room))
|
||||
.layer(axum::middleware::from_fn(super::authentication_middleware))
|
||||
}
|
||||
async fn post_create_room(
|
||||
RumaRequest(_req): RumaRequest<room::create_room::v3::IncomingRequest>,
|
||||
) -> Result<String, ApiError> {
|
||||
Ok("".into())
|
||||
}
|
@ -14,6 +14,7 @@ use super::errors::ErrorResponse;
|
||||
|
||||
pub mod auth;
|
||||
pub mod thirdparty;
|
||||
pub mod create_room;
|
||||
|
||||
async fn authentication_middleware<B>(mut req: Request<B>, next: Next<B>) -> impl IntoResponse {
|
||||
let db: &SqlitePool = req.extensions().get().unwrap();
|
||||
|
@ -1,9 +1,14 @@
|
||||
use std::sync::Arc;
|
||||
use std::{collections::BTreeMap, sync::Arc};
|
||||
|
||||
use axum::{routing::get, Extension};
|
||||
|
||||
use crate::{api::client_server::errors::api_error::ApiError, models::users::User};
|
||||
use crate::{
|
||||
api::client_server::errors::api_error::ApiError,
|
||||
models::users::User,
|
||||
ruma_wrapper::{RumaRequest, RumaResponse},
|
||||
};
|
||||
|
||||
use ruma::api::client::thirdparty;
|
||||
|
||||
pub fn routes() -> axum::Router {
|
||||
axum::Router::new()
|
||||
@ -12,6 +17,11 @@ pub fn routes() -> axum::Router {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn get_thirdparty_protocols(Extension(user): Extension<Arc<User>>) -> Result<String, ApiError> {
|
||||
Ok("{}".into())
|
||||
}
|
||||
async fn get_thirdparty_protocols(
|
||||
Extension(_user): Extension<Arc<User>>,
|
||||
RumaRequest(_req): RumaRequest<thirdparty::get_protocols::v3::IncomingRequest>,
|
||||
) -> Result<RumaResponse<thirdparty::get_protocols::v3::Response>, ApiError> {
|
||||
Ok(RumaResponse(thirdparty::get_protocols::v3::Response::new(
|
||||
BTreeMap::new(),
|
||||
)))
|
||||
}
|
||||
|
17
src/main.rs
17
src/main.rs
@ -1,5 +1,3 @@
|
||||
#![allow(unused)]
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::{
|
||||
@ -9,17 +7,15 @@ use axum::{
|
||||
Extension, Router,
|
||||
};
|
||||
use config::Config;
|
||||
use tower_http::{
|
||||
cors::CorsLayer,
|
||||
trace::{DefaultOnRequest, DefaultOnResponse, TraceLayer},
|
||||
};
|
||||
use tracing::Level;
|
||||
use tower_http::{cors::CorsLayer, trace::TraceLayer};
|
||||
|
||||
mod api;
|
||||
mod config;
|
||||
mod models;
|
||||
mod requests;
|
||||
mod responses;
|
||||
mod ruma_wrapper;
|
||||
mod state_resolution;
|
||||
mod types;
|
||||
|
||||
#[tokio::main]
|
||||
@ -44,7 +40,8 @@ async fn main() -> anyhow::Result<()> {
|
||||
let client_server = Router::new()
|
||||
.merge(api::client_server::versions::routes())
|
||||
.merge(api::client_server::r0::auth::routes())
|
||||
.merge(api::client_server::r0::thirdparty::routes());
|
||||
.merge(api::client_server::r0::thirdparty::routes())
|
||||
.merge(api::client_server::r0::create_room::routes());
|
||||
|
||||
let router = Router::new()
|
||||
.nest("/_matrix/client", client_server)
|
||||
@ -61,7 +58,7 @@ async fn main() -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn fallback(mut request: Request<Body>) -> StatusCode {
|
||||
println!("{} {}", request.method(), request.uri());
|
||||
async fn fallback(request: Request<Body>) -> StatusCode {
|
||||
tracing::error!("{} {}", request.method(), request.uri());
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
|
72
src/models/events.rs
Normal file
72
src/models/events.rs
Normal file
@ -0,0 +1,72 @@
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
use crate::types::{uuid::Uuid, event_type::EventType};
|
||||
|
||||
use super::{rooms::Room, users::User};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Event {
|
||||
uuid: Uuid,
|
||||
room_uuid: Uuid,
|
||||
r#type: EventType,
|
||||
state_key: Option<String>,
|
||||
sender_uuid: Uuid,
|
||||
origin_server_ts: i64,
|
||||
content: String,
|
||||
}
|
||||
|
||||
impl Event {
|
||||
fn new(
|
||||
room: &Room,
|
||||
r#type: EventType,
|
||||
state_key: Option<String>,
|
||||
sender: &User,
|
||||
origin_server_ts: i64,
|
||||
content: &str,
|
||||
) -> anyhow::Result<Self> {
|
||||
Ok(Self {
|
||||
uuid: uuid::Uuid::new_v4().into(),
|
||||
room_uuid: room.uuid().to_owned(),
|
||||
state_key,
|
||||
r#type,
|
||||
sender_uuid: sender.uuid().to_owned(),
|
||||
origin_server_ts,
|
||||
content: content.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn create(&self, conn: &SqlitePool) -> anyhow::Result<Self> {
|
||||
Ok(sqlx::query_as!(
|
||||
Self,
|
||||
"insert into events(uuid, room_uuid, type, state_key, sender_uuid, origin_server_ts, content)
|
||||
values(?, ?, ?, ?, ?, ?, ?)
|
||||
returning uuid as 'uuid: Uuid', room_uuid as 'room_uuid: Uuid', type as 'type: EventType', state_key, sender_uuid as 'sender_uuid: Uuid', origin_server_ts, content",
|
||||
self.uuid,
|
||||
self.room_uuid,
|
||||
self.r#type,
|
||||
self.state_key,
|
||||
self.sender_uuid,
|
||||
self.origin_server_ts,
|
||||
self.content
|
||||
)
|
||||
.fetch_one(conn)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn all_for_room(conn: &SqlitePool, room: &Room) -> anyhow::Result<Vec<Event>> {
|
||||
let room_uuid = room.uuid();
|
||||
Ok(sqlx::query_as!(
|
||||
Self,
|
||||
"select uuid as 'uuid: Uuid', room_uuid as 'room_uuid: Uuid', type as 'type: EventType', state_key, sender_uuid as 'sender_uuid: Uuid', origin_server_ts, content
|
||||
from events
|
||||
where room_uuid = ?",
|
||||
room_uuid
|
||||
)
|
||||
.fetch_all(conn)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub fn content(&self) -> serde_json::Value {
|
||||
serde_json::from_str(&self.content).expect("has to be valid json")
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
pub mod devices;
|
||||
pub mod events;
|
||||
pub mod rooms;
|
||||
pub mod sessions;
|
||||
pub mod users;
|
||||
|
42
src/models/rooms.rs
Normal file
42
src/models/rooms.rs
Normal file
@ -0,0 +1,42 @@
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
use crate::types::uuid::Uuid;
|
||||
|
||||
use super::events::Event;
|
||||
|
||||
pub struct Room {
|
||||
uuid: Uuid,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl Room {
|
||||
fn new(name: &str) -> anyhow::Result<Self> {
|
||||
Ok(Self {
|
||||
uuid: uuid::Uuid::new_v4().into(),
|
||||
name: name.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn create(&self, conn: &SqlitePool) -> anyhow::Result<Self> {
|
||||
Ok(sqlx::query_as!(
|
||||
Self,
|
||||
"insert into rooms(uuid, name)
|
||||
values(?, ?)
|
||||
returning uuid as 'uuid: Uuid', name",
|
||||
self.uuid,
|
||||
self.name
|
||||
)
|
||||
.fetch_one(conn)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn events(&self, conn: &SqlitePool) -> anyhow::Result<Vec<Event>> {
|
||||
Event::all_for_room(conn, self).await
|
||||
}
|
||||
|
||||
/// Get a reference to the room's uuid.
|
||||
#[must_use]
|
||||
pub fn uuid(&self) -> &Uuid {
|
||||
&self.uuid
|
||||
}
|
||||
}
|
29
src/requests/create_room_request.rs
Normal file
29
src/requests/create_room_request.rs
Normal file
@ -0,0 +1,29 @@
|
||||
use crate::types::user_id::UserId;
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
pub struct CreateRoomRequest {
|
||||
/// Extra keys, such as `m.federate`, to be added to the content of the `m.room.create` event.
|
||||
creation_content: Option<()>,
|
||||
/// List of state events to set in the initial room. Used for overriding the default state
|
||||
initial_state: Vec<()>,
|
||||
/// List of user IDs to invite to the room
|
||||
invite: Option<Vec<String>>,
|
||||
/// List of thirdparty IDs to invite to the room
|
||||
invite_3pid: Option<Vec<()>>,
|
||||
/// Indicate if room is a direct chat room
|
||||
is_direct: Option<bool>,
|
||||
/// Set name of the room
|
||||
name: Option<String>,
|
||||
/// Used to override the default power level event
|
||||
power_level_content_override: Option<()>,
|
||||
/// Preset for room creation
|
||||
preset: Option<()>,
|
||||
/// Desired room alias local part
|
||||
room_alias_name: Option<String>,
|
||||
/// Version of room to create. Defaults to server default
|
||||
room_version: Option<String>,
|
||||
/// Sets rooms topic
|
||||
topic: Option<String>,
|
||||
/// Sets rooms visibility
|
||||
visibility: ()
|
||||
}
|
@ -1 +1,2 @@
|
||||
pub mod registration;
|
||||
pub mod create_room_request;
|
65
src/ruma_wrapper.rs
Normal file
65
src/ruma_wrapper.rs
Normal file
@ -0,0 +1,65 @@
|
||||
use axum::{
|
||||
body::{Bytes, HttpBody, Full},
|
||||
extract::{FromRequest, Path},
|
||||
response::IntoResponse,
|
||||
BoxError,
|
||||
};
|
||||
use http::StatusCode;
|
||||
use ruma::{
|
||||
api::{IncomingRequest, OutgoingResponse},
|
||||
exports::bytes::{BufMut, BytesMut},
|
||||
serde::CanonicalJsonValue,
|
||||
};
|
||||
|
||||
use crate::api::client_server::errors::api_error::ApiError;
|
||||
|
||||
pub struct RumaRequest<R>(pub R)
|
||||
where
|
||||
R: IncomingRequest;
|
||||
|
||||
#[axum::async_trait]
|
||||
impl<R, B> FromRequest<B> for RumaRequest<R>
|
||||
where
|
||||
R: IncomingRequest,
|
||||
B: HttpBody + Send,
|
||||
B::Data: Send,
|
||||
B::Error: Into<BoxError>,
|
||||
{
|
||||
type Rejection = ApiError;
|
||||
|
||||
async fn from_request(
|
||||
req: &mut axum::extract::RequestParts<B>,
|
||||
) -> Result<Self, Self::Rejection> {
|
||||
let path_params = Path::<Vec<String>>::from_request(req)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!(e))?;
|
||||
let body = Bytes::from_request(req)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!(e))?;
|
||||
|
||||
let json =
|
||||
serde_json::from_slice::<CanonicalJsonValue>(&body).map_err(|e| anyhow::anyhow!(e))?;
|
||||
let mut buf = BytesMut::new().writer();
|
||||
serde_json::to_writer(&mut buf, &json).expect("can't fail");
|
||||
let body = buf.into_inner().freeze();
|
||||
|
||||
let builder = http::Request::builder().uri(req.uri()).method(req.method());
|
||||
let request = builder.body(body).map_err(|e| anyhow::anyhow!(e))?;
|
||||
Ok(Self(
|
||||
R::try_from_http_request(request, &path_params).map_err(|e| anyhow::anyhow!(e))?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RumaResponse<R>(pub R)
|
||||
where
|
||||
R: OutgoingResponse;
|
||||
|
||||
impl<R: OutgoingResponse> IntoResponse for RumaResponse<R> {
|
||||
fn into_response(self) -> axum::response::Response {
|
||||
match self.0.try_into_http_response::<BytesMut>() {
|
||||
Ok(res) => res.map(BytesMut::freeze).map(Full::new).into_response(),
|
||||
Err(_) => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
|
||||
}
|
||||
}
|
||||
}
|
1
src/state_resolution/mod.rs
Normal file
1
src/state_resolution/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
mod v2;
|
102
src/state_resolution/v2.rs
Normal file
102
src/state_resolution/v2.rs
Normal file
@ -0,0 +1,102 @@
|
||||
use crate::models::events::Event;
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
future::Future,
|
||||
};
|
||||
use tracing::info;
|
||||
|
||||
type StateMap<T> = HashMap<StateTuple, T>;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||
pub struct StateTuple {
|
||||
event_type: String,
|
||||
state_key: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct EventId {
|
||||
id: Box<str>,
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(state_sets, auth_chain_sets, get_event_callback))]
|
||||
pub async fn resolve<F, Fut>(
|
||||
room_id: &str, // TODO: own type
|
||||
state_sets: Vec<StateMap<EventId>>,
|
||||
auth_chain_sets: Vec<HashSet<EventId>>,
|
||||
get_event_callback: F,
|
||||
) -> StateMap<EventId>
|
||||
where
|
||||
F: Fn(&EventId) -> Fut,
|
||||
Fut: Future<Output = Option<Event>>,
|
||||
{
|
||||
info!("Calculating conflicted state");
|
||||
|
||||
let (unconflicted_state, conflicted_state) = separate_state(&state_sets);
|
||||
|
||||
if conflicted_state.is_empty() {
|
||||
return unconflicted_state;
|
||||
}
|
||||
|
||||
info!("{} conflicted_state entries", conflicted_state.len());
|
||||
info!("Calculating auth_chain differences");
|
||||
|
||||
let conflicted_set =
|
||||
get_auth_chain_differences(auth_chain_sets).chain(conflicted_state.into_values().flatten());
|
||||
let mut conflicted = HashSet::new();
|
||||
for eid in conflicted_set {
|
||||
if let Some(event) = get_event_callback(&eid).await {
|
||||
conflicted.insert(event);
|
||||
}
|
||||
}
|
||||
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// separates states from multiple state_maps into unconflicted and conflicted state
|
||||
///
|
||||
/// For the set of all state_tuples find all event_ids.
|
||||
/// If one event_id is found it is unconflicted, otherwise it is conflicted
|
||||
fn separate_state(
|
||||
state_sets: &[StateMap<EventId>],
|
||||
) -> (StateMap<EventId>, StateMap<HashSet<EventId>>) {
|
||||
let mut unconflicted_state: StateMap<EventId> = StateMap::new();
|
||||
let mut conflicted_state: HashMap<StateTuple, HashSet<EventId>> = StateMap::new();
|
||||
|
||||
for key in state_sets
|
||||
.iter()
|
||||
.flat_map(HashMap::keys)
|
||||
.map(ToOwned::to_owned)
|
||||
.collect::<HashSet<StateTuple>>()
|
||||
{
|
||||
let mut event_ids: HashSet<EventId> = state_sets
|
||||
.iter()
|
||||
.filter_map(|state_set| state_set.get(&key))
|
||||
.map(ToOwned::to_owned)
|
||||
.collect();
|
||||
|
||||
if event_ids.len() == 1 {
|
||||
unconflicted_state.insert(key, event_ids.into_iter().next().expect("len() is 1"));
|
||||
} else {
|
||||
conflicted_state.insert(key, event_ids);
|
||||
}
|
||||
}
|
||||
|
||||
(unconflicted_state, conflicted_state)
|
||||
}
|
||||
|
||||
fn get_auth_chain_differences(
|
||||
auth_chain_sets: Vec<HashSet<EventId>>,
|
||||
) -> impl Iterator<Item = EventId> {
|
||||
let num_sets = auth_chain_sets.len();
|
||||
|
||||
let mut id_counts: HashMap<EventId, usize> = HashMap::new();
|
||||
for id in auth_chain_sets.into_iter().flatten() {
|
||||
*id_counts.entry(id).or_default() += 1;
|
||||
}
|
||||
|
||||
id_counts
|
||||
.into_iter()
|
||||
.filter_map(move |(id, count)| (count < num_sets).then(move || id))
|
||||
}
|
||||
|
||||
fn is_control_event() {}
|
12
src/types/client_event.rs
Normal file
12
src/types/client_event.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use super::{uuid::Uuid, user_id::UserId};
|
||||
|
||||
pub struct ClientEvent {
|
||||
content: (),
|
||||
event_id: Uuid,
|
||||
origin_server_ts: u64,
|
||||
room_id: String,
|
||||
sender: UserId,
|
||||
state_key: Option<String>,
|
||||
r#type: String,
|
||||
unsigned: ()
|
||||
}
|
82
src/types/event_type.rs
Normal file
82
src/types/event_type.rs
Normal file
@ -0,0 +1,82 @@
|
||||
use sqlx::{encode::IsNull, Sqlite};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum EventType {
|
||||
RoomCreate,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl sqlx::Type<Sqlite> for EventType {
|
||||
fn type_info() -> <Sqlite as sqlx::Database>::TypeInfo {
|
||||
<&str as sqlx::Type<Sqlite>>::type_info()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'e> sqlx::Encode<'e, Sqlite> for EventType {
|
||||
fn encode_by_ref(
|
||||
&self,
|
||||
buf: &mut <Sqlite as sqlx::database::HasArguments<'e>>::ArgumentBuffer,
|
||||
) -> sqlx::encode::IsNull {
|
||||
buf.push(sqlx::sqlite::SqliteArgumentValue::Text(
|
||||
match self {
|
||||
EventType::RoomCreate => "m.room.create",
|
||||
EventType::Unknown => "???"
|
||||
}
|
||||
.into(),
|
||||
));
|
||||
|
||||
IsNull::No
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> sqlx::Decode<'d, Sqlite> for EventType {
|
||||
fn decode(
|
||||
value: <Sqlite as sqlx::database::HasValueRef<'d>>::ValueRef,
|
||||
) -> Result<Self, sqlx::error::BoxDynError> {
|
||||
Ok(match <&str as sqlx::Decode<Sqlite>>::decode(value)? {
|
||||
"m.room.create" => EventType::RoomCreate,
|
||||
_ => EventType::Unknown,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for EventType {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(match self {
|
||||
EventType::RoomCreate => "m.room.create",
|
||||
EventType::Unknown => "dev.fuckwit.unknown_event"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for EventType {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
struct IdentifierVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for IdentifierVisitor {
|
||||
type Value = EventType;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("Identifier")
|
||||
}
|
||||
|
||||
fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
match v {
|
||||
"m.id.user" => Ok(EventType::RoomCreate),
|
||||
_ => Err(serde::de::Error::custom("Unknown identifier")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_str(IdentifierVisitor {})
|
||||
}
|
||||
}
|
@ -6,4 +6,7 @@ pub mod identifier_type;
|
||||
pub mod user_id;
|
||||
pub mod user_interactive_authorization;
|
||||
pub mod uuid;
|
||||
pub mod server_name;
|
||||
pub mod server_name;
|
||||
pub mod client_event;
|
||||
pub mod room_event;
|
||||
pub mod event_type;
|
7
src/types/room_event.rs
Normal file
7
src/types/room_event.rs
Normal file
@ -0,0 +1,7 @@
|
||||
pub enum RoomEvent {
|
||||
Create(RoomCreateEvent)
|
||||
}
|
||||
|
||||
pub struct RoomCreateEvent {
|
||||
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
use sqlx::{encode::IsNull, Sqlite, Type};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Uuid(pub uuid::Uuid);
|
||||
|
||||
impl Uuid {
|
||||
|
Loading…
x
Reference in New Issue
Block a user