refactor: capture errors with line numbers

refactor: update file api
This commit is contained in:
Paul Makles
2024-09-29 14:37:08 +01:00
parent 4fd66b2719
commit 3080ec1f5a
22 changed files with 273 additions and 130 deletions

View File

@@ -5,7 +5,7 @@ use crate::{
bson::{doc, from_bson, from_document, to_document, Bson, DateTime, Document},
options::FindOptions,
},
Invite, MongoDb, DISCRIMINATOR_SEARCH_SPACE,
AbstractChannels, AbstractServers, Channel, Invite, MongoDb, DISCRIMINATOR_SEARCH_SPACE,
};
use bson::oid::ObjectId;
use futures::StreamExt;
@@ -20,7 +20,7 @@ struct MigrationInfo {
revision: i32,
}
pub const LATEST_REVISION: i32 = 29;
pub const LATEST_REVISION: i32 = 30;
pub async fn migrate_database(db: &MongoDb) {
let migrations = db.col::<Document>("migrations");
@@ -1139,6 +1139,54 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
.expect("Failed to create attachment_hashes index.");
}
if revision <= 29 {
info!("Running migration [revision 29 / 29-09-2024]: Add creator_id to webhooks.");
#[derive(serde::Serialize, serde::Deserialize)]
struct WebhookShell {
_id: String,
channel_id: String,
}
let invites = db
.db()
.collection::<WebhookShell>("channel_webhooks")
.find(doc! {}, None)
.await
.expect("webhooks")
.filter_map(|s| async { s.ok() })
.collect::<Vec<WebhookShell>>()
.await;
for invite in invites {
let channel = db.fetch_channel(&invite.channel_id).await.expect("channel");
let creator_id = match channel {
Channel::Group { owner, .. } => owner,
Channel::TextChannel { server, .. } | Channel::VoiceChannel { server, .. } => {
let server = db.fetch_server(&server).await.expect("server");
server.owner
}
_ => unreachable!("not server or group channel!"),
};
db.db()
.collection::<Document>("channel_webhooks")
.update_one(
doc! {
"_id": invite._id,
},
doc! {
"$set" : {
"creator_id": creator_id
}
},
None,
)
.await
.expect("update webhook");
}
}
// Need to migrate fields on attachments, change `user_id`, `object_id`, etc to `parent`.
// Reminder to update LATEST_REVISION when adding new migrations.

View File

@@ -17,6 +17,9 @@ auto_derived_partial!(
#[serde(skip_serializing_if = "Option::is_none")]
pub avatar: Option<File>,
/// User that created this webhook
pub creator_id: String,
/// The channel this webhook belongs to
pub channel_id: String,
@@ -43,6 +46,7 @@ impl Default for Webhook {
id: Default::default(),
name: Default::default(),
avatar: None,
creator_id: Default::default(),
channel_id: Default::default(),
permissions: Default::default(),
token: Default::default(),

View File

@@ -19,9 +19,11 @@ auto_derived_partial!(
/// When this file was uploaded
pub uploaded_at: Option<Timestamp>, // these are Option<>s to not break file uploads on legacy Autumn
/// ID of user who uploaded this file
#[serde(skip_serializing_if = "Option::is_none")]
pub uploader_id: Option<String>, // these are Option<>s to not break file uploads on legacy Autumn
/// What the file was used for
#[serde(skip_serializing_if = "Option::is_none")]
pub used_for: Option<FileUsedFor>,
/// Whether this file was deleted
@@ -63,6 +65,7 @@ auto_derived!(
ServerBanner,
Emoji,
UserAvatar,
WebhookAvatar,
UserProfileBackground,
LegacyGroupIcon,
ChannelIcon,
@@ -86,44 +89,154 @@ impl File {
}
/// Use a file for a message attachment
pub async fn use_attachment(db: &Database, id: &str, parent: &str) -> Result<File> {
db.find_and_use_attachment(id, "attachments", "message", parent)
.await
pub async fn use_attachment(
db: &Database,
id: &str,
parent: &str,
uploader_id: &str,
) -> Result<File> {
db.find_and_use_attachment(
id,
"attachments",
FileUsedFor {
id: parent.to_owned(),
object_type: FileUsedForType::Message,
},
uploader_id.to_owned(),
)
.await
}
/// Use a file for a user profile background
pub async fn use_background(db: &Database, id: &str, parent: &str) -> Result<File> {
db.find_and_use_attachment(id, "backgrounds", "user", parent)
.await
pub async fn use_background(
db: &Database,
id: &str,
parent: &str,
uploader_id: &str,
) -> Result<File> {
db.find_and_use_attachment(
id,
"backgrounds",
FileUsedFor {
id: parent.to_owned(),
object_type: FileUsedForType::UserProfileBackground,
},
uploader_id.to_owned(),
)
.await
}
/// Use a file for a user avatar
pub async fn use_avatar(db: &Database, id: &str, parent: &str) -> Result<File> {
db.find_and_use_attachment(id, "avatars", "user", parent)
.await
pub async fn use_user_avatar(
db: &Database,
id: &str,
parent: &str,
uploader_id: &str,
) -> Result<File> {
db.find_and_use_attachment(
id,
"avatars",
FileUsedFor {
id: parent.to_owned(),
object_type: FileUsedForType::UserAvatar,
},
uploader_id.to_owned(),
)
.await
}
/// Use a file for an icon
pub async fn use_icon(db: &Database, id: &str, parent: &str) -> Result<File> {
db.find_and_use_attachment(id, "icons", "object", parent)
.await
/// Use a file for a webhook avatar
pub async fn use_webhook_avatar(
db: &Database,
id: &str,
parent: &str,
uploader_id: &str,
) -> Result<File> {
db.find_and_use_attachment(
id,
"avatars",
FileUsedFor {
id: parent.to_owned(),
object_type: FileUsedForType::WebhookAvatar,
},
uploader_id.to_owned(),
)
.await
}
/// Use a file for a server icon
pub async fn use_server_icon(db: &Database, id: &str, parent: &str) -> Result<File> {
db.find_and_use_attachment(id, "icons", "object", parent)
.await
pub async fn use_server_icon(
db: &Database,
id: &str,
parent: &str,
uploader_id: &str,
) -> Result<File> {
db.find_and_use_attachment(
id,
"icons",
FileUsedFor {
id: parent.to_owned(),
object_type: FileUsedForType::ServerIcon,
},
uploader_id.to_owned(),
)
.await
}
/// Use a file for a channel icon
pub async fn use_channel_icon(
db: &Database,
id: &str,
parent: &str,
uploader_id: &str,
) -> Result<File> {
db.find_and_use_attachment(
id,
"icons",
FileUsedFor {
id: parent.to_owned(),
object_type: FileUsedForType::ChannelIcon,
},
uploader_id.to_owned(),
)
.await
}
/// Use a file for a server banner
pub async fn use_banner(db: &Database, id: &str, parent: &str) -> Result<File> {
db.find_and_use_attachment(id, "banners", "server", parent)
.await
pub async fn use_server_banner(
db: &Database,
id: &str,
parent: &str,
uploader_id: &str,
) -> Result<File> {
db.find_and_use_attachment(
id,
"banners",
FileUsedFor {
id: parent.to_owned(),
object_type: FileUsedForType::ServerBanner,
},
uploader_id.to_owned(),
)
.await
}
/// Use a file for an emoji
pub async fn use_emoji(db: &Database, id: &str, parent: &str) -> Result<File> {
db.find_and_use_attachment(id, "emojis", "object", parent)
.await
pub async fn use_emoji(
db: &Database,
id: &str,
parent: &str,
uploader_id: &str,
) -> Result<File> {
db.find_and_use_attachment(
id,
"emojis",
FileUsedFor {
id: parent.to_owned(),
object_type: FileUsedForType::Emoji,
},
uploader_id.to_owned(),
)
.await
}
}

View File

@@ -2,6 +2,8 @@ use revolt_result::Result;
use crate::File;
use super::FileUsedFor;
mod mongodb;
mod reference;
@@ -18,8 +20,8 @@ pub trait AbstractAttachments: Sync + Send {
&self,
id: &str,
tag: &str,
parent_type: &str,
parent_id: &str,
used_for: FileUsedFor,
uploader_id: String,
) -> Result<File>;
/// Mark an attachment as having been reported.

View File

@@ -1,7 +1,10 @@
use bson::to_document;
use bson::Document;
use revolt_config::report_internal_error;
use revolt_result::Result;
use crate::File;
use crate::FileUsedFor;
use crate::MongoDb;
use super::AbstractAttachments;
@@ -34,10 +37,9 @@ impl AbstractAttachments for MongoDb {
&self,
id: &str,
tag: &str,
parent_type: &str,
parent_id: &str,
used_for: FileUsedFor,
uploader_id: String,
) -> Result<File> {
let key = format!("{parent_type}_id");
let file = query!(
self,
find_one,
@@ -45,7 +47,7 @@ impl AbstractAttachments for MongoDb {
doc! {
"_id": id,
"tag": tag,
&key: {
"used_for": {
"$exists": false
}
}
@@ -59,7 +61,8 @@ impl AbstractAttachments for MongoDb {
},
doc! {
"$set": {
key: parent_id
"used_for": report_internal_error!(to_document(&used_for))?,
"uploader_id": uploader_id
}
},
None,

View File

@@ -1,6 +1,7 @@
use revolt_result::Result;
use crate::File;
use crate::FileUsedFor;
use crate::ReferenceDb;
use super::AbstractAttachments;
@@ -37,19 +38,14 @@ impl AbstractAttachments for ReferenceDb {
&self,
id: &str,
tag: &str,
parent_type: &str,
parent_id: &str,
used_for: FileUsedFor,
uploader_id: String,
) -> Result<File> {
let mut files = self.files.lock().await;
if let Some(file) = files.get_mut(id) {
if file.tag == tag {
match parent_type {
"message" => file.message_id = Some(parent_id.to_owned()),
"user" => file.user_id = Some(parent_id.to_owned()),
"object" => file.object_id = Some(parent_id.to_owned()),
"server" => file.server_id = Some(parent_id.to_owned()),
_ => unreachable!(),
}
file.uploader_id = Some(uploader_id);
file.used_for = Some(used_for);
Ok(file.clone())
} else {

View File

@@ -370,10 +370,8 @@ impl Message {
}
for attachment_id in data.attachments.as_deref().unwrap_or_default() {
attachments.push(
db.find_and_use_attachment(attachment_id, "attachments", "message", &message_id)
.await?,
);
attachments
.push(File::use_attachment(db, attachment_id, &message_id, author.id()).await?);
}
if !attachments.is_empty() {
@@ -499,10 +497,7 @@ impl Message {
})?;
let media = if let Some(id) = embed.media {
Some(
db.find_and_use_attachment(&id, "attachments", "message", &self.id)
.await?,
)
Some(File::use_attachment(db, &id, &self.id, &self.author).await?)
} else {
None
};
@@ -661,7 +656,7 @@ impl Message {
) -> Result<()> {
let media: Option<v0::File> = if let Some(id) = embed.media {
Some(
db.find_and_use_attachment(&id, "attachments", "message", &self.id)
File::use_attachment(db, &id, &self.id, &self.author)
.await?
.into(),
)

View File

@@ -20,6 +20,7 @@ use crate::Database;
/// Payload information, before assembly
#[derive(Debug)]
#[allow(non_snake_case)]
pub struct ApnPayload {
message: Message,
url: String,
@@ -29,6 +30,7 @@ pub struct ApnPayload {
}
#[derive(Serialize, Debug)]
#[allow(non_snake_case)]
struct Payload<'a> {
aps: APS<'a>,
#[serde(skip_serializing)]

View File

@@ -11,7 +11,7 @@ use base64::{
use deadqueue::limited::Queue;
use fcm_v1::auth::{Authenticator, ServiceAccountKey};
use once_cell::sync::Lazy;
use revolt_config::config;
use revolt_config::{config, report_internal_error};
use revolt_models::v0::PushNotification;
use revolt_presence::filter_online;
use serde_json::json;
@@ -116,12 +116,11 @@ pub async fn worker(db: Database, authifier_db: AuthifierDatabase) {
if fcm_error.contains("404 (Not Found)") {
println!("Unregistering {:?}", session.id);
if let Err(err) = db
.remove_push_subscription_by_session_id(&session.id)
.await
{
revolt_config::capture_error(&err);
}
report_internal_error!(
db.remove_push_subscription_by_session_id(&session.id)
.await
)
.ok();
}
}
} else {

View File

@@ -108,6 +108,7 @@ impl From<crate::Webhook> for Webhook {
id: value.id,
name: value.name,
avatar: value.avatar.map(|file| file.into()),
creator_id: value.creator_id,
channel_id: value.channel_id,
token: value.token,
permissions: value.permissions,
@@ -121,6 +122,7 @@ impl From<crate::PartialWebhook> for PartialWebhook {
id: value.id,
name: value.name,
avatar: value.avatar.map(|file| file.into()),
creator_id: value.creator_id,
channel_id: value.channel_id,
token: value.token,
permissions: value.permissions,