Append attachment URL to post content if attachment is too large
This commit is contained in:
parent
01c894da9d
commit
5a3ef41277
4 changed files with 31 additions and 13 deletions
|
@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Replace post attachments and other related objects when processing `Update(Note)` activity.
|
- Replace post attachments and other related objects when processing `Update(Note)` activity.
|
||||||
|
- Append attachment URL to post content if attachment size exceeds limit.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,9 @@ pub enum FetchError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
FileError(#[from] std::io::Error),
|
FileError(#[from] std::io::Error),
|
||||||
|
|
||||||
|
#[error("file size exceeds limit")]
|
||||||
|
FileTooLarge,
|
||||||
|
|
||||||
#[error("too many objects")]
|
#[error("too many objects")]
|
||||||
RecursionError,
|
RecursionError,
|
||||||
|
|
||||||
|
@ -122,13 +125,13 @@ pub async fn fetch_file(
|
||||||
let file_size: usize = file_size.try_into()
|
let file_size: usize = file_size.try_into()
|
||||||
.expect("value should be within bounds");
|
.expect("value should be within bounds");
|
||||||
if file_size > file_max_size {
|
if file_size > file_max_size {
|
||||||
return Err(FetchError::OtherError("file is too large"));
|
return Err(FetchError::FileTooLarge);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
let file_data = response.bytes().await?;
|
let file_data = response.bytes().await?;
|
||||||
let file_size = file_data.len();
|
let file_size = file_data.len();
|
||||||
if file_size > file_max_size {
|
if file_size > file_max_size {
|
||||||
return Err(FetchError::OtherError("file is too large"));
|
return Err(FetchError::FileTooLarge);
|
||||||
};
|
};
|
||||||
let maybe_media_type = maybe_media_type
|
let maybe_media_type = maybe_media_type
|
||||||
.map(|media_type| media_type.to_string())
|
.map(|media_type| media_type.to_string())
|
||||||
|
|
|
@ -6,7 +6,7 @@ use uuid::Uuid;
|
||||||
|
|
||||||
use crate::activitypub::{
|
use crate::activitypub::{
|
||||||
constants::{AP_MEDIA_TYPE, AP_PUBLIC, AS_MEDIA_TYPE},
|
constants::{AP_MEDIA_TYPE, AP_PUBLIC, AS_MEDIA_TYPE},
|
||||||
fetcher::fetchers::fetch_file,
|
fetcher::fetchers::{fetch_file, FetchError},
|
||||||
fetcher::helpers::{
|
fetcher::helpers::{
|
||||||
get_or_import_profile_by_actor_address,
|
get_or_import_profile_by_actor_address,
|
||||||
get_or_import_profile_by_actor_id,
|
get_or_import_profile_by_actor_id,
|
||||||
|
@ -129,10 +129,11 @@ pub async fn get_object_attachments(
|
||||||
db_client: &impl DatabaseClient,
|
db_client: &impl DatabaseClient,
|
||||||
object: &Object,
|
object: &Object,
|
||||||
author: &DbActorProfile,
|
author: &DbActorProfile,
|
||||||
) -> Result<Vec<Uuid>, HandlerError> {
|
) -> Result<(Vec<Uuid>, Vec<String>), HandlerError> {
|
||||||
let instance = config.instance();
|
let instance = config.instance();
|
||||||
let media_dir = config.media_dir();
|
let media_dir = config.media_dir();
|
||||||
let mut attachments = vec![];
|
let mut attachments = vec![];
|
||||||
|
let mut unprocessed = vec![];
|
||||||
if let Some(ref value) = object.attachment {
|
if let Some(ref value) = object.attachment {
|
||||||
let list: Vec<Attachment> = parse_property_value(value)
|
let list: Vec<Attachment> = parse_property_value(value)
|
||||||
.map_err(|_| ValidationError("invalid attachment property"))?;
|
.map_err(|_| ValidationError("invalid attachment property"))?;
|
||||||
|
@ -157,17 +158,24 @@ pub async fn get_object_attachments(
|
||||||
};
|
};
|
||||||
let attachment_url = attachment.url
|
let attachment_url = attachment.url
|
||||||
.ok_or(ValidationError("attachment URL is missing"))?;
|
.ok_or(ValidationError("attachment URL is missing"))?;
|
||||||
let (file_name, file_size, maybe_media_type) = fetch_file(
|
let (file_name, file_size, maybe_media_type) = match fetch_file(
|
||||||
&instance,
|
&instance,
|
||||||
&attachment_url,
|
&attachment_url,
|
||||||
attachment.media_type.as_deref(),
|
attachment.media_type.as_deref(),
|
||||||
ATTACHMENT_MAX_SIZE,
|
ATTACHMENT_MAX_SIZE,
|
||||||
&media_dir,
|
&media_dir,
|
||||||
).await
|
).await {
|
||||||
.map_err(|err| {
|
Ok(file) => file,
|
||||||
log::warn!("{}", err);
|
Err(FetchError::FileTooLarge) => {
|
||||||
ValidationError("failed to fetch attachment")
|
log::warn!("attachment is too large: {}", attachment_url);
|
||||||
})?;
|
unprocessed.push(attachment_url);
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
Err(other_error) => {
|
||||||
|
log::warn!("{}", other_error);
|
||||||
|
return Err(ValidationError("failed to fetch attachment").into());
|
||||||
|
},
|
||||||
|
};
|
||||||
log::info!("downloaded attachment {}", attachment_url);
|
log::info!("downloaded attachment {}", attachment_url);
|
||||||
downloaded.push((file_name, file_size, maybe_media_type));
|
downloaded.push((file_name, file_size, maybe_media_type));
|
||||||
// Stop downloading if limit is reached
|
// Stop downloading if limit is reached
|
||||||
|
@ -187,7 +195,7 @@ pub async fn get_object_attachments(
|
||||||
attachments.push(db_attachment.id);
|
attachments.push(db_attachment.id);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
Ok(attachments)
|
Ok((attachments, unprocessed))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_object_tags(
|
pub async fn get_object_tags(
|
||||||
|
@ -498,12 +506,15 @@ pub async fn handle_note(
|
||||||
let object_url = get_object_url(&object)?;
|
let object_url = get_object_url(&object)?;
|
||||||
content += &create_content_link(object_url);
|
content += &create_content_link(object_url);
|
||||||
};
|
};
|
||||||
let attachments = get_object_attachments(
|
let (attachments, unprocessed) = get_object_attachments(
|
||||||
config,
|
config,
|
||||||
db_client,
|
db_client,
|
||||||
&object,
|
&object,
|
||||||
&author,
|
&author,
|
||||||
).await?;
|
).await?;
|
||||||
|
for attachment_url in unprocessed {
|
||||||
|
content += &create_content_link(attachment_url);
|
||||||
|
};
|
||||||
if content.is_empty() && attachments.is_empty() {
|
if content.is_empty() && attachments.is_empty() {
|
||||||
return Err(ValidationError("post is empty").into());
|
return Err(ValidationError("post is empty").into());
|
||||||
};
|
};
|
||||||
|
|
|
@ -54,12 +54,15 @@ async fn handle_update_note(
|
||||||
let object_url = get_object_url(&object)?;
|
let object_url = get_object_url(&object)?;
|
||||||
content += &create_content_link(object_url);
|
content += &create_content_link(object_url);
|
||||||
};
|
};
|
||||||
let attachments = get_object_attachments(
|
let (attachments, unprocessed) = get_object_attachments(
|
||||||
config,
|
config,
|
||||||
db_client,
|
db_client,
|
||||||
&object,
|
&object,
|
||||||
&post.author,
|
&post.author,
|
||||||
).await?;
|
).await?;
|
||||||
|
for attachment_url in unprocessed {
|
||||||
|
content += &create_content_link(attachment_url);
|
||||||
|
};
|
||||||
if content.is_empty() && attachments.is_empty() {
|
if content.is_empty() && attachments.is_empty() {
|
||||||
return Err(ValidationError("post is empty").into());
|
return Err(ValidationError("post is empty").into());
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue