Add limits.media.emoji_size_limit configuration parameter

This commit is contained in:
silverpill 2023-03-25 21:50:10 +00:00
parent 0521f1f731
commit 5e1f441e8b
15 changed files with 80 additions and 43 deletions

View file

@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Added `prune-remote-emojis` command. - Added `prune-remote-emojis` command.
- Prune remote emojis in background. - Prune remote emojis in background.
- Added `limits.media.emoji_size_limit` configuration parameter.
### Changed ### Changed

View file

@ -14,7 +14,11 @@ use mitra::ethereum::{
sync::save_current_block_number, sync::save_current_block_number,
utils::key_to_ethereum_address, utils::key_to_ethereum_address,
}; };
use mitra::media::{remove_files, remove_media}; use mitra::media::{
remove_files,
remove_media,
MediaStorage,
};
use mitra::models::{ use mitra::models::{
attachments::queries::delete_unused_attachments, attachments::queries::delete_unused_attachments,
cleanup::find_orphaned_files, cleanup::find_orphaned_files,
@ -230,7 +234,7 @@ impl RefetchActor {
update_remote_profile( update_remote_profile(
db_client, db_client,
&config.instance(), &config.instance(),
&config.media_dir(), &MediaStorage::from(config),
profile, profile,
actor, actor,
).await?; ).await?;

View file

@ -38,6 +38,7 @@ fn deserialize_file_size<'de, D>(
} }
const fn default_file_size_limit() -> usize { 20_000_000 } // 20 MB const fn default_file_size_limit() -> usize { 20_000_000 } // 20 MB
const fn default_emoji_size_limit() -> usize { 500_000 } // 500 kB
#[derive(Clone, Deserialize)] #[derive(Clone, Deserialize)]
pub struct MediaLimits { pub struct MediaLimits {
@ -46,12 +47,19 @@ pub struct MediaLimits {
deserialize_with = "deserialize_file_size", deserialize_with = "deserialize_file_size",
)] )]
pub file_size_limit: usize, pub file_size_limit: usize,
#[serde(
default = "default_emoji_size_limit",
deserialize_with = "deserialize_file_size",
)]
pub emoji_size_limit: usize,
} }
impl Default for MediaLimits { impl Default for MediaLimits {
fn default() -> Self { fn default() -> Self {
Self { Self {
file_size_limit: default_file_size_limit(), file_size_limit: default_file_size_limit(),
emoji_size_limit: default_emoji_size_limit(),
} }
} }
} }

View file

@ -12,6 +12,7 @@ use crate::activitypub::{
vocabulary::{EMOJI, HASHTAG}, vocabulary::{EMOJI, HASHTAG},
}; };
use crate::database::DatabaseClient; use crate::database::DatabaseClient;
use crate::media::MediaStorage;
use crate::models::{ use crate::models::{
profiles::queries::{create_profile, update_profile}, profiles::queries::{create_profile, update_profile},
profiles::types::{ profiles::types::{
@ -104,7 +105,7 @@ fn parse_aliases(actor: &Actor) -> Vec<String> {
async fn parse_tags( async fn parse_tags(
db_client: &impl DatabaseClient, db_client: &impl DatabaseClient,
instance: &Instance, instance: &Instance,
media_dir: &Path, storage: &MediaStorage,
actor: &Actor, actor: &Actor,
) -> Result<Vec<Uuid>, HandlerError> { ) -> Result<Vec<Uuid>, HandlerError> {
let mut emojis = vec![]; let mut emojis = vec![];
@ -118,7 +119,7 @@ async fn parse_tags(
match handle_emoji( match handle_emoji(
db_client, db_client,
instance, instance,
media_dir, storage,
tag_value, tag_value,
).await? { ).await? {
Some(emoji) => { Some(emoji) => {
@ -138,7 +139,7 @@ async fn parse_tags(
pub async fn create_remote_profile( pub async fn create_remote_profile(
db_client: &mut impl DatabaseClient, db_client: &mut impl DatabaseClient,
instance: &Instance, instance: &Instance,
media_dir: &Path, storage: &MediaStorage,
actor: Actor, actor: Actor,
) -> Result<DbActorProfile, HandlerError> { ) -> Result<DbActorProfile, HandlerError> {
let actor_address = actor.address()?; let actor_address = actor.address()?;
@ -148,7 +149,7 @@ pub async fn create_remote_profile(
let (maybe_avatar, maybe_banner) = fetch_actor_images( let (maybe_avatar, maybe_banner) = fetch_actor_images(
instance, instance,
&actor, &actor,
media_dir, &storage.media_dir,
None, None,
None, None,
).await; ).await;
@ -158,7 +159,7 @@ pub async fn create_remote_profile(
let emojis = parse_tags( let emojis = parse_tags(
db_client, db_client,
instance, instance,
media_dir, storage,
&actor, &actor,
).await?; ).await?;
let mut profile_data = ProfileCreateData { let mut profile_data = ProfileCreateData {
@ -185,7 +186,7 @@ pub async fn create_remote_profile(
pub async fn update_remote_profile( pub async fn update_remote_profile(
db_client: &mut impl DatabaseClient, db_client: &mut impl DatabaseClient,
instance: &Instance, instance: &Instance,
media_dir: &Path, storage: &MediaStorage,
profile: DbActorProfile, profile: DbActorProfile,
actor: Actor, actor: Actor,
) -> Result<DbActorProfile, HandlerError> { ) -> Result<DbActorProfile, HandlerError> {
@ -207,7 +208,7 @@ pub async fn update_remote_profile(
let (maybe_avatar, maybe_banner) = fetch_actor_images( let (maybe_avatar, maybe_banner) = fetch_actor_images(
instance, instance,
&actor, &actor,
media_dir, &storage.media_dir,
profile.avatar, profile.avatar,
profile.banner, profile.banner,
).await; ).await;
@ -217,7 +218,7 @@ pub async fn update_remote_profile(
let emojis = parse_tags( let emojis = parse_tags(
db_client, db_client,
instance, instance,
media_dir, storage,
&actor, &actor,
).await?; ).await?;
let mut profile_data = ProfileUpdateData { let mut profile_data = ProfileUpdateData {

View file

@ -24,6 +24,7 @@ use crate::json_signatures::{
JsonSigner, JsonSigner,
}, },
}; };
use crate::media::MediaStorage;
use crate::models::{ use crate::models::{
profiles::queries::get_profile_by_remote_actor_id, profiles::queries::get_profile_by_remote_actor_id,
profiles::types::DbActorProfile, profiles::types::DbActorProfile,
@ -89,7 +90,7 @@ async fn get_signer(
match get_or_import_profile_by_actor_id( match get_or_import_profile_by_actor_id(
db_client, db_client,
&config.instance(), &config.instance(),
&config.media_dir(), &MediaStorage::from(config),
signer_id, signer_id,
).await { ).await {
Ok(profile) => profile, Ok(profile) => profile,

View file

@ -1,5 +1,4 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::path::Path;
use mitra_config::{Config, Instance}; use mitra_config::{Config, Instance};
@ -12,6 +11,7 @@ use crate::activitypub::{
}; };
use crate::database::{DatabaseClient, DatabaseError}; use crate::database::{DatabaseClient, DatabaseError};
use crate::errors::ValidationError; use crate::errors::ValidationError;
use crate::media::MediaStorage;
use crate::models::{ use crate::models::{
posts::helpers::get_local_post_by_id, posts::helpers::get_local_post_by_id,
posts::queries::get_post_by_remote_object_id, posts::queries::get_post_by_remote_object_id,
@ -33,7 +33,7 @@ use super::fetchers::{
pub async fn get_or_import_profile_by_actor_id( pub async fn get_or_import_profile_by_actor_id(
db_client: &mut impl DatabaseClient, db_client: &mut impl DatabaseClient,
instance: &Instance, instance: &Instance,
media_dir: &Path, storage: &MediaStorage,
actor_id: &str, actor_id: &str,
) -> Result<DbActorProfile, HandlerError> { ) -> Result<DbActorProfile, HandlerError> {
if actor_id.starts_with(&instance.url()) { if actor_id.starts_with(&instance.url()) {
@ -52,7 +52,7 @@ pub async fn get_or_import_profile_by_actor_id(
let profile_updated = update_remote_profile( let profile_updated = update_remote_profile(
db_client, db_client,
instance, instance,
media_dir, storage,
profile, profile,
actor, actor,
).await?; ).await?;
@ -81,7 +81,7 @@ pub async fn get_or_import_profile_by_actor_id(
let profile_updated = update_remote_profile( let profile_updated = update_remote_profile(
db_client, db_client,
instance, instance,
media_dir, storage,
profile, profile,
actor, actor,
).await?; ).await?;
@ -92,7 +92,7 @@ pub async fn get_or_import_profile_by_actor_id(
let profile = create_remote_profile( let profile = create_remote_profile(
db_client, db_client,
instance, instance,
media_dir, storage,
actor, actor,
).await?; ).await?;
profile profile
@ -109,7 +109,7 @@ pub async fn get_or_import_profile_by_actor_id(
pub async fn import_profile_by_actor_address( pub async fn import_profile_by_actor_address(
db_client: &mut impl DatabaseClient, db_client: &mut impl DatabaseClient,
instance: &Instance, instance: &Instance,
media_dir: &Path, storage: &MediaStorage,
actor_address: &ActorAddress, actor_address: &ActorAddress,
) -> Result<DbActorProfile, HandlerError> { ) -> Result<DbActorProfile, HandlerError> {
if actor_address.hostname == instance.hostname() { if actor_address.hostname == instance.hostname() {
@ -130,7 +130,7 @@ pub async fn import_profile_by_actor_address(
let profile = create_remote_profile( let profile = create_remote_profile(
db_client, db_client,
instance, instance,
media_dir, storage,
actor, actor,
).await?; ).await?;
Ok(profile) Ok(profile)
@ -140,7 +140,7 @@ pub async fn import_profile_by_actor_address(
pub async fn get_or_import_profile_by_actor_address( pub async fn get_or_import_profile_by_actor_address(
db_client: &mut impl DatabaseClient, db_client: &mut impl DatabaseClient,
instance: &Instance, instance: &Instance,
media_dir: &Path, storage: &MediaStorage,
actor_address: &ActorAddress, actor_address: &ActorAddress,
) -> Result<DbActorProfile, HandlerError> { ) -> Result<DbActorProfile, HandlerError> {
let acct = actor_address.acct(&instance.hostname()); let acct = actor_address.acct(&instance.hostname());
@ -156,7 +156,7 @@ pub async fn get_or_import_profile_by_actor_address(
import_profile_by_actor_address( import_profile_by_actor_address(
db_client, db_client,
instance, instance,
media_dir, storage,
actor_address, actor_address,
).await? ).await?
}, },

View file

@ -11,6 +11,7 @@ use crate::activitypub::{
}; };
use crate::database::{DatabaseClient, DatabaseError}; use crate::database::{DatabaseClient, DatabaseError};
use crate::errors::ValidationError; use crate::errors::ValidationError;
use crate::media::MediaStorage;
use crate::models::{ use crate::models::{
posts::queries::{ posts::queries::{
create_post, create_post,
@ -54,7 +55,7 @@ pub async fn handle_announce(
let author = get_or_import_profile_by_actor_id( let author = get_or_import_profile_by_actor_id(
db_client, db_client,
&config.instance(), &config.instance(),
&config.media_dir(), &MediaStorage::from(config),
&activity.actor, &activity.actor,
).await?; ).await?;
let post_id = match parse_local_object_id( let post_id = match parse_local_object_id(

View file

@ -1,5 +1,4 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::path::Path;
use chrono::Utc; use chrono::Utc;
use serde_json::{Value as JsonValue}; use serde_json::{Value as JsonValue};
@ -27,6 +26,7 @@ use crate::activitypub::{
}; };
use crate::database::{DatabaseClient, DatabaseError}; use crate::database::{DatabaseClient, DatabaseError};
use crate::errors::ValidationError; use crate::errors::ValidationError;
use crate::media::MediaStorage;
use crate::models::{ use crate::models::{
attachments::queries::create_attachment, attachments::queries::create_attachment,
emojis::queries::{ emojis::queries::{
@ -45,7 +45,6 @@ use crate::models::{
use crate::validators::{ use crate::validators::{
emojis::{ emojis::{
validate_emoji_name, validate_emoji_name,
EMOJI_MAX_SIZE,
EMOJI_MEDIA_TYPES, EMOJI_MEDIA_TYPES,
}, },
posts::{ posts::{
@ -242,7 +241,7 @@ pub fn get_object_links(
pub async fn handle_emoji( pub async fn handle_emoji(
db_client: &impl DatabaseClient, db_client: &impl DatabaseClient,
instance: &Instance, instance: &Instance,
media_dir: &Path, storage: &MediaStorage,
tag_value: JsonValue, tag_value: JsonValue,
) -> Result<Option<DbEmoji>, HandlerError> { ) -> Result<Option<DbEmoji>, HandlerError> {
let tag: EmojiTag = match serde_json::from_value(tag_value) { let tag: EmojiTag = match serde_json::from_value(tag_value) {
@ -279,8 +278,8 @@ pub async fn handle_emoji(
instance, instance,
&tag.icon.url, &tag.icon.url,
tag.icon.media_type.as_deref(), tag.icon.media_type.as_deref(),
EMOJI_MAX_SIZE, storage.emoji_size_limit,
media_dir, &storage.media_dir,
).await { ).await {
Ok(file) => file, Ok(file) => file,
Err(error) => { Err(error) => {
@ -338,7 +337,7 @@ pub async fn get_object_tags(
redirects: &HashMap<String, String>, redirects: &HashMap<String, String>,
) -> Result<(Vec<Uuid>, Vec<String>, Vec<Uuid>, Vec<Uuid>), HandlerError> { ) -> Result<(Vec<Uuid>, Vec<String>, Vec<Uuid>, Vec<Uuid>), HandlerError> {
let instance = config.instance(); let instance = config.instance();
let media_dir = config.media_dir(); let storage = MediaStorage::from(config);
let mut mentions = vec![]; let mut mentions = vec![];
let mut hashtags = vec![]; let mut hashtags = vec![];
let mut links = vec![]; let mut links = vec![];
@ -383,7 +382,7 @@ pub async fn get_object_tags(
match get_or_import_profile_by_actor_id( match get_or_import_profile_by_actor_id(
db_client, db_client,
&instance, &instance,
&media_dir, &storage,
&href, &href,
).await { ).await {
Ok(profile) => { Ok(profile) => {
@ -413,7 +412,7 @@ pub async fn get_object_tags(
let profile = match get_or_import_profile_by_actor_address( let profile = match get_or_import_profile_by_actor_address(
db_client, db_client,
&instance, &instance,
&media_dir, &storage,
&actor_address, &actor_address,
).await { ).await {
Ok(profile) => profile, Ok(profile) => profile,
@ -469,7 +468,7 @@ pub async fn get_object_tags(
match handle_emoji( match handle_emoji(
db_client, db_client,
&instance, &instance,
&media_dir, &storage,
tag_value, tag_value,
).await? { ).await? {
Some(emoji) => { Some(emoji) => {
@ -534,7 +533,7 @@ pub async fn handle_note(
redirects: &HashMap<String, String>, redirects: &HashMap<String, String>,
) -> Result<Post, HandlerError> { ) -> Result<Post, HandlerError> {
let instance = config.instance(); let instance = config.instance();
let media_dir = config.media_dir(); let storage = MediaStorage::from(config);
match object.object_type.as_str() { match object.object_type.as_str() {
NOTE => (), NOTE => (),
ARTICLE | EVENT | QUESTION | PAGE | VIDEO => { ARTICLE | EVENT | QUESTION | PAGE | VIDEO => {
@ -550,7 +549,7 @@ pub async fn handle_note(
let author = get_or_import_profile_by_actor_id( let author = get_or_import_profile_by_actor_id(
db_client, db_client,
&instance, &instance,
&media_dir, &storage,
&author_id, &author_id,
).await.map_err(|err| { ).await.map_err(|err| {
log::warn!("failed to import {} ({})", author_id, err); log::warn!("failed to import {} ({})", author_id, err);

View file

@ -12,6 +12,7 @@ use crate::activitypub::{
}; };
use crate::database::{DatabaseClient, DatabaseError}; use crate::database::{DatabaseClient, DatabaseError};
use crate::errors::ValidationError; use crate::errors::ValidationError;
use crate::media::MediaStorage;
use crate::models::{ use crate::models::{
relationships::queries::{ relationships::queries::{
create_remote_follow_request_opt, create_remote_follow_request_opt,
@ -40,7 +41,7 @@ pub async fn handle_follow(
let source_profile = get_or_import_profile_by_actor_id( let source_profile = get_or_import_profile_by_actor_id(
db_client, db_client,
&config.instance(), &config.instance(),
&config.media_dir(), &MediaStorage::from(config),
&activity.actor, &activity.actor,
).await?; ).await?;
let source_actor = source_profile.actor_json let source_actor = source_profile.actor_json

View file

@ -13,6 +13,7 @@ use crate::activitypub::{
}; };
use crate::database::{DatabaseClient, DatabaseError}; use crate::database::{DatabaseClient, DatabaseError};
use crate::errors::ValidationError; use crate::errors::ValidationError;
use crate::media::MediaStorage;
use crate::models::{ use crate::models::{
reactions::queries::create_reaction, reactions::queries::create_reaction,
}; };
@ -36,7 +37,7 @@ pub async fn handle_like(
let author = get_or_import_profile_by_actor_id( let author = get_or_import_profile_by_actor_id(
db_client, db_client,
&config.instance(), &config.instance(),
&config.media_dir(), &MediaStorage::from(config),
&activity.actor, &activity.actor,
).await?; ).await?;
let post_id = match get_post_by_object_id( let post_id = match get_post_by_object_id(

View file

@ -15,6 +15,7 @@ use crate::activitypub::{
}; };
use crate::database::{DatabaseClient, DatabaseError}; use crate::database::{DatabaseClient, DatabaseError};
use crate::errors::ValidationError; use crate::errors::ValidationError;
use crate::media::MediaStorage;
use crate::models::{ use crate::models::{
notifications::queries::create_move_notification, notifications::queries::create_move_notification,
profiles::helpers::find_aliases, profiles::helpers::find_aliases,
@ -49,7 +50,7 @@ pub async fn handle_move(
}; };
let instance = config.instance(); let instance = config.instance();
let media_dir = config.media_dir(); let storage = MediaStorage::from(config);
let old_profile = if let Ok(username) = parse_local_actor_id( let old_profile = if let Ok(username) = parse_local_actor_id(
&instance.url(), &instance.url(),
&activity.object, &activity.object,
@ -60,7 +61,7 @@ pub async fn handle_move(
get_or_import_profile_by_actor_id( get_or_import_profile_by_actor_id(
db_client, db_client,
&instance, &instance,
&media_dir, &storage,
&activity.object, &activity.object,
).await? ).await?
}; };
@ -68,7 +69,7 @@ pub async fn handle_move(
let new_profile = get_or_import_profile_by_actor_id( let new_profile = get_or_import_profile_by_actor_id(
db_client, db_client,
&instance, &instance,
&media_dir, &storage,
&activity.target, &activity.target,
).await?; ).await?;
let new_actor = new_profile.actor_json.as_ref() let new_actor = new_profile.actor_json.as_ref()

View file

@ -23,6 +23,7 @@ use crate::activitypub::{
}; };
use crate::database::{DatabaseClient, DatabaseError}; use crate::database::{DatabaseClient, DatabaseError};
use crate::errors::ValidationError; use crate::errors::ValidationError;
use crate::media::MediaStorage;
use crate::models::{ use crate::models::{
posts::queries::{ posts::queries::{
get_post_by_remote_object_id, get_post_by_remote_object_id,
@ -110,7 +111,7 @@ async fn handle_update_person(
update_remote_profile( update_remote_profile(
db_client, db_client,
&config.instance(), &config.instance(),
&config.media_dir(), &MediaStorage::from(config),
profile, profile,
activity.object, activity.object,
).await?; ).await?;

View file

@ -21,6 +21,7 @@ use crate::activitypub::{
use crate::database::{DatabaseClient, DatabaseError}; use crate::database::{DatabaseClient, DatabaseError};
use crate::errors::ValidationError; use crate::errors::ValidationError;
use crate::ethereum::utils::validate_ethereum_address; use crate::ethereum::utils::validate_ethereum_address;
use crate::media::MediaStorage;
use crate::models::{ use crate::models::{
posts::{ posts::{
helpers::{can_view_post, get_local_post_by_id}, helpers::{can_view_post, get_local_post_by_id},
@ -124,7 +125,7 @@ async fn search_profiles_or_import(
match import_profile_by_actor_address( match import_profile_by_actor_address(
db_client, db_client,
&config.instance(), &config.instance(),
&config.media_dir(), &MediaStorage::from(config),
&actor_address, &actor_address,
).await { ).await {
Ok(profile) => { Ok(profile) => {
@ -204,7 +205,7 @@ async fn find_profile_by_url(
get_or_import_profile_by_actor_id( get_or_import_profile_by_actor_id(
db_client, db_client,
&config.instance(), &config.instance(),
&config.media_dir(), &MediaStorage::from(config),
url, url,
).await ).await
.map_err(|err| log::warn!("{}", err)) .map_err(|err| log::warn!("{}", err))

View file

@ -18,6 +18,7 @@ use crate::database::{
}; };
use crate::errors::ValidationError; use crate::errors::ValidationError;
use crate::mastodon_api::accounts::helpers::follow_or_create_request; use crate::mastodon_api::accounts::helpers::follow_or_create_request;
use crate::media::MediaStorage;
use crate::models::{ use crate::models::{
profiles::types::DbActorProfile, profiles::types::DbActorProfile,
relationships::queries::{ relationships::queries::{
@ -88,11 +89,12 @@ pub async fn import_follows_task(
address_list: Vec<ActorAddress>, address_list: Vec<ActorAddress>,
) -> Result<(), anyhow::Error> { ) -> Result<(), anyhow::Error> {
let db_client = &mut **get_database_client(db_pool).await?; let db_client = &mut **get_database_client(db_pool).await?;
let storage = MediaStorage::from(config);
for actor_address in address_list { for actor_address in address_list {
let profile = match get_or_import_profile_by_actor_address( let profile = match get_or_import_profile_by_actor_address(
db_client, db_client,
&config.instance(), &config.instance(),
&config.media_dir(), &storage,
&actor_address, &actor_address,
).await { ).await {
Ok(profile) => profile, Ok(profile) => profile,
@ -129,12 +131,13 @@ pub async fn move_followers_task(
) -> Result<(), anyhow::Error> { ) -> Result<(), anyhow::Error> {
let db_client = &mut **get_database_client(db_pool).await?; let db_client = &mut **get_database_client(db_pool).await?;
let instance = config.instance(); let instance = config.instance();
let storage = MediaStorage::from(config);
let mut remote_followers = vec![]; let mut remote_followers = vec![];
for follower_address in address_list { for follower_address in address_list {
let follower = match get_or_import_profile_by_actor_address( let follower = match get_or_import_profile_by_actor_address(
db_client, db_client,
&instance, &instance,
&config.media_dir(), &storage,
&follower_address, &follower_address,
).await { ).await {
Ok(profile) => profile, Ok(profile) => profile,

View file

@ -1,6 +1,6 @@
use std::fs::remove_file; use std::fs::remove_file;
use std::io::Error; use std::io::Error;
use std::path::Path; use std::path::{Path, PathBuf};
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
@ -87,6 +87,20 @@ pub async fn remove_media(
} }
} }
pub struct MediaStorage {
pub media_dir: PathBuf,
pub emoji_size_limit: usize,
}
impl From<&Config> for MediaStorage {
fn from(config: &Config) -> Self {
Self {
media_dir: config.media_dir(),
emoji_size_limit: config.limits.media.emoji_size_limit,
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use mitra_utils::files::sniff_media_type; use mitra_utils::files::sniff_media_type;