Compare commits

..
Author SHA1 Message Date
IAmTomahawkx 71e7fe3086 chore: bump release for patch 2024-12-27 16:35:02 -08:00
IAmTomahawkx 03f2e3b1bf fix: last message in a channel could not be acked, resulting in ghost dms and channel messages 2024-12-27 16:29:56 -08:00
Paul Makles 8bbb579d23 ci: revert back to GH runner 2024-12-26 14:59:03 +00:00
Paul Makles 6448af071b ci: remove purge command 2024-12-22 16:10:09 +00:00
Paul Makles c2a0ab71df ci: bring our own runner 2024-12-22 16:03:49 +00:00
Paul Makles 7a17165c24 ci: try pin to 20.04 2024-12-22 15:24:36 +00:00
Paul Makles d7213fa409 fix(ci): try to work-around runner being killed 2024-12-22 14:13:30 +00:00
Paul Makles 479f0402ca ci: switch to ubuntu 24.04 pre-emptively
trying to fix build cancellations
2024-12-22 13:30:27 +00:00
Paul Makles b62eeef80c chore(autumn): conditional animation stripping
feat(autumn): add /original redirect for files
fix(autumn): block non-images for non-attachment tags
fix(ci): use correct port for Rust test
2024-12-22 12:35:06 +00:00
17 changed files with 107 additions and 87 deletions

No files matched your search

+2 -2
View File
@@ -67,7 +67,7 @@ jobs:
if: github.event_name != 'pull_request' && github.ref_name == 'main' if: github.event_name != 'pull_request' && github.ref_name == 'main'
uses: nev7n/wait_for_response@v1 uses: nev7n/wait_for_response@v1
with: with:
url: "http://localhost:8000/" url: "http://localhost:14702/"
- name: Checkout API repository - name: Checkout API repository
if: github.event_name != 'pull_request' && github.ref_name == 'main' if: github.event_name != 'pull_request' && github.ref_name == 'main'
@@ -79,7 +79,7 @@ jobs:
- name: Download OpenAPI specification - name: Download OpenAPI specification
if: github.event_name != 'pull_request' && github.ref_name == 'main' if: github.event_name != 'pull_request' && github.ref_name == 'main'
run: curl http://localhost:8000/openapi.json -o api/OpenAPI.json run: curl http://localhost:14702/openapi.json -o api/OpenAPI.json
- name: Commit changes - name: Commit changes
if: github.event_name != 'pull_request' && github.ref_name == 'main' if: github.event_name != 'pull_request' && github.ref_name == 'main'
Generated
+13 -13
View File
@@ -5523,7 +5523,7 @@ dependencies = [
[[package]] [[package]]
name = "revolt-autumn" name = "revolt-autumn"
version = "0.8.0" version = "0.8.1"
dependencies = [ dependencies = [
"axum", "axum",
"axum-macros", "axum-macros",
@@ -5560,7 +5560,7 @@ dependencies = [
[[package]] [[package]]
name = "revolt-bonfire" name = "revolt-bonfire"
version = "0.8.0" version = "0.8.1"
dependencies = [ dependencies = [
"async-channel 2.3.1", "async-channel 2.3.1",
"async-std", "async-std",
@@ -5590,7 +5590,7 @@ dependencies = [
[[package]] [[package]]
name = "revolt-config" name = "revolt-config"
version = "0.8.0" version = "0.8.1"
dependencies = [ dependencies = [
"async-std", "async-std",
"cached", "cached",
@@ -5606,7 +5606,7 @@ dependencies = [
[[package]] [[package]]
name = "revolt-database" name = "revolt-database"
version = "0.8.0" version = "0.8.1"
dependencies = [ dependencies = [
"amqprs", "amqprs",
"async-lock 2.8.0", "async-lock 2.8.0",
@@ -5655,7 +5655,7 @@ dependencies = [
[[package]] [[package]]
name = "revolt-delta" name = "revolt-delta"
version = "0.8.0" version = "0.8.1"
dependencies = [ dependencies = [
"amqprs", "amqprs",
"async-channel 1.6.1", "async-channel 1.6.1",
@@ -5702,7 +5702,7 @@ dependencies = [
[[package]] [[package]]
name = "revolt-files" name = "revolt-files"
version = "0.8.0" version = "0.8.1"
dependencies = [ dependencies = [
"aes-gcm", "aes-gcm",
"aws-config", "aws-config",
@@ -5725,7 +5725,7 @@ dependencies = [
[[package]] [[package]]
name = "revolt-january" name = "revolt-january"
version = "0.8.0" version = "0.8.1"
dependencies = [ dependencies = [
"async-recursion", "async-recursion",
"axum", "axum",
@@ -5753,7 +5753,7 @@ dependencies = [
[[package]] [[package]]
name = "revolt-models" name = "revolt-models"
version = "0.8.0" version = "0.8.1"
dependencies = [ dependencies = [
"indexmap 1.9.3", "indexmap 1.9.3",
"iso8601-timestamp 0.2.11", "iso8601-timestamp 0.2.11",
@@ -5771,7 +5771,7 @@ dependencies = [
[[package]] [[package]]
name = "revolt-nodejs-bindings" name = "revolt-nodejs-bindings"
version = "0.8.0" version = "0.8.1"
dependencies = [ dependencies = [
"async-std", "async-std",
"neon", "neon",
@@ -5784,7 +5784,7 @@ dependencies = [
[[package]] [[package]]
name = "revolt-permissions" name = "revolt-permissions"
version = "0.8.0" version = "0.8.1"
dependencies = [ dependencies = [
"async-std", "async-std",
"async-trait", "async-trait",
@@ -5799,7 +5799,7 @@ dependencies = [
[[package]] [[package]]
name = "revolt-presence" name = "revolt-presence"
version = "0.8.0" version = "0.8.1"
dependencies = [ dependencies = [
"async-std", "async-std",
"log", "log",
@@ -5810,7 +5810,7 @@ dependencies = [
[[package]] [[package]]
name = "revolt-pushd" name = "revolt-pushd"
version = "0.8.0" version = "0.8.1"
dependencies = [ dependencies = [
"amqprs", "amqprs",
"async-trait", "async-trait",
@@ -5834,7 +5834,7 @@ dependencies = [
[[package]] [[package]]
name = "revolt-result" name = "revolt-result"
version = "0.8.0" version = "0.8.1"
dependencies = [ dependencies = [
"axum", "axum",
"revolt_okapi", "revolt_okapi",
+4 -4
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-nodejs-bindings" name = "revolt-nodejs-bindings"
version = "0.8.0" version = "0.8.1"
description = "Node.js bindings for the Revolt software" description = "Node.js bindings for the Revolt software"
authors = ["Paul Makles <me@insrt.uk>"] authors = ["Paul Makles <me@insrt.uk>"]
license = "MIT" license = "MIT"
@@ -20,6 +20,6 @@ serde = { version = "1", features = ["derive"] }
async-std = "1.12.0" async-std = "1.12.0"
revolt-config = { version = "0.8.0", path = "../../core/config" } revolt-config = { version = "0.8.1", path = "../../core/config" }
revolt-result = { version = "0.8.0", path = "../../core/result" } revolt-result = { version = "0.8.1", path = "../../core/result" }
revolt-database = { version = "0.8.0", path = "../../core/database" } revolt-database = { version = "0.8.1", path = "../../core/database" }
+2 -2
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-bonfire" name = "revolt-bonfire"
version = "0.8.0" version = "0.8.1"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
edition = "2021" edition = "2021"
@@ -41,7 +41,7 @@ revolt-result = { path = "../core/result" }
revolt-models = { path = "../core/models" } revolt-models = { path = "../core/models" }
revolt-config = { path = "../core/config" } revolt-config = { path = "../core/config" }
revolt-database = { path = "../core/database" } revolt-database = { path = "../core/database" }
revolt-permissions = { version = "0.8.0", path = "../core/permissions" } revolt-permissions = { version = "0.8.1", path = "../core/permissions" }
revolt-presence = { path = "../core/presence", features = ["redis-is-patched"] } revolt-presence = { path = "../core/presence", features = ["redis-is-patched"] }
# redis # redis
+2 -2
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-config" name = "revolt-config"
version = "0.8.0" version = "0.8.1"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"] authors = ["Paul Makles <me@insrt.uk>"]
@@ -34,4 +34,4 @@ pretty_env_logger = "0.4.0"
sentry = "0.31.5" sentry = "0.31.5"
# Core # Core
revolt-result = { version = "0.8.0", path = "../result", optional = true } revolt-result = { version = "0.8.1", path = "../result", optional = true }
+6 -6
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-database" name = "revolt-database"
version = "0.8.0" version = "0.8.1"
edition = "2021" edition = "2021"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
authors = ["Paul Makles <me@insrt.uk>"] authors = ["Paul Makles <me@insrt.uk>"]
@@ -24,15 +24,15 @@ default = ["mongodb", "async-std-runtime", "tasks"]
[dependencies] [dependencies]
# Core # Core
revolt-config = { version = "0.8.0", path = "../config", features = [ revolt-config = { version = "0.8.1", path = "../config", features = [
"report-macros", "report-macros",
] } ] }
revolt-result = { version = "0.8.0", path = "../result" } revolt-result = { version = "0.8.1", path = "../result" }
revolt-models = { version = "0.8.0", path = "../models", features = [ revolt-models = { version = "0.8.1", path = "../models", features = [
"validator", "validator",
] } ] }
revolt-presence = { version = "0.8.0", path = "../presence" } revolt-presence = { version = "0.8.1", path = "../presence" }
revolt-permissions = { version = "0.8.0", path = "../permissions", features = [ revolt-permissions = { version = "0.8.1", path = "../permissions", features = [
"serde", "serde",
"bson", "bson",
] } ] }
@@ -30,7 +30,7 @@ impl AbstractChannelUnreads for MongoDb {
doc! { doc! {
"$pull": { "$pull": {
"mentions": { "mentions": {
"$lt": message_id "$lte": message_id
} }
}, },
"$set": { "$set": {
+3 -3
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-files" name = "revolt-files"
version = "0.8.0" version = "0.8.1"
edition = "2021" edition = "2021"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
authors = ["Paul Makles <me@insrt.uk>"] authors = ["Paul Makles <me@insrt.uk>"]
@@ -20,10 +20,10 @@ typenum = "1.17.0"
aws-config = "1.5.5" aws-config = "1.5.5"
aws-sdk-s3 = { version = "1.46.0", features = ["behavior-version-latest"] } aws-sdk-s3 = { version = "1.46.0", features = ["behavior-version-latest"] }
revolt-config = { version = "0.8.0", path = "../config", features = [ revolt-config = { version = "0.8.1", path = "../config", features = [
"report-macros", "report-macros",
] } ] }
revolt-result = { version = "0.8.0", path = "../result" } revolt-result = { version = "0.8.1", path = "../result" }
# image processing # image processing
jxl-oxide = "0.8.1" jxl-oxide = "0.8.1"
+3 -3
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-models" name = "revolt-models"
version = "0.8.0" version = "0.8.1"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"] authors = ["Paul Makles <me@insrt.uk>"]
@@ -20,8 +20,8 @@ default = ["serde", "partials", "rocket"]
[dependencies] [dependencies]
# Core # Core
revolt-config = { version = "0.8.0", path = "../config" } revolt-config = { version = "0.8.1", path = "../config" }
revolt-permissions = { version = "0.8.0", path = "../permissions" } revolt-permissions = { version = "0.8.1", path = "../permissions" }
# Utility # Utility
regex = "1" regex = "1"
+2 -2
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-permissions" name = "revolt-permissions"
version = "0.8.0" version = "0.8.1"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"] authors = ["Paul Makles <me@insrt.uk>"]
@@ -21,7 +21,7 @@ async-std = { version = "1.8.0", features = ["attributes"] }
[dependencies] [dependencies]
# Core # Core
revolt-result = { version = "0.8.0", path = "../result" } revolt-result = { version = "0.8.1", path = "../result" }
# Utility # Utility
auto_ops = "0.3.0" auto_ops = "0.3.0"
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-presence" name = "revolt-presence"
version = "0.8.0" version = "0.8.1"
edition = "2021" edition = "2021"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
authors = ["Paul Makles <me@insrt.uk>"] authors = ["Paul Makles <me@insrt.uk>"]
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-result" name = "revolt-result"
version = "0.8.0" version = "0.8.1"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"] authors = ["Paul Makles <me@insrt.uk>"]
+4 -4
View File
@@ -1,12 +1,12 @@
[package] [package]
name = "revolt-pushd" name = "revolt-pushd"
version = "0.8.0" version = "0.8.1"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
revolt-config = { version = "0.8.0", path = "../../core/config" } revolt-config = { version = "0.8.1", path = "../../core/config" }
revolt-database = { version = "0.8.0", path = "../../core/database" } revolt-database = { version = "0.8.1", path = "../../core/database" }
revolt-models = { version = "0.8.0", path = "../../core/models", features = [ revolt-models = { version = "0.8.1", path = "../../core/models", features = [
"validator", "validator",
] } ] }
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-delta" name = "revolt-delta"
version = "0.8.0" version = "0.8.1"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
authors = ["Paul Makles <paulmakles@gmail.com>"] authors = ["Paul Makles <paulmakles@gmail.com>"]
edition = "2018" edition = "2018"
+5 -5
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-autumn" name = "revolt-autumn"
version = "0.8.0" version = "0.8.1"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
@@ -42,12 +42,12 @@ tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] } tracing-subscriber = { version = "0.3", features = ["env-filter"] }
# Core crates # Core crates
revolt-files = { version = "0.8.0", path = "../../core/files" } revolt-files = { version = "0.8.1", path = "../../core/files" }
revolt-config = { version = "0.8.0", path = "../../core/config" } revolt-config = { version = "0.8.1", path = "../../core/config" }
revolt-database = { version = "0.8.0", path = "../../core/database", features = [ revolt-database = { version = "0.8.1", path = "../../core/database", features = [
"axum-impl", "axum-impl",
] } ] }
revolt-result = { version = "0.8.0", path = "../../core/result", features = [ revolt-result = { version = "0.8.1", path = "../../core/result", features = [
"utoipa", "utoipa",
"axum", "axum",
] } ] }
+52 -32
View File
@@ -216,6 +216,27 @@ async fn upload_file(
nanoid::nanoid!(42) nanoid::nanoid!(42)
}; };
// Determine the mime type for the file
let mime_type = determine_mime_type(&mut file.contents, &buf, &filename);
// Check blocklist for mime type
if config
.files
.blocked_mime_types
.iter()
.any(|m| m == mime_type)
{
return Err(create_error!(FileTypeNotAllowed));
}
// Determine metadata for the file
let metadata = generate_metadata(&file.contents, mime_type);
// Block non-images for non-attachment uploads
if !matches!(tag, Tag::attachments) && !matches!(metadata, Metadata::Image { .. }) {
return Err(create_error!(FileTypeNotAllowed));
}
// Find an existing hash and use that if possible // Find an existing hash and use that if possible
let file_hash_exists = if let Ok(file_hash) = db let file_hash_exists = if let Ok(file_hash) = db
.fetch_attachment_hash(&format!("{original_hash:02x}")) .fetch_attachment_hash(&format!("{original_hash:02x}"))
@@ -239,22 +260,6 @@ async fn upload_file(
false false
}; };
// Determine the mime type for the file
let mime_type = determine_mime_type(&mut file.contents, &buf, &filename);
// Check blocklist for mime type
if config
.files
.blocked_mime_types
.iter()
.any(|m| m == mime_type)
{
return Err(create_error!(FileTypeNotAllowed));
}
// Determine metadata for the file
let metadata = generate_metadata(&file.contents, mime_type);
// Strip metadata // Strip metadata
let (buf, metadata) = strip_metadata(file.contents, buf, metadata, mime_type).await?; let (buf, metadata) = strip_metadata(file.contents, buf, metadata, mime_type).await?;
@@ -322,21 +327,23 @@ pub static CACHE_CONTROL: &str = "public, max-age=604800, must-revalidate";
/// Fetch preview of file /// Fetch preview of file
/// ///
/// This route will only return image content. /// This route will only return image content. <br>
/// For all other file types, please use the fetch route (you will receive a redirect if you try to use this route anyways!). /// For all other file types, please use the fetch route (you will receive a redirect if you try to use this route anyways!).
/// ///
/// Depending on the given tag, the file will be re-processed to fit the criteria: /// Depending on the given tag, the file will be re-processed to fit the criteria:
/// ///
/// | Tag | Image Resolution <sup>†</sup> | /// | Tag | Image Resolution <sup>†</sup> | Animations stripped by preview <sup>‡</sup> |
/// | :-: | --- | /// | :-: | --- | :-: |
/// | attachments | Up to 1280px on any axis | /// | attachments | Up to 1280px on any axis | ❌ |
/// | avatars | Up to 128px on any axis | /// | avatars | Up to 128px on any axis | ✅ |
/// | backgrounds | Up to 1280x720px | /// | backgrounds | Up to 1280x720px | ❌ |
/// | icons | Up to 128px on any axis | /// | icons | Up to 128px on any axis | ✅ |
/// | banners | Up to 480px on any axis | /// | banners | Up to 480px on any axis | ❌ |
/// | emojis | Up to 128px on any axis | /// | emojis | Up to 128px on any axis | ❌ |
/// ///
/// <sup>†</sup> aspect ratio will always be preserved /// <sup>†</sup> aspect ratio will always be preserved
///
/// <sup>‡</sup> to fetch animated variant, suffix `/{file_name}` or `/original` to the path
#[utoipa::path( #[utoipa::path(
get, get,
path = "/{tag}/{file_id}", path = "/{tag}/{file_id}",
@@ -352,8 +359,8 @@ async fn fetch_preview(
State(db): State<Database>, State(db): State<Database>,
Path((tag, file_id)): Path<(Tag, String)>, Path((tag, file_id)): Path<(Tag, String)>,
) -> Result<Response> { ) -> Result<Response> {
let tag: &'static str = tag.into(); let tag_str: &'static str = tag.clone().into();
let file = db.fetch_attachment(tag, &file_id).await?; let file = db.fetch_attachment(tag_str, &file_id).await?;
// Ignore deleted files // Ignore deleted files
if file.deleted.is_some_and(|v| v) { if file.deleted.is_some_and(|v| v) {
@@ -367,10 +374,14 @@ async fn fetch_preview(
let hash = file.as_hash(&db).await?; let hash = file.as_hash(&db).await?;
// Only process image files and don't process GIFs let is_animated = hash.content_type == "image/gif"; // TODO: extract this data from files
if !matches!(hash.metadata, Metadata::Image { .. }) || hash.content_type == "image/gif" {
// Only process image files and don't process GIFs if not avatar or icon
if !matches!(hash.metadata, Metadata::Image { .. })
|| (is_animated && !matches!(tag, Tag::avatars | Tag::icons))
{
return Ok( return Ok(
Redirect::permanent(&format!("/{tag}/{file_id}/{}", file.filename)).into_response(), Redirect::permanent(&format!("/{tag_str}/{file_id}/{}", file.filename)).into_response(),
); );
} }
@@ -380,7 +391,7 @@ async fn fetch_preview(
// Read image and create thumbnail // Read image and create thumbnail
let data = create_thumbnail( let data = create_thumbnail(
decode_image(&mut Cursor::new(data), &file.content_type)?, decode_image(&mut Cursor::new(data), &file.content_type)?,
tag, tag_str,
) )
.await; .await;
@@ -398,6 +409,8 @@ async fn fetch_preview(
/// Fetch original file /// Fetch original file
/// ///
/// Content disposition header will be set to 'attachment' to prevent browser from rendering anything. /// Content disposition header will be set to 'attachment' to prevent browser from rendering anything.
///
/// Using `original` as the file name parameter will redirect you to the original file.
#[utoipa::path( #[utoipa::path(
get, get,
path = "/{tag}/{file_id}/{file_name}", path = "/{tag}/{file_id}/{file_name}",
@@ -414,7 +427,8 @@ async fn fetch_file(
State(db): State<Database>, State(db): State<Database>,
Path((tag, file_id, file_name)): Path<(Tag, String, String)>, Path((tag, file_id, file_name)): Path<(Tag, String, String)>,
) -> Result<Response> { ) -> Result<Response> {
let file = db.fetch_attachment(tag.into(), &file_id).await?; let tag: &'static str = tag.clone().into();
let file = db.fetch_attachment(tag, &file_id).await?;
// Ignore deleted files // Ignore deleted files
if file.deleted.is_some_and(|v| v) { if file.deleted.is_some_and(|v| v) {
@@ -428,6 +442,12 @@ async fn fetch_file(
// Ensure filename is correct // Ensure filename is correct
if file_name != file.filename { if file_name != file.filename {
if file_name == "original" {
return Ok(
Redirect::permanent(&format!("/{tag}/{file_id}/{}", file.filename)).into_response(),
);
}
return Err(create_error!(NotFound)); return Err(create_error!(NotFound));
} }
+5 -5
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-january" name = "revolt-january"
version = "0.8.0" version = "0.8.1"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
@@ -31,13 +31,13 @@ tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] } tracing-subscriber = { version = "0.3", features = ["env-filter"] }
# Core crates # Core crates
revolt-config = { version = "0.8.0", path = "../../core/config" } revolt-config = { version = "0.8.1", path = "../../core/config" }
revolt-models = { version = "0.8.0", path = "../../core/models" } revolt-models = { version = "0.8.1", path = "../../core/models" }
revolt-result = { version = "0.8.0", path = "../../core/result", features = [ revolt-result = { version = "0.8.1", path = "../../core/result", features = [
"utoipa", "utoipa",
"axum", "axum",
] } ] }
revolt-files = { version = "0.8.0", path = "../../core/files" } revolt-files = { version = "0.8.1", path = "../../core/files" }
# Axum / web server # Axum / web server
axum = { version = "0.7.5" } axum = { version = "0.7.5" }