feat(server): add random suffix mode (#69)
* add random suffix mode * fix linter issues * add test case * fix linter issues * add comments, remove empty lines and single line declarations * more test cases * refactor(config): rename suffix_mode to random_suffix * refactor(paste): clean up the random suffix logic * chore(config): add random suffix example to default config * docs(readme): mention random suffix feature * test(fixtures): add fixture test for random suffix mode * random_suffix -> suffix_mode * fix default extension for .dotfile w/o extension * fix formatting * style(format): fix the indentation for random suffix fixture --------- Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
This commit is contained in:
parent
d740ae7e89
commit
62bbfef6a3
|
@ -68,6 +68,7 @@ Here you can read the blog post about how it is deployed on Shuttle: [https://bl
|
|||
- random file names (optional)
|
||||
- pet name (e.g. `capital-mosquito.txt`)
|
||||
- alphanumeric string (e.g. `yB84D2Dv.txt`)
|
||||
- random suffix (e.g. `file.MRV5as.tar.gz`)
|
||||
- supports expiring links
|
||||
- auto-expiration of files (optional)
|
||||
- auto-deletion of expired files (optional)
|
||||
|
@ -192,7 +193,6 @@ supported units:
|
|||
- `months`, `month`, `M`
|
||||
- `years`, `year`, `y`
|
||||
|
||||
|
||||
#### One shot files
|
||||
|
||||
```sh
|
||||
|
|
|
@ -39,6 +39,7 @@ content_type = "text/plain; charset=utf-8"
|
|||
[paste]
|
||||
random_url = { enabled = true, type = "petname", words = 2, separator = "-" }
|
||||
#random_url = { enabled = true, type = "alphanumeric", length = 8 }
|
||||
#random_url = { enabled = true, type = "alphanumeric", length = 6, suffix_mode = true }
|
||||
default_extension = "txt"
|
||||
mime_override = [
|
||||
{ mime = "image/jpeg", regex = "^.*\\.jpg$" },
|
||||
|
|
|
@ -6,4 +6,4 @@ upload_path="./upload"
|
|||
[paste]
|
||||
random_url = { enabled = false, type = "petname", words = 2, separator = "-" }
|
||||
default_extension = "txt"
|
||||
duplicate_files = false
|
||||
duplicate_files = true
|
||||
|
|
|
@ -4,6 +4,7 @@ content="test data"
|
|||
|
||||
setup() {
|
||||
echo "$content" > file
|
||||
echo "$content" > .file
|
||||
}
|
||||
|
||||
run_test() {
|
||||
|
@ -11,9 +12,11 @@ run_test() {
|
|||
test "$file_url" = "http://localhost:8000/file.txt"
|
||||
test "$content" = "$(cat upload/file.txt)"
|
||||
test "$content" = "$(curl -s $file_url)"
|
||||
file_url2=$(curl -s -F "file=@.file" localhost:8000)
|
||||
test "$file_url2" = "http://localhost:8000/.file.txt"
|
||||
}
|
||||
|
||||
teardown() {
|
||||
rm file
|
||||
rm file .file
|
||||
rm -r upload
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
[server]
|
||||
address = "127.0.0.1:8000"
|
||||
max_content_length = "10MB"
|
||||
upload_path = "./upload"
|
||||
|
||||
[paste]
|
||||
random_url = { enabled = true, type = "alphanumeric", length = "6", suffix_mode = true }
|
||||
default_extension = "txt"
|
||||
duplicate_files = true
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
content="test data"
|
||||
|
||||
setup() {
|
||||
echo "$content" >file
|
||||
}
|
||||
|
||||
run_test() {
|
||||
first_file_url=$(curl -s -F "file=@file" localhost:8000)
|
||||
test "$first_file_url" != "http://localhost:8000/file.txt"
|
||||
test "$content" = "$(curl -s $first_file_url)"
|
||||
|
||||
second_file_url=$(curl -s -F "file=@file" localhost:8000)
|
||||
test "$first_file_url" != "http://localhost:8000/file.txt"
|
||||
test "$content" = "$(curl -s $first_file_url)"
|
||||
|
||||
test "$first_file_url" != "$second_file_url"
|
||||
|
||||
test "$(cat upload/${first_file_url/http:\/\/localhost:8000\//})" \
|
||||
= "$(cat upload/${second_file_url/http:\/\/localhost:8000\//})"
|
||||
|
||||
[[ $(find upload/ -name "file.*.txt" -print -quit 2>/dev/null) ]] || exit 1
|
||||
}
|
||||
|
||||
teardown() {
|
||||
rm file
|
||||
rm -r upload
|
||||
}
|
114
src/paste.rs
114
src/paste.rs
|
@ -113,31 +113,54 @@ impl Paste {
|
|||
.and_then(|v| v.to_str())
|
||||
{
|
||||
Some("-") => String::from("stdin"),
|
||||
Some(".") => String::from("file"),
|
||||
Some(v) => v.to_string(),
|
||||
None => String::from("file"),
|
||||
};
|
||||
let mut path = self
|
||||
.type_
|
||||
.get_path(&config.server.upload_path)
|
||||
.join(file_name);
|
||||
match path.clone().extension() {
|
||||
Some(extension) => {
|
||||
if let Some(file_name) = config.paste.random_url.generate() {
|
||||
path.set_file_name(file_name);
|
||||
path.set_extension(extension);
|
||||
}
|
||||
.join(&file_name);
|
||||
let mut parts: Vec<&str> = file_name.split('.').collect();
|
||||
let mut dotfile = false;
|
||||
let mut lower_bound = 1;
|
||||
let mut file_name = match parts[0] {
|
||||
"" => {
|
||||
// Index shifts one to the right in the array for the rest of the string (the extension)
|
||||
dotfile = true;
|
||||
lower_bound = 2;
|
||||
// If the first array element is empty, it means the file started with a dot (e.g.: .foo)
|
||||
format!(".{}", parts[1])
|
||||
}
|
||||
None => {
|
||||
if let Some(file_name) = config.paste.random_url.generate() {
|
||||
path.set_file_name(file_name);
|
||||
_ => parts[0].to_string(),
|
||||
};
|
||||
let mut extension = if parts.len() > lower_bound {
|
||||
// To get the rest (the extension), we have to remove the first element of the array, which is the filename
|
||||
parts.remove(0);
|
||||
if dotfile {
|
||||
// If the filename starts with a dot, we have to remove another element, because the first element was empty
|
||||
parts.remove(0);
|
||||
}
|
||||
parts.join(".")
|
||||
} else {
|
||||
file_type
|
||||
.map(|t| t.extension())
|
||||
.unwrap_or(&config.paste.default_extension)
|
||||
.to_string()
|
||||
};
|
||||
if let Some(random_text) = config.paste.random_url.generate() {
|
||||
if let Some(suffix_mode) = config.paste.random_url.suffix_mode {
|
||||
if suffix_mode {
|
||||
extension = format!("{}.{}", random_text, extension);
|
||||
} else {
|
||||
file_name = random_text;
|
||||
}
|
||||
path.set_extension(
|
||||
file_type
|
||||
.map(|t| t.extension())
|
||||
.unwrap_or(&config.paste.default_extension),
|
||||
);
|
||||
} else {
|
||||
file_name = random_text;
|
||||
}
|
||||
}
|
||||
path.set_file_name(file_name);
|
||||
path.set_extension(extension);
|
||||
let file_name = path
|
||||
.file_name()
|
||||
.map(|v| v.to_string_lossy())
|
||||
|
@ -274,6 +297,67 @@ mod tests {
|
|||
);
|
||||
fs::remove_file(file_name)?;
|
||||
|
||||
config.paste.random_url = RandomURLConfig {
|
||||
enabled: true,
|
||||
length: Some(4),
|
||||
type_: RandomURLType::Alphanumeric,
|
||||
suffix_mode: Some(true),
|
||||
..RandomURLConfig::default()
|
||||
};
|
||||
let paste = Paste {
|
||||
data: vec![116, 101, 115, 115, 117, 115],
|
||||
type_: PasteType::File,
|
||||
};
|
||||
let file_name = paste.store_file("foo.tar.gz", None, &config)?;
|
||||
assert_eq!("tessus", fs::read_to_string(&file_name)?);
|
||||
assert!(file_name.ends_with(".tar.gz"));
|
||||
assert!(file_name.starts_with("foo."));
|
||||
fs::remove_file(file_name)?;
|
||||
|
||||
config.paste.random_url = RandomURLConfig {
|
||||
enabled: true,
|
||||
length: Some(4),
|
||||
type_: RandomURLType::Alphanumeric,
|
||||
suffix_mode: Some(true),
|
||||
..RandomURLConfig::default()
|
||||
};
|
||||
let paste = Paste {
|
||||
data: vec![116, 101, 115, 115, 117, 115],
|
||||
type_: PasteType::File,
|
||||
};
|
||||
let file_name = paste.store_file(".foo.tar.gz", None, &config)?;
|
||||
assert_eq!("tessus", fs::read_to_string(&file_name)?);
|
||||
assert!(file_name.ends_with(".tar.gz"));
|
||||
assert!(file_name.starts_with(".foo."));
|
||||
fs::remove_file(file_name)?;
|
||||
|
||||
config.paste.random_url = RandomURLConfig {
|
||||
enabled: true,
|
||||
length: Some(4),
|
||||
type_: RandomURLType::Alphanumeric,
|
||||
suffix_mode: Some(false),
|
||||
..RandomURLConfig::default()
|
||||
};
|
||||
let paste = Paste {
|
||||
data: vec![116, 101, 115, 115, 117, 115],
|
||||
type_: PasteType::File,
|
||||
};
|
||||
let file_name = paste.store_file("foo.tar.gz", None, &config)?;
|
||||
assert_eq!("tessus", fs::read_to_string(&file_name)?);
|
||||
assert!(file_name.ends_with(".tar.gz"));
|
||||
fs::remove_file(file_name)?;
|
||||
|
||||
config.paste.default_extension = String::from("txt");
|
||||
config.paste.random_url.enabled = false;
|
||||
let paste = Paste {
|
||||
data: vec![120, 121, 122],
|
||||
type_: PasteType::File,
|
||||
};
|
||||
let file_name = paste.store_file(".foo", None, &config)?;
|
||||
assert_eq!("xyz", fs::read_to_string(&file_name)?);
|
||||
assert_eq!(".foo.txt", file_name);
|
||||
fs::remove_file(file_name)?;
|
||||
|
||||
config.paste.default_extension = String::from("bin");
|
||||
config.paste.random_url.enabled = false;
|
||||
config.paste.random_url = RandomURLConfig {
|
||||
|
|
|
@ -14,6 +14,8 @@ pub struct RandomURLConfig {
|
|||
/// Type of the random URL.
|
||||
#[serde(rename = "type")]
|
||||
pub type_: RandomURLType,
|
||||
/// Append a random string to the original filename.
|
||||
pub suffix_mode: Option<bool>,
|
||||
}
|
||||
|
||||
impl RandomURLConfig {
|
||||
|
|
Loading…
Reference in New Issue