Refractor route code.
This commit is contained in:
@@ -1,688 +0,0 @@
|
|||||||
use super::Response;
|
|
||||||
use crate::database::{
|
|
||||||
self, channel::Channel, get_relationship, get_relationship_internal, message::Message,
|
|
||||||
user::User, Permission, PermissionCalculator, Relationship,
|
|
||||||
};
|
|
||||||
use crate::util::vec_to_set;
|
|
||||||
|
|
||||||
use chrono::prelude::*;
|
|
||||||
use mongodb::bson::{doc, from_bson, Bson, Document};
|
|
||||||
use mongodb::options::FindOptions;
|
|
||||||
use num_enum::TryFromPrimitive;
|
|
||||||
use rocket::request::Form;
|
|
||||||
use rocket_contrib::json::Json;
|
|
||||||
use rocket::futures::StreamExt;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use ulid::Ulid;
|
|
||||||
|
|
||||||
const MAXGROUPSIZE: usize = 50;
|
|
||||||
|
|
||||||
#[derive(Debug, TryFromPrimitive)]
|
|
||||||
#[repr(usize)]
|
|
||||||
pub enum ChannelType {
|
|
||||||
DM = 0,
|
|
||||||
GROUPDM = 1,
|
|
||||||
GUILDCHANNEL = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! with_permissions {
|
|
||||||
($user: expr, $target: expr) => {{
|
|
||||||
let permissions = PermissionCalculator::new($user.clone())
|
|
||||||
.channel($target.clone())
|
|
||||||
.fetch_data()
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let value = permissions.as_permission().await;
|
|
||||||
if !value.get_access() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
value
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct CreateGroup {
|
|
||||||
name: String,
|
|
||||||
nonce: String,
|
|
||||||
users: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// create a new group
|
|
||||||
#[post("/create", data = "<info>")]
|
|
||||||
pub async fn create_group(user: User, info: Json<CreateGroup>) -> Response {
|
|
||||||
let name: String = info.name.chars().take(32).collect();
|
|
||||||
let nonce: String = info.nonce.chars().take(32).collect();
|
|
||||||
|
|
||||||
let mut set = vec_to_set(&info.users);
|
|
||||||
set.insert(user.id.clone());
|
|
||||||
|
|
||||||
if set.len() > MAXGROUPSIZE {
|
|
||||||
return Response::BadRequest(json!({ "error": "Maximum group size is 50." }));
|
|
||||||
}
|
|
||||||
|
|
||||||
let col = database::get_collection("channels");
|
|
||||||
if let Some(_) = col.find_one(doc! { "nonce": nonce.clone() }, None).await.unwrap() {
|
|
||||||
return Response::BadRequest(json!({ "error": "Group already created!" }));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut query = vec![];
|
|
||||||
for item in &set {
|
|
||||||
if item == &user.id {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
query.push(Bson::String(item.clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(result) = database::get_collection("users").find(
|
|
||||||
doc! {
|
|
||||||
"_id": {
|
|
||||||
"$in": &query
|
|
||||||
}
|
|
||||||
},
|
|
||||||
FindOptions::builder().limit(query.len() as i64).build(),
|
|
||||||
).await {
|
|
||||||
if result.collect::<Vec<Result<Document, _>>>().await.len() != query.len() {
|
|
||||||
return Response::BadRequest(json!({ "error": "Specified non-existant user(s)." }));
|
|
||||||
}
|
|
||||||
|
|
||||||
for item in set {
|
|
||||||
if item == user.id {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if get_relationship_internal(&user.id, &item, &user.relations) != Relationship::Friend {
|
|
||||||
return Response::BadRequest(json!({ "error": "Not friends with user(s)." }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
query.push(Bson::String(user.id.clone()));
|
|
||||||
|
|
||||||
let id = Ulid::new().to_string();
|
|
||||||
if col
|
|
||||||
.insert_one(
|
|
||||||
doc! {
|
|
||||||
"_id": id.clone(),
|
|
||||||
"nonce": nonce,
|
|
||||||
"type": ChannelType::GROUPDM as u32,
|
|
||||||
"recipients": &query,
|
|
||||||
"name": name,
|
|
||||||
"owner": &user.id,
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
Response::Success(json!({ "id": id }))
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(json!({ "error": "Failed to create guild channel." }))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(json!({ "error": "Failed to validate users." }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// fetch channel information
|
|
||||||
#[get("/<target>")]
|
|
||||||
pub async fn channel(user: User, target: Channel) -> Option<Response> {
|
|
||||||
with_permissions!(user, target);
|
|
||||||
Some(Response::Success(target.serialise()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [groups] add user to channel
|
|
||||||
#[put("/<target>/recipients/<member>")]
|
|
||||||
pub async fn add_member(user: User, target: Channel, member: User) -> Option<Response> {
|
|
||||||
if target.channel_type != 1 {
|
|
||||||
return Some(Response::BadRequest(json!({ "error": "Not a group DM." })));
|
|
||||||
}
|
|
||||||
|
|
||||||
with_permissions!(user, target);
|
|
||||||
|
|
||||||
let recp = target.recipients.as_ref().unwrap();
|
|
||||||
if recp.len() == 50 {
|
|
||||||
return Some(Response::BadRequest(
|
|
||||||
json!({ "error": "Maximum group size is 50." }),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let set = vec_to_set(&recp);
|
|
||||||
if set.get(&member.id).is_some() {
|
|
||||||
return Some(Response::BadRequest(
|
|
||||||
json!({ "error": "User already in group!" }),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
match get_relationship(&user, &member) {
|
|
||||||
Relationship::Friend => {
|
|
||||||
if database::get_collection("channels")
|
|
||||||
.update_one(
|
|
||||||
doc! { "_id": target.id.clone() },
|
|
||||||
doc! {
|
|
||||||
"$push": {
|
|
||||||
"recipients": &member.id
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
if (Message {
|
|
||||||
id: Ulid::new().to_string(),
|
|
||||||
nonce: None,
|
|
||||||
channel: target.id.clone(),
|
|
||||||
author: "system".to_string(),
|
|
||||||
content: format!("<@{}> added <@{}> to the group.", &user.id, &member.id),
|
|
||||||
edited: None,
|
|
||||||
previous_content: vec![],
|
|
||||||
})
|
|
||||||
.send(&target)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
/*notifications::send_message_given_channel(
|
|
||||||
Notification::group_user_join(UserJoin {
|
|
||||||
id: target.id.clone(),
|
|
||||||
user: member.id.clone(),
|
|
||||||
}),
|
|
||||||
&target, FIXME
|
|
||||||
);*/
|
|
||||||
|
|
||||||
Some(Response::Result(super::Status::Ok))
|
|
||||||
} else {
|
|
||||||
Some(Response::PartialStatus(
|
|
||||||
json!({ "error": "Failed to send join message, but user has been added." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to add user to group." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => Some(Response::BadRequest(
|
|
||||||
json!({ "error": "Not friends with user." }),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [groups] remove user from channel
|
|
||||||
#[delete("/<target>/recipients/<member>")]
|
|
||||||
pub async fn remove_member(user: User, target: Channel, member: User) -> Option<Response> {
|
|
||||||
if target.channel_type != 1 {
|
|
||||||
return Some(Response::BadRequest(json!({ "error": "Not a group DM." })));
|
|
||||||
}
|
|
||||||
|
|
||||||
if &user.id == &member.id {
|
|
||||||
return Some(Response::BadRequest(
|
|
||||||
json!({ "error": "Cannot kick yourself, leave the channel instead." }),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let permissions = with_permissions!(user, target);
|
|
||||||
|
|
||||||
if !permissions.get_kick_members() {
|
|
||||||
return Some(Response::LackingPermission(Permission::KickMembers));
|
|
||||||
}
|
|
||||||
|
|
||||||
let set = vec_to_set(target.recipients.as_ref().unwrap());
|
|
||||||
if set.get(&member.id).is_none() {
|
|
||||||
return Some(Response::BadRequest(
|
|
||||||
json!({ "error": "User not in group!" }),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if database::get_collection("channels")
|
|
||||||
.update_one(
|
|
||||||
doc! { "_id": &target.id },
|
|
||||||
doc! {
|
|
||||||
"$pull": {
|
|
||||||
"recipients": &member.id
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
if (Message {
|
|
||||||
id: Ulid::new().to_string(),
|
|
||||||
nonce: None,
|
|
||||||
channel: target.id.clone(),
|
|
||||||
author: "system".to_string(),
|
|
||||||
content: format!("<@{}> removed <@{}> from the group.", &user.id, &member.id),
|
|
||||||
edited: None,
|
|
||||||
previous_content: vec![],
|
|
||||||
})
|
|
||||||
.send(&target)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
/*notifications::send_message_given_channel(
|
|
||||||
Notification::group_user_leave(UserLeave {
|
|
||||||
id: target.id.clone(),
|
|
||||||
user: member.id.clone(),
|
|
||||||
}),
|
|
||||||
&target, FIXME
|
|
||||||
);*/
|
|
||||||
|
|
||||||
Some(Response::Result(super::Status::Ok))
|
|
||||||
} else {
|
|
||||||
Some(Response::PartialStatus(
|
|
||||||
json!({ "error": "Failed to send join message, but user has been removed." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to add user to group." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// delete channel
|
|
||||||
/// or leave group DM
|
|
||||||
/// or close DM conversation
|
|
||||||
#[delete("/<target>")]
|
|
||||||
pub async fn delete(user: User, target: Channel) -> Option<Response> {
|
|
||||||
let permissions = with_permissions!(user, target);
|
|
||||||
|
|
||||||
if !permissions.get_manage_channels() {
|
|
||||||
return Some(Response::LackingPermission(Permission::ManageChannels));
|
|
||||||
}
|
|
||||||
|
|
||||||
let col = database::get_collection("channels");
|
|
||||||
let target_id = target.id.clone();
|
|
||||||
|
|
||||||
let try_delete = async || {
|
|
||||||
let messages = database::get_collection("messages");
|
|
||||||
|
|
||||||
if messages
|
|
||||||
.delete_many(doc! { "channel": &target_id }, None)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
if col.delete_one(doc! { "_id": &target_id }, None).await.is_ok() {
|
|
||||||
Some(Response::Result(super::Status::Ok))
|
|
||||||
} else {
|
|
||||||
Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to delete channel." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to delete messages." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match target.channel_type {
|
|
||||||
0 => {
|
|
||||||
if col
|
|
||||||
.update_one(
|
|
||||||
doc! { "_id": &target_id },
|
|
||||||
doc! { "$set": { "active": false } },
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
Some(Response::Result(super::Status::Ok))
|
|
||||||
} else {
|
|
||||||
Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to close channel." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1 => {
|
|
||||||
let mut recipients = vec_to_set(
|
|
||||||
target
|
|
||||||
.recipients
|
|
||||||
.as_ref()
|
|
||||||
.expect("Missing recipients on Group DM."),
|
|
||||||
);
|
|
||||||
let owner = target.owner.as_ref().expect("Missing owner on Group DM.");
|
|
||||||
|
|
||||||
if recipients.len() == 1 {
|
|
||||||
try_delete().await
|
|
||||||
} else {
|
|
||||||
recipients.remove(&user.id);
|
|
||||||
let new_owner = if owner == &user.id {
|
|
||||||
recipients.iter().next().unwrap()
|
|
||||||
} else {
|
|
||||||
&owner
|
|
||||||
};
|
|
||||||
|
|
||||||
if col
|
|
||||||
.update_one(
|
|
||||||
doc! { "_id": target_id },
|
|
||||||
doc! {
|
|
||||||
"$set": {
|
|
||||||
"owner": new_owner,
|
|
||||||
},
|
|
||||||
"$pull": {
|
|
||||||
"recipients": &user.id,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
if (Message {
|
|
||||||
id: Ulid::new().to_string(),
|
|
||||||
nonce: None,
|
|
||||||
channel: target.id.clone(),
|
|
||||||
author: "system".to_string(),
|
|
||||||
content: format!("<@{}> left the group.", &user.id),
|
|
||||||
edited: None,
|
|
||||||
previous_content: vec![],
|
|
||||||
})
|
|
||||||
.send(&target)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
/*notifications::send_message_given_channel(
|
|
||||||
Notification::group_user_leave(UserLeave {
|
|
||||||
id: target.id.clone(),
|
|
||||||
user: user.id.clone(),
|
|
||||||
}),
|
|
||||||
&target, FIXME
|
|
||||||
);*/
|
|
||||||
|
|
||||||
Some(Response::Result(super::Status::Ok))
|
|
||||||
} else {
|
|
||||||
Some(Response::PartialStatus(
|
|
||||||
json!({ "error": "Failed to send leave message, but you have left the group." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to remove you from the group." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
2 => {
|
|
||||||
let guild_id = target.guild.unwrap();
|
|
||||||
if database::get_collection("guilds")
|
|
||||||
.update_one(
|
|
||||||
doc! { "_id": &guild_id },
|
|
||||||
doc! {
|
|
||||||
"$pull": {
|
|
||||||
"invites": {
|
|
||||||
"channel": &target.id
|
|
||||||
},
|
|
||||||
"channels": &target.id
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
/*notifications::send_message_threaded(
|
|
||||||
None,
|
|
||||||
guild_id.clone(),
|
|
||||||
Notification::guild_channel_delete(ChannelDelete {
|
|
||||||
id: guild_id.clone(),
|
|
||||||
channel: target.id.clone(),
|
|
||||||
}), FIXME
|
|
||||||
);*/
|
|
||||||
|
|
||||||
try_delete().await
|
|
||||||
} else {
|
|
||||||
Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to remove invites." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Unknown error has occurred." }),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, FromForm)]
|
|
||||||
pub struct MessageFetchOptions {
|
|
||||||
limit: Option<i64>,
|
|
||||||
before: Option<String>,
|
|
||||||
after: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// fetch channel messages
|
|
||||||
#[get("/<target>/messages?<options..>")]
|
|
||||||
pub async fn messages(
|
|
||||||
user: User,
|
|
||||||
target: Channel,
|
|
||||||
options: Form<MessageFetchOptions>,
|
|
||||||
) -> Option<Response> {
|
|
||||||
let permissions = with_permissions!(user, target);
|
|
||||||
|
|
||||||
if !permissions.get_read_messages() {
|
|
||||||
return Some(Response::LackingPermission(Permission::ReadMessages));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ! FIXME: update wiki to reflect changes
|
|
||||||
let mut query = doc! { "channel": target.id };
|
|
||||||
|
|
||||||
if let Some(before) = &options.before {
|
|
||||||
query.insert("_id", doc! { "$lt": before });
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(after) = &options.after {
|
|
||||||
query.insert("_id", doc! { "$gt": after });
|
|
||||||
}
|
|
||||||
|
|
||||||
let limit = if let Some(limit) = options.limit {
|
|
||||||
limit.min(100).max(0)
|
|
||||||
} else {
|
|
||||||
50
|
|
||||||
};
|
|
||||||
|
|
||||||
let col = database::get_collection("messages");
|
|
||||||
let mut result = col
|
|
||||||
.find(
|
|
||||||
query,
|
|
||||||
FindOptions::builder()
|
|
||||||
.limit(limit)
|
|
||||||
.sort(doc! {
|
|
||||||
"_id": -1
|
|
||||||
})
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut messages = Vec::new();
|
|
||||||
while let Some(item) = result.next().await {
|
|
||||||
let message: Message =
|
|
||||||
from_bson(Bson::Document(item.unwrap())).expect("Failed to unwrap message.");
|
|
||||||
messages.push(json!({
|
|
||||||
"id": message.id,
|
|
||||||
"author": message.author,
|
|
||||||
"content": message.content,
|
|
||||||
"edited": if let Some(t) = message.edited { Some(t.timestamp()) } else { None }
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(Response::Success(json!(messages)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct SendMessage {
|
|
||||||
content: String,
|
|
||||||
nonce: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// send a message to a channel
|
|
||||||
#[post("/<target>/messages", data = "<message>")]
|
|
||||||
pub async fn send_message(user: User, target: Channel, message: Json<SendMessage>) -> Option<Response> {
|
|
||||||
let permissions = with_permissions!(user, target);
|
|
||||||
|
|
||||||
if !permissions.get_send_messages() {
|
|
||||||
if target.channel_type == 0 {
|
|
||||||
return Some(Response::LackingPermission(Permission::SendDirectMessages));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Some(Response::LackingPermission(Permission::SendMessages));
|
|
||||||
}
|
|
||||||
|
|
||||||
let content: String = message.content.chars().take(2000).collect();
|
|
||||||
let nonce: String = message.nonce.chars().take(32).collect();
|
|
||||||
|
|
||||||
if content.len() == 0 {
|
|
||||||
return Some(Response::NotAcceptable(
|
|
||||||
json!({ "error": "No message content!" }),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let col = database::get_collection("messages");
|
|
||||||
if col
|
|
||||||
.find_one(doc! { "nonce": nonce.clone() }, None)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
return Some(Response::BadRequest(
|
|
||||||
json!({ "error": "Message already sent!" }),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let id = Ulid::new().to_string();
|
|
||||||
let message = Message {
|
|
||||||
id: id.clone(),
|
|
||||||
nonce: Some(nonce),
|
|
||||||
channel: target.id.clone(),
|
|
||||||
author: user.id,
|
|
||||||
content,
|
|
||||||
edited: None,
|
|
||||||
previous_content: vec![],
|
|
||||||
};
|
|
||||||
|
|
||||||
if message.send(&target).await {
|
|
||||||
Some(Response::Success(json!({ "id": id })))
|
|
||||||
} else {
|
|
||||||
Some(Response::BadRequest(
|
|
||||||
json!({ "error": "Failed to send message." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// get a message
|
|
||||||
#[get("/<target>/messages/<message>")]
|
|
||||||
pub async fn get_message(user: User, target: Channel, message: Message) -> Option<Response> {
|
|
||||||
let permissions = with_permissions!(user, target);
|
|
||||||
|
|
||||||
if !permissions.get_read_messages() {
|
|
||||||
return Some(Response::LackingPermission(Permission::ReadMessages));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ! CHECK IF USER HAS PERMISSION TO VIEW EDITS OF MESSAGES
|
|
||||||
let mut entries = vec![];
|
|
||||||
for entry in message.previous_content {
|
|
||||||
entries.push(json!({
|
|
||||||
"content": entry.content,
|
|
||||||
"time": entry.time.timestamp(),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(Response::Success(json!({
|
|
||||||
"id": message.id,
|
|
||||||
"author": message.author,
|
|
||||||
"content": message.content,
|
|
||||||
"edited": if let Some(t) = message.edited { Some(t.timestamp()) } else { None },
|
|
||||||
"previous_content": entries,
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct EditMessage {
|
|
||||||
content: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// edit a message
|
|
||||||
#[patch("/<target>/messages/<message>", data = "<edit>")]
|
|
||||||
pub async fn edit_message(
|
|
||||||
user: User,
|
|
||||||
target: Channel,
|
|
||||||
message: Message,
|
|
||||||
edit: Json<EditMessage>,
|
|
||||||
) -> Option<Response> {
|
|
||||||
with_permissions!(user, target);
|
|
||||||
|
|
||||||
if message.author != user.id {
|
|
||||||
return Some(Response::Unauthorized(
|
|
||||||
json!({ "error": "You did not send this message." }),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let col = database::get_collection("messages");
|
|
||||||
let time = if let Some(edited) = message.edited {
|
|
||||||
edited.0
|
|
||||||
} else {
|
|
||||||
Ulid::from_string(&message.id).unwrap().datetime()
|
|
||||||
};
|
|
||||||
|
|
||||||
let edited = Utc::now();
|
|
||||||
match col.update_one(
|
|
||||||
doc! { "_id": message.id.clone() },
|
|
||||||
doc! {
|
|
||||||
"$set": {
|
|
||||||
"content": &edit.content,
|
|
||||||
"edited": Bson::DateTime(edited)
|
|
||||||
},
|
|
||||||
"$push": {
|
|
||||||
"previous_content": {
|
|
||||||
"content": &message.content,
|
|
||||||
"time": time,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await {
|
|
||||||
Ok(_) => {
|
|
||||||
/*notifications::send_message_given_channel(
|
|
||||||
Notification::message_edit(Edit {
|
|
||||||
id: message.id.clone(),
|
|
||||||
channel: target.id.clone(),
|
|
||||||
author: message.author.clone(),
|
|
||||||
content: edit.content.clone(),
|
|
||||||
}),
|
|
||||||
&target, FIXME
|
|
||||||
);*/
|
|
||||||
|
|
||||||
Some(Response::Result(super::Status::Ok))
|
|
||||||
}
|
|
||||||
Err(_) => Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to update message." }),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// delete a message
|
|
||||||
#[delete("/<target>/messages/<message>")]
|
|
||||||
pub async fn delete_message(user: User, target: Channel, message: Message) -> Option<Response> {
|
|
||||||
let permissions = with_permissions!(user, target);
|
|
||||||
|
|
||||||
if !permissions.get_manage_messages() && message.author != user.id {
|
|
||||||
return Some(Response::LackingPermission(Permission::ManageMessages));
|
|
||||||
}
|
|
||||||
|
|
||||||
let col = database::get_collection("messages");
|
|
||||||
|
|
||||||
match col.delete_one(doc! { "_id": &message.id }, None).await {
|
|
||||||
Ok(_) => {
|
|
||||||
/*notifications::send_message_given_channel(
|
|
||||||
Notification::message_delete(Delete {
|
|
||||||
id: message.id.clone(),
|
|
||||||
}),
|
|
||||||
&target, FIXME
|
|
||||||
);*/
|
|
||||||
|
|
||||||
Some(Response::Result(super::Status::Ok))
|
|
||||||
}
|
|
||||||
Err(_) => Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to delete message." }),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
5
src/routes/channels/mod.rs
Normal file
5
src/routes/channels/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
use rocket::Route;
|
||||||
|
|
||||||
|
pub fn routes() -> Vec<Route> {
|
||||||
|
routes! []
|
||||||
|
}
|
||||||
@@ -1,818 +0,0 @@
|
|||||||
use super::channel::ChannelType;
|
|
||||||
use super::Response;
|
|
||||||
use crate::database::guild::{fetch_member as get_member, get_invite, Guild, MemberKey};
|
|
||||||
use crate::database::{
|
|
||||||
self, channel::fetch_channel, channel::Channel, guild::serialise_guilds_with_channels,
|
|
||||||
user::User, Permission, PermissionCalculator,
|
|
||||||
};
|
|
||||||
use crate::util::gen_token;
|
|
||||||
|
|
||||||
use mongodb::bson::{doc, Bson};
|
|
||||||
use mongodb::options::{FindOneOptions, FindOptions};
|
|
||||||
use rocket::request::Form;
|
|
||||||
use rocket_contrib::json::Json;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use rocket::futures::StreamExt;
|
|
||||||
use ulid::Ulid;
|
|
||||||
|
|
||||||
// ! FIXME: GET RID OF THIS
|
|
||||||
macro_rules! with_permissions {
|
|
||||||
($user: expr, $target: expr) => {{
|
|
||||||
let permissions = PermissionCalculator::new($user.clone())
|
|
||||||
.guild($target.clone())
|
|
||||||
.fetch_data()
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let value = permissions.as_permission().await;
|
|
||||||
if !value.get_access() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
(value, permissions.member.unwrap())
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// fetch your guilds
|
|
||||||
#[get("/@me")]
|
|
||||||
pub async fn my_guilds(user: User) -> Response {
|
|
||||||
if let Ok(gids) = user.find_guilds().await {
|
|
||||||
if let Ok(data) = serialise_guilds_with_channels(&gids).await {
|
|
||||||
Response::Success(json!(data))
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(json!({ "error": "Failed to fetch guilds." }))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(json!({ "error": "Failed to fetch memberships." }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// fetch a guild
|
|
||||||
#[get("/<target>")]
|
|
||||||
pub async fn guild(user: User, target: Guild) -> Option<Response> {
|
|
||||||
with_permissions!(user, target);
|
|
||||||
|
|
||||||
if let Ok(result) = target.seralise_with_channels().await {
|
|
||||||
Some(Response::Success(result))
|
|
||||||
} else {
|
|
||||||
Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to fetch channels!" }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// delete or leave a guild
|
|
||||||
#[delete("/<target>")]
|
|
||||||
pub async fn remove_guild(user: User, target: Guild) -> Option<Response> {
|
|
||||||
with_permissions!(user, target);
|
|
||||||
|
|
||||||
if user.id == target.owner {
|
|
||||||
let channels = database::get_collection("channels");
|
|
||||||
if let Ok(mut result) = channels.find(
|
|
||||||
doc! {
|
|
||||||
"type": 2,
|
|
||||||
"guild": &target.id
|
|
||||||
},
|
|
||||||
FindOptions::builder().projection(doc! { "_id": 1 }).build(),
|
|
||||||
).await {
|
|
||||||
let mut values = vec![];
|
|
||||||
while let Some(item) = result.next().await {
|
|
||||||
if let Ok(doc) = item {
|
|
||||||
values.push(Bson::String(doc.get_str("_id").unwrap().to_string()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if database::get_collection("messages")
|
|
||||||
.delete_many(
|
|
||||||
doc! {
|
|
||||||
"channel": {
|
|
||||||
"$in": values
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
if channels
|
|
||||||
.delete_many(
|
|
||||||
doc! {
|
|
||||||
"type": 2,
|
|
||||||
"guild": &target.id,
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
if database::get_collection("members")
|
|
||||||
.delete_many(
|
|
||||||
doc! {
|
|
||||||
"_id.guild": &target.id,
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
if database::get_collection("guilds")
|
|
||||||
.delete_one(
|
|
||||||
doc! {
|
|
||||||
"_id": &target.id
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
/*notifications::send_message_threaded(
|
|
||||||
None,
|
|
||||||
target.id.clone(),
|
|
||||||
Notification::guild_delete(Delete {
|
|
||||||
id: target.id.clone(),
|
|
||||||
}), FIXME
|
|
||||||
);*/
|
|
||||||
|
|
||||||
Some(Response::Result(super::Status::Ok))
|
|
||||||
} else {
|
|
||||||
Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to delete guild." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to delete guild members." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to delete guild channels." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to delete guild messages." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Could not fetch channels." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else if database::get_collection("members")
|
|
||||||
.delete_one(
|
|
||||||
doc! {
|
|
||||||
"_id.guild": &target.id,
|
|
||||||
"_id.user": &user.id,
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
/*notifications::send_message_threaded(
|
|
||||||
None,
|
|
||||||
target.id.clone(),
|
|
||||||
Notification::guild_user_leave(UserLeave {
|
|
||||||
id: target.id.clone(),
|
|
||||||
user: user.id.clone(),
|
|
||||||
banned: false,
|
|
||||||
}), FIXME
|
|
||||||
);*/
|
|
||||||
|
|
||||||
Some(Response::Result(super::Status::Ok))
|
|
||||||
} else {
|
|
||||||
Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to remove you from the guild." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct CreateChannel {
|
|
||||||
nonce: String,
|
|
||||||
name: String,
|
|
||||||
description: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// create a new channel
|
|
||||||
#[post("/<target>/channels", data = "<info>")]
|
|
||||||
pub async fn create_channel(user: User, target: Guild, info: Json<CreateChannel>) -> Option<Response> {
|
|
||||||
let (permissions, _) = with_permissions!(user, target);
|
|
||||||
|
|
||||||
if !permissions.get_manage_channels() {
|
|
||||||
return Some(Response::LackingPermission(Permission::ManageChannels));
|
|
||||||
}
|
|
||||||
|
|
||||||
let nonce: String = info.nonce.chars().take(32).collect();
|
|
||||||
let name: String = info.name.chars().take(32).collect();
|
|
||||||
let description: String = info
|
|
||||||
.description
|
|
||||||
.clone()
|
|
||||||
.unwrap_or(String::new())
|
|
||||||
.chars()
|
|
||||||
.take(255)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if let Ok(result) =
|
|
||||||
database::get_collection("channels").find_one(doc! { "nonce": &nonce }, None)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
if result.is_some() {
|
|
||||||
return Some(Response::BadRequest(
|
|
||||||
json!({ "error": "Channel already created." }),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let id = Ulid::new().to_string();
|
|
||||||
if database::get_collection("channels")
|
|
||||||
.insert_one(
|
|
||||||
doc! {
|
|
||||||
"_id": &id,
|
|
||||||
"nonce": &nonce,
|
|
||||||
"type": 2,
|
|
||||||
"guild": &target.id,
|
|
||||||
"name": &name,
|
|
||||||
"description": &description,
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
if database::get_collection("guilds")
|
|
||||||
.update_one(
|
|
||||||
doc! {
|
|
||||||
"_id": &target.id
|
|
||||||
},
|
|
||||||
doc! {
|
|
||||||
"$addToSet": {
|
|
||||||
"channels": &id
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
/*notifications::send_message_threaded(
|
|
||||||
None,
|
|
||||||
target.id.clone(),
|
|
||||||
Notification::guild_channel_create(ChannelCreate {
|
|
||||||
id: target.id.clone(),
|
|
||||||
channel: id.clone(),
|
|
||||||
name: name.clone(),
|
|
||||||
description: description.clone(),
|
|
||||||
}), FIXME
|
|
||||||
);*/
|
|
||||||
|
|
||||||
Some(Response::Success(json!({ "id": &id })))
|
|
||||||
} else {
|
|
||||||
Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Couldn't save channel list." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Couldn't create channel." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Some(Response::BadRequest(
|
|
||||||
json!({ "error": "Failed to check if channel was made." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct InviteOptions {
|
|
||||||
// ? TODO: add options
|
|
||||||
}
|
|
||||||
|
|
||||||
/// create a new invite
|
|
||||||
#[post("/<target>/channels/<channel>/invite", data = "<_options>")]
|
|
||||||
pub async fn create_invite(
|
|
||||||
user: User,
|
|
||||||
target: Guild,
|
|
||||||
channel: Channel,
|
|
||||||
_options: Json<InviteOptions>,
|
|
||||||
) -> Option<Response> {
|
|
||||||
let (permissions, _) = with_permissions!(user, target);
|
|
||||||
|
|
||||||
if !permissions.get_create_invite() {
|
|
||||||
return Some(Response::LackingPermission(Permission::CreateInvite));
|
|
||||||
}
|
|
||||||
|
|
||||||
let code = gen_token(7);
|
|
||||||
if database::get_collection("guilds")
|
|
||||||
.update_one(
|
|
||||||
doc! { "_id": target.id },
|
|
||||||
doc! {
|
|
||||||
"$push": {
|
|
||||||
"invites": {
|
|
||||||
"code": &code,
|
|
||||||
"creator": user.id,
|
|
||||||
"channel": channel.id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
Some(Response::Success(json!({ "code": code })))
|
|
||||||
} else {
|
|
||||||
Some(Response::BadRequest(
|
|
||||||
json!({ "error": "Failed to create invite." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// remove an invite
|
|
||||||
#[delete("/<target>/invites/<code>")]
|
|
||||||
pub async fn remove_invite(user: User, target: Guild, code: String) -> Option<Response> {
|
|
||||||
let (permissions, _) = with_permissions!(user, target);
|
|
||||||
|
|
||||||
if let Some((guild_id, _, invite)) = get_invite(&code, None).await {
|
|
||||||
if invite.creator != user.id && !permissions.get_manage_server() {
|
|
||||||
return Some(Response::LackingPermission(Permission::ManageServer));
|
|
||||||
}
|
|
||||||
|
|
||||||
if database::get_collection("guilds")
|
|
||||||
.update_one(
|
|
||||||
doc! {
|
|
||||||
"_id": &guild_id,
|
|
||||||
},
|
|
||||||
doc! {
|
|
||||||
"$pull": {
|
|
||||||
"invites": {
|
|
||||||
"code": &code
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
Some(Response::Result(super::Status::Ok))
|
|
||||||
} else {
|
|
||||||
Some(Response::BadRequest(
|
|
||||||
json!({ "error": "Failed to delete invite." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Some(Response::NotFound(
|
|
||||||
json!({ "error": "Failed to fetch invite or code is invalid." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// fetch all guild invites
|
|
||||||
#[get("/<target>/invites")]
|
|
||||||
pub async fn fetch_invites(user: User, target: Guild) -> Option<Response> {
|
|
||||||
let (permissions, _) = with_permissions!(user, target);
|
|
||||||
|
|
||||||
if !permissions.get_manage_server() {
|
|
||||||
return Some(Response::LackingPermission(Permission::ManageServer));
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(Response::Success(json!(target.invites)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// view an invite before joining
|
|
||||||
#[get("/join/<code>", rank = 1)]
|
|
||||||
pub async fn fetch_invite(user: User, code: String) -> Response {
|
|
||||||
if let Some((guild_id, name, invite)) = get_invite(&code, user.id).await {
|
|
||||||
match fetch_channel(&invite.channel).await {
|
|
||||||
Ok(result) => {
|
|
||||||
if let Some(channel) = result {
|
|
||||||
Response::Success(json!({
|
|
||||||
"guild": {
|
|
||||||
"id": guild_id,
|
|
||||||
"name": name,
|
|
||||||
},
|
|
||||||
"channel": {
|
|
||||||
"id": channel.id,
|
|
||||||
"name": channel.name,
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
Response::NotFound(json!({ "error": "Channel does not exist." }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => Response::InternalServerError(json!({ "error": err })),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Response::NotFound(json!({ "error": "Failed to fetch invite or code is invalid." }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// join a guild using an invite
|
|
||||||
#[post("/join/<code>", rank = 1)]
|
|
||||||
pub async fn use_invite(user: User, code: String) -> Response {
|
|
||||||
if let Some((guild_id, _, invite)) = get_invite(&code, Some(user.id.clone())).await {
|
|
||||||
if let Ok(result) = database::get_collection("members").find_one(
|
|
||||||
doc! {
|
|
||||||
"_id.guild": &guild_id,
|
|
||||||
"_id.user": &user.id
|
|
||||||
},
|
|
||||||
FindOneOptions::builder()
|
|
||||||
.projection(doc! { "_id": 1 })
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
.await {
|
|
||||||
if result.is_none() {
|
|
||||||
if database::get_collection("members")
|
|
||||||
.insert_one(
|
|
||||||
doc! {
|
|
||||||
"_id": {
|
|
||||||
"guild": &guild_id,
|
|
||||||
"user": &user.id
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
/*notifications::send_message_threaded(
|
|
||||||
None,
|
|
||||||
guild_id.clone(),
|
|
||||||
Notification::guild_user_join(UserJoin {
|
|
||||||
id: guild_id.clone(),
|
|
||||||
user: user.id.clone(),
|
|
||||||
}), FIXME
|
|
||||||
);*/
|
|
||||||
|
|
||||||
Response::Success(json!({
|
|
||||||
"guild": &guild_id,
|
|
||||||
"channel": &invite.channel,
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to add you to the guild." }),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Response::BadRequest(json!({ "error": "Already in the guild." }))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to check if you're in the guild." }),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Response::NotFound(json!({ "error": "Failed to fetch invite or code is invalid." }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct CreateGuild {
|
|
||||||
name: String,
|
|
||||||
description: Option<String>,
|
|
||||||
nonce: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// create a new guild
|
|
||||||
#[post("/create", data = "<info>")]
|
|
||||||
pub async fn create_guild(user: User, info: Json<CreateGuild>) -> Response {
|
|
||||||
if !user.email_verification.verified {
|
|
||||||
return Response::Unauthorized(json!({ "error": "Email not verified!" }));
|
|
||||||
}
|
|
||||||
|
|
||||||
let name: String = info.name.chars().take(32).collect();
|
|
||||||
let description: String = info
|
|
||||||
.description
|
|
||||||
.clone()
|
|
||||||
.unwrap_or("No description.".to_string())
|
|
||||||
.chars()
|
|
||||||
.take(255)
|
|
||||||
.collect();
|
|
||||||
let nonce: String = info.nonce.chars().take(32).collect();
|
|
||||||
|
|
||||||
let channels = database::get_collection("channels");
|
|
||||||
let col = database::get_collection("guilds");
|
|
||||||
if col
|
|
||||||
.find_one(doc! { "nonce": nonce.clone() }, None)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
return Response::BadRequest(json!({ "error": "Guild already created!" }));
|
|
||||||
}
|
|
||||||
|
|
||||||
let id = Ulid::new().to_string();
|
|
||||||
let channel_id = Ulid::new().to_string();
|
|
||||||
if channels
|
|
||||||
.insert_one(
|
|
||||||
doc! {
|
|
||||||
"_id": channel_id.clone(),
|
|
||||||
"type": ChannelType::GUILDCHANNEL as u32,
|
|
||||||
"name": "general",
|
|
||||||
"description": "",
|
|
||||||
"guild": id.clone(),
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
return Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to create guild channel." }),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if database::get_collection("members")
|
|
||||||
.insert_one(
|
|
||||||
doc! {
|
|
||||||
"_id": {
|
|
||||||
"guild": &id,
|
|
||||||
"user": &user.id
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
return Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to add you to members list." }),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if col
|
|
||||||
.insert_one(
|
|
||||||
doc! {
|
|
||||||
"_id": &id,
|
|
||||||
"nonce": nonce,
|
|
||||||
"name": name,
|
|
||||||
"description": description,
|
|
||||||
"owner": &user.id,
|
|
||||||
"channels": [ channel_id.clone() ],
|
|
||||||
"invites": [],
|
|
||||||
"bans": [],
|
|
||||||
"default_permissions": 51,
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
Response::Success(json!({ "id": id }))
|
|
||||||
} else {
|
|
||||||
channels
|
|
||||||
.delete_one(doc! { "_id": channel_id }, None)
|
|
||||||
.await
|
|
||||||
.expect("Failed to delete the channel we just made.");
|
|
||||||
|
|
||||||
Response::InternalServerError(json!({ "error": "Failed to create guild." }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// fetch a guild's member
|
|
||||||
#[get("/<target>/members")]
|
|
||||||
pub async fn fetch_members(user: User, target: Guild) -> Option<Response> {
|
|
||||||
with_permissions!(user, target);
|
|
||||||
|
|
||||||
if let Ok(mut result) =
|
|
||||||
database::get_collection("members").find(doc! { "_id.guild": target.id }, None)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
let mut users = vec![];
|
|
||||||
|
|
||||||
while let Some(item) = result.next().await {
|
|
||||||
if let Ok(doc) = item {
|
|
||||||
users.push(json!({
|
|
||||||
"id": doc.get_document("_id").unwrap().get_str("user").unwrap(),
|
|
||||||
"nickname": doc.get_str("nickname").ok(),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(Response::Success(json!(users)))
|
|
||||||
} else {
|
|
||||||
Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to fetch members." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// fetch a guild member
|
|
||||||
#[get("/<target>/members/<other>")]
|
|
||||||
pub async fn fetch_member(user: User, target: Guild, other: String) -> Option<Response> {
|
|
||||||
with_permissions!(user, target);
|
|
||||||
|
|
||||||
if let Ok(result) = get_member(MemberKey(target.id, other)).await {
|
|
||||||
if let Some(member) = result {
|
|
||||||
Some(Response::Success(json!({
|
|
||||||
"id": member.id.user,
|
|
||||||
"nickname": member.nickname,
|
|
||||||
})))
|
|
||||||
} else {
|
|
||||||
Some(Response::NotFound(
|
|
||||||
json!({ "error": "Member does not exist!" }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to fetch member." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// kick a guild member
|
|
||||||
#[delete("/<target>/members/<other>")]
|
|
||||||
pub async fn kick_member(user: User, target: Guild, other: String) -> Option<Response> {
|
|
||||||
let (permissions, _) = with_permissions!(user, target);
|
|
||||||
|
|
||||||
if user.id == other {
|
|
||||||
return Some(Response::BadRequest(
|
|
||||||
json!({ "error": "Cannot kick yourself." }),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !permissions.get_kick_members() {
|
|
||||||
return Some(Response::LackingPermission(Permission::KickMembers));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(result) = get_member(MemberKey(target.id.clone(), other.clone())).await {
|
|
||||||
if result.is_none() {
|
|
||||||
return Some(Response::BadRequest(
|
|
||||||
json!({ "error": "User not part of guild." }),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to fetch member." }),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if database::get_collection("members")
|
|
||||||
.delete_one(
|
|
||||||
doc! {
|
|
||||||
"_id.guild": &target.id,
|
|
||||||
"_id.user": &other,
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
/*notifications::send_message_threaded(
|
|
||||||
None,
|
|
||||||
target.id.clone(),
|
|
||||||
Notification::guild_user_leave(UserLeave {
|
|
||||||
id: target.id.clone(),
|
|
||||||
user: other.clone(),
|
|
||||||
banned: false,
|
|
||||||
}), FIXME
|
|
||||||
);*/
|
|
||||||
|
|
||||||
Some(Response::Result(super::Status::Ok))
|
|
||||||
} else {
|
|
||||||
Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to kick member." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, FromForm)]
|
|
||||||
pub struct BanOptions {
|
|
||||||
reason: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ban a guild member
|
|
||||||
#[put("/<target>/members/<other>/ban?<options..>")]
|
|
||||||
pub async fn ban_member(
|
|
||||||
user: User,
|
|
||||||
target: Guild,
|
|
||||||
other: String,
|
|
||||||
options: Form<BanOptions>,
|
|
||||||
) -> Option<Response> {
|
|
||||||
let (permissions, _) = with_permissions!(user, target);
|
|
||||||
let reason: String = options
|
|
||||||
.reason
|
|
||||||
.clone()
|
|
||||||
.unwrap_or("No reason specified.".to_string())
|
|
||||||
.chars()
|
|
||||||
.take(64)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if user.id == other {
|
|
||||||
return Some(Response::BadRequest(
|
|
||||||
json!({ "error": "Cannot ban yourself." }),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !permissions.get_ban_members() {
|
|
||||||
return Some(Response::LackingPermission(Permission::BanMembers));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(result) = get_member(MemberKey(target.id.clone(), other.clone())).await {
|
|
||||||
if result.is_none() {
|
|
||||||
return Some(Response::BadRequest(
|
|
||||||
json!({ "error": "User not part of guild." }),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to fetch member." }),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if database::get_collection("guilds")
|
|
||||||
.update_one(
|
|
||||||
doc! { "_id": &target.id },
|
|
||||||
doc! {
|
|
||||||
"$push": {
|
|
||||||
"bans": {
|
|
||||||
"id": &other,
|
|
||||||
"reason": reason,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
return Some(Response::BadRequest(
|
|
||||||
json!({ "error": "Failed to add ban to guild." }),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if database::get_collection("members")
|
|
||||||
.delete_one(
|
|
||||||
doc! {
|
|
||||||
"_id.guild": &target.id,
|
|
||||||
"_id.user": &other,
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
/*notifications::send_message_threaded(
|
|
||||||
None,
|
|
||||||
target.id.clone(),
|
|
||||||
Notification::guild_user_leave(UserLeave {
|
|
||||||
id: target.id.clone(),
|
|
||||||
user: other.clone(),
|
|
||||||
banned: true,
|
|
||||||
}), FIXME
|
|
||||||
);*/
|
|
||||||
|
|
||||||
Some(Response::Result(super::Status::Ok))
|
|
||||||
} else {
|
|
||||||
Some(Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to kick member after adding to ban list." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// unban a guild member
|
|
||||||
#[delete("/<target>/members/<other>/ban")]
|
|
||||||
pub async fn unban_member(user: User, target: Guild, other: String) -> Option<Response> {
|
|
||||||
let (permissions, _) = with_permissions!(user, target);
|
|
||||||
|
|
||||||
if user.id == other {
|
|
||||||
return Some(Response::BadRequest(
|
|
||||||
json!({ "error": "Cannot unban yourself (not checking if you're banned)." }),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !permissions.get_ban_members() {
|
|
||||||
return Some(Response::LackingPermission(Permission::BanMembers));
|
|
||||||
}
|
|
||||||
|
|
||||||
if target.bans.iter().any(|v| v.id == other) {
|
|
||||||
return Some(Response::BadRequest(json!({ "error": "User not banned." })));
|
|
||||||
}
|
|
||||||
|
|
||||||
if database::get_collection("guilds")
|
|
||||||
.update_one(
|
|
||||||
doc! {
|
|
||||||
"_id": &target.id
|
|
||||||
},
|
|
||||||
doc! {
|
|
||||||
"$pull": {
|
|
||||||
"bans": {
|
|
||||||
"$elemMatch": {
|
|
||||||
"id": &other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
Some(Response::Result(super::Status::Ok))
|
|
||||||
} else {
|
|
||||||
Some(Response::BadRequest(
|
|
||||||
json!({ "error": "Failed to remove ban." }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
5
src/routes/guild/mod.rs
Normal file
5
src/routes/guild/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
use rocket::Route;
|
||||||
|
|
||||||
|
pub fn routes() -> Vec<Route> {
|
||||||
|
routes! []
|
||||||
|
}
|
||||||
@@ -2,71 +2,20 @@ pub use rocket::http::Status;
|
|||||||
pub use rocket::response::Redirect;
|
pub use rocket::response::Redirect;
|
||||||
use rocket::Rocket;
|
use rocket::Rocket;
|
||||||
|
|
||||||
// use crate::database::Permission;
|
mod root;
|
||||||
// use rocket_contrib::json::JsonValue;
|
mod users;
|
||||||
|
mod guild;
|
||||||
/* pub mod channel;
|
mod onboard;
|
||||||
pub mod guild;
|
mod channels;
|
||||||
pub mod user; */
|
|
||||||
pub mod root;
|
|
||||||
|
|
||||||
/* #[derive(Responder)]
|
|
||||||
pub enum Response {
|
|
||||||
#[response()]
|
|
||||||
Result(Status),
|
|
||||||
#[response()]
|
|
||||||
Success(JsonValue),
|
|
||||||
#[response()]
|
|
||||||
Redirect(Redirect),
|
|
||||||
#[response(status = 207)]
|
|
||||||
PartialStatus(JsonValue),
|
|
||||||
#[response(status = 400)]
|
|
||||||
BadRequest(JsonValue),
|
|
||||||
#[response(status = 401)]
|
|
||||||
Unauthorized(JsonValue),
|
|
||||||
#[response(status = 401)]
|
|
||||||
LackingPermission(Permission),
|
|
||||||
#[response(status = 404)]
|
|
||||||
NotFound(JsonValue),
|
|
||||||
#[response(status = 406)]
|
|
||||||
NotAcceptable(JsonValue),
|
|
||||||
#[response(status = 409)]
|
|
||||||
Conflict(JsonValue),
|
|
||||||
#[response(status = 410)]
|
|
||||||
Gone(JsonValue),
|
|
||||||
#[response(status = 418)]
|
|
||||||
Teapot(JsonValue),
|
|
||||||
#[response(status = 422)]
|
|
||||||
UnprocessableEntity(JsonValue),
|
|
||||||
#[response(status = 429)]
|
|
||||||
TooManyRequests(JsonValue),
|
|
||||||
#[response(status = 500)]
|
|
||||||
InternalServerError(JsonValue),
|
|
||||||
}
|
|
||||||
|
|
||||||
use rocket::http::ContentType;
|
|
||||||
use rocket::request::Request;
|
|
||||||
use std::io::Cursor;
|
|
||||||
|
|
||||||
use rocket::response::{Responder, Result};
|
|
||||||
|
|
||||||
impl<'a> Responder<'a, 'static> for Permission {
|
|
||||||
fn respond_to(self, _: &Request) -> Result<'static> {
|
|
||||||
let body = format!(
|
|
||||||
"{{\"error\":\"Lacking permission: {:?}.\",\"permission\":{}}}",
|
|
||||||
self, self as u32,
|
|
||||||
);
|
|
||||||
|
|
||||||
rocket::response::Response::build()
|
|
||||||
.header(ContentType::JSON)
|
|
||||||
.sized_body(body.len(), Cursor::new(body))
|
|
||||||
.ok()
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
|
|
||||||
pub fn mount(rocket: Rocket) -> Rocket {
|
pub fn mount(rocket: Rocket) -> Rocket {
|
||||||
rocket
|
rocket
|
||||||
.mount("/", routes![root::root, root::teapot])
|
.mount("/", routes![root::root, root::teapot])
|
||||||
|
.mount("/onboard", onboard::routes())
|
||||||
|
.mount("/users", users::routes())
|
||||||
|
.mount("/channels", channels::routes())
|
||||||
|
.mount("/guild", guild::routes())
|
||||||
|
|
||||||
/*.mount(
|
/*.mount(
|
||||||
"/users",
|
"/users",
|
||||||
routes![
|
routes![
|
||||||
|
|||||||
7
src/routes/onboard/hello.rs
Normal file
7
src/routes/onboard/hello.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
use crate::util::result::Result;
|
||||||
|
use rauth::auth::Session;
|
||||||
|
|
||||||
|
#[get("/hello")]
|
||||||
|
pub async fn req(session: Session) -> Result<String> {
|
||||||
|
Ok("try onboard user".to_string())
|
||||||
|
}
|
||||||
9
src/routes/onboard/mod.rs
Normal file
9
src/routes/onboard/mod.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
use rocket::Route;
|
||||||
|
|
||||||
|
mod hello;
|
||||||
|
|
||||||
|
pub fn routes() -> Vec<Route> {
|
||||||
|
routes! [
|
||||||
|
hello::req
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,752 +0,0 @@
|
|||||||
use super::Response;
|
|
||||||
use crate::database::{
|
|
||||||
self, get_relationship, get_relationship_internal, user::User, Relationship,
|
|
||||||
};
|
|
||||||
use crate::routes::channel;
|
|
||||||
|
|
||||||
use mongodb::bson::doc;
|
|
||||||
use mongodb::options::{Collation, FindOneOptions, FindOptions};
|
|
||||||
use rocket_contrib::json::Json;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use rocket::futures::StreamExt;
|
|
||||||
use ulid::Ulid;
|
|
||||||
|
|
||||||
/// retrieve your user information
|
|
||||||
#[get("/@me")]
|
|
||||||
pub async fn me(user: User) -> Response {
|
|
||||||
Response::Success(user.serialise(Relationship::SELF as i32))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// retrieve another user's information
|
|
||||||
#[get("/<target>")]
|
|
||||||
pub async fn user(user: User, target: User) -> Response {
|
|
||||||
let relationship = get_relationship(&user, &target) as i32;
|
|
||||||
Response::Success(user.serialise(relationship))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct UserQuery {
|
|
||||||
username: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// find a user by their username
|
|
||||||
#[post("/query", data = "<query>")]
|
|
||||||
pub async fn query(user: User, query: Json<UserQuery>) -> Response {
|
|
||||||
let col = database::get_collection("users");
|
|
||||||
|
|
||||||
if let Ok(result) = col.find_one(
|
|
||||||
doc! { "username": query.username.clone() },
|
|
||||||
FindOneOptions::builder()
|
|
||||||
.collation(Collation::builder().locale("en").strength(2).build())
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
.await {
|
|
||||||
if let Some(doc) = result {
|
|
||||||
let id = doc.get_str("_id").unwrap();
|
|
||||||
Response::Success(json!({
|
|
||||||
"id": id,
|
|
||||||
"username": doc.get_str("username").unwrap(),
|
|
||||||
"display_name": doc.get_str("display_name").unwrap(),
|
|
||||||
"relationship": get_relationship_internal(&user.id, &id, &user.relations) as i32
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
Response::NotFound(json!({
|
|
||||||
"error": "User not found!"
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(json!({ "error": "Failed database query." }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct LookupQuery {
|
|
||||||
username: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// lookup a user on Revolt
|
|
||||||
/// currently only supports exact username searches
|
|
||||||
#[post("/lookup", data = "<query>")]
|
|
||||||
pub fn lookup(user: User, query: Json<LookupQuery>) -> Response {
|
|
||||||
let relationships = user.fetch_relationships();
|
|
||||||
let col = database::get_collection("users");
|
|
||||||
|
|
||||||
if let Ok(users) = col.find(
|
|
||||||
doc! { "username": query.username.clone() },
|
|
||||||
FindOptions::builder()
|
|
||||||
.projection(doc! { "_id": 1, "username": 1 })
|
|
||||||
.limit(10)
|
|
||||||
.build(),
|
|
||||||
) {
|
|
||||||
let mut results = Vec::new();
|
|
||||||
for item in users {
|
|
||||||
if let Ok(doc) = item {
|
|
||||||
let id = doc.get_str("id").unwrap();
|
|
||||||
results.push(json!({
|
|
||||||
"id": id,
|
|
||||||
"username": doc.get_str("username").unwrap(),
|
|
||||||
"relationship": get_relationship_internal(&user.id, &id, &relationships) as i32
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Response::Success(json!(results))
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(json!({ "error": "Failed database query." }))
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/// retrieve all of your DMs
|
|
||||||
#[get("/@me/dms")]
|
|
||||||
pub async fn dms(user: User) -> Response {
|
|
||||||
let col = database::get_collection("channels");
|
|
||||||
|
|
||||||
if let Ok(mut results) = col.find(
|
|
||||||
doc! {
|
|
||||||
"$or": [
|
|
||||||
{
|
|
||||||
"type": channel::ChannelType::DM as i32,
|
|
||||||
"active": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": channel::ChannelType::GROUPDM as i32
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"recipients": user.id
|
|
||||||
},
|
|
||||||
FindOptions::builder().projection(doc! {}).build(),
|
|
||||||
)
|
|
||||||
.await {
|
|
||||||
let mut channels = Vec::new();
|
|
||||||
while let Some(item) = results.next().await {
|
|
||||||
if let Ok(doc) = item {
|
|
||||||
let id = doc.get_str("_id").unwrap();
|
|
||||||
let last_message = doc.get_document("last_message").unwrap();
|
|
||||||
let recipients = doc.get_array("recipients").unwrap();
|
|
||||||
|
|
||||||
match doc.get_i32("type").unwrap() {
|
|
||||||
0 => {
|
|
||||||
channels.push(json!({
|
|
||||||
"id": id,
|
|
||||||
"type": 0,
|
|
||||||
"last_message": last_message,
|
|
||||||
"recipients": recipients,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
1 => {
|
|
||||||
channels.push(json!({
|
|
||||||
"id": id,
|
|
||||||
"type": 1,
|
|
||||||
"recipients": recipients,
|
|
||||||
"name": doc.get_str("name").unwrap(),
|
|
||||||
"owner": doc.get_str("owner").unwrap(),
|
|
||||||
"description": doc.get_str("description").unwrap_or(""),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Response::Success(json!(channels))
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(json!({ "error": "Failed database query." }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// open a DM with a user
|
|
||||||
#[get("/<target>/dm")]
|
|
||||||
pub async fn dm(user: User, target: User) -> Response {
|
|
||||||
let col = database::get_collection("channels");
|
|
||||||
|
|
||||||
if let Ok(result) = col.find_one(
|
|
||||||
doc! { "type": channel::ChannelType::DM as i32, "recipients": { "$all": [ user.id.clone(), target.id.clone() ] } },
|
|
||||||
None
|
|
||||||
)
|
|
||||||
.await {
|
|
||||||
if let Some(channel) = result {
|
|
||||||
Response::Success( json!({ "id": channel.get_str("_id").unwrap() }))
|
|
||||||
} else {
|
|
||||||
let id = Ulid::new();
|
|
||||||
|
|
||||||
if col.insert_one(
|
|
||||||
doc! {
|
|
||||||
"_id": id.to_string(),
|
|
||||||
"type": channel::ChannelType::DM as i32,
|
|
||||||
"recipients": [ user.id, target.id ],
|
|
||||||
"active": false
|
|
||||||
},
|
|
||||||
None
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok() {
|
|
||||||
Response::Success(json!({ "id": id.to_string() }))
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(json!({ "error": "Failed to create new channel." }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(json!({ "error": "Failed server query." }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// retrieve all of your friends
|
|
||||||
#[get("/@me/friend")]
|
|
||||||
pub async fn get_friends(user: User) -> Response {
|
|
||||||
let mut results = Vec::new();
|
|
||||||
if let Some(arr) = user.relations {
|
|
||||||
for item in arr {
|
|
||||||
results.push(json!({
|
|
||||||
"id": item.id,
|
|
||||||
"status": item.status
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Response::Success(json!(results))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// retrieve friend status with user
|
|
||||||
#[get("/<target>/friend")]
|
|
||||||
pub async fn get_friend(user: User, target: User) -> Response {
|
|
||||||
Response::Success(json!({ "status": get_relationship(&user, &target) as i32 }))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// create or accept a friend request
|
|
||||||
#[put("/<target>/friend")]
|
|
||||||
pub async fn add_friend(user: User, target: User) -> Response {
|
|
||||||
let col = database::get_collection("users");
|
|
||||||
|
|
||||||
match get_relationship(&user, &target) {
|
|
||||||
Relationship::Friend => Response::BadRequest(json!({ "error": "Already friends." })),
|
|
||||||
Relationship::Outgoing => {
|
|
||||||
Response::BadRequest(json!({ "error": "Already sent a friend request." }))
|
|
||||||
}
|
|
||||||
Relationship::Incoming => {
|
|
||||||
if col
|
|
||||||
.update_one(
|
|
||||||
doc! {
|
|
||||||
"_id": user.id.clone(),
|
|
||||||
"relations.id": target.id.clone()
|
|
||||||
},
|
|
||||||
doc! {
|
|
||||||
"$set": {
|
|
||||||
"relations.$.status": Relationship::Friend as i32
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
if col
|
|
||||||
.update_one(
|
|
||||||
doc! {
|
|
||||||
"_id": target.id.clone(),
|
|
||||||
"relations.id": user.id.clone()
|
|
||||||
},
|
|
||||||
doc! {
|
|
||||||
"$set": {
|
|
||||||
"relations.$.status": Relationship::Friend as i32
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
/*notifications::send_message_threaded(
|
|
||||||
vec![target.id.clone()],
|
|
||||||
None,
|
|
||||||
Notification::user_friend_status(FriendStatus {
|
|
||||||
id: target.id.clone(),
|
|
||||||
user: user.id.clone(),
|
|
||||||
status: Relationship::Friend as i32,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
notifications::send_message_threaded(
|
|
||||||
vec![user.id.clone()],
|
|
||||||
None,
|
|
||||||
Notification::user_friend_status(FriendStatus {
|
|
||||||
id: user.id.clone(),
|
|
||||||
user: target.id.clone(),
|
|
||||||
status: Relationship::Friend as i32,
|
|
||||||
}), FIXME
|
|
||||||
);*/
|
|
||||||
|
|
||||||
Response::Success(json!({ "status": Relationship::Friend as i32 }))
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to commit! Try re-adding them as a friend." }),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to commit to database, try again." }),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Relationship::Blocked => {
|
|
||||||
Response::BadRequest(json!({ "error": "You have blocked this person." }))
|
|
||||||
}
|
|
||||||
Relationship::BlockedOther => {
|
|
||||||
Response::Conflict(json!({ "error": "You have been blocked by this person." }))
|
|
||||||
}
|
|
||||||
Relationship::NONE => {
|
|
||||||
if col
|
|
||||||
.update_one(
|
|
||||||
doc! {
|
|
||||||
"_id": user.id.clone()
|
|
||||||
},
|
|
||||||
doc! {
|
|
||||||
"$push": {
|
|
||||||
"relations": {
|
|
||||||
"id": target.id.clone(),
|
|
||||||
"status": Relationship::Outgoing as i32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
if col
|
|
||||||
.update_one(
|
|
||||||
doc! {
|
|
||||||
"_id": target.id.clone()
|
|
||||||
},
|
|
||||||
doc! {
|
|
||||||
"$push": {
|
|
||||||
"relations": {
|
|
||||||
"id": user.id.clone(),
|
|
||||||
"status": Relationship::Incoming as i32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
/*notifications::send_message_threaded(
|
|
||||||
vec![user.id.clone()],
|
|
||||||
None,
|
|
||||||
Notification::user_friend_status(FriendStatus {
|
|
||||||
id: user.id.clone(),
|
|
||||||
user: target.id.clone(),
|
|
||||||
status: Relationship::Outgoing as i32,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
notifications::send_message_threaded(
|
|
||||||
vec![target.id.clone()],
|
|
||||||
None,
|
|
||||||
Notification::user_friend_status(FriendStatus {
|
|
||||||
id: target.id.clone(),
|
|
||||||
user: user.id.clone(),
|
|
||||||
status: Relationship::Incoming as i32,
|
|
||||||
}), FIXME
|
|
||||||
);*/
|
|
||||||
|
|
||||||
Response::Success(json!({ "status": Relationship::Outgoing as i32 }))
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to commit! Try re-adding them as a friend." }),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to commit to database, try again." }),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Relationship::SELF => {
|
|
||||||
Response::BadRequest(json!({ "error": "You're already friends with yourself, no? c:" }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// remove a friend or deny a request
|
|
||||||
#[delete("/<target>/friend")]
|
|
||||||
pub async fn remove_friend(user: User, target: User) -> Response {
|
|
||||||
let col = database::get_collection("users");
|
|
||||||
|
|
||||||
match get_relationship(&user, &target) {
|
|
||||||
Relationship::Friend | Relationship::Outgoing | Relationship::Incoming => {
|
|
||||||
if col
|
|
||||||
.update_one(
|
|
||||||
doc! {
|
|
||||||
"_id": user.id.clone()
|
|
||||||
},
|
|
||||||
doc! {
|
|
||||||
"$pull": {
|
|
||||||
"relations": {
|
|
||||||
"id": target.id.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
if col
|
|
||||||
.update_one(
|
|
||||||
doc! {
|
|
||||||
"_id": target.id.clone()
|
|
||||||
},
|
|
||||||
doc! {
|
|
||||||
"$pull": {
|
|
||||||
"relations": {
|
|
||||||
"id": user.id.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
/*notifications::send_message_threaded(
|
|
||||||
vec![user.id.clone()],
|
|
||||||
None,
|
|
||||||
Notification::user_friend_status(FriendStatus {
|
|
||||||
id: user.id.clone(),
|
|
||||||
user: target.id.clone(),
|
|
||||||
status: Relationship::NONE as i32,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
notifications::send_message_threaded(
|
|
||||||
vec![target.id.clone()],
|
|
||||||
None,
|
|
||||||
Notification::user_friend_status(FriendStatus {
|
|
||||||
id: target.id.clone(),
|
|
||||||
user: user.id.clone(),
|
|
||||||
status: Relationship::NONE as i32,
|
|
||||||
}), FIXME
|
|
||||||
);*/
|
|
||||||
|
|
||||||
Response::Success(json!({ "status": Relationship::NONE as i32 }))
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to commit! Target remains in same state." }),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to commit to database, try again." }),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Relationship::Blocked
|
|
||||||
| Relationship::BlockedOther
|
|
||||||
| Relationship::NONE
|
|
||||||
| Relationship::SELF => Response::BadRequest(json!({ "error": "This has no effect." })),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// block a user
|
|
||||||
#[put("/<target>/block")]
|
|
||||||
pub async fn block_user(user: User, target: User) -> Response {
|
|
||||||
let col = database::get_collection("users");
|
|
||||||
|
|
||||||
match get_relationship(&user, &target) {
|
|
||||||
Relationship::Friend | Relationship::Incoming | Relationship::Outgoing => {
|
|
||||||
if col
|
|
||||||
.update_one(
|
|
||||||
doc! {
|
|
||||||
"_id": user.id.clone(),
|
|
||||||
"relations.id": target.id.clone()
|
|
||||||
},
|
|
||||||
doc! {
|
|
||||||
"$set": {
|
|
||||||
"relations.$.status": Relationship::Blocked as i32
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
if col
|
|
||||||
.update_one(
|
|
||||||
doc! {
|
|
||||||
"_id": target.id.clone(),
|
|
||||||
"relations.id": user.id.clone()
|
|
||||||
},
|
|
||||||
doc! {
|
|
||||||
"$set": {
|
|
||||||
"relations.$.status": Relationship::BlockedOther as i32
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
/*notifications::send_message_threaded(
|
|
||||||
vec![user.id.clone()],
|
|
||||||
None,
|
|
||||||
Notification::user_friend_status(FriendStatus {
|
|
||||||
id: user.id.clone(),
|
|
||||||
user: target.id.clone(),
|
|
||||||
status: Relationship::Blocked as i32,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
notifications::send_message_threaded(
|
|
||||||
vec![target.id.clone()],
|
|
||||||
None,
|
|
||||||
Notification::user_friend_status(FriendStatus {
|
|
||||||
id: target.id.clone(),
|
|
||||||
user: user.id.clone(),
|
|
||||||
status: Relationship::BlockedOther as i32,
|
|
||||||
}), FIXME
|
|
||||||
);*/
|
|
||||||
|
|
||||||
Response::Success(json!({ "status": Relationship::Blocked as i32 }))
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to commit! Try blocking the user again, remove it first." }),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to commit to database, try again." }),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Relationship::NONE => {
|
|
||||||
if col
|
|
||||||
.update_one(
|
|
||||||
doc! {
|
|
||||||
"_id": user.id.clone(),
|
|
||||||
},
|
|
||||||
doc! {
|
|
||||||
"$push": {
|
|
||||||
"relations": {
|
|
||||||
"id": target.id.clone(),
|
|
||||||
"status": Relationship::Blocked as i32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
if col
|
|
||||||
.update_one(
|
|
||||||
doc! {
|
|
||||||
"_id": target.id.clone(),
|
|
||||||
},
|
|
||||||
doc! {
|
|
||||||
"$push": {
|
|
||||||
"relations": {
|
|
||||||
"id": user.id.clone(),
|
|
||||||
"status": Relationship::BlockedOther as i32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
/*notifications::send_message_threaded(
|
|
||||||
vec![user.id.clone()],
|
|
||||||
None,
|
|
||||||
Notification::user_friend_status(FriendStatus {
|
|
||||||
id: user.id.clone(),
|
|
||||||
user: target.id.clone(),
|
|
||||||
status: Relationship::Blocked as i32,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
notifications::send_message_threaded(
|
|
||||||
vec![target.id.clone()],
|
|
||||||
None,
|
|
||||||
Notification::user_friend_status(FriendStatus {
|
|
||||||
id: target.id.clone(),
|
|
||||||
user: user.id.clone(),
|
|
||||||
status: Relationship::BlockedOther as i32,
|
|
||||||
}), FIXME
|
|
||||||
);*/
|
|
||||||
|
|
||||||
Response::Success(json!({ "status": Relationship::Blocked as i32 }))
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to commit! Try blocking the user again, remove it first." }),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to commit to database, try again." }),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Relationship::Blocked => {
|
|
||||||
Response::BadRequest(json!({ "error": "Already blocked this person." }))
|
|
||||||
}
|
|
||||||
Relationship::BlockedOther => {
|
|
||||||
if col
|
|
||||||
.update_one(
|
|
||||||
doc! {
|
|
||||||
"_id": user.id.clone(),
|
|
||||||
"relations.id": target.id.clone()
|
|
||||||
},
|
|
||||||
doc! {
|
|
||||||
"$set": {
|
|
||||||
"relations.$.status": Relationship::Blocked as i32
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
/*notifications::send_message_threaded(
|
|
||||||
vec![user.id.clone()],
|
|
||||||
None,
|
|
||||||
Notification::user_friend_status(FriendStatus {
|
|
||||||
id: user.id.clone(),
|
|
||||||
user: target.id.clone(),
|
|
||||||
status: Relationship::Blocked as i32,
|
|
||||||
}), FIXME
|
|
||||||
);*/
|
|
||||||
|
|
||||||
Response::Success(json!({ "status": Relationship::Blocked as i32 }))
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to commit to database, try again." }),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Relationship::SELF => Response::BadRequest(json!({ "error": "This has no effect." })),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// unblock a user
|
|
||||||
#[delete("/<target>/block")]
|
|
||||||
pub async fn unblock_user(user: User, target: User) -> Response {
|
|
||||||
let col = database::get_collection("users");
|
|
||||||
|
|
||||||
match get_relationship(&user, &target) {
|
|
||||||
Relationship::Blocked => match get_relationship(&target, &user) {
|
|
||||||
Relationship::Blocked => {
|
|
||||||
if col
|
|
||||||
.update_one(
|
|
||||||
doc! {
|
|
||||||
"_id": user.id.clone(),
|
|
||||||
"relations.id": target.id.clone()
|
|
||||||
},
|
|
||||||
doc! {
|
|
||||||
"$set": {
|
|
||||||
"relations.$.status": Relationship::BlockedOther as i32
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
/*notifications::send_message_threaded(
|
|
||||||
vec![user.id.clone()],
|
|
||||||
None,
|
|
||||||
Notification::user_friend_status(FriendStatus {
|
|
||||||
id: user.id.clone(),
|
|
||||||
user: target.id.clone(),
|
|
||||||
status: Relationship::BlockedOther as i32,
|
|
||||||
}), FIXME
|
|
||||||
);*/
|
|
||||||
|
|
||||||
Response::Success(json!({ "status": Relationship::BlockedOther as i32 }))
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to commit to database, try again." }),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Relationship::BlockedOther => {
|
|
||||||
if col
|
|
||||||
.update_one(
|
|
||||||
doc! {
|
|
||||||
"_id": user.id.clone()
|
|
||||||
},
|
|
||||||
doc! {
|
|
||||||
"$pull": {
|
|
||||||
"relations": {
|
|
||||||
"id": target.id.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
if col
|
|
||||||
.update_one(
|
|
||||||
doc! {
|
|
||||||
"_id": target.id.clone()
|
|
||||||
},
|
|
||||||
doc! {
|
|
||||||
"$pull": {
|
|
||||||
"relations": {
|
|
||||||
"id": user.id.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
/*notifications::send_message_threaded(
|
|
||||||
vec![user.id.clone()],
|
|
||||||
None,
|
|
||||||
Notification::user_friend_status(FriendStatus {
|
|
||||||
id: user.id.clone(),
|
|
||||||
user: target.id.clone(),
|
|
||||||
status: Relationship::NONE as i32,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
notifications::send_message_threaded(
|
|
||||||
vec![target.id.clone()],
|
|
||||||
None,
|
|
||||||
Notification::user_friend_status(FriendStatus {
|
|
||||||
id: target.id.clone(),
|
|
||||||
user: user.id.clone(),
|
|
||||||
status: Relationship::NONE as i32,
|
|
||||||
}), FIXME
|
|
||||||
);*/
|
|
||||||
|
|
||||||
Response::Success(json!({ "status": Relationship::NONE as i32 }))
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to commit! Target remains in same state." }),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Response::InternalServerError(
|
|
||||||
json!({ "error": "Failed to commit to database, try again." }),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
|
||||||
Relationship::BlockedOther => {
|
|
||||||
Response::BadRequest(json!({ "error": "Cannot remove block by other user." }))
|
|
||||||
}
|
|
||||||
Relationship::Friend
|
|
||||||
| Relationship::Incoming
|
|
||||||
| Relationship::Outgoing
|
|
||||||
| Relationship::SELF
|
|
||||||
| Relationship::NONE => Response::BadRequest(json!({ "error": "This has no effect." })),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
7
src/routes/users/fetch_user.rs
Normal file
7
src/routes/users/fetch_user.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
use crate::util::result::Result;
|
||||||
|
|
||||||
|
#[get("/<id>")]
|
||||||
|
pub async fn req(id: String) -> Result<String> {
|
||||||
|
println!("{}", id);
|
||||||
|
Ok("LETS FUCKING GOOOO".to_string())
|
||||||
|
}
|
||||||
9
src/routes/users/mod.rs
Normal file
9
src/routes/users/mod.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
use rocket::Route;
|
||||||
|
|
||||||
|
mod fetch_user;
|
||||||
|
|
||||||
|
pub fn routes() -> Vec<Route> {
|
||||||
|
routes! [
|
||||||
|
fetch_user::req
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user