Refactor utils::files and move upload helpers to mastodon_api::uploads module

This commit is contained in:
silverpill 2022-08-18 21:01:35 +00:00
parent c546840787
commit 3b56b29785
7 changed files with 62 additions and 57 deletions

View file

@ -9,7 +9,7 @@ use crate::activitypub::actors::types::{Actor, ActorAddress};
use crate::activitypub::constants::ACTIVITY_CONTENT_TYPE;
use crate::config::Instance;
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;
const FETCHER_CONNECTION_TIMEOUT: u64 = 30;
@ -26,7 +26,7 @@ pub enum FetchError {
JsonParseError(#[from] serde_json::Error),
#[error(transparent)]
FileError(#[from] FileError),
FileError(#[from] std::io::Error),
#[error("{0}")]
OtherError(&'static str),
@ -86,7 +86,7 @@ pub async fn fetch_file(
let client = build_client()?;
let response = client.get(url).send().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))
}

View file

@ -6,6 +6,7 @@ use uuid::Uuid;
use crate::errors::ValidationError;
use crate::frontend::get_subscription_page_url;
use crate::mastodon_api::uploads::{UploadError, save_validated_b64_file};
use crate::models::profiles::types::{
DbActorProfile,
ExtraField,
@ -20,7 +21,7 @@ use crate::models::users::types::{
validate_local_username,
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/
#[derive(Serialize)]
@ -182,7 +183,7 @@ fn process_b64_image_field_value(
form_value: Option<String>,
db_value: Option<String>,
output_dir: &Path,
) -> Result<Option<String>, FileError> {
) -> Result<Option<String>, UploadError> {
let maybe_file_name = match form_value {
Some(b64_data) => {
if b64_data.is_empty() {
@ -210,7 +211,7 @@ impl AccountUpdateData {
current_identity_proofs: &[IdentityProof],
current_payment_options: &[PaymentOption],
media_dir: &Path,
) -> Result<ProfileUpdateData, FileError> {
) -> Result<ProfileUpdateData, UploadError> {
let avatar = process_b64_image_field_value(
self.avatar, current_avatar.clone(), media_dir,
)?;

View file

@ -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::statuses::helpers::build_status_list;
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::profiles::queries::{
get_profile_by_id,
@ -64,7 +65,6 @@ use crate::utils::crypto::{
generate_private_key,
serialize_private_key,
};
use crate::utils::files::FileError;
use super::helpers::get_relationship;
use super::types::{
Account,
@ -197,10 +197,10 @@ async fn update_credentials(
)
.map_err(|err| {
match err {
FileError::Base64DecodingError(_) => {
UploadError::Base64DecodingError(_) => {
HttpError::ValidationError("base64 decoding error".into())
},
FileError::InvalidMediaType => {
UploadError::InvalidMediaType => {
HttpError::ValidationError("invalid media type".into())
},
_ => HttpError::InternalError,

View file

@ -5,8 +5,8 @@ use crate::config::Config;
use crate::database::{Pool, get_database_client};
use crate::errors::HttpError;
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::utils::files::{FileError, save_b64_file};
use super::types::{AttachmentCreateData, Attachment};
#[post("")]
@ -22,7 +22,9 @@ async fn create_attachment_view(
&attachment_data.file,
&config.media_dir(),
).map_err(|err| match err {
FileError::Base64DecodingError(err) => HttpError::ValidationError(err.to_string()),
UploadError::Base64DecodingError(err) => {
HttpError::ValidationError(err.to_string())
},
_ => HttpError::InternalError,
})?;
let db_attachment = create_attachment(

View file

@ -9,5 +9,6 @@ mod pagination;
pub mod search;
pub mod statuses;
pub mod timelines;
mod uploads;
const MASTODON_API_VERSION: &str = "3.0.0";

View 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))
}

View file

@ -4,6 +4,7 @@ use std::fs::{
File,
Permissions,
};
use std::io::Error;
use std::io::prelude::*;
use std::os::unix::fs::PermissionsExt;
use std::path::Path;
@ -12,19 +13,7 @@ use mime_guess::get_mime_extensions_str;
use mime_sniffer::MimeTypeSniffer;
use sha2::{Digest, Sha256};
#[derive(thiserror::Error, Debug)]
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> {
pub fn sniff_media_type(data: &[u8]) -> Option<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
}
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)?;
file.write_all(data)?;
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);
set_permissions(file_path, permissions)?;
Ok(())
@ -57,43 +46,16 @@ pub fn set_file_permissions(file_path: &Path, mode: u32) -> Result<(), FileError
pub fn save_file(
data: Vec<u8>,
output_dir: &Path,
) -> Result<(String, Option<String>), FileError> {
let media_type = sniff_media_type(&data);
media_type: Option<String>,
) -> 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_path = output_dir.join(&file_name);
write_file(&data, &file_path)?;
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 {
format!("{}/media/{}", instance_url, file_name)
}