mirror of
https://github.com/amigan/rustypaste-pretty.git
synced 2024-11-22 04:19:47 -05:00
feat(paste): support overriding MIME types
This commit is contained in:
parent
0296bc0594
commit
03348cb38f
7 changed files with 119 additions and 0 deletions
13
Cargo.lock
generated
13
Cargo.lock
generated
|
@ -1520,9 +1520,12 @@ dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"infer",
|
"infer",
|
||||||
"log",
|
"log",
|
||||||
|
"mime",
|
||||||
"petname",
|
"petname",
|
||||||
"rand 0.8.4",
|
"rand 0.8.4",
|
||||||
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_regex",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1602,6 +1605,16 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_regex"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8136f1a4ea815d7eac4101cfd0b16dc0cb5e1fe1b8609dfd728058656b7badf"
|
||||||
|
dependencies = [
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_urlencoded"
|
name = "serde_urlencoded"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
|
|
|
@ -24,6 +24,9 @@ petname = "1.1.0"
|
||||||
rand = "0.8.4"
|
rand = "0.8.4"
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
url = "2.2.2"
|
url = "2.2.2"
|
||||||
|
mime = "0.3.16"
|
||||||
|
regex = "1.5.4"
|
||||||
|
serde_regex = "1.1.0"
|
||||||
|
|
||||||
[dependencies.config]
|
[dependencies.config]
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
|
|
|
@ -8,3 +8,12 @@ upload_path="./upload"
|
||||||
random_url = { enabled = true, type = "petname", words = 2, separator = "-" }
|
random_url = { enabled = true, type = "petname", words = 2, separator = "-" }
|
||||||
#random_url = { enabled = true, type = "alphanumeric", length = 8 }
|
#random_url = { enabled = true, type = "alphanumeric", length = 8 }
|
||||||
default_extension = "txt"
|
default_extension = "txt"
|
||||||
|
mime_override = [
|
||||||
|
{ mime = "image/jpeg", regex = "^.*\\.jpg$" },
|
||||||
|
{ mime = "image/png", regex = "^.*\\.png$" },
|
||||||
|
{ mime = "image/svg+xml", regex = "^.*\\.svg$" },
|
||||||
|
{ mime = "video/webm", regex = "^.*\\.webm$" },
|
||||||
|
{ mime = "video/x-matroska", regex = "^.*\\.mkv$" },
|
||||||
|
{ mime = "application/octet-stream", regex = "^.*\\.bin$" },
|
||||||
|
{ mime = "text/plain", regex = "^.*\\.(log|txt|diff)$" },
|
||||||
|
]
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::mime::MimeMatcher;
|
||||||
use crate::random::RandomURLConfig;
|
use crate::random::RandomURLConfig;
|
||||||
use byte_unit::Byte;
|
use byte_unit::Byte;
|
||||||
use config::{self, ConfigError};
|
use config::{self, ConfigError};
|
||||||
|
@ -32,6 +33,8 @@ pub struct PasteConfig {
|
||||||
pub random_url: RandomURLConfig,
|
pub random_url: RandomURLConfig,
|
||||||
/// Default file extension.
|
/// Default file extension.
|
||||||
pub default_extension: String,
|
pub default_extension: String,
|
||||||
|
/// Media type override options.
|
||||||
|
pub mime_override: Vec<MimeMatcher>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
|
|
|
@ -18,3 +18,6 @@ pub mod auth;
|
||||||
|
|
||||||
/// Storage handler.
|
/// Storage handler.
|
||||||
pub mod paste;
|
pub mod paste;
|
||||||
|
|
||||||
|
/// Media type handler.
|
||||||
|
pub mod mime;
|
||||||
|
|
83
src/mime.rs
Normal file
83
src/mime.rs
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
use actix_files::file_extension_to_mime;
|
||||||
|
use mime::{FromStrError, Mime};
|
||||||
|
use regex::Regex;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
/// Matcher for MIME types.
|
||||||
|
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct MimeMatcher {
|
||||||
|
/// MIME type to set for the matched file name.
|
||||||
|
pub mime: String,
|
||||||
|
/// Regex for matching the file name.
|
||||||
|
#[serde(with = "serde_regex")]
|
||||||
|
pub regex: Option<Regex>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the appropriate media type using an array of
|
||||||
|
/// [`MIME matcher`]s and the file name.
|
||||||
|
///
|
||||||
|
/// [`MIME matcher`]: MimeMatcher
|
||||||
|
pub fn get_mime_type(
|
||||||
|
mime_matchers: &[MimeMatcher],
|
||||||
|
file_name: String,
|
||||||
|
) -> Result<Mime, FromStrError> {
|
||||||
|
let path = PathBuf::from(&file_name);
|
||||||
|
let mut mime_type = file_extension_to_mime(
|
||||||
|
path.extension()
|
||||||
|
.map(|v| v.to_str())
|
||||||
|
.flatten()
|
||||||
|
.unwrap_or_default(),
|
||||||
|
);
|
||||||
|
for matcher in mime_matchers {
|
||||||
|
if matcher
|
||||||
|
.regex
|
||||||
|
.as_ref()
|
||||||
|
.map(|r| r.is_match(&file_name))
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
mime_type = Mime::from_str(&matcher.mime)?;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(mime_type)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_match_mime_type() {
|
||||||
|
assert_eq!(
|
||||||
|
mime::TEXT_PLAIN,
|
||||||
|
get_mime_type(
|
||||||
|
&[MimeMatcher {
|
||||||
|
mime: String::from("text/plain"),
|
||||||
|
regex: Regex::new("^.*\\.test$").ok(),
|
||||||
|
}],
|
||||||
|
String::from("mime.test")
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
mime::IMAGE_PNG,
|
||||||
|
get_mime_type(
|
||||||
|
&[MimeMatcher {
|
||||||
|
mime: String::from("image/png"),
|
||||||
|
regex: Regex::new("^.*\\.PNG$").ok(),
|
||||||
|
}],
|
||||||
|
String::from("image.PNG")
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
mime::APPLICATION_PDF,
|
||||||
|
get_mime_type(&[], String::from("book.pdf")).unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
mime::APPLICATION_OCTET_STREAM,
|
||||||
|
get_mime_type(&[], String::from("x.unknown")).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::auth;
|
use crate::auth;
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::header::ContentDisposition;
|
use crate::header::ContentDisposition;
|
||||||
|
use crate::mime;
|
||||||
use crate::paste::{Paste, PasteType};
|
use crate::paste::{Paste, PasteType};
|
||||||
use actix_files::NamedFile;
|
use actix_files::NamedFile;
|
||||||
use actix_multipart::Multipart;
|
use actix_multipart::Multipart;
|
||||||
|
@ -38,6 +39,10 @@ async fn serve(
|
||||||
match paste_type {
|
match paste_type {
|
||||||
PasteType::File => Ok(NamedFile::open(&path)?
|
PasteType::File => Ok(NamedFile::open(&path)?
|
||||||
.disable_content_disposition()
|
.disable_content_disposition()
|
||||||
|
.set_content_type(
|
||||||
|
mime::get_mime_type(&config.paste.mime_override, file.to_string())
|
||||||
|
.map_err(error::ErrorInternalServerError)?,
|
||||||
|
)
|
||||||
.prefer_utf8(true)
|
.prefer_utf8(true)
|
||||||
.into_response(&request)?),
|
.into_response(&request)?),
|
||||||
PasteType::Url => Ok(HttpResponse::Found()
|
PasteType::Url => Ok(HttpResponse::Found()
|
||||||
|
|
Loading…
Reference in a new issue