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

File & directory deletion feature #1093

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

cyqsimon
Copy link
Contributor

@cyqsimon cyqsimon commented Apr 3, 2023

Closes #397.

Todo

  • CLI options
  • Backend API
  • Frontend controls
  • Tests

@cyqsimon
Copy link
Contributor Author

cyqsimon commented Apr 3, 2023

miniserve/src/main.rs

Lines 329 to 330 in 82dd826

// Allow file upload
app.service(web::resource("/upload").route(web::post().to(file_upload::upload_file)));

I am curious why the upload feature is implemented like so. I guess it's good for unforeseen future extensibility, but there is semantic duplication between the /upload endpoint and the POST method.

The real question I want to ask, is should I implement the deletion feature in a similar manner? I.e. something like this:

app.service(web::resource("/delete").route(web::delete().to(file_rm::rm_file)));

@svenstaro
Copy link
Owner

Are you suggesting it might have been better to just have POST always try to upload stuff? It would be simpler for sure but as you are saying, it's not great if we ever want a second POST for something. I think this is currently an ok compromise.

I see how this is a bit of a conundrum with delete. I also want to add WebDAV at some point and that will introduce even more semantics. Perhaps it's the best to go with what you suggested just now and eat the semantic duplication for the time being. It's not like it can't be changed later.

@bgusach
Copy link

bgusach commented Aug 25, 2023

Hi,

unfortunately I can't directly help... just wanted to say that I appreciate the effort and if it gets done at some point, my life would be somehow better :)

@cyqsimon
Copy link
Contributor Author

Hi,

unfortunately I can't directly help... just wanted to say that I appreciate the effort and if it gets done at some point, my life would be somehow better :)

Thanks for reminding me. I have too many things on hand and honestly just forgot about this whole matter. I will find some time to work on it this week, hopefully.

@cyqsimon
Copy link
Contributor Author

cyqsimon commented Jan 29, 2024

Rebased on #1331.

@cyqsimon
Copy link
Contributor Author

Rebased on master.

@cyqsimon
Copy link
Contributor Author

Rebased on master.

@svenstaro
Copy link
Owner

Could you add some tests? Since this is going to be deleting things, it needs some proper tests.

@cyqsimon
Copy link
Contributor Author

cyqsimon commented Jul 7, 2024

Could you add some tests? Since this is going to be deleting things, it needs some proper tests.

Yeah I'm working on that on and off. The tests are actually written but several are failing. My original intention was to combine the tests and the fixes into one commit, but I guess there's no harm in splitting them up. I'll push the tests first and then look to fix them.

@cyqsimon
Copy link
Contributor Author

cyqsimon commented Jul 7, 2024

If you can spare some time, please take a look at the test suite; make sure they are testing the correct behaviour. Thanks.

@svenstaro
Copy link
Owner

Yeah I'm working on that on and off. The tests are actually written but several are failing. My original intention was to combine the tests and the fixes into one commit, but I guess there's no harm in splitting them up. I'll push the tests first and then look to fix them.

No worries. There's no rush to get this in. Just take your time and make sure things look good.

If you can spare some time, please take a look at the test suite; make sure they are testing the correct behaviour. Thanks.

I will try to squeeze a review in but no promises, I'm fairly low on time currently.

@cyqsimon
Copy link
Contributor Author

Rebased on #1454.

@cyqsimon
Copy link
Contributor Author

This should be ready now, finally. Everything works as expected, as far as I can tell. But you may wish to merge the other PR first.

@cyqsimon cyqsimon marked this pull request as ready for review September 15, 2024 16:30
@svenstaro
Copy link
Owner

Needs a rebase and then I will do a proper review. :)

@cyqsimon
Copy link
Contributor Author

Actually, let me do an interactive rebase to reorganise things. Then it'd be a bit tidier.

@qtekk
Copy link

qtekk commented Oct 16, 2024

hey, how's the feature going?

@cyqsimon
Copy link
Contributor Author

hey, how's the feature going?

Implementation is complete. Just waiting on review.

@svenstaro You got time to take a look?

@svenstaro
Copy link
Owner

I will take some time. Might take a look on the weekend.

@cyqsimon
Copy link
Contributor Author

cyqsimon commented Jan 7, 2025

Hi Sven, please don't forget about this one 😅

@svenstaro
Copy link
Owner

Sorry, reviewing now.

@svenstaro
Copy link
Owner

svenstaro commented Jan 10, 2025

Thoughts:

  • Do we want any kind of confirmation? Currently you might miss-click and a file is just gone.
  • Do we want to warn in case a path given to -R is not contained in the served paths? For instance, miniserve -R /tmp /some/dir doesn't do anything.
  • Should we abort if we provide a path to a file but also -R? We don't do this with -u but it might make sense as a followup? So for instance, miniserve -R /tmp /some/file.

@cyqsimon
Copy link
Contributor Author

cyqsimon commented Jan 10, 2025

Good points.

  • Do we want any kind of confirmation? Currently you might miss-click and a file is just gone.

That would be nice. I would also want a toggle/checkbox that allows turning off confirmation, in case where you want to delete several things en-mass.

I'll give this a shot, but I'm not great at front end coding. We'll see.

  • Do we want to warn in case a path given to -R is not contained in the served paths? For instance, miniserve -R /tmp /some/dir doesn't do anything.

I think there's some misunderstanding. The paths accepted by -u and -R are actually URL paths relative to /, not file system paths relative to CWD. This means:

  1. the specified directory should never escape the serve root, excepting symlink shenanigans (all covered by tests)
  2. all paths are valid because new directories can be created on the fly; even something like -R ../.. is legal because navigating up the serve root is prevented, so effectively it's the same as -R .
  3. -R /foo/bar and -R foo/bar are equivalent

So I believe the implementation needs no changes, but it would be prudent to note this in the CLI help document though. I'll make that happen. Also it might be a good idea to log a message at INFO level noting this behaviour.

  • Should we abort if we provide a path to a file but also -R? We don't do this with -u but it might make sense as a followup? So for instance, miniserve -R /tmp /some/file.

Oh I actually didn't know we support serving a single file. That's a nice feature.

Hmm yeah this is very interesting indeed. Because the semantics of -u and -R aren't really compatible with this mode of operation.

But first of all, should we allow removing (or for -u, overwriting) in this mode at all? Personally I believe the answer is yes, but I'd like to hear your opinions too.

And if the answer to the previous question is yes, obviously -u and -R with arguments would make no sense (since a file cannot have subdirectories). Should this be a warning or a hard error?

@cyqsimon
Copy link
Contributor Author

For points 2 and 3, the changes would apply to both -u and -R. So I think it's better organisationally if these form a separate PR, queued after this one.

Copy link
Owner

@svenstaro svenstaro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review done. I think we're almost there. :)

data/style.scss Show resolved Hide resolved
Comment on lines +21 to +25
include!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/",
"src/path_utils.rs"
));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a really weird to include something. Is there a reason to do this over use miniserve::path_utils? You would have to make it public but that's fine.

format!("rm?path=/{}", make_get_path(unencoded_path))
}

fn assert_rm_ok(base_url: Url, unencoded_paths: &[impl AsRef<Path>]) -> anyhow::Result<()> {
Copy link
Owner

@svenstaro svenstaro Jan 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't use anyhow::Result in any tests. Could you please take a look at how we do this in upload_files.rs and follow that? Basically use fixtures::Error with normal Results.

Comment on lines +47 to +57
/// Files nested at different levels under the same root directory
///
/// This is not a `&[&str]` because in percent-encoding, path segments and full paths
/// are encoded differently.
#[allow(dead_code)]
pub static DEEPLY_NESTED_FILE: &str = "very/deeply/nested/test.rs";
pub static NESTED_FILES_UNDER_SINGLE_ROOT: LazyLock<Vec<&Path>> = LazyLock::new(|| {
vec![
Path::new("someDir/alpha"),
Path::new("someDir/some_sub_dir/bravo"),
]
});
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't really understand. Where is this relevant and why now?

dir.to_str().unwrap()
})
.collect::<Vec<_>>();
dbg!(&dir_names);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to keep this dbg!() here? If so, it should at least get a better message with it.

Comment on lines +77 to +78
/// The `check_paths_exist` parameter allows skipping this check before and after
/// the deletion attempt in case these paths should be inaccessible via GET.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good start but please also describe the intent of the function as well.

format!("rm?path=/{}", make_get_path(unencoded_path))
}

fn assert_rm_ok(base_url: Url, unencoded_paths: &[impl AsRef<Path>]) -> anyhow::Result<()> {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please give this function a proper doc comment as it's quite complex.

Comment on lines +114 to +121
fn rm_disabled_by_default(server: TestServer) -> anyhow::Result<()> {
let paths = [FILES, DIRECTORIES]
.concat()
.into_iter()
.map(Path::new)
.chain(iter::once(DEEPLY_NESTED_FILE.as_ref()))
.collect::<Vec<_>>();
assert_rm_err(server.url(), &paths, true)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, for all of these days I wonder whether it wouldn't be nicer and more obvious to use rstest more in the way that it was intended and have test-cases generated? Currently all paths are tested in the same test which is not really the intention of rstest. Maybe take a look at how we do things in upload_files.rs and try to follow that?

@svenstaro
Copy link
Owner

Excuse the conflicts, should be fairly easy to rebase, though.

@svenstaro
Copy link
Owner

Good points.

  • Do we want any kind of confirmation? Currently you might miss-click and a file is just gone.

That would be nice. I would also want a toggle/checkbox that allows turning off confirmation, in case where you want to delete several things en-mass.

I'll give this a shot, but I'm not great at front end coding. We'll see.

Yeah, I think that would be safer and probably also more convenient. I can't think of many cases where I want to just click something and it'll be deleted right then and there without confirmation. I suspect users have also grown to expect confirmations for deletions.

  • Do we want to warn in case a path given to -R is not contained in the served paths? For instance, miniserve -R /tmp /some/dir doesn't do anything.

I think there's some misunderstanding. The paths accepted by -u and -R are actually URL paths relative to /, not file system paths relative to CWD. This means:

1. the specified directory should never escape the serve root, excepting symlink shenanigans (all covered by tests)

2. all paths are valid because new directories can be created on the fly; even something like `-R ../..` is legal because navigating up the serve root is prevented, so effectively it's the same as `-R .`

3. `-R /foo/bar` and `-R foo/bar` are equivalent

So I believe the implementation needs no changes, but it would be prudent to note this in the CLI help document though. I'll make that happen. Also it might be a good idea to log a message at INFO level noting this behaviour.

You are correct, of course. I didn't consider that the paths can become valid during runtime. I think documenting this more clearly would make sense for sure as it had even me confused :D

  • Should we abort if we provide a path to a file but also -R? We don't do this with -u but it might make sense as a followup? So for instance, miniserve -R /tmp /some/file.

Oh I actually didn't know we support serving a single file. That's a nice feature.

Hmm yeah this is very interesting indeed. Because the semantics of -u and -R aren't really compatible with this mode of operation.

But first of all, should we allow removing (or for -u, overwriting) in this mode at all? Personally I believe the answer is yes, but I'd like to hear your opinions too.

I'm not really sure it makes sense. How would we even present that to the user? Currently serving a single file just gives you that file directly without any UI. I'm not sure supporting -u or -R would make any sense outside of an API-esque usage scenario.

And if the answer to the previous question is yes, obviously -u and -R with arguments would make no sense (since a file cannot have subdirectories). Should this be a warning or a hard error?

@svenstaro
Copy link
Owner

For points 2 and 3, the changes would apply to both -u and -R. So I think it's better organisationally if these form a separate PR, queued after this one.

Agreed, no need to do it in this one.

@svenstaro
Copy link
Owner

@cyqsimon wanna push this over the finish line?

@cyqsimon
Copy link
Contributor Author

@cyqsimon wanna push this over the finish line?

Yeah but I'm on vacation at the moment and connectivity is shoddy. Thanks for the reminder though, will try to find some time for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

How about add "delete file" feature?
4 participants