Skip to content

Commit 877465b

Browse files
authored
feat: channels (merge #3 from channels)
Replaces the current `context` system in favor of any-sized `channels`
2 parents 5911e4e + 05930bf commit 877465b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1170
-710
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ The main technologies implemented in this project are:
1111
* **Websockets:** powered by `axum`
1212
* **Rest API:** powered by `axum`
1313
* **PostgreSQL:** powered by `diesel`
14-
* **Protobuf:** powered by `prost` and `protobuf.js`
14+
* **Protobuf _(soon to be dropped)_:** powered by `prost` and `protobuf.js`
1515
* **Serialization:** powered by `serde`
1616
* **Authentication:** powered by `jwt`
1717
* **Hashing:** powered by `argon2`

server/macros/src/lib.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,27 +22,28 @@ pub fn packet(args: TokenStream, item: TokenStream) -> TokenStream {
2222
let struct_item = parse_macro_input!(item as syn::ItemStruct);
2323
let ident = struct_item.ident.clone();
2424
let expanded = quote! {
25+
#[derive(Clone, PartialEq, serde::Serialize, serde::Deserialize)]
2526
#struct_item
2627

27-
impl PacketStaticId for #ident {
28+
impl crate::server::messages::PacketStaticId for #ident {
2829
fn get_id() -> i32 {
2930
#id
3031
}
3132
}
3233

33-
impl Packet for #ident {
34+
impl crate::server::messages::Packet for #ident {
3435
fn get_id(&self) -> i32 {
3536
#id
3637
}
3738

3839
fn encode_data(&self) -> Vec<u8> {
3940
let mut buffer = Vec::new();
40-
prost::Message::encode(self, &mut buffer).unwrap();
41+
serde_json::to_writer(&mut buffer, self).expect("Failed to encode packet data");
4142
buffer
4243
}
4344

44-
fn decode_data(buffer: &[u8]) -> Result<Self, prost::DecodeError> {
45-
prost::Message::decode(buffer)
45+
fn decode_data(buffer: &[u8]) -> Result<Self, serde_json::Error> {
46+
serde_json::from_slice(buffer)
4647
}
4748
}
4849

server/src/entity/mod.rs

Lines changed: 0 additions & 12 deletions
This file was deleted.

server/src/main.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
mod server;
2-
mod entity;
2+
mod schema;
33
mod database;
44
mod util;
55

@@ -28,7 +28,8 @@ use tower_http::auth::{AsyncRequireAuthorizationLayer, AsyncAuthorizeRequest};
2828
use tower_http::cors::CorsLayer;
2929
use tracing::Subscriber;
3030
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
31-
use crate::entity::user::User;
31+
use crate::schema::ctes::select_messages_from;
32+
use crate::schema::users::User;
3233
use crate::server::gateway::Gateway;
3334
use crate::server::messages::Packet;
3435
use crate::server::rest::middlewares::{authorize, IrisAuth};
@@ -60,22 +61,24 @@ async fn main() {
6061
tracing_subscriber::registry()
6162
.with(
6263
tracing_subscriber::EnvFilter::try_from_default_env()
63-
.unwrap_or_else(|_| "example_websockets=debug,tower_http=debug ,diesel=debug".into()),
64+
.unwrap_or_else(|_| "example_websockets=debug,tower_http=info,diesel=debug".into()),
6465
)
6566
.with(tracing_subscriber::fmt::layer())
6667
.init();
6768

6869
let app = Router::new()
6970
.route("/ws", get(server::subscribe_chat_handshake))
71+
.route("/api/search", post(server::rest::search::search))
7072
.route("/api/users/@me", get(server::rest::user::get_self))
7173
.route("/api/contacts/@me", get(server::rest::contacts::get_contacts))
7274
.route("/api/contacts/:contact_id", get(server::rest::contacts::get_contact))
73-
.route("/api/channels/:context_id/messages", post(server::rest::messages::create_message))
74-
.route("/api/channels/:context_id/messages", get(server::rest::messages::get_messages))
75-
.route("/api/channels/:context_id/messages/:message_id", put(server::rest::messages::edit_message))
76-
.route("/api/channels/:context_id/messages/:message_id", delete(server::rest::messages::delete_message))
77-
.route("/api/channels/:context_id/messages/:message_id/reactions", post(server::rest::reactions::add_reaction))
78-
.route("/api/channels/:context_id/messages/:message_id/reactions/:reaction_id", delete(server::rest::reactions::remove_reaction))
75+
.route("/api/contacts/:contact_id/chat", post(server::rest::contacts::chat))
76+
.route("/api/channels/:channel_id/messages", post(server::rest::messages::create_message))
77+
.route("/api/channels/:channel_id/messages", get(server::rest::messages::get_messages))
78+
.route("/api/channels/:channel_id/messages/:message_id", put(server::rest::messages::edit_message))
79+
.route("/api/channels/:channel_id/messages/:message_id", delete(server::rest::messages::delete_message))
80+
.route("/api/channels/:channel_id/messages/:message_id/reactions", post(server::rest::reactions::add_reaction))
81+
.route("/api/channels/:channel_id/messages/:message_id/reactions/:reaction_id", delete(server::rest::reactions::remove_reaction))
7982
.route_layer(
8083
middleware::from_fn(authorize)
8184
)

server/src/schema/channels.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use diesel::sql_types::BigInt;
2+
use diesel::{Identifiable, Queryable, QueryableByName, Selectable, table};
3+
use crate::schema::users::users;
4+
5+
#[derive(Queryable, Identifiable)]
6+
#[table_name = "channels"]
7+
#[primary_key(channel_id)]
8+
pub struct Channel {
9+
pub channel_id: i64,
10+
pub channel_type: i32,
11+
}
12+
13+
#[derive(Queryable, Identifiable, Selectable)]
14+
#[table_name = "channel_members"]
15+
#[primary_key(channel_id, user_id)]
16+
pub struct ChannelMember {
17+
pub channel_id: i64,
18+
pub user_id: i64
19+
}
20+
21+
table! {
22+
channels (channel_id) {
23+
channel_id -> BigInt,
24+
channel_type -> Integer,
25+
}
26+
}
27+
28+
table! {
29+
channel_members (channel_id, user_id) {
30+
channel_id -> BigInt,
31+
user_id -> BigInt,
32+
joined_at -> Timestamp,
33+
}
34+
}
35+
36+
diesel::joinable!(channel_members -> channels (channel_id));
37+
diesel::joinable!(channel_members -> users (user_id));
38+
39+
#[derive(Queryable, QueryableByName)]
40+
pub struct PrivateChannelQuery {
41+
#[sql_type = "BigInt"]
42+
pub channel_id: i64
43+
}

server/src/schema/ctes.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// global variable
2+
pub const REACTIONS_WITH_ME: &str = r#"
3+
WITH reactions_with_me AS (
4+
SELECT
5+
reactions.reaction_id,
6+
reactions.message_id,
7+
reactions.emoji,
8+
reactions.reaction_count,
9+
bool_or(reaction_users.user_id = $1) AS me
10+
FROM
11+
reactions
12+
LEFT JOIN reaction_users ON reactions.reaction_id = reaction_users.reaction_id
13+
GROUP BY
14+
reactions.reaction_id, reactions.message_id, reactions.emoji, reactions.reaction_count
15+
)
16+
"#;
17+
18+
pub const SELECT_MESSAGES: &str = r#"
19+
SELECT
20+
qm.message_id,
21+
qm.user_id,
22+
qm.content,
23+
qm.channel_id,
24+
qm.reception_status,
25+
qm.edited,
26+
qm.reply_to,
27+
u.name AS author_name,
28+
u.username AS author_username,
29+
COALESCE(
30+
json_agg(
31+
json_build_object(
32+
'reaction_id', reactions_with_me.reaction_id,
33+
'count', reactions_with_me.reaction_count,
34+
'me', reactions_with_me.me,
35+
'emoji', reactions_with_me.emoji
36+
)
37+
) FILTER (WHERE reactions_with_me.message_id IS NOT NULL AND reactions_with_me.reaction_count > 0),
38+
'[]'
39+
) AS reactions
40+
"#;
41+
42+
pub fn select_messages_from(
43+
from: &str
44+
) -> String {
45+
let string = format!(r#"
46+
{},
47+
querying_messages AS (
48+
{}
49+
)
50+
{}
51+
FROM querying_messages qm
52+
LEFT JOIN reactions_with_me ON reactions_with_me.message_id = qm.message_id
53+
LEFT JOIN users u ON qm.user_id = u.user_id
54+
GROUP BY
55+
qm.message_id, qm.user_id, qm.content, qm.channel_id, qm.reception_status, qm.edited, qm.reply_to, u.name, u.username
56+
ORDER BY
57+
qm.message_id DESC
58+
"#, REACTIONS_WITH_ME, from, SELECT_MESSAGES);
59+
return string;
60+
}
Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
11
use diesel::pg::Pg;
2-
use diesel::sql_types::{Nullable, Text, BigInt, SmallInt};
2+
use diesel::sql_types::{Nullable, Text, BigInt, SmallInt, VarChar};
33
use diesel::{allow_tables_to_appear_in_same_query, Associations, Identifiable, Insertable, Queryable, QueryableByName, Selectable};
4-
use crate::entity::user::users;
4+
use crate::schema::users::users;
55
use crate::User;
66
use diesel::sql_types::Bool;
77

8-
pub type ContextType = i16;
9-
108
#[derive(Queryable, Identifiable, Associations, Selectable, Insertable)]
119
#[diesel(belongs_to(User))]
1210
#[diesel(table_name = messages)]
11+
#[primary_key(message_id)]
1312
pub struct Message {
14-
pub id: i64,
13+
pub message_id: i64,
1514
pub user_id: i64,
1615
pub content: String,
17-
pub context: i64,
18-
pub context_type: ContextType,
16+
pub channel_id: i64,
1917
pub reception_status: i16,
2018
pub edited: bool,
2119
pub reply_to: Option<i64>
@@ -24,12 +22,11 @@ pub struct Message {
2422
diesel::table! {
2523
use diesel::sql_types::*;
2624

27-
messages (id) {
28-
id -> BigInt,
25+
messages (message_id) {
26+
message_id -> BigInt,
2927
user_id -> BigInt,
3028
content -> Text,
31-
context -> BigInt,
32-
context_type -> SmallInt,
29+
channel_id -> BigInt,
3330
reception_status -> SmallInt,
3431
edited -> Bool,
3532
reply_to -> Nullable<BigInt>
@@ -38,15 +35,12 @@ diesel::table! {
3835

3936
diesel::joinable!(messages -> users (user_id));
4037

41-
allow_tables_to_appear_in_same_query!(
42-
users,
43-
messages,
44-
);
45-
4638
#[derive(QueryableByName, Queryable, Debug)]
47-
pub struct ContactWithLastMessage {
39+
pub struct ContactWithChannel {
40+
#[sql_type = "BigInt"]
41+
pub user_id: i64,
4842
#[sql_type = "BigInt"]
49-
pub id: i64,
43+
pub channel_id: i64,
5044
#[sql_type = "Text"]
5145
pub name: String,
5246
#[sql_type = "Text"]
@@ -58,25 +52,29 @@ pub struct ContactWithLastMessage {
5852
#[sql_type = "Nullable<SmallInt>"]
5953
pub reception_status: Option<i16>,
6054
#[sql_type = "BigInt"]
61-
pub reception_status_count: i64
55+
pub unread_reception_count: i64
6256
}
6357

6458
#[derive(QueryableByName, Queryable, Debug)]
6559
pub struct CompleteMessage {
6660
#[sql_type = "BigInt"]
67-
pub id: i64,
61+
pub message_id: i64,
6862
#[sql_type = "BigInt"]
6963
pub user_id: i64,
7064
#[sql_type = "Text"]
7165
pub content: String,
7266
#[sql_type = "BigInt"]
73-
pub context: i64,
67+
pub channel_id: i64,
7468
#[sql_type = "SmallInt"]
7569
pub reception_status: i16,
7670
#[sql_type = "Bool"]
7771
pub edited: bool,
7872
#[sql_type = "Nullable<BigInt>"]
7973
pub reply_to: Option<i64>,
74+
#[sql_type = "VarChar"]
75+
pub author_name: String,
76+
#[sql_type = "VarChar"]
77+
pub author_username: String,
8078
#[sql_type = "Text"]
8179
pub reactions: String
8280
}

server/src/schema/mod.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
use diesel::allow_tables_to_appear_in_same_query;
3+
use serde::Serialize;
4+
5+
pub mod users;
6+
pub mod messages;
7+
pub mod reactions;
8+
pub mod channels;
9+
pub mod ctes;
10+
11+
use crate::schema::users::users as users_table;
12+
use crate::schema::channels::channels as channels_table;
13+
use crate::schema::channels::channel_members as channel_members_table;
14+
use crate::schema::messages::messages as messages_table;
15+
use crate::schema::reactions::reactions as reactions_table;
16+
use crate::schema::reactions::reaction_users as reaction_users_table;
17+
18+
allow_tables_to_appear_in_same_query!(
19+
users_table,
20+
channels_table,
21+
channel_members_table,
22+
messages_table,
23+
reactions_table,
24+
reaction_users_table
25+
);

server/src/entity/reactions.rs renamed to server/src/schema/reactions.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use diesel::{allow_tables_to_appear_in_same_query, Identifiable, Insertable, Queryable, Selectable};
2-
use crate::entity::message::messages;
3-
use crate::entity::user::users;
2+
use crate::schema::messages::messages;
3+
use crate::schema::users::users;
44
use diesel::sql_types::{BigInt, Text, Varchar, Integer, Bool, Serial};
55
use diesel::prelude::*;
66
use serde::{Deserialize, Serialize};
@@ -49,12 +49,6 @@ diesel::joinable!(reactions -> messages (message_id));
4949
diesel::joinable!(reaction_users -> reactions (reaction_id));
5050
diesel::joinable!(reaction_users -> users (user_id));
5151

52-
allow_tables_to_appear_in_same_query!(
53-
messages,
54-
reactions,
55-
reaction_users,
56-
);
57-
5852
#[derive(Insertable)]
5953
#[diesel(table_name = reactions)]
6054
pub struct ReactionInsert {
@@ -69,7 +63,7 @@ pub struct ReactionUserInsert {
6963
pub user_id: i64
7064
}
7165

72-
#[derive(Debug, Serialize, Deserialize, QueryableByName)]
66+
#[derive(Debug, Serialize, Deserialize, QueryableByName, Clone, PartialEq)]
7367
pub struct ReactionSummary {
7468
#[sql_type = "Text"]
7569
pub emoji: String,

server/src/entity/user.rs renamed to server/src/schema/users.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@ use diesel::{Identifiable, Insertable, Queryable, Selectable};
22

33
#[derive(Debug, Identifiable, Queryable, Selectable, Insertable, Clone)]
44
#[diesel(table_name = users)]
5-
#[diesel(primary_key(id))]
5+
#[diesel(primary_key(user_id))]
66
pub struct User {
7-
pub id: i64,
7+
pub user_id: i64,
88
pub name: String,
99
pub username: String,
1010
pub password: String,
1111
pub email: String,
1212
}
1313

1414
diesel::table! {
15-
users (id) {
16-
id -> BigInt,
15+
users (user_id) {
16+
user_id -> BigInt,
1717
name -> Varchar,
1818
username -> Varchar,
1919
email -> Varchar,

0 commit comments

Comments
 (0)