mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-05-26 20:49:55 +00:00
feat: Remove support for server-side blurhashing
This commit is contained in:
@@ -322,8 +322,6 @@ pub(crate) async fn check_registration_token_validity(
|
||||
/// Runs through all the deactivation steps:
|
||||
///
|
||||
/// - Mark as deactivated
|
||||
/// - Removing display name
|
||||
/// - Removing avatar URL and blurhash
|
||||
/// - Removing all profile data
|
||||
/// - Leaving all rooms (and forgets all of them)
|
||||
pub async fn full_user_deactivate(
|
||||
|
||||
+1
-12
@@ -21,7 +21,6 @@ use ruma::{
|
||||
},
|
||||
media::create_content,
|
||||
},
|
||||
assign,
|
||||
};
|
||||
use service::media::mxc::Mxc;
|
||||
|
||||
@@ -76,17 +75,7 @@ pub(crate) async fn create_content_route(
|
||||
return Err!(Request(Unknown("Failed to save uploaded media")));
|
||||
}
|
||||
|
||||
let blurhash = body.generate_blurhash.then(|| {
|
||||
services
|
||||
.media
|
||||
.create_blurhash(&body.file, content_type, filename)
|
||||
.ok()
|
||||
.flatten()
|
||||
});
|
||||
|
||||
Ok(assign!(create_content::v3::Response::new(mxc.to_string().into()), {
|
||||
blurhash: blurhash.flatten(),
|
||||
}))
|
||||
Ok(create_content::v3::Response::new(mxc.to_string().into()))
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/client/v1/media/thumbnail/{serverName}/{mediaId}`
|
||||
|
||||
@@ -247,7 +247,6 @@ pub(crate) async fn invite_helper(
|
||||
let mut content = RoomMemberEventContent::new(MembershipState::Invite);
|
||||
content.displayname = services.users.displayname(recipient_user).await.ok();
|
||||
content.avatar_url = services.users.avatar_url(recipient_user).await.ok();
|
||||
content.blurhash = services.users.blurhash(recipient_user).await.ok();
|
||||
content.is_direct = Some(is_direct);
|
||||
content.reason = reason;
|
||||
|
||||
|
||||
@@ -343,7 +343,6 @@ async fn knock_room_helper_local(
|
||||
let mut content = RoomMemberEventContent::new(MembershipState::Knock);
|
||||
content.displayname = services.users.displayname(sender_user).await.ok();
|
||||
content.avatar_url = services.users.avatar_url(sender_user).await.ok();
|
||||
content.blurhash = services.users.blurhash(sender_user).await.ok();
|
||||
content.reason.clone_from(&reason.clone());
|
||||
|
||||
// Try normal knock first
|
||||
@@ -527,7 +526,6 @@ async fn knock_room_helper_remote(
|
||||
let mut knock_content = RoomMemberEventContent::new(MembershipState::Knock);
|
||||
knock_content.displayname = services.users.displayname(sender_user).await.ok();
|
||||
knock_content.avatar_url = services.users.avatar_url(sender_user).await.ok();
|
||||
knock_content.blurhash = services.users.blurhash(sender_user).await.ok();
|
||||
knock_content.reason = reason;
|
||||
|
||||
knock_event_stub.insert(
|
||||
|
||||
@@ -23,8 +23,7 @@ use crate::Ruma;
|
||||
|
||||
/// # `GET /_matrix/client/v3/profile/{userId}`
|
||||
///
|
||||
/// Returns the displayname, avatar_url, blurhash, and custom profile fields of
|
||||
/// the user.
|
||||
/// Returns the user's profile information.
|
||||
///
|
||||
/// - If user is on another server and we do not have a local copy already,
|
||||
/// fetch profile over federation.
|
||||
@@ -322,19 +321,9 @@ async fn set_profile_field(
|
||||
services.users.set_avatar_url(user_id, None);
|
||||
},
|
||||
| other =>
|
||||
if other.field_name().as_str() == "blurhash" {
|
||||
if let Some(Value::String(blurhash)) = other.value() {
|
||||
services.users.set_blurhash(user_id, Some(blurhash));
|
||||
} else {
|
||||
services.users.set_blurhash(user_id, None);
|
||||
}
|
||||
} else {
|
||||
services.users.set_profile_key(
|
||||
user_id,
|
||||
other.field_name().as_str(),
|
||||
other.value(),
|
||||
);
|
||||
},
|
||||
services
|
||||
.users
|
||||
.set_profile_key(user_id, other.field_name().as_str(), other.value()),
|
||||
}
|
||||
|
||||
// If the user is local and changed their displayname or avatar_url, update it
|
||||
|
||||
@@ -288,7 +288,6 @@ pub(crate) async fn create_room_route(
|
||||
let mut join_event = RoomMemberEventContent::new(MembershipState::Join);
|
||||
join_event.displayname = services.users.displayname(sender_user).await.ok();
|
||||
join_event.avatar_url = services.users.avatar_url(sender_user).await.ok();
|
||||
join_event.blurhash = services.users.blurhash(sender_user).await.ok();
|
||||
join_event.is_direct = Some(body.is_direct);
|
||||
|
||||
debug_info!("Joining {sender_user} to room {room_id}");
|
||||
|
||||
@@ -271,7 +271,6 @@ pub(crate) async fn upgrade_room_route(
|
||||
&assign!(RoomMemberEventContent::new(MembershipState::Join), {
|
||||
displayname: services.users.displayname(sender_user).await.ok(),
|
||||
avatar_url: services.users.avatar_url(sender_user).await.ok(),
|
||||
blurhash: services.users.blurhash(sender_user).await.ok(),
|
||||
}),
|
||||
),
|
||||
sender_user,
|
||||
|
||||
@@ -2117,10 +2117,6 @@ pub struct Config {
|
||||
#[serde(default)]
|
||||
pub antispam: Option<Antispam>,
|
||||
|
||||
/// display: nested
|
||||
#[serde(default)]
|
||||
pub blurhashing: BlurhashConfig,
|
||||
|
||||
/// Configuration for MatrixRTC (MSC4143) transport discovery.
|
||||
/// display: nested
|
||||
#[serde(default)]
|
||||
@@ -2196,31 +2192,6 @@ pub struct WellKnownConfig {
|
||||
pub support_pgp_key: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Default)]
|
||||
#[allow(rustdoc::broken_intra_doc_links, rustdoc::bare_urls)]
|
||||
#[config_example_generator(filename = "conduwuit-example.toml", section = "global.blurhashing")]
|
||||
pub struct BlurhashConfig {
|
||||
/// blurhashing x component, 4 is recommended by https://blurha.sh/
|
||||
///
|
||||
/// default: 4
|
||||
#[serde(default = "default_blurhash_x_component")]
|
||||
pub components_x: u32,
|
||||
/// blurhashing y component, 3 is recommended by https://blurha.sh/
|
||||
///
|
||||
/// default: 3
|
||||
#[serde(default = "default_blurhash_y_component")]
|
||||
pub components_y: u32,
|
||||
/// Max raw size that the server will blurhash, this is the size of the
|
||||
/// image after converting it to raw data, it should be higher than the
|
||||
/// upload limit but not too high. The higher it is the higher the
|
||||
/// potential load will be for clients requesting blurhashes. The default
|
||||
/// is 33.55MB. Setting it to 0 disables blurhashing.
|
||||
///
|
||||
/// default: 33554432
|
||||
#[serde(default = "default_blurhash_max_raw_size")]
|
||||
pub blurhash_max_raw_size: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Default)]
|
||||
#[config_example_generator(filename = "conduwuit-example.toml", section = "global.matrix_rtc")]
|
||||
pub struct MatrixRtcConfig {
|
||||
@@ -2752,13 +2723,3 @@ fn default_client_response_timeout() -> u64 { 120 }
|
||||
fn default_client_shutdown_timeout() -> u64 { 15 }
|
||||
|
||||
fn default_sender_shutdown_timeout() -> u64 { 5 }
|
||||
|
||||
// blurhashing defaults recommended by https://blurha.sh/
|
||||
// 2^25
|
||||
pub(super) fn default_blurhash_max_raw_size() -> u64 { 33_554_432 }
|
||||
|
||||
pub(super) fn default_blurhash_x_component() -> u32 { 4 }
|
||||
|
||||
pub(super) fn default_blurhash_y_component() -> u32 { 3 }
|
||||
|
||||
// end recommended & blurhashing defaults
|
||||
|
||||
@@ -376,7 +376,7 @@ pub(super) static MAPS: &[Descriptor] = &[
|
||||
},
|
||||
Descriptor {
|
||||
name: "userid_blurhash",
|
||||
..descriptor::RANDOM_SMALL
|
||||
..descriptor::DROPPED
|
||||
},
|
||||
Descriptor {
|
||||
name: "userid_dehydrateddevice",
|
||||
|
||||
@@ -47,7 +47,6 @@ default = [
|
||||
"bindgen-runtime", # replace with bindgen-static on alpine
|
||||
]
|
||||
standard = [
|
||||
"blurhashing",
|
||||
"brotli_compression",
|
||||
"element_hacks",
|
||||
"gzip_compression",
|
||||
@@ -71,9 +70,6 @@ full = [
|
||||
"tokio_console",
|
||||
]
|
||||
|
||||
blurhashing = [
|
||||
"conduwuit-service/blurhashing",
|
||||
]
|
||||
brotli_compression = [
|
||||
"conduwuit-api/brotli_compression",
|
||||
"conduwuit-core/brotli_compression",
|
||||
|
||||
@@ -16,10 +16,6 @@ crate-type = [
|
||||
]
|
||||
|
||||
[features]
|
||||
blurhashing = [
|
||||
"dep:image",
|
||||
"dep:blurhash",
|
||||
]
|
||||
brotli_compression = [
|
||||
"conduwuit-core/brotli_compression",
|
||||
"reqwest/brotli",
|
||||
@@ -119,8 +115,6 @@ tracing.workspace = true
|
||||
url.workspace = true
|
||||
webpage.workspace = true
|
||||
webpage.optional = true
|
||||
blurhash.workspace = true
|
||||
blurhash.optional = true
|
||||
recaptcha-verify = { version = "0.2.0", default-features = false }
|
||||
reqwest_recaptcha = { package = "reqwest", version = "0.12.28", default-features = false, features = ["rustls-tls-native-roots-no-provider"] } # As long as recaptcha-verify's reqwest is outdated
|
||||
yansi.workspace = true
|
||||
|
||||
@@ -1,179 +0,0 @@
|
||||
#[cfg(feature = "blurhashing")]
|
||||
use conduwuit::config::BlurhashConfig as CoreBlurhashConfig;
|
||||
use conduwuit::{Result, implement};
|
||||
|
||||
use super::Service;
|
||||
|
||||
#[implement(Service)]
|
||||
#[cfg(not(feature = "blurhashing"))]
|
||||
pub fn create_blurhash(
|
||||
&self,
|
||||
_file: &[u8],
|
||||
_content_type: Option<&str>,
|
||||
_file_name: Option<&str>,
|
||||
) -> Result<Option<String>> {
|
||||
conduwuit::debug_warn!("blurhashing on upload support was not compiled");
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[implement(Service)]
|
||||
#[cfg(feature = "blurhashing")]
|
||||
pub fn create_blurhash(
|
||||
&self,
|
||||
file: &[u8],
|
||||
content_type: Option<&str>,
|
||||
file_name: Option<&str>,
|
||||
) -> Result<Option<String>> {
|
||||
let config = BlurhashConfig::from(self.services.server.config.blurhashing);
|
||||
|
||||
// since 0 means disabled blurhashing, skipped blurhashing
|
||||
if config.size_limit == 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
get_blurhash_from_request(file, content_type, file_name, config)
|
||||
.map_err(|e| conduwuit::err!(debug_error!("blurhashing error: {e}")))
|
||||
.map(Some)
|
||||
}
|
||||
|
||||
/// Returns the blurhash or a blurhash error which implements Display.
|
||||
#[tracing::instrument(
|
||||
name = "blurhash",
|
||||
level = "debug",
|
||||
skip(data),
|
||||
fields(
|
||||
bytes = data.len(),
|
||||
),
|
||||
)]
|
||||
#[cfg(feature = "blurhashing")]
|
||||
fn get_blurhash_from_request(
|
||||
data: &[u8],
|
||||
mime: Option<&str>,
|
||||
filename: Option<&str>,
|
||||
config: BlurhashConfig,
|
||||
) -> Result<String, BlurhashingError> {
|
||||
// Get format image is supposed to be in
|
||||
let format = get_format_from_data_mime_and_filename(data, mime, filename)?;
|
||||
|
||||
// Get the image reader for said image format
|
||||
let decoder = get_image_decoder_with_format_and_data(format, data)?;
|
||||
|
||||
// Check image size makes sense before unpacking whole image
|
||||
if is_image_above_size_limit(&decoder, config) {
|
||||
return Err(BlurhashingError::ImageTooLarge);
|
||||
}
|
||||
|
||||
let image = image::DynamicImage::from_decoder(decoder)?;
|
||||
|
||||
blurhash_an_image(&image, config)
|
||||
}
|
||||
|
||||
/// Gets the Image Format value from the data,mime, and filename
|
||||
/// It first checks if the mime is a valid image format
|
||||
/// Then it checks if the filename has a format, otherwise just guess based on
|
||||
/// the binary data Assumes that mime and filename extension won't be for a
|
||||
/// different file format than file.
|
||||
#[cfg(feature = "blurhashing")]
|
||||
fn get_format_from_data_mime_and_filename(
|
||||
data: &[u8],
|
||||
mime: Option<&str>,
|
||||
filename: Option<&str>,
|
||||
) -> Result<image::ImageFormat, BlurhashingError> {
|
||||
let extension = filename
|
||||
.map(std::path::Path::new)
|
||||
.and_then(std::path::Path::extension)
|
||||
.map(std::ffi::OsStr::to_string_lossy);
|
||||
|
||||
mime.or(extension.as_deref())
|
||||
.and_then(image::ImageFormat::from_mime_type)
|
||||
.map_or_else(|| image::guess_format(data).map_err(Into::into), Ok)
|
||||
}
|
||||
|
||||
#[cfg(feature = "blurhashing")]
|
||||
fn get_image_decoder_with_format_and_data(
|
||||
image_format: image::ImageFormat,
|
||||
data: &[u8],
|
||||
) -> Result<Box<dyn image::ImageDecoder + '_>, BlurhashingError> {
|
||||
let mut image_reader = image::ImageReader::new(std::io::Cursor::new(data));
|
||||
image_reader.set_format(image_format);
|
||||
Ok(Box::new(image_reader.into_decoder()?))
|
||||
}
|
||||
|
||||
#[cfg(feature = "blurhashing")]
|
||||
fn is_image_above_size_limit<T: image::ImageDecoder>(
|
||||
decoder: &T,
|
||||
blurhash_config: BlurhashConfig,
|
||||
) -> bool {
|
||||
decoder.total_bytes() >= blurhash_config.size_limit
|
||||
}
|
||||
|
||||
#[cfg(feature = "blurhashing")]
|
||||
#[tracing::instrument(name = "encode", level = "debug", skip_all)]
|
||||
#[inline]
|
||||
fn blurhash_an_image(
|
||||
image: &image::DynamicImage,
|
||||
blurhash_config: BlurhashConfig,
|
||||
) -> Result<String, BlurhashingError> {
|
||||
Ok(blurhash::encode_image(
|
||||
blurhash_config.components_x,
|
||||
blurhash_config.components_y,
|
||||
&image.to_rgba8(),
|
||||
)?)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct BlurhashConfig {
|
||||
pub components_x: u32,
|
||||
pub components_y: u32,
|
||||
|
||||
/// size limit in bytes
|
||||
pub size_limit: u64,
|
||||
}
|
||||
|
||||
#[cfg(feature = "blurhashing")]
|
||||
impl From<CoreBlurhashConfig> for BlurhashConfig {
|
||||
fn from(value: CoreBlurhashConfig) -> Self {
|
||||
Self {
|
||||
components_x: value.components_x,
|
||||
components_y: value.components_y,
|
||||
size_limit: value.blurhash_max_raw_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg(feature = "blurhashing")]
|
||||
pub enum BlurhashingError {
|
||||
HashingLibError(Box<dyn std::error::Error + Send>),
|
||||
#[cfg(feature = "blurhashing")]
|
||||
ImageError(Box<image::ImageError>),
|
||||
ImageTooLarge,
|
||||
}
|
||||
|
||||
#[cfg(feature = "blurhashing")]
|
||||
impl From<image::ImageError> for BlurhashingError {
|
||||
fn from(value: image::ImageError) -> Self { Self::ImageError(Box::new(value)) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "blurhashing")]
|
||||
impl From<blurhash::Error> for BlurhashingError {
|
||||
fn from(value: blurhash::Error) -> Self { Self::HashingLibError(Box::new(value)) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "blurhashing")]
|
||||
impl std::fmt::Display for BlurhashingError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Blurhash Error:")?;
|
||||
match &self {
|
||||
| Self::ImageTooLarge => write!(f, "Image was too large to blurhash")?,
|
||||
| Self::HashingLibError(e) =>
|
||||
write!(f, "There was an error with the blurhashing library => {e}")?,
|
||||
#[cfg(feature = "blurhashing")]
|
||||
| Self::ImageError(e) =>
|
||||
write!(f, "There was an error with the image loading library => {e}")?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
pub mod blurhash;
|
||||
mod data;
|
||||
pub(super) mod migrations;
|
||||
pub mod mxc;
|
||||
|
||||
@@ -240,7 +240,6 @@ impl Service {
|
||||
let mut content = RoomMemberEventContent::new(MembershipState::Join);
|
||||
content.displayname = self.services.users.displayname(sender_user).await.ok();
|
||||
content.avatar_url = self.services.users.avatar_url(sender_user).await.ok();
|
||||
content.blurhash = self.services.users.blurhash(sender_user).await.ok();
|
||||
content.reason.clone_from(&reason);
|
||||
content.join_authorized_via_users_server = auth_user;
|
||||
|
||||
@@ -351,7 +350,6 @@ impl Service {
|
||||
let mut join_content = RoomMemberEventContent::new(MembershipState::Join);
|
||||
join_content.displayname = self.services.users.displayname(sender_user).await.ok();
|
||||
join_content.avatar_url = self.services.users.avatar_url(sender_user).await.ok();
|
||||
join_content.blurhash = self.services.users.blurhash(sender_user).await.ok();
|
||||
join_content.reason = reason;
|
||||
join_content
|
||||
.join_authorized_via_users_server
|
||||
|
||||
@@ -79,7 +79,6 @@ struct Data {
|
||||
userdeviceid_token: Arc<Map>,
|
||||
userfilterid_filter: Arc<Map>,
|
||||
userid_avatarurl: Arc<Map>,
|
||||
userid_blurhash: Arc<Map>,
|
||||
userid_dehydrateddevice: Arc<Map>,
|
||||
userid_devicelistversion: Arc<Map>,
|
||||
userid_displayname: Arc<Map>,
|
||||
@@ -120,7 +119,6 @@ impl crate::Service for Service {
|
||||
userdeviceid_token: args.db["userdeviceid_token"].clone(),
|
||||
userfilterid_filter: args.db["userfilterid_filter"].clone(),
|
||||
userid_avatarurl: args.db["userid_avatarurl"].clone(),
|
||||
userid_blurhash: args.db["userid_blurhash"].clone(),
|
||||
userid_dehydrateddevice: args.db["userid_dehydrateddevice"].clone(),
|
||||
userid_devicelistversion: args.db["userid_devicelistversion"].clone(),
|
||||
userid_displayname: args.db["userid_displayname"].clone(),
|
||||
@@ -430,20 +428,6 @@ impl Service {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the blurhash of a user.
|
||||
pub async fn blurhash(&self, user_id: &UserId) -> Result<String> {
|
||||
self.db.userid_blurhash.get(user_id).await.deserialized()
|
||||
}
|
||||
|
||||
/// Sets a new avatar_url or removes it if avatar_url is None.
|
||||
pub fn set_blurhash(&self, user_id: &UserId, blurhash: Option<String>) {
|
||||
if let Some(blurhash) = blurhash {
|
||||
self.db.userid_blurhash.insert(user_id, blurhash);
|
||||
} else {
|
||||
self.db.userid_blurhash.remove(user_id);
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a new device to a user.
|
||||
pub async fn create_device(
|
||||
&self,
|
||||
@@ -1379,7 +1363,6 @@ impl Service {
|
||||
pub async fn clear_profile(&self, user_id: &UserId) {
|
||||
self.set_displayname(user_id, None);
|
||||
self.set_avatar_url(user_id, None);
|
||||
self.set_blurhash(user_id, None);
|
||||
self.all_profile_keys(user_id)
|
||||
.ready_for_each(|(key, _)| self.set_profile_key(user_id, &key, None))
|
||||
.await;
|
||||
|
||||
Reference in New Issue
Block a user