Refactor utils::files and move upload helpers to mastodon_api::uploads module
This commit is contained in:
parent
c546840787
commit
3b56b29785
7 changed files with 62 additions and 57 deletions
|
@ -9,7 +9,7 @@ use crate::activitypub::actors::types::{Actor, ActorAddress};
|
||||||
use crate::activitypub::constants::ACTIVITY_CONTENT_TYPE;
|
use crate::activitypub::constants::ACTIVITY_CONTENT_TYPE;
|
||||||
use crate::config::Instance;
|
use crate::config::Instance;
|
||||||
use crate::http_signatures::create::{create_http_signature, SignatureError};
|
use crate::http_signatures::create::{create_http_signature, SignatureError};
|
||||||
use crate::utils::files::{save_file, FileError};
|
use crate::utils::files::save_file;
|
||||||
use crate::webfinger::types::JsonResourceDescriptor;
|
use crate::webfinger::types::JsonResourceDescriptor;
|
||||||
|
|
||||||
const FETCHER_CONNECTION_TIMEOUT: u64 = 30;
|
const FETCHER_CONNECTION_TIMEOUT: u64 = 30;
|
||||||
|
@ -26,7 +26,7 @@ pub enum FetchError {
|
||||||
JsonParseError(#[from] serde_json::Error),
|
JsonParseError(#[from] serde_json::Error),
|
||||||
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
FileError(#[from] FileError),
|
FileError(#[from] std::io::Error),
|
||||||
|
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
OtherError(&'static str),
|
OtherError(&'static str),
|
||||||
|
@ -86,7 +86,7 @@ pub async fn fetch_file(
|
||||||
let client = build_client()?;
|
let client = build_client()?;
|
||||||
let response = client.get(url).send().await?;
|
let response = client.get(url).send().await?;
|
||||||
let file_data = response.bytes().await?;
|
let file_data = response.bytes().await?;
|
||||||
let (file_name, media_type) = save_file(file_data.to_vec(), output_dir)?;
|
let (file_name, media_type) = save_file(file_data.to_vec(), output_dir, None)?;
|
||||||
Ok((file_name, media_type))
|
Ok((file_name, media_type))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ use uuid::Uuid;
|
||||||
|
|
||||||
use crate::errors::ValidationError;
|
use crate::errors::ValidationError;
|
||||||
use crate::frontend::get_subscription_page_url;
|
use crate::frontend::get_subscription_page_url;
|
||||||
|
use crate::mastodon_api::uploads::{UploadError, save_validated_b64_file};
|
||||||
use crate::models::profiles::types::{
|
use crate::models::profiles::types::{
|
||||||
DbActorProfile,
|
DbActorProfile,
|
||||||
ExtraField,
|
ExtraField,
|
||||||
|
@ -20,7 +21,7 @@ use crate::models::users::types::{
|
||||||
validate_local_username,
|
validate_local_username,
|
||||||
User,
|
User,
|
||||||
};
|
};
|
||||||
use crate::utils::files::{FileError, save_validated_b64_file, get_file_url};
|
use crate::utils::files::get_file_url;
|
||||||
|
|
||||||
/// https://docs.joinmastodon.org/entities/field/
|
/// https://docs.joinmastodon.org/entities/field/
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
@ -182,7 +183,7 @@ fn process_b64_image_field_value(
|
||||||
form_value: Option<String>,
|
form_value: Option<String>,
|
||||||
db_value: Option<String>,
|
db_value: Option<String>,
|
||||||
output_dir: &Path,
|
output_dir: &Path,
|
||||||
) -> Result<Option<String>, FileError> {
|
) -> Result<Option<String>, UploadError> {
|
||||||
let maybe_file_name = match form_value {
|
let maybe_file_name = match form_value {
|
||||||
Some(b64_data) => {
|
Some(b64_data) => {
|
||||||
if b64_data.is_empty() {
|
if b64_data.is_empty() {
|
||||||
|
@ -210,7 +211,7 @@ impl AccountUpdateData {
|
||||||
current_identity_proofs: &[IdentityProof],
|
current_identity_proofs: &[IdentityProof],
|
||||||
current_payment_options: &[PaymentOption],
|
current_payment_options: &[PaymentOption],
|
||||||
media_dir: &Path,
|
media_dir: &Path,
|
||||||
) -> Result<ProfileUpdateData, FileError> {
|
) -> Result<ProfileUpdateData, UploadError> {
|
||||||
let avatar = process_b64_image_field_value(
|
let avatar = process_b64_image_field_value(
|
||||||
self.avatar, current_avatar.clone(), media_dir,
|
self.avatar, current_avatar.clone(), media_dir,
|
||||||
)?;
|
)?;
|
||||||
|
|
|
@ -30,6 +30,7 @@ use crate::mastodon_api::oauth::auth::get_current_user;
|
||||||
use crate::mastodon_api::pagination::get_paginated_response;
|
use crate::mastodon_api::pagination::get_paginated_response;
|
||||||
use crate::mastodon_api::statuses::helpers::build_status_list;
|
use crate::mastodon_api::statuses::helpers::build_status_list;
|
||||||
use crate::mastodon_api::statuses::types::Status;
|
use crate::mastodon_api::statuses::types::Status;
|
||||||
|
use crate::mastodon_api::uploads::UploadError;
|
||||||
use crate::models::posts::queries::get_posts_by_author;
|
use crate::models::posts::queries::get_posts_by_author;
|
||||||
use crate::models::profiles::queries::{
|
use crate::models::profiles::queries::{
|
||||||
get_profile_by_id,
|
get_profile_by_id,
|
||||||
|
@ -64,7 +65,6 @@ use crate::utils::crypto::{
|
||||||
generate_private_key,
|
generate_private_key,
|
||||||
serialize_private_key,
|
serialize_private_key,
|
||||||
};
|
};
|
||||||
use crate::utils::files::FileError;
|
|
||||||
use super::helpers::get_relationship;
|
use super::helpers::get_relationship;
|
||||||
use super::types::{
|
use super::types::{
|
||||||
Account,
|
Account,
|
||||||
|
@ -197,10 +197,10 @@ async fn update_credentials(
|
||||||
)
|
)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
match err {
|
match err {
|
||||||
FileError::Base64DecodingError(_) => {
|
UploadError::Base64DecodingError(_) => {
|
||||||
HttpError::ValidationError("base64 decoding error".into())
|
HttpError::ValidationError("base64 decoding error".into())
|
||||||
},
|
},
|
||||||
FileError::InvalidMediaType => {
|
UploadError::InvalidMediaType => {
|
||||||
HttpError::ValidationError("invalid media type".into())
|
HttpError::ValidationError("invalid media type".into())
|
||||||
},
|
},
|
||||||
_ => HttpError::InternalError,
|
_ => HttpError::InternalError,
|
||||||
|
|
|
@ -5,8 +5,8 @@ use crate::config::Config;
|
||||||
use crate::database::{Pool, get_database_client};
|
use crate::database::{Pool, get_database_client};
|
||||||
use crate::errors::HttpError;
|
use crate::errors::HttpError;
|
||||||
use crate::mastodon_api::oauth::auth::get_current_user;
|
use crate::mastodon_api::oauth::auth::get_current_user;
|
||||||
|
use crate::mastodon_api::uploads::{UploadError, save_b64_file};
|
||||||
use crate::models::attachments::queries::create_attachment;
|
use crate::models::attachments::queries::create_attachment;
|
||||||
use crate::utils::files::{FileError, save_b64_file};
|
|
||||||
use super::types::{AttachmentCreateData, Attachment};
|
use super::types::{AttachmentCreateData, Attachment};
|
||||||
|
|
||||||
#[post("")]
|
#[post("")]
|
||||||
|
@ -22,7 +22,9 @@ async fn create_attachment_view(
|
||||||
&attachment_data.file,
|
&attachment_data.file,
|
||||||
&config.media_dir(),
|
&config.media_dir(),
|
||||||
).map_err(|err| match err {
|
).map_err(|err| match err {
|
||||||
FileError::Base64DecodingError(err) => HttpError::ValidationError(err.to_string()),
|
UploadError::Base64DecodingError(err) => {
|
||||||
|
HttpError::ValidationError(err.to_string())
|
||||||
|
},
|
||||||
_ => HttpError::InternalError,
|
_ => HttpError::InternalError,
|
||||||
})?;
|
})?;
|
||||||
let db_attachment = create_attachment(
|
let db_attachment = create_attachment(
|
||||||
|
|
|
@ -9,5 +9,6 @@ mod pagination;
|
||||||
pub mod search;
|
pub mod search;
|
||||||
pub mod statuses;
|
pub mod statuses;
|
||||||
pub mod timelines;
|
pub mod timelines;
|
||||||
|
mod uploads;
|
||||||
|
|
||||||
const MASTODON_API_VERSION: &str = "3.0.0";
|
const MASTODON_API_VERSION: &str = "3.0.0";
|
||||||
|
|
39
src/mastodon_api/uploads.rs
Normal file
39
src/mastodon_api/uploads.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use crate::utils::files::{save_file, sniff_media_type};
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub enum UploadError {
|
||||||
|
#[error(transparent)]
|
||||||
|
WriteError(#[from] std::io::Error),
|
||||||
|
|
||||||
|
#[error("base64 decoding error")]
|
||||||
|
Base64DecodingError(#[from] base64::DecodeError),
|
||||||
|
|
||||||
|
#[error("invalid media type")]
|
||||||
|
InvalidMediaType,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save_b64_file(
|
||||||
|
b64data: &str,
|
||||||
|
output_dir: &Path,
|
||||||
|
) -> Result<(String, Option<String>), UploadError> {
|
||||||
|
let data = base64::decode(b64data)?;
|
||||||
|
Ok(save_file(data, output_dir, None)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save_validated_b64_file(
|
||||||
|
b64data: &str,
|
||||||
|
output_dir: &Path,
|
||||||
|
media_type_prefix: &str,
|
||||||
|
) -> Result<(String, String), UploadError> {
|
||||||
|
let data = base64::decode(b64data)?;
|
||||||
|
let media_type = sniff_media_type(&data)
|
||||||
|
.ok_or(UploadError::InvalidMediaType)?;
|
||||||
|
if !media_type.starts_with(media_type_prefix) {
|
||||||
|
return Err(UploadError::InvalidMediaType);
|
||||||
|
};
|
||||||
|
let (file_name, _) =
|
||||||
|
save_file(data, output_dir, Some(media_type.clone()))?;
|
||||||
|
Ok((file_name, media_type))
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ use std::fs::{
|
||||||
File,
|
File,
|
||||||
Permissions,
|
Permissions,
|
||||||
};
|
};
|
||||||
|
use std::io::Error;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
@ -12,19 +13,7 @@ use mime_guess::get_mime_extensions_str;
|
||||||
use mime_sniffer::MimeTypeSniffer;
|
use mime_sniffer::MimeTypeSniffer;
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
pub fn sniff_media_type(data: &[u8]) -> Option<String> {
|
||||||
pub enum FileError {
|
|
||||||
#[error(transparent)]
|
|
||||||
WriteError(#[from] std::io::Error),
|
|
||||||
|
|
||||||
#[error("base64 decoding error")]
|
|
||||||
Base64DecodingError(#[from] base64::DecodeError),
|
|
||||||
|
|
||||||
#[error("invalid media type")]
|
|
||||||
InvalidMediaType,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sniff_media_type(data: &[u8]) -> Option<String> {
|
|
||||||
data.sniff_mime_type().map(|val| val.to_string())
|
data.sniff_mime_type().map(|val| val.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,13 +31,13 @@ fn get_file_name(data: &[u8], media_type: Option<&str>) -> String {
|
||||||
file_name
|
file_name
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_file(data: &[u8], file_path: &Path) -> Result<(), FileError> {
|
pub fn write_file(data: &[u8], file_path: &Path) -> Result<(), Error> {
|
||||||
let mut file = File::create(file_path)?;
|
let mut file = File::create(file_path)?;
|
||||||
file.write_all(data)?;
|
file.write_all(data)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_file_permissions(file_path: &Path, mode: u32) -> Result<(), FileError> {
|
pub fn set_file_permissions(file_path: &Path, mode: u32) -> Result<(), Error> {
|
||||||
let permissions = Permissions::from_mode(mode);
|
let permissions = Permissions::from_mode(mode);
|
||||||
set_permissions(file_path, permissions)?;
|
set_permissions(file_path, permissions)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -57,43 +46,16 @@ pub fn set_file_permissions(file_path: &Path, mode: u32) -> Result<(), FileError
|
||||||
pub fn save_file(
|
pub fn save_file(
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
output_dir: &Path,
|
output_dir: &Path,
|
||||||
) -> Result<(String, Option<String>), FileError> {
|
media_type: Option<String>,
|
||||||
let media_type = sniff_media_type(&data);
|
) -> Result<(String, Option<String>), Error> {
|
||||||
|
// Sniff media type if not provided
|
||||||
|
let media_type = media_type.or(sniff_media_type(&data));
|
||||||
let file_name = get_file_name(&data, media_type.as_deref());
|
let file_name = get_file_name(&data, media_type.as_deref());
|
||||||
let file_path = output_dir.join(&file_name);
|
let file_path = output_dir.join(&file_name);
|
||||||
write_file(&data, &file_path)?;
|
write_file(&data, &file_path)?;
|
||||||
Ok((file_name, media_type))
|
Ok((file_name, media_type))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save_b64_file(
|
|
||||||
b64data: &str,
|
|
||||||
output_dir: &Path,
|
|
||||||
) -> Result<(String, Option<String>), FileError> {
|
|
||||||
let data = base64::decode(b64data)?;
|
|
||||||
let media_type = sniff_media_type(&data);
|
|
||||||
let file_name = get_file_name(&data, media_type.as_deref());
|
|
||||||
let file_path = output_dir.join(&file_name);
|
|
||||||
write_file(&data, &file_path)?;
|
|
||||||
Ok((file_name, media_type))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn save_validated_b64_file(
|
|
||||||
b64data: &str,
|
|
||||||
output_dir: &Path,
|
|
||||||
media_type_prefix: &str,
|
|
||||||
) -> Result<(String, String), FileError> {
|
|
||||||
let data = base64::decode(b64data)?;
|
|
||||||
let media_type = sniff_media_type(&data)
|
|
||||||
.ok_or(FileError::InvalidMediaType)?;
|
|
||||||
if !media_type.starts_with(media_type_prefix) {
|
|
||||||
return Err(FileError::InvalidMediaType);
|
|
||||||
}
|
|
||||||
let file_name = get_file_name(&data, Some(&media_type));
|
|
||||||
let file_path = output_dir.join(&file_name);
|
|
||||||
write_file(&data, &file_path)?;
|
|
||||||
Ok((file_name, media_type))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_file_url(instance_url: &str, file_name: &str) -> String {
|
pub fn get_file_url(instance_url: &str, file_name: &str) -> String {
|
||||||
format!("{}/media/{}", instance_url, file_name)
|
format!("{}/media/{}", instance_url, file_name)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue