Compare commits

...
8 Commits
25 changed files with 149 additions and 74 deletions
Generated
+12 -12
View File
@@ -5580,7 +5580,7 @@ dependencies = [
[[package]]
name = "revolt-autumn"
version = "0.7.18"
version = "0.7.19"
dependencies = [
"axum",
"axum-macros",
@@ -5617,7 +5617,7 @@ dependencies = [
[[package]]
name = "revolt-bonfire"
version = "0.7.18"
version = "0.7.19"
dependencies = [
"async-channel 2.3.1",
"async-std",
@@ -5647,7 +5647,7 @@ dependencies = [
[[package]]
name = "revolt-config"
version = "0.7.18"
version = "0.7.19"
dependencies = [
"async-std",
"cached",
@@ -5664,7 +5664,7 @@ dependencies = [
[[package]]
name = "revolt-database"
version = "0.7.18"
version = "0.7.19"
dependencies = [
"async-lock 2.8.0",
"async-recursion",
@@ -5712,7 +5712,7 @@ dependencies = [
[[package]]
name = "revolt-delta"
version = "0.7.18"
version = "0.7.19"
dependencies = [
"async-channel 1.6.1",
"async-std",
@@ -5759,7 +5759,7 @@ dependencies = [
[[package]]
name = "revolt-files"
version = "0.7.18"
version = "0.7.19"
dependencies = [
"aes-gcm 0.10.3",
"aws-config",
@@ -5782,7 +5782,7 @@ dependencies = [
[[package]]
name = "revolt-january"
version = "0.7.18"
version = "0.7.19"
dependencies = [
"async-recursion",
"axum",
@@ -5810,7 +5810,7 @@ dependencies = [
[[package]]
name = "revolt-models"
version = "0.7.18"
version = "0.7.19"
dependencies = [
"indexmap 1.9.3",
"iso8601-timestamp 0.2.11",
@@ -5828,7 +5828,7 @@ dependencies = [
[[package]]
name = "revolt-nodejs-bindings"
version = "0.7.18"
version = "0.7.19"
dependencies = [
"async-std",
"neon",
@@ -5841,7 +5841,7 @@ dependencies = [
[[package]]
name = "revolt-permissions"
version = "0.7.18"
version = "0.7.19"
dependencies = [
"async-std",
"async-trait",
@@ -5856,7 +5856,7 @@ dependencies = [
[[package]]
name = "revolt-presence"
version = "0.7.18"
version = "0.7.19"
dependencies = [
"async-std",
"log",
@@ -5867,7 +5867,7 @@ dependencies = [
[[package]]
name = "revolt-result"
version = "0.7.18"
version = "0.7.19"
dependencies = [
"axum",
"revolt_okapi",
+4 -4
View File
@@ -1,6 +1,6 @@
[package]
name = "revolt-nodejs-bindings"
version = "0.7.18"
version = "0.7.19"
description = "Node.js bindings for the Revolt software"
authors = ["Paul Makles <me@insrt.uk>"]
license = "MIT"
@@ -20,6 +20,6 @@ serde = { version = "1", features = ["derive"] }
async-std = "1.12.0"
revolt-config = { version = "0.7.18", path = "../../core/config" }
revolt-result = { version = "0.7.18", path = "../../core/result" }
revolt-database = { version = "0.7.18", path = "../../core/database" }
revolt-config = { version = "0.7.19", path = "../../core/config" }
revolt-result = { version = "0.7.19", path = "../../core/result" }
revolt-database = { version = "0.7.19", path = "../../core/database" }
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "revolt-bonfire"
version = "0.7.18"
version = "0.7.19"
license = "AGPL-3.0-or-later"
edition = "2021"
@@ -41,7 +41,7 @@ revolt-result = { path = "../core/result" }
revolt-models = { path = "../core/models" }
revolt-config = { path = "../core/config" }
revolt-database = { path = "../core/database" }
revolt-permissions = { version = "0.7.18", path = "../core/permissions" }
revolt-permissions = { version = "0.7.19", path = "../core/permissions" }
revolt-presence = { path = "../core/presence", features = ["redis-is-patched"] }
# redis
+4 -6
View File
@@ -301,25 +301,23 @@ async fn listener(
PayloadType::Json => message
.value
.as_str()
.and_then(|s| serde_json::from_str::<EventV1>(s.as_ref()).ok()),
.and_then(|s| report_internal_error!(serde_json::from_str::<EventV1>(s.as_ref())).ok()),
PayloadType::Msgpack => message
.value
.as_bytes()
.and_then(|b| rmp_serde::from_slice::<EventV1>(b).ok()),
.and_then(|b| report_internal_error!(rmp_serde::from_slice::<EventV1>(b)).ok()),
PayloadType::Bincode => message
.value
.as_bytes()
.and_then(|b| bincode::deserialize::<EventV1>(b).ok()),
.and_then(|b| report_internal_error!(bincode::deserialize::<EventV1>(b)).ok()),
};
let Some(mut event) = event else {
let err = format!(
"Failed to deserialise an event for {}! Introspection: `{:?}`",
"Failed to deserialise event for {}: `{:?}`",
message.channel,
message
.value
.as_string()
.map(|x| x.chars().take(32).collect::<String>())
);
error!("{}", err);
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "revolt-config"
version = "0.7.18"
version = "0.7.19"
edition = "2021"
license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"]
@@ -35,4 +35,4 @@ pretty_env_logger = "0.4.0"
sentry = "0.31.5"
# Core
revolt-result = { version = "0.7.18", path = "../result", optional = true }
revolt-result = { version = "0.7.19", path = "../result", optional = true }
+6 -6
View File
@@ -1,6 +1,6 @@
[package]
name = "revolt-database"
version = "0.7.18"
version = "0.7.19"
edition = "2021"
license = "AGPL-3.0-or-later"
authors = ["Paul Makles <me@insrt.uk>"]
@@ -24,15 +24,15 @@ default = ["mongodb", "async-std-runtime", "tasks"]
[dependencies]
# Core
revolt-config = { version = "0.7.18", path = "../config", features = [
revolt-config = { version = "0.7.19", path = "../config", features = [
"report-macros",
] }
revolt-result = { version = "0.7.18", path = "../result" }
revolt-models = { version = "0.7.18", path = "../models", features = [
revolt-result = { version = "0.7.19", path = "../result" }
revolt-models = { version = "0.7.19", path = "../models", features = [
"validator",
] }
revolt-presence = { version = "0.7.18", path = "../presence" }
revolt-permissions = { version = "0.7.18", path = "../permissions", features = [
revolt-presence = { version = "0.7.19", path = "../presence" }
revolt-permissions = { version = "0.7.19", path = "../permissions", features = [
"serde",
"bson",
] }
+10 -1
View File
@@ -2,7 +2,10 @@ use authifier::AuthifierEvent;
use serde::{Deserialize, Serialize};
use revolt_models::v0::{
AppendMessage, Channel, ChannelUnread, Emoji, FieldsChannel, FieldsMember, FieldsMessage, FieldsRole, FieldsServer, FieldsUser, FieldsWebhook, Member, MemberCompositeKey, Message, PartialChannel, PartialMember, PartialMessage, PartialRole, PartialServer, PartialUser, PartialWebhook, RemovalIntention, Report, Server, User, UserSettings, Webhook
AppendMessage, Channel, ChannelUnread, Emoji, FieldsChannel, FieldsMember, FieldsMessage,
FieldsRole, FieldsServer, FieldsUser, FieldsWebhook, Member, MemberCompositeKey, Message,
PartialChannel, PartialMember, PartialMessage, PartialRole, PartialServer, PartialUser,
PartialWebhook, RemovalIntention, Report, Server, User, UserSettings, Webhook,
};
use revolt_result::Error;
@@ -89,6 +92,7 @@ pub enum EventV1 {
id: String,
channel: String,
data: PartialMessage,
#[serde(default)]
clear: Vec<FieldsMessage>,
},
@@ -140,6 +144,7 @@ pub enum EventV1 {
ServerUpdate {
id: String,
data: PartialServer,
#[serde(default)]
clear: Vec<FieldsServer>,
},
@@ -150,6 +155,7 @@ pub enum EventV1 {
ServerMemberUpdate {
id: MemberCompositeKey,
data: PartialMember,
#[serde(default)]
clear: Vec<FieldsMember>,
},
@@ -168,6 +174,7 @@ pub enum EventV1 {
id: String,
role_id: String,
data: PartialRole,
#[serde(default)]
clear: Vec<FieldsRole>,
},
@@ -178,6 +185,7 @@ pub enum EventV1 {
UserUpdate {
id: String,
data: PartialUser,
#[serde(default)]
clear: Vec<FieldsUser>,
event_id: Option<String>,
},
@@ -212,6 +220,7 @@ pub enum EventV1 {
ChannelUpdate {
id: String,
data: PartialChannel,
#[serde(default)]
clear: Vec<FieldsChannel>,
},
@@ -1,4 +1,4 @@
use std::collections::HashSet;
use std::{collections::HashSet, hash::RandomState};
use indexmap::{IndexMap, IndexSet};
use iso8601_timestamp::Timestamp;
@@ -338,7 +338,34 @@ impl Message {
}
if !mentions.is_empty() {
message.mentions.replace(mentions.into_iter().collect());
// FIXME: temp fix to stop spam attacks
match channel {
Channel::DirectMessage { ref recipients, .. }
| Channel::Group { ref recipients, .. } => {
let recipients_hash: HashSet<&String, RandomState> =
HashSet::from_iter(recipients.iter());
mentions.retain(|m| recipients_hash.contains(m));
}
Channel::TextChannel { ref server, .. }
| Channel::VoiceChannel { ref server, .. } => {
let mentions_vec = Vec::from_iter(mentions.iter().cloned());
let valid_members = db.fetch_members(server.as_str(), &mentions_vec[..]).await;
if let Ok(valid_members) = valid_members {
let valid_ids: HashSet<String, RandomState> = HashSet::from_iter(
valid_members.iter().map(|member| member.id.user.clone()),
);
mentions.retain(|m| valid_ids.contains(m));
} else {
revolt_config::capture_error(&valid_members.unwrap_err());
}
}
Channel::SavedMessages { .. } => mentions.clear(),
}
if !mentions.is_empty() {
message.mentions.replace(mentions.into_iter().collect());
}
}
if !replies.is_empty() {
+3 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "revolt-files"
version = "0.7.18"
version = "0.7.19"
edition = "2021"
license = "AGPL-3.0-or-later"
authors = ["Paul Makles <me@insrt.uk>"]
@@ -20,10 +20,10 @@ typenum = "1.17.0"
aws-config = "1.5.5"
aws-sdk-s3 = { version = "1.46.0", features = ["behavior-version-latest"] }
revolt-config = { version = "0.7.18", path = "../config", features = [
revolt-config = { version = "0.7.19", path = "../config", features = [
"report-macros",
] }
revolt-result = { version = "0.7.18", path = "../result" }
revolt-result = { version = "0.7.19", path = "../result" }
# image processing
jxl-oxide = "0.8.1"
+18 -7
View File
@@ -124,13 +124,24 @@ pub fn image_size(f: &NamedTempFile) -> Option<(usize, usize)> {
}
/// Determine size of image with buffer
pub fn image_size_vec(v: &[u8]) -> Option<(usize, usize)> {
if let Ok(size) = imagesize::blob_size(v)
.inspect_err(|err| tracing::error!("Failed to generate image size! {err:?}"))
{
Some((size.width, size.height))
} else {
None
pub fn image_size_vec(v: &[u8], mime: &str) -> Option<(usize, usize)> {
match mime {
"image/svg+xml" => {
let tree =
report_internal_error!(usvg::Tree::from_data(v, &Default::default())).ok()?;
let size = tree.size();
Some((size.width() as usize, size.height() as usize))
}
_ => {
if let Ok(size) = imagesize::blob_size(v)
.inspect_err(|err| tracing::error!("Failed to generate image size! {err:?}"))
{
Some((size.width, size.height))
} else {
None
}
}
}
}
+3 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "revolt-models"
version = "0.7.18"
version = "0.7.19"
edition = "2021"
license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"]
@@ -20,8 +20,8 @@ default = ["serde", "partials", "rocket"]
[dependencies]
# Core
revolt-config = { version = "0.7.18", path = "../config" }
revolt-permissions = { version = "0.7.18", path = "../permissions" }
revolt-config = { version = "0.7.19", path = "../config" }
revolt-permissions = { version = "0.7.19", path = "../permissions" }
# Utility
regex = "1"
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "revolt-permissions"
version = "0.7.18"
version = "0.7.19"
edition = "2021"
license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"]
@@ -21,7 +21,7 @@ async-std = { version = "1.8.0", features = ["attributes"] }
[dependencies]
# Core
revolt-result = { version = "0.7.18", path = "../result" }
revolt-result = { version = "0.7.19", path = "../result" }
# Utility
auto_ops = "0.3.0"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "revolt-presence"
version = "0.7.18"
version = "0.7.19"
edition = "2021"
license = "AGPL-3.0-or-later"
authors = ["Paul Makles <me@insrt.uk>"]
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "revolt-result"
version = "0.7.18"
version = "0.7.19"
edition = "2021"
license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"]
+1
View File
@@ -80,6 +80,7 @@ impl IntoResponse for Error {
ErrorType::FileTooLarge { .. } => StatusCode::UNPROCESSABLE_ENTITY,
ErrorType::FileTypeNotAllowed => StatusCode::BAD_REQUEST,
ErrorType::ImageProcessingFailed => StatusCode::INTERNAL_SERVER_ERROR,
ErrorType::NoEmbedData => StatusCode::BAD_REQUEST,
};
(status, Json(&self)).into_response()
+1
View File
@@ -163,6 +163,7 @@ pub enum ErrorType {
},
FileTypeNotAllowed,
ImageProcessingFailed,
NoEmbedData,
// ? Legacy errors
VosoUnavailable,
+1
View File
@@ -85,6 +85,7 @@ impl<'r> Responder<'r, 'static> for Error {
ErrorType::FileTooLarge { .. } => Status::UnprocessableEntity,
ErrorType::FileTypeNotAllowed => Status::BadRequest,
ErrorType::ImageProcessingFailed => Status::InternalServerError,
ErrorType::NoEmbedData => Status::BadRequest,
};
// Serialize the error data structure into JSON.
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "revolt-delta"
version = "0.7.18"
version = "0.7.19"
license = "AGPL-3.0-or-later"
authors = ["Paul Makles <paulmakles@gmail.com>"]
edition = "2018"
+5 -5
View File
@@ -1,6 +1,6 @@
[package]
name = "revolt-autumn"
version = "0.7.18"
version = "0.7.19"
edition = "2021"
[dependencies]
@@ -42,12 +42,12 @@ tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
# Core crates
revolt-files = { version = "0.7.18", path = "../../core/files" }
revolt-config = { version = "0.7.18", path = "../../core/config" }
revolt-database = { version = "0.7.18", path = "../../core/database", features = [
revolt-files = { version = "0.7.19", path = "../../core/files" }
revolt-config = { version = "0.7.19", path = "../../core/config" }
revolt-database = { version = "0.7.19", path = "../../core/database", features = [
"axum-impl",
] }
revolt-result = { version = "0.7.18", path = "../../core/result", features = [
revolt-result = { version = "0.7.19", path = "../../core/result", features = [
"utoipa",
"axum",
] }
+5 -5
View File
@@ -1,6 +1,6 @@
[package]
name = "revolt-january"
version = "0.7.18"
version = "0.7.19"
edition = "2021"
[dependencies]
@@ -31,13 +31,13 @@ tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
# Core crates
revolt-config = { version = "0.7.18", path = "../../core/config" }
revolt-models = { version = "0.7.18", path = "../../core/models" }
revolt-result = { version = "0.7.18", path = "../../core/result", features = [
revolt-config = { version = "0.7.19", path = "../../core/config" }
revolt-models = { version = "0.7.19", path = "../../core/models" }
revolt-result = { version = "0.7.19", path = "../../core/result", features = [
"utoipa",
"axum",
] }
revolt-files = { version = "0.7.18", path = "../../core/files" }
revolt-files = { version = "0.7.19", path = "../../core/files" }
# Axum / web server
axum = { version = "0.7.5" }
+7 -2
View File
@@ -1,6 +1,7 @@
use axum::{extract::Query, response::IntoResponse, routing::get, Json, Router};
use reqwest::header;
use revolt_result::Result;
use revolt_models::v0::Embed;
use revolt_result::{create_error, Result};
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
@@ -87,5 +88,9 @@ async fn embed(
Query(UrlQuery { url }): Query<UrlQuery>,
// TypedHeader(Authorization(_bearer)): TypedHeader<Authorization<Bearer>>,
) -> Result<impl IntoResponse> {
Request::generate_embed(url).await.map(Json)
match Request::generate_embed(url).await {
Ok(Embed::None) => Err(create_error!(NoEmbedData)),
result => result,
}
.map(Json)
}
+9 -8
View File
@@ -122,20 +122,21 @@ impl Request {
_ => Ok(None),
}
} else {
let response = if let Some(Request { response, .. }) = request {
response
let request = if let Some(request) = request {
request
} else {
let Request { response, mime } = Request::new(url).await?;
if matches!(mime.type_(), mime::IMAGE) {
response
let request = Request::new(url).await?;
if matches!(request.mime.type_(), mime::IMAGE) {
request
} else {
return Err(create_error!(FileTypeNotAllowed));
}
};
if let Some((width, height)) =
image_size_vec(&report_internal_error!(response.bytes().await)?)
{
if let Some((width, height)) = image_size_vec(
&report_internal_error!(request.response.bytes().await)?,
request.mime.as_ref(),
) {
Ok(Some(Image {
url: url.to_owned(),
width,
+6 -1
View File
@@ -156,8 +156,13 @@ pub async fn create_website_embed(original_url: &str, document: &str) -> Option<
}
}
// remove image if video exists
if metadata.video.is_some() {
metadata.image.take();
}
// fetch image size if missing
if metadata.special.is_none() && metadata.image.is_none() {
if metadata.special.is_none() {
if let Some(Image {
width, height, url, ..
}) = &metadata.image
+1
View File
@@ -2,6 +2,7 @@
- [Introduction](./hello.md)
- [Project Structure]()
- [Creating new API features](./new_features.md)
- [Testing]()
- [Writing a new database test]()
- [Writing a new API test]()
+15
View File
@@ -0,0 +1,15 @@
# New API features
New API features must be documented where appropriate, this document aims to cover everywhere you need to update for new features.
Before writing new API features, generally a good idea to:
- Consult with other developers in the [Revolt Developers space](https://rvlt.gg/API)
- If it's a relatively big feature, also [write an RFC](https://github.com/revoltchat/rfcs)
When your feature is ready to release, ensure to:
- Update backend documentation (what you're reading now!) if applicable
- Update the [developers documentation](https://github.com/revoltchat/wiki) if applicable
- Update the Feature Matrix (or ask someone that can to do so)
- Ensure it is properly listed in the backend release changelog