Merge remote-tracking branch 'origin/main' into feat/livekit

This commit is contained in:
Zomatree
2025-08-18 07:44:52 +01:00
120 changed files with 372 additions and 351 deletions

View File

@@ -11,7 +11,7 @@ use rocket_empty::EmptyResponse;
pub async fn delete_bot(
db: &State<Database>,
user: User,
target: Reference,
target: Reference<'_>,
) -> Result<EmptyResponse> {
let bot = target.as_bot(db).await?;
if bot.owner != user.id {

View File

@@ -14,7 +14,7 @@ use validator::Validate;
pub async fn edit_bot(
db: &State<Database>,
user: User,
target: Reference,
target: Reference<'_>,
data: Json<DataEditBot>,
) -> Result<Json<v0::BotWithUserResponse>> {
let data = data.into_inner();
@@ -37,7 +37,7 @@ pub async fn edit_bot(
if data.public.is_none()
&& data.analytics.is_none()
&& data.interactions_url.is_none()
&& data.remove.is_none()
&& data.remove.is_empty()
{
return Ok(Json(v0::BotWithUserResponse {
bot: bot.into(),
@@ -64,7 +64,6 @@ pub async fn edit_bot(
db,
partial,
remove
.unwrap_or_default()
.into_iter()
.map(|v| v.into())
.collect(),
@@ -100,7 +99,7 @@ mod test {
.body(
json!(v0::DataEditBot {
public: Some(true),
remove: Some(vec![FieldsBot::Token]),
remove: vec![FieldsBot::Token],
..Default::default()
})
.to_string(),

View File

@@ -11,7 +11,7 @@ use rocket::{serde::json::Json, State};
pub async fn fetch_bot(
db: &State<Database>,
user: User,
bot: Reference,
bot: Reference<'_>,
) -> Result<Json<FetchBotResponse>> {
if user.bot.is_some() {
return Err(create_error!(IsBot));

View File

@@ -13,9 +13,9 @@ use rocket::State;
pub async fn fetch_public_bot(
db: &State<Database>,
user: Option<User>,
target: Reference,
target: Reference<'_>,
) -> Result<Json<PublicBot>> {
let bot = db.fetch_bot(&target.id).await?;
let bot = db.fetch_bot(target.id).await?;
if !bot.public && user.is_none_or(|x| x.id != bot.owner) {
return Err(create_error!(NotFound));
}

View File

@@ -20,7 +20,7 @@ pub async fn invite_bot(
db: &State<Database>,
amqp: &State<AMQP>,
user: User,
target: Reference,
target: Reference<'_>,
dest: Json<v0::InviteBotDestination>,
) -> Result<EmptyResponse> {
if user.bot.is_some() {

View File

@@ -15,8 +15,8 @@ use rocket_empty::EmptyResponse;
pub async fn ack(
db: &State<Database>,
user: User,
target: Reference,
message: Reference,
target: Reference<'_>,
message: Reference<'_>,
) -> Result<EmptyResponse> {
if user.bot.is_some() {
return Err(create_error!(IsBot));
@@ -29,7 +29,7 @@ pub async fn ack(
.throw_if_lacking_channel_permission(ChannelPermission::ViewChannel)?;
channel
.ack(&user.id, &message.id)
.ack(&user.id, message.id)
.await
.map(|_| EmptyResponse)
}

View File

@@ -17,7 +17,7 @@ pub async fn delete(
voice_client: &State<VoiceClient>,
amqp: &State<AMQP>,
user: User,
target: Reference,
target: Reference<'_>,
options: v0::OptionsChannelDelete,
) -> Result<EmptyResponse> {
let mut channel = target.as_channel(db).await?;

View File

@@ -17,7 +17,7 @@ pub async fn edit(
db: &State<Database>,
amqp: &State<AMQP>,
user: User,
target: Reference,
target: Reference<'_>,
data: Json<v0::DataEditChannel>,
) -> Result<Json<v0::Channel>> {
let data = data.into_inner();
@@ -39,7 +39,7 @@ pub async fn edit(
&& data.nsfw.is_none()
&& data.owner.is_none()
&& data.voice.is_none()
&& data.remove.is_none()
&& data.remove.is_empty()
{
return Ok(Json(channel.into()));
}
@@ -97,23 +97,21 @@ pub async fn edit(
nsfw,
..
} => {
if let Some(fields) = &data.remove {
if fields.contains(&v0::FieldsChannel::Icon) {
if let Some(icon) = &icon {
db.mark_attachment_as_deleted(&icon.id).await?;
}
if data.remove.contains(&v0::FieldsChannel::Icon) {
if let Some(icon) = &icon {
db.mark_attachment_as_deleted(&icon.id).await?;
}
}
for field in fields {
match field {
v0::FieldsChannel::Description => {
description.take();
}
v0::FieldsChannel::Icon => {
icon.take();
}
_ => {}
for field in &data.remove {
match field {
v0::FieldsChannel::Description => {
description.take();
}
v0::FieldsChannel::Icon => {
icon.take();
}
_ => {}
}
}
@@ -202,23 +200,21 @@ pub async fn edit(
voice,
..
} => {
if let Some(fields) = &data.remove {
if fields.contains(&v0::FieldsChannel::Icon) {
if let Some(icon) = &icon {
db.mark_attachment_as_deleted(&icon.id).await?;
}
if data.remove.contains(&v0::FieldsChannel::Icon) {
if let Some(icon) = &icon {
db.mark_attachment_as_deleted(&icon.id).await?;
}
}
for field in fields {
match field {
v0::FieldsChannel::Description => {
description.take();
}
v0::FieldsChannel::Icon => {
icon.take();
}
_ => {}
for field in &data.remove {
match field {
v0::FieldsChannel::Description => {
description.take();
}
v0::FieldsChannel::Icon => {
icon.take();
}
_ => {}
}
}
@@ -255,7 +251,6 @@ pub async fn edit(
db,
partial,
data.remove
.unwrap_or_default()
.into_iter()
.map(|f| f.into())
.collect(),

View File

@@ -16,7 +16,7 @@ use rocket::{serde::json::Json, State};
pub async fn fetch(
db: &State<Database>,
user: User,
target: Reference,
target: Reference<'_>,
) -> Result<Json<v0::Channel>> {
let channel = target.as_channel(db).await?;

View File

@@ -17,8 +17,8 @@ pub async fn add_member(
db: &State<Database>,
amqp: &State<AMQP>,
user: User,
group_id: Reference,
member_id: Reference,
group_id: Reference<'_>,
member_id: Reference<'_>,
) -> Result<EmptyResponse> {
if user.bot.is_some() {
return Err(create_error!(IsBot));

View File

@@ -15,8 +15,8 @@ pub async fn remove_member(
voice_client: &State<VoiceClient>,
amqp: &State<AMQP>,
user: User,
target: Reference,
member: Reference,
target: Reference<'_>,
member: Reference<'_>,
) -> Result<EmptyResponse> {
if user.bot.is_some() {
return Err(create_error!(IsBot));

View File

@@ -18,7 +18,7 @@ use rocket::{serde::json::Json, State};
pub async fn create_invite(
db: &State<Database>,
user: User,
target: Reference,
target: Reference<'_>,
) -> Result<Json<v0::Invite>> {
if user.bot.is_some() {
return Err(create_error!(IsBot));

View File

@@ -17,7 +17,7 @@ use rocket::{serde::json::Json, State};
pub async fn fetch_members(
db: &State<Database>,
user: User,
target: Reference,
target: Reference<'_>,
) -> Result<Json<Vec<v0::User>>> {
let channel = target.as_channel(db).await?;
let mut query = DatabasePermissionQuery::new(db, &user).channel(&channel);

View File

@@ -22,7 +22,7 @@ use validator::Validate;
pub async fn bulk_delete_messages(
db: &State<Database>,
user: User,
target: Reference,
target: Reference<'_>,
options: Json<v0::OptionsBulkDelete>,
) -> Result<EmptyResponse> {
let options = options.into_inner();
@@ -51,7 +51,7 @@ pub async fn bulk_delete_messages(
.await
.throw_if_lacking_channel_permission(ChannelPermission::ManageMessages)?;
Message::bulk_delete(db, &target.id, options.ids)
Message::bulk_delete(db, target.id, options.ids)
.await
.map(|_| EmptyResponse)
}

View File

@@ -17,8 +17,8 @@ use rocket_empty::EmptyResponse;
pub async fn clear_reactions(
db: &State<Database>,
user: User,
target: Reference,
msg: Reference,
target: Reference<'_>,
msg: Reference<'_>,
) -> Result<EmptyResponse> {
let channel = target.as_channel(db).await?;
let mut query = DatabasePermissionQuery::new(db, &user).channel(&channel);

View File

@@ -15,10 +15,10 @@ use rocket_empty::EmptyResponse;
pub async fn delete(
db: &State<Database>,
user: User,
target: Reference,
msg: Reference,
target: Reference<'_>,
msg: Reference<'_>,
) -> Result<EmptyResponse> {
let message = msg.as_message_in_channel(db, &target.id).await?;
let message = msg.as_message_in_channel(db, target.id).await?;
if message.author != user.id {
let channel = target.as_channel(db).await?;

View File

@@ -18,8 +18,8 @@ use validator::Validate;
pub async fn edit(
db: &State<Database>,
user: User,
target: Reference,
msg: Reference,
target: Reference<'_>,
msg: Reference<'_>,
edit: Json<v0::DataEditMessage>,
) -> Result<Json<v0::Message>> {
let edit = edit.into_inner();

View File

@@ -15,8 +15,8 @@ use rocket::{serde::json::Json, State};
pub async fn fetch(
db: &State<Database>,
user: User,
target: Reference,
msg: Reference,
target: Reference<'_>,
msg: Reference<'_>,
) -> Result<Json<v0::Message>> {
let channel = target.as_channel(db).await?;
let mut query = DatabasePermissionQuery::new(db, &user).channel(&channel);

View File

@@ -1,4 +1,7 @@
use revolt_database::{util::{permissions::DatabasePermissionQuery, reference::Reference}, Channel, Database, PartialMessage, SystemMessage, User, AMQP};
use revolt_database::{
util::{permissions::DatabasePermissionQuery, reference::Reference},
Channel, Database, PartialMessage, SystemMessage, User, AMQP,
};
use revolt_models::v0::MessageAuthor;
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::{create_error, Result};
@@ -14,8 +17,8 @@ pub async fn message_pin(
db: &State<Database>,
amqp: &State<AMQP>,
user: User,
target: Reference,
msg: Reference,
target: Reference<'_>,
msg: Reference<'_>,
) -> Result<EmptyResponse> {
let channel = target.as_channel(db).await?;
@@ -169,7 +172,7 @@ mod test {
})
.await;
let updated_message = Reference::from_unchecked(message.id)
let updated_message = Reference::from_unchecked(&message.id)
.as_message(&harness.db)
.await
.expect("Failed to find updated message");

View File

@@ -16,7 +16,7 @@ use validator::Validate;
pub async fn query(
db: &State<Database>,
user: User,
target: Reference,
target: Reference<'_>,
options: v0::OptionsQueryMessages,
) -> Result<Json<v0::BulkMessageResponse>> {
options.validate().map_err(|error| {

View File

@@ -15,9 +15,9 @@ use rocket_empty::EmptyResponse;
pub async fn react_message(
db: &State<Database>,
user: User,
target: Reference,
msg: Reference,
emoji: Reference,
target: Reference<'_>,
msg: Reference<'_>,
emoji: Reference<'_>,
) -> Result<EmptyResponse> {
let channel = target.as_channel(db).await?;
let mut query = DatabasePermissionQuery::new(db, &user).channel(&channel);
@@ -30,7 +30,7 @@ pub async fn react_message(
// Add the reaction
message
.add_reaction(db, &user, &emoji.id)
.add_reaction(db, &user, emoji.id)
.await
.map(|_| EmptyResponse)
}

View File

@@ -16,7 +16,7 @@ use validator::Validate;
pub async fn search(
db: &State<Database>,
user: User,
target: Reference,
target: Reference<'_>,
options: Json<v0::DataMessageSearch>,
) -> Result<Json<v0::BulkMessageResponse>> {
if user.bot.is_some() {

View File

@@ -21,7 +21,7 @@ pub async fn message_send(
db: &State<Database>,
amqp: &State<AMQP>,
user: User,
target: Reference,
target: Reference<'_>,
data: Json<v0::DataMessageSend>,
idempotency: IdempotencyKey,
) -> Result<Json<v0::Message>> {
@@ -204,7 +204,7 @@ mod test {
Member::create(&harness.db, &server, &user, Some(channels.clone()))
.await
.expect("Failed to create member");
let member = Reference::from_unchecked(user.id.clone())
let member = Reference::from_unchecked(&user.id)
.as_member(&harness.db, &server.id)
.await
.expect("Failed to get member");
@@ -244,7 +244,7 @@ mod test {
Member::create(&harness.db, &server, &second_user, Some(channels.clone()))
.await
.expect("Failed to create second member");
let mut second_member = Reference::from_unchecked(second_user.id.clone())
let mut second_member = Reference::from_unchecked(&second_user.id)
.as_member(&harness.db, &server.id)
.await
.expect("Failed to get second member");

View File

@@ -14,8 +14,8 @@ pub async fn message_unpin(
db: &State<Database>,
amqp: &State<AMQP>,
user: User,
target: Reference,
msg: Reference,
target: Reference<'_>,
msg: Reference<'_>,
) -> Result<EmptyResponse> {
let channel = target.as_channel(db).await?;
@@ -91,7 +91,7 @@ mod test {
Member::create(&harness.db, &server, &user, Some(channels.clone()))
.await
.expect("Failed to create member");
let member = Reference::from_unchecked(user.id.clone())
let member = Reference::from_unchecked(&user.id)
.as_member(&harness.db, &server.id)
.await
.expect("Failed to get member");
@@ -174,7 +174,7 @@ mod test {
})
.await;
let updated_message = Reference::from_unchecked(message.id)
let updated_message = Reference::from_unchecked(&message.id)
.as_message(&harness.db)
.await
.expect("Failed to find updated message");

View File

@@ -18,9 +18,9 @@ use rocket_empty::EmptyResponse;
pub async fn unreact_message(
db: &State<Database>,
user: User,
target: Reference,
msg: Reference,
emoji: Reference,
target: Reference<'_>,
msg: Reference<'_>,
emoji: Reference<'_>,
options: v0::OptionsUnreact,
) -> Result<EmptyResponse> {
let channel = target.as_channel(db).await?;
@@ -41,14 +41,14 @@ pub async fn unreact_message(
// Check if we should wipe all of this reaction
if remove_all {
return message
.clear_reaction(db, &emoji.id)
.clear_reaction(db, emoji.id)
.await
.map(|_| EmptyResponse);
}
// Remove the reaction
message
.remove_reaction(db, options.user_id.as_ref().unwrap_or(&user.id), &emoji.id)
.remove_reaction(db, options.user_id.as_ref().unwrap_or(&user.id), emoji.id)
.await
.map(|_| EmptyResponse)
}

View File

@@ -50,7 +50,7 @@ pub fn routes() -> (Vec<Route>, OpenApi) {
group_remove_member::remove_member,
voice_join::call,
permissions_set::set_role_permissions,
permissions_set_default::set_default_permissions,
permissions_set_default::set_default_channel_permissions,
message_react::react_message,
message_unreact::unreact_message,
message_clear_reactions::clear_reactions,

View File

@@ -17,7 +17,7 @@ pub async fn set_role_permissions(
db: &State<Database>,
voice_client: &State<VoiceClient>,
user: User,
target: Reference,
target: Reference<'_>,
role_id: String,
data: Json<v0::DataSetRolePermissions>,
) -> Result<Json<v0::Channel>> {

View File

@@ -13,11 +13,11 @@ use rocket::{serde::json::Json, State};
/// Channel must be a `Group`, `TextChannel` or `VoiceChannel`.
#[openapi(tag = "Channel Permissions")]
#[put("/<target>/permissions/default", data = "<data>", rank = 1)]
pub async fn set_default_permissions(
pub async fn set_default_channel_permissions(
db: &State<Database>,
voice_client: &State<VoiceClient>,
user: User,
target: Reference,
target: Reference<'_>,
data: Json<v0::DataDefaultChannelPermissions>,
) -> Result<Json<v0::Channel>> {
let data = data.into_inner();
@@ -72,7 +72,7 @@ pub async fn set_default_permissions(
}
let server = match channel.server() {
Some(server_id) => Some(Reference::from_unchecked(server_id.to_string()).as_server(db).await?),
Some(server_id) => Some(Reference::from_unchecked(server_id).as_server(db).await?),
None => None
};

View File

@@ -22,7 +22,7 @@ pub async fn call(
db: &State<Database>,
voice_client: &State<VoiceClient>,
user: User,
target: Reference,
target: Reference<'_>,
data: Json<v0::DataJoinCall>,
) -> Result<Json<v0::CreateVoiceUserResponse>> {
if !voice_client.is_enabled() {
@@ -79,7 +79,7 @@ pub async fn call(
for channel_id in get_user_voice_channels(&user.id).await? {
let node = get_channel_node(&channel_id).await?.unwrap();
let channel = Reference::from_unchecked(channel_id.clone())
let channel = Reference::from_unchecked(&channel_id)
.as_channel(db)
.await?;

View File

@@ -19,7 +19,7 @@ use validator::Validate;
pub async fn create_webhook(
db: &State<Database>,
user: User,
target: Reference,
target: Reference<'_>,
data: Json<v0::CreateWebhookBody>,
) -> Result<Json<v0::Webhook>> {
let data = data.into_inner();

View File

@@ -15,7 +15,7 @@ use rocket::{serde::json::Json, State};
pub async fn fetch_webhooks(
db: &State<Database>,
user: User,
channel_id: Reference,
channel_id: Reference<'_>,
) -> Result<Json<Vec<Webhook>>> {
let channel = channel_id.as_channel(db).await?;

View File

@@ -16,7 +16,7 @@ use rocket_empty::EmptyResponse;
pub async fn delete_emoji(
db: &State<Database>,
user: User,
emoji_id: Reference,
emoji_id: Reference<'_>,
) -> Result<EmptyResponse> {
// Fetch the emoji
let emoji = emoji_id.as_emoji(db).await?;
@@ -26,7 +26,7 @@ pub async fn delete_emoji(
// Otherwise, validate we have permission to delete from parent
match &emoji.parent {
EmojiParent::Server { id } => {
let server = db.fetch_server(id).await?;
let server = db.fetch_server(id.as_str()).await?;
// Check for permission
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);

View File

@@ -9,7 +9,7 @@ use rocket::{serde::json::Json, State};
/// Fetch an emoji by its id.
#[openapi(tag = "Emojis")]
#[get("/emoji/<emoji_id>")]
pub async fn fetch_emoji(db: &State<Database>, emoji_id: Reference) -> Result<Json<v0::Emoji>> {
pub async fn fetch_emoji(db: &State<Database>, emoji_id: Reference<'_>) -> Result<Json<v0::Emoji>> {
emoji_id
.as_emoji(db)
.await

View File

@@ -12,7 +12,7 @@ use rocket_empty::EmptyResponse;
/// Delete an invite by its id.
#[openapi(tag = "Invites")]
#[delete("/<target>")]
pub async fn delete(db: &State<Database>, user: User, target: Reference) -> Result<EmptyResponse> {
pub async fn delete(db: &State<Database>, user: User, target: Reference<'_>) -> Result<EmptyResponse> {
let invite = target.as_invite(db).await?;
if user.id == invite.creator() {

View File

@@ -8,7 +8,7 @@ use rocket::{serde::json::Json, State};
/// Fetch an invite by its id.
#[openapi(tag = "Invites")]
#[get("/<target>")]
pub async fn fetch(db: &State<Database>, target: Reference) -> Result<Json<v0::InviteResponse>> {
pub async fn fetch(db: &State<Database>, target: Reference<'_>) -> Result<Json<v0::InviteResponse>> {
Ok(Json(match target.as_invite(db).await? {
Invite::Server {
channel, creator, ..
@@ -27,7 +27,7 @@ pub async fn fetch(db: &State<Database>, target: Reference) -> Result<Json<v0::I
let server = db.fetch_server(&server).await?;
v0::InviteResponse::Server {
code: target.id,
code: target.id.to_string(),
member_count: db.fetch_member_count(&server.id).await? as i64,
server_id: server.id,
server_name: server.name,
@@ -57,7 +57,7 @@ pub async fn fetch(db: &State<Database>, target: Reference) -> Result<Json<v0::I
description,
..
} => v0::InviteResponse::Group {
code: target.id,
code: target.id.to_string(),
channel_id: id,
channel_name: name,
channel_description: description,

View File

@@ -12,7 +12,7 @@ pub async fn join(
db: &State<Database>,
amqp: &State<AMQP>,
user: User,
target: Reference,
target: Reference<'_>,
) -> Result<Json<v0::InviteJoinResponse>> {
if user.bot.is_some() {
return Err(create_error!(IsBot));

View File

@@ -19,8 +19,8 @@ pub async fn ban(
db: &State<Database>,
voice_client: &State<VoiceClient>,
user: User,
server: Reference,
target: Reference,
server: Reference<'_>,
target: Reference<'_>,
data: Json<v0::DataBanCreate>,
) -> Result<Json<v0::ServerBan>> {
let data = data.into_inner();
@@ -65,7 +65,7 @@ pub async fn ban(
}
}
ServerBan::create(db, &server, &target.id, data.reason)
ServerBan::create(db, &server, target.id, data.reason)
.await
.map(Into::into)
.map(Json)

View File

@@ -17,7 +17,7 @@ use rocket::State;
pub async fn list(
db: &State<Database>,
user: User,
target: Reference,
target: Reference<'_>,
) -> Result<Json<v0::BanListResult>> {
let server = target.as_server(db).await?;
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);

View File

@@ -15,8 +15,8 @@ use rocket_empty::EmptyResponse;
pub async fn unban(
db: &State<Database>,
user: User,
server: Reference,
target: Reference,
server: Reference<'_>,
target: Reference<'_>,
) -> Result<EmptyResponse> {
let server = server.as_server(db).await?;
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);

View File

@@ -16,7 +16,7 @@ use validator::Validate;
pub async fn create_server_channel(
db: &State<Database>,
user: User,
server: Reference,
server: Reference<'_>,
data: Json<v0::DataCreateServerChannel>,
) -> Result<Json<v0::Channel>> {
let data = data.into_inner();

View File

@@ -15,7 +15,7 @@ use rocket::{serde::json::Json, State};
pub async fn list_emoji(
db: &State<Database>,
user: User,
target: Reference,
target: Reference<'_>,
) -> Result<Json<Vec<v0::Emoji>>> {
let server = target.as_server(db).await?;
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);

View File

@@ -15,7 +15,7 @@ use rocket::{serde::json::Json, State};
pub async fn invites(
db: &State<Database>,
user: User,
target: Reference,
target: Reference<'_>,
) -> Result<Json<Vec<v0::Invite>>> {
let server = target.as_server(db).await?;
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);

View File

@@ -31,8 +31,8 @@ pub async fn edit(
db: &State<Database>,
voice_client: &State<VoiceClient>,
user: User,
server: Reference,
member: Reference,
server: Reference<'_>,
member: Reference<'_>,
data: Json<v0::DataMemberEdit>,
) -> Result<Json<v0::Member>> {
let data = data.into_inner();
@@ -52,13 +52,7 @@ pub async fn edit(
let permissions = calculate_server_permissions(&mut query).await;
// Check permissions in server
if data.nickname.is_some()
|| data
.remove
.as_ref()
.map(|x| x.contains(&v0::FieldsMember::Nickname))
.unwrap_or_default()
{
if data.nickname.is_some() || data.remove.contains(&v0::FieldsMember::Nickname) {
if user.id == member.id.user {
permissions.throw_if_lacking_channel_permission(ChannelPermission::ChangeNickname)?;
} else {
@@ -66,13 +60,7 @@ pub async fn edit(
}
}
if data.avatar.is_some()
|| data
.remove
.as_ref()
.map(|x| x.contains(&v0::FieldsMember::Avatar))
.unwrap_or_default()
{
if data.avatar.is_some() || data.remove.contains(&v0::FieldsMember::Avatar) {
if user.id == member.id.user {
permissions.throw_if_lacking_channel_permission(ChannelPermission::ChangeAvatar)?;
} else {
@@ -80,23 +68,11 @@ pub async fn edit(
}
}
if data.roles.is_some()
|| data
.remove
.as_ref()
.map(|x| x.contains(&v0::FieldsMember::Roles))
.unwrap_or_default()
{
if data.roles.is_some() || data.remove.contains(&v0::FieldsMember::Roles) {
permissions.throw_if_lacking_channel_permission(ChannelPermission::AssignRoles)?;
}
if data.timeout.is_some()
|| data
.remove
.as_ref()
.map(|x| x.contains(&v0::FieldsMember::Timeout))
.unwrap_or_default()
{
if data.timeout.is_some() || data.remove.contains(&v0::FieldsMember::Timeout) {
if data.timeout.is_some() && member.id.user == user.id {
return Err(create_error!(CannotTimeoutYourself));
}
@@ -121,7 +97,7 @@ pub async fn edit(
// ensure the channel we are moving them to is in the server and is a voice channel
let channel = Reference::from_unchecked(new_channel.clone())
let channel = Reference::from_unchecked(new_channel)
.as_channel(db)
.await
.map_err(|_| create_error!(UnknownChannel))?;
@@ -192,11 +168,9 @@ pub async fn edit(
};
// 1. Remove fields from object
if let Some(fields) = &remove {
if fields.contains(&v0::FieldsMember::Avatar) {
if let Some(avatar) = &member.avatar {
db.mark_attachment_as_deleted(&avatar.id).await?;
}
if remove.contains(&v0::FieldsMember::Avatar) {
if let Some(avatar) = &member.avatar {
db.mark_attachment_as_deleted(&avatar.id).await?;
}
}
@@ -205,19 +179,10 @@ pub async fn edit(
partial.avatar = Some(File::use_user_avatar(db, &avatar, &user.id, &user.id).await?);
}
let remove_contains_voice = remove
.as_ref()
.map(|r| r.contains(FieldsMember::CanPublish) || r.contains(FieldsMember::CanReceive))
.unwrap_or_default();
let remove_contains_voice = remove.contains(FieldsMember::CanPublish) || remove.contains(FieldsMember::CanReceive);
member
.update(
db,
partial,
remove
.map(|v| v.into_iter().map(Into::into).collect())
.unwrap_or_default(),
)
.update(db, partial, remove.into_iter().map(Into::into).collect())
.await?;
if let Some(new_voice_channel) = new_voice_channel {
@@ -261,7 +226,7 @@ pub async fn edit(
if let Some(channel) = get_user_voice_channel_in_server(&target_user.id, &server.id).await?
{
let node = get_channel_node(&channel).await?.unwrap();
let channel = Reference::from_unchecked(channel).as_channel(db).await?;
let channel = Reference::from_unchecked(&channel).as_channel(db).await?;
sync_user_voice_permissions(
db,

View File

@@ -36,7 +36,7 @@ pub struct MemberQueryResponse {
pub async fn member_experimental_query(
db: &State<Database>,
user: User,
target: Reference,
target: Reference<'_>,
options: OptionsQueryMembers,
) -> Result<Json<MemberQueryResponse>> {
if !options.experimental_api {

View File

@@ -15,8 +15,8 @@ use rocket::{serde::json::Json, State};
pub async fn fetch(
db: &State<Database>,
user: User,
target: Reference,
member: Reference,
target: Reference<'_>,
member: Reference<'_>,
roles: Option<bool>,
) -> Result<Json<v0::MemberResponse>> {
let server = target.as_server(db).await?;

View File

@@ -15,7 +15,7 @@ use rocket::{serde::json::Json, State};
pub async fn fetch_all(
db: &State<Database>,
user: User,
target: Reference,
target: Reference<'_>,
options: v0::OptionsFetchAllMembers,
) -> Result<Json<v0::AllMemberResponse>> {
let server = target.as_server(db).await?;

View File

@@ -17,8 +17,8 @@ pub async fn kick(
db: &State<Database>,
voice_client: &State<VoiceClient>,
user: User,
target: Reference,
member: Reference,
target: Reference<'_>,
member: Reference<'_>,
) -> Result<EmptyResponse> {
let server = target.as_server(db).await?;

View File

@@ -17,8 +17,8 @@ mod permissions_set_default;
mod roles_create;
mod roles_delete;
mod roles_edit;
mod roles_fetch;
mod roles_edit_positions;
mod roles_fetch;
mod server_ack;
mod server_create;
mod server_delete;
@@ -47,7 +47,7 @@ pub fn routes() -> (Vec<Route>, OpenApi) {
roles_fetch::fetch,
roles_delete::delete,
permissions_set::set_role_permission,
permissions_set_default::set_default_permissions,
permissions_set_default::set_default_server_permissions,
emoji_list::list_emoji,
roles_edit_positions::edit_role_ranks
]

View File

@@ -15,7 +15,7 @@ pub async fn set_role_permission(
db: &State<Database>,
voice_client: &State<VoiceClient>,
user: User,
target: Reference,
target: Reference<'_>,
role_id: String,
data: Json<v0::DataSetServerRolePermission>,
) -> Result<Json<v0::Server>> {
@@ -41,7 +41,7 @@ pub async fn set_role_permission(
.await?;
for channel_id in &server.channels {
let channel = Reference::from_unchecked(channel_id.clone()).as_channel(db).await?;
let channel = Reference::from_unchecked(channel_id).as_channel(db).await?;
sync_voice_permissions(db, voice_client, &channel, Some(&server), Some(&role_id)).await?;
};

View File

@@ -13,11 +13,11 @@ use rocket::{serde::json::Json, State};
/// Sets permissions for the default role in this server.
#[openapi(tag = "Server Permissions")]
#[put("/<target>/permissions/default", data = "<data>", rank = 1)]
pub async fn set_default_permissions(
pub async fn set_default_server_permissions(
db: &State<Database>,
voice_client: &State<VoiceClient>,
user: User,
target: Reference,
target: Reference<'_>,
data: Json<DataPermissionsValue>,
) -> Result<Json<v0::Server>> {
let data = data.into_inner();
@@ -40,7 +40,7 @@ pub async fn set_default_permissions(
.await?;
for channel_id in &server.channels {
let channel = Reference::from_unchecked(channel_id.clone()).as_channel(db).await?;
let channel = Reference::from_unchecked(channel_id).as_channel(db).await?;
sync_voice_permissions(db, voice_client, &channel, Some(&server), None).await?;
};

View File

@@ -17,7 +17,7 @@ use validator::Validate;
pub async fn create(
db: &State<Database>,
user: User,
target: Reference,
target: Reference<'_>,
data: Json<v0::DataCreateRole>,
) -> Result<Json<v0::NewRoleResponse>> {
let data = data.into_inner();
@@ -40,20 +40,10 @@ pub async fn create(
}));
};
let member_rank = query.get_member_rank();
let rank = if let Some(given_rank) = data.rank {
if given_rank <= member_rank.unwrap_or(i64::MIN) {
return Err(create_error!(NotElevated));
}
given_rank
} else {
member_rank.unwrap_or(0).saturating_add(1)
};
let role = Role {
name: data.name,
rank,
// Rank of the new role should be below the lowest role
rank: server.roles.len() as i64,
colour: None,
hoist: false,
permissions: Default::default(),

View File

@@ -16,7 +16,7 @@ use rocket_empty::EmptyResponse;
pub async fn delete(
db: &State<Database>,
user: User,
target: Reference,
target: Reference<'_>,
role_id: String,
voice_client: &State<VoiceClient>
) -> Result<EmptyResponse> {
@@ -34,7 +34,7 @@ pub async fn delete(
}
for channel_id in &server.channels {
let channel = Reference::from_unchecked(channel_id.clone()).as_channel(db).await?;
let channel = Reference::from_unchecked(channel_id).as_channel(db).await?;
sync_voice_permissions(db, voice_client, &channel, Some(&server), Some(&role_id)).await?;
};

View File

@@ -18,7 +18,7 @@ pub async fn edit(
db: &State<Database>,
voice_client: &State<VoiceClient>,
user: User,
target: Reference,
target: Reference<'_>,
role_id: String,
data: Json<v0::DataEditRole>,
) -> Result<Json<v0::Role>> {
@@ -63,14 +63,12 @@ pub async fn edit(
&server.id,
&role_id,
partial,
remove
.map(|v| v.into_iter().map(Into::into).collect())
.unwrap_or_default(),
remove.into_iter().map(Into::into).collect(),
)
.await?;
for channel_id in &server.channels {
let channel = Reference::from_unchecked(channel_id.clone()).as_channel(db).await?;
let channel = Reference::from_unchecked(channel_id).as_channel(db).await?;
sync_voice_permissions(db, voice_client, &channel, Some(&server), Some(&role_id)).await?;
};

View File

@@ -15,7 +15,7 @@ use rocket::{serde::json::Json, State};
pub async fn edit_role_ranks(
db: &State<Database>,
user: User,
target: Reference,
target: Reference<'_>,
data: Json<v0::DataEditRoleRanks>,
) -> Result<Json<v0::Server>> {
let data = data.into_inner();

View File

@@ -15,7 +15,7 @@ use rocket::{serde::json::Json, State};
pub async fn fetch(
db: &State<Database>,
user: User,
target: Reference,
target: Reference<'_>,
role_id: String,
) -> Result<Json<v0::Role>> {
let mut server = target.as_server(db).await?;

View File

@@ -12,7 +12,7 @@ use rocket_empty::EmptyResponse;
/// Mark all channels in a server as read.
#[openapi(tag = "Server Information")]
#[put("/<target>/ack")]
pub async fn ack(db: &State<Database>, user: User, target: Reference) -> Result<EmptyResponse> {
pub async fn ack(db: &State<Database>, user: User, target: Reference<'_>) -> Result<EmptyResponse> {
if user.bot.is_some() {
return Err(create_error!(IsBot));
}

View File

@@ -18,11 +18,11 @@ pub async fn delete(
db: &State<Database>,
voice_client: &State<VoiceClient>,
user: User,
target: Reference,
target: Reference<'_>,
options: v0::OptionsServerDelete,
) -> Result<EmptyResponse> {
let server = target.as_server(db).await?;
let member = db.fetch_member(&target.id, &user.id).await?;
let member = db.fetch_member(target.id, &user.id).await?;
if server.owner == user.id {
for channel_id in &server.channels {

View File

@@ -18,7 +18,7 @@ use validator::Validate;
pub async fn edit(
db: &State<Database>,
user: User,
target: Reference,
target: Reference<'_>,
data: Json<v0::DataEditServer>,
) -> Result<Json<v0::Server>> {
let data = data.into_inner();
@@ -43,7 +43,7 @@ pub async fn edit(
&& data.flags.is_none()
&& data.analytics.is_none()
&& data.discoverable.is_none()
&& data.remove.is_none()
&& data.remove.is_empty()
{
return Ok(Json(server.into()));
} else if data.name.is_some()
@@ -52,7 +52,7 @@ pub async fn edit(
|| data.banner.is_some()
|| data.system_messages.is_some()
|| data.analytics.is_some()
|| data.remove.is_some()
|| !data.remove.is_empty()
{
permissions.throw_if_lacking_channel_permission(ChannelPermission::ManageServer)?;
}
@@ -96,17 +96,15 @@ pub async fn edit(
};
// 1. Remove fields from object
if let Some(fields) = &remove {
if fields.contains(&v0::FieldsServer::Banner) {
if let Some(banner) = &server.banner {
db.mark_attachment_as_deleted(&banner.id).await?;
}
if remove.contains(&v0::FieldsServer::Banner) {
if let Some(banner) = &server.banner {
db.mark_attachment_as_deleted(&banner.id).await?;
}
}
if fields.contains(&v0::FieldsServer::Icon) {
if let Some(icon) = &server.icon {
db.mark_attachment_as_deleted(&icon.id).await?;
}
if remove.contains(&v0::FieldsServer::Icon) {
if let Some(icon) = &server.icon {
db.mark_attachment_as_deleted(&icon.id).await?;
}
}
@@ -149,13 +147,7 @@ pub async fn edit(
}
server
.update(
db,
partial,
remove
.map(|v| v.into_iter().map(Into::into).collect())
.unwrap_or_default(),
)
.update(db, partial, remove.into_iter().map(Into::into).collect())
.await?;
Ok(Json(server.into()))

View File

@@ -15,7 +15,7 @@ use rocket::{serde::json::Json, State};
pub async fn fetch(
db: &State<Database>,
user: User,
target: Reference,
target: Reference<'_>,
options: v0::OptionsFetchServer,
) -> Result<Json<v0::FetchServerResponse>> {
let server = target.as_server(db).await?;

View File

@@ -14,7 +14,7 @@ pub async fn add(
db: &State<Database>,
amqp: &State<AMQP>,
mut user: User,
target: Reference,
target: Reference<'_>,
) -> Result<Json<v0::User>> {
let mut target = target.as_user(db).await?;

View File

@@ -13,7 +13,7 @@ use rocket::State;
pub async fn block(
db: &State<Database>,
mut user: User,
target: Reference,
target: Reference<'_>,
) -> Result<Json<v0::User>> {
let mut target = target.as_user(db).await?;

View File

@@ -14,7 +14,7 @@ use validator::Validate;
pub async fn edit(
db: &State<Database>,
mut user: User,
target: Reference,
target: Reference<'_>,
data: Json<v0::DataEditUser>,
) -> Result<Json<v0::User>> {
let data = data.into_inner();
@@ -53,31 +53,29 @@ pub async fn edit(
&& data.avatar.is_none()
&& data.badges.is_none()
&& data.flags.is_none()
&& data.remove.is_none()
&& data.remove.is_empty()
{
return Ok(Json(user.into_self(false).await));
}
// 1. Remove fields from object
if let Some(fields) = &data.remove {
if fields.contains(&v0::FieldsUser::Avatar) {
if let Some(avatar) = &user.avatar {
db.mark_attachment_as_deleted(&avatar.id).await?;
if data.remove.contains(&v0::FieldsUser::Avatar) {
if let Some(avatar) = &user.avatar {
db.mark_attachment_as_deleted(&avatar.id).await?;
}
}
if data.remove.contains(&v0::FieldsUser::ProfileBackground) {
if let Some(profile) = &user.profile {
if let Some(background) = &profile.background {
db.mark_attachment_as_deleted(&background.id).await?;
}
}
}
if fields.contains(&v0::FieldsUser::ProfileBackground) {
if let Some(profile) = &user.profile {
if let Some(background) = &profile.background {
db.mark_attachment_as_deleted(&background.id).await?;
}
}
}
for field in fields {
let field: FieldsUser = field.clone().into();
user.remove_field(&field);
}
for field in &data.remove {
let field: FieldsUser = field.clone().into();
user.remove_field(&field);
}
let mut partial: PartialUser = PartialUser {
@@ -124,9 +122,7 @@ pub async fn edit(
user.update(
db,
partial,
data.remove
.map(|v| v.into_iter().map(Into::into).collect())
.unwrap_or_default(),
data.remove.into_iter().map(Into::into).collect(),
)
.await?;

View File

@@ -17,7 +17,7 @@ use rocket::{serde::json::Json, State};
pub async fn profile(
db: &State<Database>,
user: User,
target: Reference,
target: Reference<'_>,
) -> Result<Json<v0::UserProfile>> {
if user.id == target.id {
return Ok(Json(user.profile.map(Into::into).unwrap_or_default()));

View File

@@ -13,7 +13,7 @@ use rocket::{serde::json::Json, State};
/// Retrieve a user's information.
#[openapi(tag = "User Information")]
#[get("/<target>")]
pub async fn fetch(db: &State<Database>, user: User, target: Reference) -> Result<Json<v0::User>> {
pub async fn fetch(db: &State<Database>, user: User, target: Reference<'_>) -> Result<Json<v0::User>> {
if user.id == target.id {
return Ok(Json(user.into_self(false).await));
}

View File

@@ -10,7 +10,7 @@ use rocket::{serde::json::Json, State};
#[get("/<target>/flags")]
pub async fn fetch_user_flags(
db: &State<Database>,
target: Reference,
target: Reference<'_>,
) -> Result<Json<v0::FlagResponse>> {
let flags = if let Ok(target) = target.as_user(db).await {
target.flags.unwrap_or_default()

View File

@@ -16,7 +16,7 @@ use rocket::State;
pub async fn mutual(
db: &State<Database>,
user: User,
target: Reference,
target: Reference<'_>,
) -> Result<Json<v0::MutualResponse>> {
if target.id == user.id {
return Err(create_error!(InvalidOperation));

View File

@@ -17,7 +17,7 @@ use rocket::{serde::json::Json, State};
pub async fn open_dm(
db: &State<Database>,
user: User,
target: Reference,
target: Reference<'_>,
) -> Result<Json<v0::Channel>> {
let target = target.as_user(db).await?;

View File

@@ -13,7 +13,7 @@ use rocket::State;
pub async fn remove(
db: &State<Database>,
mut user: User,
target: Reference,
target: Reference<'_>,
) -> Result<Json<v0::User>> {
let mut target = target.as_user(db).await?;

View File

@@ -13,7 +13,7 @@ use rocket::State;
pub async fn unblock(
db: &State<Database>,
mut user: User,
target: Reference,
target: Reference<'_>,
) -> Result<Json<v0::User>> {
let mut target = target.as_user(db).await?;

View File

@@ -15,7 +15,7 @@ use rocket_empty::EmptyResponse;
pub async fn webhook_delete(
db: &State<Database>,
user: User,
webhook_id: Reference,
webhook_id: Reference<'_>,
) -> Result<EmptyResponse> {
let webhook = webhook_id.as_webhook(db).await?;
let channel = db.fetch_channel(&webhook.channel_id).await?;

View File

@@ -10,7 +10,7 @@ use rocket_empty::EmptyResponse;
#[delete("/<webhook_id>/<token>")]
pub async fn webhook_delete_token(
db: &State<Database>,
webhook_id: Reference,
webhook_id: Reference<'_>,
token: String,
) -> Result<EmptyResponse> {
let webhook = webhook_id.as_webhook(db).await?;

View File

@@ -15,7 +15,7 @@ use validator::Validate;
#[patch("/<webhook_id>", data = "<data>")]
pub async fn webhook_edit(
db: &State<Database>,
webhook_id: Reference,
webhook_id: Reference<'_>,
user: User,
data: Json<DataEditWebhook>,
) -> Result<Json<Webhook>> {

View File

@@ -12,7 +12,7 @@ use rocket::{serde::json::Json, State};
#[patch("/<webhook_id>/<token>", data = "<data>")]
pub async fn webhook_edit_token(
db: &State<Database>,
webhook_id: Reference,
webhook_id: Reference<'_>,
token: String,
data: Json<DataEditWebhook>,
) -> Result<Json<Webhook>> {

View File

@@ -18,7 +18,7 @@ use validator::Validate;
pub async fn webhook_execute(
db: &State<Database>,
amqp: &State<AMQP>,
webhook_id: Reference,
webhook_id: Reference<'_>,
token: String,
data: Json<v0::DataMessageSend>,
idempotency: IdempotencyKey,

View File

@@ -752,7 +752,7 @@ fn convert_event(data: &str, event_name: &str) -> Result<Event> {
pub async fn webhook_execute_github(
db: &State<Database>,
amqp: &State<AMQP>,
webhook_id: Reference,
webhook_id: Reference<'_>,
token: String,
event: EventHeader<'_>,
data: String,

View File

@@ -14,7 +14,7 @@ use rocket::{serde::json::Json, State};
#[get("/<webhook_id>")]
pub async fn webhook_fetch(
db: &State<Database>,
webhook_id: Reference,
webhook_id: Reference<'_>,
user: User,
) -> Result<Json<ResponseWebhook>> {
let webhook = webhook_id.as_webhook(db).await?;

View File

@@ -10,7 +10,7 @@ use rocket::{serde::json::Json, State};
#[get("/<webhook_id>/<token>")]
pub async fn webhook_fetch_token(
db: &State<Database>,
webhook_id: Reference,
webhook_id: Reference<'_>,
token: String,
) -> Result<Json<Webhook>> {
let webhook = webhook_id.as_webhook(db).await?;