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

@@ -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?;