refactor: capture errors with line numbers
refactor: update file api
This commit is contained in:
@@ -14,6 +14,7 @@ use futures::{
|
|||||||
FutureExt, SinkExt, StreamExt, TryStreamExt,
|
FutureExt, SinkExt, StreamExt, TryStreamExt,
|
||||||
};
|
};
|
||||||
use redis_kiss::{PayloadType, REDIS_PAYLOAD_TYPE, REDIS_URI};
|
use redis_kiss::{PayloadType, REDIS_PAYLOAD_TYPE, REDIS_URI};
|
||||||
|
use revolt_config::report_internal_error;
|
||||||
use revolt_database::{
|
use revolt_database::{
|
||||||
events::{client::EventV1, server::ClientMessage},
|
events::{client::EventV1, server::ClientMessage},
|
||||||
Database, User, UserHint,
|
Database, User, UserHint,
|
||||||
@@ -99,27 +100,21 @@ pub async fn client(db: &'static Database, stream: TcpStream, addr: SocketAddr)
|
|||||||
let user_id = state.cache.user_id.clone();
|
let user_id = state.cache.user_id.clone();
|
||||||
|
|
||||||
// Notify socket we have authenticated.
|
// Notify socket we have authenticated.
|
||||||
if let Err(err) = write.send(config.encode(&EventV1::Authenticated)).await {
|
if report_internal_error!(write.send(config.encode(&EventV1::Authenticated)).await).is_err() {
|
||||||
error!("Failed to write: {err:?}");
|
|
||||||
sentry::capture_error(&err);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Download required data to local cache and send Ready payload.
|
// Download required data to local cache and send Ready payload.
|
||||||
let ready_payload = match state
|
let ready_payload = match report_internal_error!(
|
||||||
.generate_ready_payload(db, config.get_ready_payload_fields())
|
state
|
||||||
.await
|
.generate_ready_payload(db, config.get_ready_payload_fields())
|
||||||
{
|
.await
|
||||||
|
) {
|
||||||
Ok(ready_payload) => ready_payload,
|
Ok(ready_payload) => ready_payload,
|
||||||
Err(err) => {
|
Err(_) => return,
|
||||||
sentry::capture_error(&err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(err) = write.send(config.encode(&ready_payload)).await {
|
if report_internal_error!(write.send(config.encode(&ready_payload)).await).is_err() {
|
||||||
error!("Failed to write: {err:?}");
|
|
||||||
sentry::capture_error(&err);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,21 +209,16 @@ async fn listener(
|
|||||||
write: &Mutex<WsWriter>,
|
write: &Mutex<WsWriter>,
|
||||||
) {
|
) {
|
||||||
let redis_config = RedisConfig::from_url(&REDIS_URI).unwrap();
|
let redis_config = RedisConfig::from_url(&REDIS_URI).unwrap();
|
||||||
let subscriber = match fred::types::Builder::from_config(redis_config).build_subscriber_client()
|
let subscriber = match report_internal_error!(
|
||||||
{
|
fred::types::Builder::from_config(redis_config).build_subscriber_client()
|
||||||
|
) {
|
||||||
Ok(subscriber) => subscriber,
|
Ok(subscriber) => subscriber,
|
||||||
Err(err) => {
|
Err(_) => return,
|
||||||
error!("Failed to build a subscriber: {err:?}");
|
|
||||||
sentry::capture_error(&err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(err) = subscriber.init().await {
|
if report_internal_error!(subscriber.init().await).is_err() {
|
||||||
error!("Failed to init subscriber: {err:?}");
|
|
||||||
sentry::capture_error(&err);
|
|
||||||
return;
|
return;
|
||||||
};
|
}
|
||||||
|
|
||||||
// Handle Redis connection dropping
|
// Handle Redis connection dropping
|
||||||
let (clean_up_s, clean_up_r) = async_channel::bounded(1);
|
let (clean_up_s, clean_up_r) = async_channel::bounded(1);
|
||||||
@@ -249,17 +239,13 @@ async fn listener(
|
|||||||
// Check for state changes for subscriptions.
|
// Check for state changes for subscriptions.
|
||||||
match state.apply_state().await {
|
match state.apply_state().await {
|
||||||
SubscriptionStateChange::Reset => {
|
SubscriptionStateChange::Reset => {
|
||||||
if let Err(err) = subscriber.unsubscribe_all().await {
|
if report_internal_error!(subscriber.unsubscribe_all().await).is_err() {
|
||||||
error!("Unsubscribe all failed: {err:?}");
|
|
||||||
sentry::capture_error(&err);
|
|
||||||
break 'out;
|
break 'out;
|
||||||
}
|
}
|
||||||
|
|
||||||
let subscribed = state.subscribed.read().await;
|
let subscribed = state.subscribed.read().await;
|
||||||
for id in subscribed.iter() {
|
for id in subscribed.iter() {
|
||||||
if let Err(err) = subscriber.subscribe(id).await {
|
if report_internal_error!(subscriber.subscribe(id).await).is_err() {
|
||||||
error!("Subscribe failed: {err:?}");
|
|
||||||
sentry::capture_error(&err);
|
|
||||||
break 'out;
|
break 'out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -272,9 +258,7 @@ async fn listener(
|
|||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
info!("{addr:?} unsubscribing from {id}");
|
info!("{addr:?} unsubscribing from {id}");
|
||||||
|
|
||||||
if let Err(err) = subscriber.unsubscribe(id).await {
|
if report_internal_error!(subscriber.unsubscribe(id).await).is_err() {
|
||||||
error!("Unsubscribe failed: {err:?}");
|
|
||||||
sentry::capture_error(&err);
|
|
||||||
break 'out;
|
break 'out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -283,9 +267,7 @@ async fn listener(
|
|||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
info!("{addr:?} subscribing to {id}");
|
info!("{addr:?} subscribing to {id}");
|
||||||
|
|
||||||
if let Err(err) = subscriber.subscribe(id).await {
|
if report_internal_error!(subscriber.subscribe(id).await).is_err() {
|
||||||
error!("Subscribe failed: {err:?}");
|
|
||||||
sentry::capture_error(&err);
|
|
||||||
break 'out;
|
break 'out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -310,13 +292,9 @@ async fn listener(
|
|||||||
_ = t2 => {},
|
_ = t2 => {},
|
||||||
message = t1 => {
|
message = t1 => {
|
||||||
// Handle incoming events.
|
// Handle incoming events.
|
||||||
let message = match message {
|
let message = match report_internal_error!(message) {
|
||||||
Ok(message) => message,
|
Ok(message) => message,
|
||||||
Err(e) => {
|
Err(_) => break 'out
|
||||||
error!("Error while consuming pub/sub messages: {e:?}");
|
|
||||||
sentry::capture_error(&e);
|
|
||||||
break 'out;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let event = match *REDIS_PAYLOAD_TYPE {
|
let event = match *REDIS_PAYLOAD_TYPE {
|
||||||
@@ -393,10 +371,7 @@ async fn listener(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(err) = subscriber.quit().await {
|
report_internal_error!(subscriber.quit().await).ok();
|
||||||
error!("{}", err);
|
|
||||||
sentry::capture_error(&err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use futures_locks::RwLock;
|
|||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
pub use sentry::capture_error;
|
pub use sentry::{capture_error, capture_message, Level};
|
||||||
|
|
||||||
#[cfg(feature = "report-macros")]
|
#[cfg(feature = "report-macros")]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
@@ -14,7 +14,10 @@ macro_rules! report_error {
|
|||||||
( $expr: expr, $error: ident $( $tt:tt )? ) => {
|
( $expr: expr, $error: ident $( $tt:tt )? ) => {
|
||||||
$expr
|
$expr
|
||||||
.inspect_err(|err| {
|
.inspect_err(|err| {
|
||||||
$crate::capture_error(err);
|
$crate::capture_message(
|
||||||
|
&format!("{err:?} ({}:{}:{})", file!(), line!(), column!()),
|
||||||
|
$crate::Level::Error,
|
||||||
|
);
|
||||||
})
|
})
|
||||||
.map_err(|_| ::revolt_result::create_error!($error))
|
.map_err(|_| ::revolt_result::create_error!($error))
|
||||||
};
|
};
|
||||||
@@ -26,7 +29,10 @@ macro_rules! report_internal_error {
|
|||||||
( $expr: expr ) => {
|
( $expr: expr ) => {
|
||||||
$expr
|
$expr
|
||||||
.inspect_err(|err| {
|
.inspect_err(|err| {
|
||||||
$crate::capture_error(err);
|
$crate::capture_message(
|
||||||
|
&format!("{err:?} ({}:{}:{})", file!(), line!(), column!()),
|
||||||
|
$crate::Level::Error,
|
||||||
|
);
|
||||||
})
|
})
|
||||||
.map_err(|_| ::revolt_result::create_error!(InternalError))
|
.map_err(|_| ::revolt_result::create_error!(InternalError))
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ default = ["mongodb", "async-std-runtime", "tasks"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Core
|
# Core
|
||||||
revolt-config = { version = "0.7.16", path = "../config" }
|
revolt-config = { version = "0.7.16", path = "../config", features = [
|
||||||
|
"report-macros",
|
||||||
|
] }
|
||||||
revolt-result = { version = "0.7.16", path = "../result" }
|
revolt-result = { version = "0.7.16", path = "../result" }
|
||||||
revolt-models = { version = "0.7.16", path = "../models", features = [
|
revolt-models = { version = "0.7.16", path = "../models", features = [
|
||||||
"validator",
|
"validator",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::{
|
|||||||
bson::{doc, from_bson, from_document, to_document, Bson, DateTime, Document},
|
bson::{doc, from_bson, from_document, to_document, Bson, DateTime, Document},
|
||||||
options::FindOptions,
|
options::FindOptions,
|
||||||
},
|
},
|
||||||
Invite, MongoDb, DISCRIMINATOR_SEARCH_SPACE,
|
AbstractChannels, AbstractServers, Channel, Invite, MongoDb, DISCRIMINATOR_SEARCH_SPACE,
|
||||||
};
|
};
|
||||||
use bson::oid::ObjectId;
|
use bson::oid::ObjectId;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
@@ -20,7 +20,7 @@ struct MigrationInfo {
|
|||||||
revision: i32,
|
revision: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const LATEST_REVISION: i32 = 29;
|
pub const LATEST_REVISION: i32 = 30;
|
||||||
|
|
||||||
pub async fn migrate_database(db: &MongoDb) {
|
pub async fn migrate_database(db: &MongoDb) {
|
||||||
let migrations = db.col::<Document>("migrations");
|
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.");
|
.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`.
|
// Need to migrate fields on attachments, change `user_id`, `object_id`, etc to `parent`.
|
||||||
|
|
||||||
// Reminder to update LATEST_REVISION when adding new migrations.
|
// Reminder to update LATEST_REVISION when adding new migrations.
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ auto_derived_partial!(
|
|||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub avatar: Option<File>,
|
pub avatar: Option<File>,
|
||||||
|
|
||||||
|
/// User that created this webhook
|
||||||
|
pub creator_id: String,
|
||||||
|
|
||||||
/// The channel this webhook belongs to
|
/// The channel this webhook belongs to
|
||||||
pub channel_id: String,
|
pub channel_id: String,
|
||||||
|
|
||||||
@@ -43,6 +46,7 @@ impl Default for Webhook {
|
|||||||
id: Default::default(),
|
id: Default::default(),
|
||||||
name: Default::default(),
|
name: Default::default(),
|
||||||
avatar: None,
|
avatar: None,
|
||||||
|
creator_id: Default::default(),
|
||||||
channel_id: Default::default(),
|
channel_id: Default::default(),
|
||||||
permissions: Default::default(),
|
permissions: Default::default(),
|
||||||
token: Default::default(),
|
token: Default::default(),
|
||||||
|
|||||||
@@ -19,9 +19,11 @@ auto_derived_partial!(
|
|||||||
/// When this file was uploaded
|
/// When this file was uploaded
|
||||||
pub uploaded_at: Option<Timestamp>, // these are Option<>s to not break file uploads on legacy Autumn
|
pub uploaded_at: Option<Timestamp>, // these are Option<>s to not break file uploads on legacy Autumn
|
||||||
/// ID of user who uploaded this file
|
/// 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
|
pub uploader_id: Option<String>, // these are Option<>s to not break file uploads on legacy Autumn
|
||||||
|
|
||||||
/// What the file was used for
|
/// What the file was used for
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub used_for: Option<FileUsedFor>,
|
pub used_for: Option<FileUsedFor>,
|
||||||
|
|
||||||
/// Whether this file was deleted
|
/// Whether this file was deleted
|
||||||
@@ -63,6 +65,7 @@ auto_derived!(
|
|||||||
ServerBanner,
|
ServerBanner,
|
||||||
Emoji,
|
Emoji,
|
||||||
UserAvatar,
|
UserAvatar,
|
||||||
|
WebhookAvatar,
|
||||||
UserProfileBackground,
|
UserProfileBackground,
|
||||||
LegacyGroupIcon,
|
LegacyGroupIcon,
|
||||||
ChannelIcon,
|
ChannelIcon,
|
||||||
@@ -86,44 +89,154 @@ impl File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Use a file for a message attachment
|
/// Use a file for a message attachment
|
||||||
pub async fn use_attachment(db: &Database, id: &str, parent: &str) -> Result<File> {
|
pub async fn use_attachment(
|
||||||
db.find_and_use_attachment(id, "attachments", "message", parent)
|
db: &Database,
|
||||||
.await
|
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
|
/// Use a file for a user profile background
|
||||||
pub async fn use_background(db: &Database, id: &str, parent: &str) -> Result<File> {
|
pub async fn use_background(
|
||||||
db.find_and_use_attachment(id, "backgrounds", "user", parent)
|
db: &Database,
|
||||||
.await
|
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
|
/// Use a file for a user avatar
|
||||||
pub async fn use_avatar(db: &Database, id: &str, parent: &str) -> Result<File> {
|
pub async fn use_user_avatar(
|
||||||
db.find_and_use_attachment(id, "avatars", "user", parent)
|
db: &Database,
|
||||||
.await
|
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
|
/// Use a file for a webhook avatar
|
||||||
pub async fn use_icon(db: &Database, id: &str, parent: &str) -> Result<File> {
|
pub async fn use_webhook_avatar(
|
||||||
db.find_and_use_attachment(id, "icons", "object", parent)
|
db: &Database,
|
||||||
.await
|
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
|
/// Use a file for a server icon
|
||||||
pub async fn use_server_icon(db: &Database, id: &str, parent: &str) -> Result<File> {
|
pub async fn use_server_icon(
|
||||||
db.find_and_use_attachment(id, "icons", "object", parent)
|
db: &Database,
|
||||||
.await
|
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
|
/// Use a file for a server banner
|
||||||
pub async fn use_banner(db: &Database, id: &str, parent: &str) -> Result<File> {
|
pub async fn use_server_banner(
|
||||||
db.find_and_use_attachment(id, "banners", "server", parent)
|
db: &Database,
|
||||||
.await
|
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
|
/// Use a file for an emoji
|
||||||
pub async fn use_emoji(db: &Database, id: &str, parent: &str) -> Result<File> {
|
pub async fn use_emoji(
|
||||||
db.find_and_use_attachment(id, "emojis", "object", parent)
|
db: &Database,
|
||||||
.await
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ use revolt_result::Result;
|
|||||||
|
|
||||||
use crate::File;
|
use crate::File;
|
||||||
|
|
||||||
|
use super::FileUsedFor;
|
||||||
|
|
||||||
mod mongodb;
|
mod mongodb;
|
||||||
mod reference;
|
mod reference;
|
||||||
|
|
||||||
@@ -18,8 +20,8 @@ pub trait AbstractAttachments: Sync + Send {
|
|||||||
&self,
|
&self,
|
||||||
id: &str,
|
id: &str,
|
||||||
tag: &str,
|
tag: &str,
|
||||||
parent_type: &str,
|
used_for: FileUsedFor,
|
||||||
parent_id: &str,
|
uploader_id: String,
|
||||||
) -> Result<File>;
|
) -> Result<File>;
|
||||||
|
|
||||||
/// Mark an attachment as having been reported.
|
/// Mark an attachment as having been reported.
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
|
use bson::to_document;
|
||||||
use bson::Document;
|
use bson::Document;
|
||||||
|
use revolt_config::report_internal_error;
|
||||||
use revolt_result::Result;
|
use revolt_result::Result;
|
||||||
|
|
||||||
use crate::File;
|
use crate::File;
|
||||||
|
use crate::FileUsedFor;
|
||||||
use crate::MongoDb;
|
use crate::MongoDb;
|
||||||
|
|
||||||
use super::AbstractAttachments;
|
use super::AbstractAttachments;
|
||||||
@@ -34,10 +37,9 @@ impl AbstractAttachments for MongoDb {
|
|||||||
&self,
|
&self,
|
||||||
id: &str,
|
id: &str,
|
||||||
tag: &str,
|
tag: &str,
|
||||||
parent_type: &str,
|
used_for: FileUsedFor,
|
||||||
parent_id: &str,
|
uploader_id: String,
|
||||||
) -> Result<File> {
|
) -> Result<File> {
|
||||||
let key = format!("{parent_type}_id");
|
|
||||||
let file = query!(
|
let file = query!(
|
||||||
self,
|
self,
|
||||||
find_one,
|
find_one,
|
||||||
@@ -45,7 +47,7 @@ impl AbstractAttachments for MongoDb {
|
|||||||
doc! {
|
doc! {
|
||||||
"_id": id,
|
"_id": id,
|
||||||
"tag": tag,
|
"tag": tag,
|
||||||
&key: {
|
"used_for": {
|
||||||
"$exists": false
|
"$exists": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,7 +61,8 @@ impl AbstractAttachments for MongoDb {
|
|||||||
},
|
},
|
||||||
doc! {
|
doc! {
|
||||||
"$set": {
|
"$set": {
|
||||||
key: parent_id
|
"used_for": report_internal_error!(to_document(&used_for))?,
|
||||||
|
"uploader_id": uploader_id
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use revolt_result::Result;
|
use revolt_result::Result;
|
||||||
|
|
||||||
use crate::File;
|
use crate::File;
|
||||||
|
use crate::FileUsedFor;
|
||||||
use crate::ReferenceDb;
|
use crate::ReferenceDb;
|
||||||
|
|
||||||
use super::AbstractAttachments;
|
use super::AbstractAttachments;
|
||||||
@@ -37,19 +38,14 @@ impl AbstractAttachments for ReferenceDb {
|
|||||||
&self,
|
&self,
|
||||||
id: &str,
|
id: &str,
|
||||||
tag: &str,
|
tag: &str,
|
||||||
parent_type: &str,
|
used_for: FileUsedFor,
|
||||||
parent_id: &str,
|
uploader_id: String,
|
||||||
) -> Result<File> {
|
) -> Result<File> {
|
||||||
let mut files = self.files.lock().await;
|
let mut files = self.files.lock().await;
|
||||||
if let Some(file) = files.get_mut(id) {
|
if let Some(file) = files.get_mut(id) {
|
||||||
if file.tag == tag {
|
if file.tag == tag {
|
||||||
match parent_type {
|
file.uploader_id = Some(uploader_id);
|
||||||
"message" => file.message_id = Some(parent_id.to_owned()),
|
file.used_for = Some(used_for);
|
||||||
"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!(),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(file.clone())
|
Ok(file.clone())
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -370,10 +370,8 @@ impl Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for attachment_id in data.attachments.as_deref().unwrap_or_default() {
|
for attachment_id in data.attachments.as_deref().unwrap_or_default() {
|
||||||
attachments.push(
|
attachments
|
||||||
db.find_and_use_attachment(attachment_id, "attachments", "message", &message_id)
|
.push(File::use_attachment(db, attachment_id, &message_id, author.id()).await?);
|
||||||
.await?,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !attachments.is_empty() {
|
if !attachments.is_empty() {
|
||||||
@@ -499,10 +497,7 @@ impl Message {
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
let media = if let Some(id) = embed.media {
|
let media = if let Some(id) = embed.media {
|
||||||
Some(
|
Some(File::use_attachment(db, &id, &self.id, &self.author).await?)
|
||||||
db.find_and_use_attachment(&id, "attachments", "message", &self.id)
|
|
||||||
.await?,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@@ -661,7 +656,7 @@ impl Message {
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let media: Option<v0::File> = if let Some(id) = embed.media {
|
let media: Option<v0::File> = if let Some(id) = embed.media {
|
||||||
Some(
|
Some(
|
||||||
db.find_and_use_attachment(&id, "attachments", "message", &self.id)
|
File::use_attachment(db, &id, &self.id, &self.author)
|
||||||
.await?
|
.await?
|
||||||
.into(),
|
.into(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ use crate::Database;
|
|||||||
|
|
||||||
/// Payload information, before assembly
|
/// Payload information, before assembly
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
pub struct ApnPayload {
|
pub struct ApnPayload {
|
||||||
message: Message,
|
message: Message,
|
||||||
url: String,
|
url: String,
|
||||||
@@ -29,6 +30,7 @@ pub struct ApnPayload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize, Debug)]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
struct Payload<'a> {
|
struct Payload<'a> {
|
||||||
aps: APS<'a>,
|
aps: APS<'a>,
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use base64::{
|
|||||||
use deadqueue::limited::Queue;
|
use deadqueue::limited::Queue;
|
||||||
use fcm_v1::auth::{Authenticator, ServiceAccountKey};
|
use fcm_v1::auth::{Authenticator, ServiceAccountKey};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use revolt_config::config;
|
use revolt_config::{config, report_internal_error};
|
||||||
use revolt_models::v0::PushNotification;
|
use revolt_models::v0::PushNotification;
|
||||||
use revolt_presence::filter_online;
|
use revolt_presence::filter_online;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
@@ -116,12 +116,11 @@ pub async fn worker(db: Database, authifier_db: AuthifierDatabase) {
|
|||||||
if fcm_error.contains("404 (Not Found)") {
|
if fcm_error.contains("404 (Not Found)") {
|
||||||
println!("Unregistering {:?}", session.id);
|
println!("Unregistering {:?}", session.id);
|
||||||
|
|
||||||
if let Err(err) = db
|
report_internal_error!(
|
||||||
.remove_push_subscription_by_session_id(&session.id)
|
db.remove_push_subscription_by_session_id(&session.id)
|
||||||
.await
|
.await
|
||||||
{
|
)
|
||||||
revolt_config::capture_error(&err);
|
.ok();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ impl From<crate::Webhook> for Webhook {
|
|||||||
id: value.id,
|
id: value.id,
|
||||||
name: value.name,
|
name: value.name,
|
||||||
avatar: value.avatar.map(|file| file.into()),
|
avatar: value.avatar.map(|file| file.into()),
|
||||||
|
creator_id: value.creator_id,
|
||||||
channel_id: value.channel_id,
|
channel_id: value.channel_id,
|
||||||
token: value.token,
|
token: value.token,
|
||||||
permissions: value.permissions,
|
permissions: value.permissions,
|
||||||
@@ -121,6 +122,7 @@ impl From<crate::PartialWebhook> for PartialWebhook {
|
|||||||
id: value.id,
|
id: value.id,
|
||||||
name: value.name,
|
name: value.name,
|
||||||
avatar: value.avatar.map(|file| file.into()),
|
avatar: value.avatar.map(|file| file.into()),
|
||||||
|
creator_id: value.creator_id,
|
||||||
channel_id: value.channel_id,
|
channel_id: value.channel_id,
|
||||||
token: value.token,
|
token: value.token,
|
||||||
permissions: value.permissions,
|
permissions: value.permissions,
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ auto_derived_partial!(
|
|||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
pub avatar: Option<File>,
|
pub avatar: Option<File>,
|
||||||
|
|
||||||
|
/// User that created this webhook
|
||||||
|
pub creator_id: String,
|
||||||
|
|
||||||
/// The channel this webhook belongs to
|
/// The channel this webhook belongs to
|
||||||
pub channel_id: String,
|
pub channel_id: String,
|
||||||
|
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ pub async fn edit(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(icon_id) = data.icon {
|
if let Some(icon_id) = data.icon {
|
||||||
partial.icon = Some(File::use_icon(db, &icon_id, id).await?);
|
partial.icon = Some(File::use_channel_icon(db, &icon_id, id, &user.id).await?);
|
||||||
*icon = partial.icon.clone();
|
*icon = partial.icon.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use revolt_database::{
|
use revolt_database::{
|
||||||
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
||||||
Channel, Database, User, Webhook,
|
Channel, Database, File, User, Webhook,
|
||||||
};
|
};
|
||||||
use revolt_models::v0;
|
use revolt_models::v0;
|
||||||
use revolt_permissions::{
|
use revolt_permissions::{
|
||||||
@@ -43,10 +43,7 @@ pub async fn create_webhook(
|
|||||||
let webhook_id = Ulid::new().to_string();
|
let webhook_id = Ulid::new().to_string();
|
||||||
|
|
||||||
let avatar = match &data.avatar {
|
let avatar = match &data.avatar {
|
||||||
Some(id) => Some(
|
Some(id) => Some(File::use_webhook_avatar(db, id, &webhook_id, &user.id).await?),
|
||||||
db.find_and_use_attachment(id, "avatars", "user", &webhook_id)
|
|
||||||
.await?,
|
|
||||||
),
|
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -54,6 +51,7 @@ pub async fn create_webhook(
|
|||||||
id: webhook_id,
|
id: webhook_id,
|
||||||
name: data.name,
|
name: data.name,
|
||||||
avatar,
|
avatar,
|
||||||
|
creator_id: user.id,
|
||||||
channel_id: channel.id().to_string(),
|
channel_id: channel.id().to_string(),
|
||||||
permissions: *DEFAULT_WEBHOOK_PERMISSIONS,
|
permissions: *DEFAULT_WEBHOOK_PERMISSIONS,
|
||||||
token: Some(nanoid::nanoid!(64)),
|
token: Some(nanoid::nanoid!(64)),
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ pub async fn create_emoji(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Find the relevant attachment
|
// Find the relevant attachment
|
||||||
let attachment = File::use_emoji(db, &id, &id).await?;
|
let attachment = File::use_emoji(db, &id, &id, &user.id).await?;
|
||||||
|
|
||||||
// Create the emoji object
|
// Create the emoji object
|
||||||
let emoji = Emoji {
|
let emoji = Emoji {
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ pub async fn edit(
|
|||||||
|
|
||||||
// 2. Apply new avatar
|
// 2. Apply new avatar
|
||||||
if let Some(avatar) = avatar {
|
if let Some(avatar) = avatar {
|
||||||
partial.avatar = Some(File::use_avatar(db, &avatar, &user.id).await?);
|
partial.avatar = Some(File::use_user_avatar(db, &avatar, &user.id, &user.id).await?);
|
||||||
}
|
}
|
||||||
|
|
||||||
member
|
member
|
||||||
|
|||||||
@@ -138,13 +138,13 @@ pub async fn edit(
|
|||||||
|
|
||||||
// 3. Apply new icon
|
// 3. Apply new icon
|
||||||
if let Some(icon) = icon {
|
if let Some(icon) = icon {
|
||||||
partial.icon = Some(File::use_server_icon(db, &icon, &server.id).await?);
|
partial.icon = Some(File::use_server_icon(db, &icon, &server.id, &user.id).await?);
|
||||||
server.icon = partial.icon.clone();
|
server.icon = partial.icon.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Apply new banner
|
// 4. Apply new banner
|
||||||
if let Some(banner) = banner {
|
if let Some(banner) = banner {
|
||||||
partial.banner = Some(File::use_banner(db, &banner, &server.id).await?);
|
partial.banner = Some(File::use_server_banner(db, &banner, &server.id, &user.id).await?);
|
||||||
server.banner = partial.banner.clone();
|
server.banner = partial.banner.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ pub async fn edit(
|
|||||||
|
|
||||||
// 2. Apply new avatar
|
// 2. Apply new avatar
|
||||||
if let Some(avatar) = data.avatar {
|
if let Some(avatar) = data.avatar {
|
||||||
partial.avatar = Some(File::use_avatar(db, &avatar, &user.id).await?);
|
partial.avatar = Some(File::use_user_avatar(db, &avatar, &user.id, &user.id).await?);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Apply new status
|
// 3. Apply new status
|
||||||
@@ -114,7 +114,8 @@ pub async fn edit(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(background) = profile.background {
|
if let Some(background) = profile.background {
|
||||||
new_profile.background = Some(File::use_background(db, &background, &user.id).await?);
|
new_profile.background =
|
||||||
|
Some(File::use_background(db, &background, &user.id, &user.id).await?);
|
||||||
}
|
}
|
||||||
|
|
||||||
partial.profile = Some(new_profile);
|
partial.profile = Some(new_profile);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use revolt_database::{
|
use revolt_database::{
|
||||||
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
||||||
Database, PartialWebhook, User,
|
Database, File, PartialWebhook, User,
|
||||||
};
|
};
|
||||||
use revolt_models::v0::{DataEditWebhook, Webhook};
|
use revolt_models::v0::{DataEditWebhook, Webhook};
|
||||||
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
|
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
|
||||||
@@ -52,10 +52,7 @@ pub async fn webhook_edit(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(avatar) = avatar {
|
if let Some(avatar) = avatar {
|
||||||
let file = db
|
let file = File::use_webhook_avatar(db, &avatar, &webhook.id, &webhook.creator_id).await?;
|
||||||
.find_and_use_attachment(&avatar, "avatars", "user", &webhook.id)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
partial.avatar = Some(file)
|
partial.avatar = Some(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use revolt_database::util::reference::Reference;
|
use revolt_database::util::reference::Reference;
|
||||||
use revolt_database::{Database, PartialWebhook};
|
use revolt_database::{Database, File, PartialWebhook};
|
||||||
use revolt_models::v0::{DataEditWebhook, Webhook};
|
use revolt_models::v0::{DataEditWebhook, Webhook};
|
||||||
use revolt_models::validator::Validate;
|
use revolt_models::validator::Validate;
|
||||||
use revolt_result::{create_error, Result};
|
use revolt_result::{create_error, Result};
|
||||||
@@ -34,7 +34,7 @@ pub async fn webhook_edit_token(
|
|||||||
name,
|
name,
|
||||||
avatar,
|
avatar,
|
||||||
permissions,
|
permissions,
|
||||||
remove
|
remove,
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
let mut partial = PartialWebhook {
|
let mut partial = PartialWebhook {
|
||||||
@@ -44,10 +44,7 @@ pub async fn webhook_edit_token(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(avatar) = avatar {
|
if let Some(avatar) = avatar {
|
||||||
let file = db
|
let file = File::use_webhook_avatar(db, &avatar, &webhook.id, &webhook.creator_id).await?;
|
||||||
.find_and_use_attachment(&avatar, "avatars", "user", &webhook.id)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
partial.avatar = Some(file)
|
partial.avatar = Some(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user