Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to set the accept input attribute to arbitrary values #755

Merged
merged 3 commits into from
Mar 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ Sometimes this is just a more practical and quick way than doing things properly

(where `$FILE` is the path to the file. This uses miniserve's default port of 8080)

### Take pictures and upload them from smartphones:

miniserve -u -m image -q

This uses the `--media-type` option, which sends a hint for the expected media type to the browser.
Some mobile browsers like Firefox on Android will offer to open the camera app when seeing this.

## Features

- Easy to use
Expand Down
20 changes: 20 additions & 0 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ use crate::auth;
use crate::errors::ContextualError;
use crate::renderer;

#[derive(clap::ArgEnum, Clone)]
pub enum MediaType {
Image,
Audio,
Video,
}

#[derive(Parser)]
#[clap(name = "miniserve", author, about, version)]
pub struct CliArgs {
Expand Down Expand Up @@ -104,6 +111,19 @@ pub struct CliArgs {
#[clap(short = 'u', long = "upload-files")]
pub file_upload: bool,

/// Specify uploadable media types
#[clap(arg_enum, short = 'm', long = "media-type", requires = "file-upload")]
pub media_type: Option<Vec<MediaType>>,

/// Directly specify the uploadable media type expression
#[clap(
short = 'M',
long = "raw-media-type",
requires = "file-upload",
conflicts_with = "media-type"
)]
pub media_type_raw: Option<String>,
svenstaro marked this conversation as resolved.
Show resolved Hide resolved

/// Enable overriding existing files during file upload
#[clap(short = 'o', long = "overwrite-files")]
pub overwrite_files: bool,
Expand Down
23 changes: 22 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ use http::HeaderMap;
#[cfg(feature = "tls")]
use rustls_pemfile as pemfile;

use crate::{args::CliArgs, auth::RequiredAuth};
use crate::{
args::{CliArgs, MediaType},
auth::RequiredAuth,
};

/// Possible characters for random routes
const ROUTE_ALPHABET: [char; 16] = [
Expand Down Expand Up @@ -81,6 +84,9 @@ pub struct MiniserveConfig {
/// Enable file upload
pub file_upload: bool,

/// HTML accept attribute value
pub uploadable_media_type: Option<String>,

/// Enable upload to override existing files
pub overwrite_files: bool,

Expand Down Expand Up @@ -188,6 +194,20 @@ impl MiniserveConfig {
#[cfg(not(feature = "tls"))]
let tls_rustls_server_config = None;

let uploadable_media_type = args.media_type_raw.or_else(|| {
args.media_type.map(|types| {
types
.into_iter()
.map(|t| match t {
MediaType::Audio => "audio/*",
MediaType::Image => "image/*",
MediaType::Video => "video/*",
})
.collect::<Vec<_>>()
.join(",")
})
});

Ok(MiniserveConfig {
verbose: args.verbose,
path: args.path.unwrap_or_else(|| PathBuf::from(".")),
Expand All @@ -207,6 +227,7 @@ impl MiniserveConfig {
overwrite_files: args.overwrite_files,
show_qrcode: args.qrcode,
file_upload: args.file_upload,
uploadable_media_type,
tar_enabled: args.enable_tar,
tar_gz_enabled: args.enable_tar_gz,
zip_enabled: args.enable_zip,
Expand Down
5 changes: 4 additions & 1 deletion src/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,10 @@ pub fn page(
form id="file_submit" action=(upload_action) method="POST" enctype="multipart/form-data" {
p { "Select a file to upload or drag it anywhere into the window" }
div {
input #file-input type="file" name="file_to_upload" required="" multiple {}
@match &conf.uploadable_media_type {
Some(accept) => {input #file-input accept=(accept) type="file" name="file_to_upload" required="" multiple {}},
None => {input #file-input type="file" name="file_to_upload" required="" multiple {}}
}
button type="submit" { "Upload file" }
}
}
Expand Down
20 changes: 20 additions & 0 deletions tests/upload_files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,23 @@ fn upload_to_symlink_directory(

Ok(())
}

/// Test setting the HTML accept attribute using -m and -M.
#[rstest]
#[case(server(&["-u"]), None)]
#[case(server(&["-u", "-m", "image"]), Some("image/*"))]
#[case(server(&["-u", "-m", "image", "-m", "audio", "-m", "video"]), Some("image/*,audio/*,video/*"))]
#[case(server(&["-u", "-m", "audio", "-m", "image", "-m", "video"]), Some("audio/*,image/*,video/*"))]
#[case(server(&["-u", "-M", "test_value"]), Some("test_value"))]
fn set_media_type(
#[case] server: TestServer,
#[case] expected_accept_value: Option<&str>,
) -> Result<(), Error> {
let body = reqwest::blocking::get(server.url())?.error_for_status()?;
let parsed = Document::from_read(body)?;

let input = parsed.find(Attr("id", "file-input")).next().unwrap();
assert_eq!(input.attr("accept"), expected_accept_value);
svenstaro marked this conversation as resolved.
Show resolved Hide resolved

Ok(())
}