# Sigoden/Dufs


Dufs is a distinctive utility file server that supports static serving, uploading, searching, accessing control, webdav...

[![demo](https://user-images.githubusercontent.com/4012553/220513063-ff0f186b-ac54-4682-9af4-47a9781dee0d.png align="left")](https://user-images.githubusercontent.com/4012553/220513063-ff0f186b-ac54-4682-9af4-47a9781dee0d.png)

## Features

* Serve static files
    
* Download folder as zip file
    
* Upload files and folders (Drag & Drop)
    
* Create/Edit/Search files
    
* Resumable/partial uploads/downloads
    
* Access control
    
* Support https
    
* Support webdav
    
* Easy to use with curl
    

## Install

### With cargo

```plaintext
cargo install dufs
```

### With docker

```plaintext
docker run -v `pwd`:/data -p 5000:5000 --rm sigoden/dufs /data -A
```

### With [Homebrew](https://brew.sh/)

```plaintext
brew install dufs
```

### Binaries on macOS, Linux, Windows

Download from [Github Releases](https://github.com/sigoden/dufs/releases), unzip and add dufs to your $PATH.

## CLI

```plaintext
Dufs is a distinctive utility file server - https://github.com/sigoden/dufs

Usage: dufs [OPTIONS] [serve-path]

Arguments:
  [serve-path]  Specific path to serve [default: .]

Options:
  -c, --config <file>        Specify configuration file
  -b, --bind <addrs>         Specify bind address or unix socket
  -p, --port <port>          Specify port to listen on [default: 5000]
      --path-prefix <path>   Specify a path prefix
      --hidden <value>       Hide paths from directory listings, e.g. tmp,*.log,*.lock
  -a, --auth <rules>         Add auth roles, e.g. user:pass@/dir1:rw,/dir2
  -A, --allow-all            Allow all operations
      --allow-upload         Allow upload files/folders
      --allow-delete         Allow delete files/folders
      --allow-search         Allow search files/folders
      --allow-symlink        Allow symlink to files/folders outside root directory
      --allow-archive        Allow zip archive generation
      --enable-cors          Enable CORS, sets `Access-Control-Allow-Origin: *`
      --render-index         Serve index.html when requesting a directory, returns 404 if not found index.html
      --render-try-index     Serve index.html when requesting a directory, returns directory listing if not found index.html
      --render-spa           Serve SPA(Single Page Application)
      --assets <path>        Set the path to the assets directory for overriding the built-in assets
      --log-format <format>  Customize http log format
      --log-file <file>      Specify the file to save logs to, other than stdout/stderr
      --compress <level>     Set zip compress level [default: low] [possible values: none, low, medium, high]
      --completions <shell>  Print shell completion script for <shell> [possible values: bash, elvish, fish, powershell, zsh]
      --tls-cert <path>      Path to an SSL/TLS certificate to serve with HTTPS
      --tls-key <path>       Path to the SSL/TLS certificate's private key
  -h, --help                 Print help
  -V, --version              Print version
```

## Examples

Serve current working directory in read-only mode

```plaintext
dufs
```

Allow all operations like upload/delete/search/create/edit...

```plaintext
dufs -A
```

Only allow upload operation

```plaintext
dufs --allow-upload
```

Serve a specific directory

```plaintext
dufs Downloads
```

Serve a single file

```plaintext
dufs linux-distro.iso
```

Serve a single-page application like react/vue

```plaintext
dufs --render-spa
```

Serve a static website with index.html

```plaintext
dufs --render-index
```

Require username/password

```plaintext
dufs -a admin:123@/:rw
```

Listen on specific host:ip

```plaintext
dufs -b 127.0.0.1 -p 80
```

Listen on unix socket

```plaintext
dufs -b /tmp/dufs.socket
```

Use https

```plaintext
dufs --tls-cert my.crt --tls-key my.key
```

## API

Upload a file

```plaintext
curl -T path-to-file http://127.0.0.1:5000/new-path/path-to-file
```

Download a file

```plaintext
curl http://127.0.0.1:5000/path-to-file           # download the file
curl http://127.0.0.1:5000/path-to-file?hash      # retrieve the sha256 hash of the file
```

Download a folder as zip file

```plaintext
curl -o path-to-folder.zip http://127.0.0.1:5000/path-to-folder?zip
```

Delete a file/folder

```plaintext
curl -X DELETE http://127.0.0.1:5000/path-to-file-or-folder
```

Create a directory

```plaintext
curl -X MKCOL http://127.0.0.1:5000/path-to-folder
```

Move the file/folder to the new path

```plaintext
curl -X MOVE http://127.0.0.1:5000/path -H "Destination: http://127.0.0.1:5000/new-path"
```

List/search directory contents

```plaintext
curl http://127.0.0.1:5000?q=Dockerfile           # search for files, similar to `find -name Dockerfile`
curl http://127.0.0.1:5000?simple                 # output names only, similar to `ls -1`
curl http://127.0.0.1:5000?json                   # output paths in json format
```

With authorization (Both basic or digest auth works)

```plaintext
curl http://127.0.0.1:5000/file --user user:pass                 # basic auth
curl http://127.0.0.1:5000/file --user user:pass --digest        # digest auth
```

Resumable downloads

```plaintext
curl -C- -o file http://127.0.0.1:5000/file
```

Resumable uploads

```plaintext
upload_offset=$(curl -I -s http://127.0.0.1:5000/file | tr -d '\r' | sed -n 's/content-length: //p')
dd skip=$upload_offset if=file status=none ibs=1 | \
  curl -X PATCH -H "X-Update-Range: append" --data-binary @- http://127.0.0.1:5000/file
```

<details><summary><div class="markdown-heading"><h2>Advanced topics</h2><a id="user-content-advanced-topics" class="anchor" href="https://github.com/sigoden/dufs#advanced-topics"></a></div></summary><div class="markdown-heading"><h3></h3><a id="user-content-access-control" class="anchor" href="https://github.com/sigoden/dufs#access-control"></a></div><p><code></code><code></code></p><div class="snippet-clipboard-content notranslate position-relative overflow-auto"><pre><code></code></pre><div class="zeroclipboard-container"></div></div><ol><li><code></code></li><li><code></code></li><li><code></code></li><li><code></code><code></code><code></code><code></code><code></code></li></ol><ul><li><code></code><code></code></li><li><code></code><code></code></li><li><code></code><code></code><code></code><code></code></li><li><code></code></li></ul><blockquote><p><code></code><code></code></p></blockquote><div class="markdown-heading"><h4></h4><a id="user-content-hashed-password" class="anchor" href="https://github.com/sigoden/dufs#hashed-password"></a></div><p></p><p></p><div class="snippet-clipboard-content notranslate position-relative overflow-auto"><pre><code></code></pre><div class="zeroclipboard-container"></div></div><p></p><div class="snippet-clipboard-content notranslate position-relative overflow-auto"><pre><code></code></pre><div class="zeroclipboard-container"></div></div><p></p><ol><li><code></code></li><li></li></ol><div class="markdown-heading"><h3></h3><a id="user-content-hide-paths" class="anchor" href="https://github.com/sigoden/dufs#hide-paths"></a></div><p><code></code></p><div class="snippet-clipboard-content notranslate position-relative overflow-auto"><pre><code></code></pre><div class="zeroclipboard-container"></div></div><blockquote><p><code></code></p></blockquote><div class="highlight highlight-source-shell notranslate position-relative overflow-auto"><pre><span class="pl-s"><span class="pl-pds"></span><span class="pl-pds"></span></span><span class="pl-c"><span class="pl-c"></span></span><span class="pl-s"><span class="pl-pds"></span><span class="pl-pds"></span></span><span class="pl-c"><span class="pl-c"></span></span><span class="pl-s"><span class="pl-pds"></span><span class="pl-pds"></span></span><span class="pl-c"><span class="pl-c"></span></span><span class="pl-s"><span class="pl-pds"></span><span class="pl-pds"></span></span><span class="pl-s"><span class="pl-pds"></span><span class="pl-pds"></span></span></pre><div class="zeroclipboard-container"></div></div><div class="markdown-heading"><h3></h3><a id="user-content-log-format" class="anchor" href="https://github.com/sigoden/dufs#log-format"></a></div><p><code></code></p><p></p><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td></td><td></td></tr><tr><td></td><td></td></tr><tr><td></td><td></td></tr><tr><td></td><td></td></tr><tr><td></td><td></td></tr></tbody></table><p><code></code></p><div class="snippet-clipboard-content notranslate position-relative overflow-auto"><pre><code></code></pre><div class="zeroclipboard-container"></div></div><p></p><div class="snippet-clipboard-content notranslate position-relative overflow-auto"><pre><code></code></pre><div class="zeroclipboard-container"></div></div><p></p><div class="snippet-clipboard-content notranslate position-relative overflow-auto"><pre><code></code></pre><div class="zeroclipboard-container"></div></div><div class="snippet-clipboard-content notranslate position-relative overflow-auto"><pre><code></code></pre><div class="zeroclipboard-container"></div></div><p></p><div class="snippet-clipboard-content notranslate position-relative overflow-auto"><pre><code></code></pre><div class="zeroclipboard-container"></div></div><div class="snippet-clipboard-content notranslate position-relative overflow-auto"><pre><code></code></pre><div class="zeroclipboard-container"></div></div><div class="markdown-heading"><h2></h2><a id="user-content-environment-variables" class="anchor" href="https://github.com/sigoden/dufs#environment-variables"></a></div><p><code></code></p><div class="snippet-clipboard-content notranslate position-relative overflow-auto"><pre><code></code></pre><div class="zeroclipboard-container"></div></div><div class="markdown-heading"><h2></h2><a id="user-content-configuration-file" class="anchor" href="https://github.com/sigoden/dufs#configuration-file"></a></div><p><code></code></p><p></p><div class="highlight highlight-source-yaml notranslate position-relative overflow-auto"><pre><span class="pl-ent"></span><span class="pl-s"><span class="pl-pds"></span><span class="pl-pds"></span></span><span class="pl-ent"></span><span class="pl-s"></span><span class="pl-ent"></span><span class="pl-c1"></span><span class="pl-ent"></span><span class="pl-s"></span><span class="pl-ent"></span><span class="pl-s"></span><span class="pl-s"><span class="pl-pds"></span><span class="pl-pds"></span></span><span class="pl-s"><span class="pl-pds"></span><span class="pl-pds"></span></span><span class="pl-ent"></span><span class="pl-s"></span><span class="pl-s"></span><span class="pl-s"><span class="pl-pds"></span><span class="pl-pds"></span></span><span class="pl-c"><span class="pl-c"></span></span><span class="pl-ent"></span><span class="pl-c1"></span><span class="pl-ent"></span><span class="pl-c1"></span><span class="pl-ent"></span><span class="pl-c1"></span><span class="pl-ent"></span><span class="pl-c1"></span><span class="pl-ent"></span><span class="pl-c1"></span><span class="pl-ent"></span><span class="pl-c1"></span><span class="pl-ent"></span><span class="pl-c1"></span><span class="pl-ent"></span><span class="pl-c1"></span><span class="pl-ent"></span><span class="pl-c1"></span><span class="pl-ent"></span><span class="pl-c1"></span><span class="pl-ent"></span><span class="pl-s"></span><span class="pl-ent"></span><span class="pl-s"><span class="pl-pds"></span><span class="pl-pds"></span></span><span class="pl-ent"></span><span class="pl-s"></span><span class="pl-ent"></span><span class="pl-s"></span><span class="pl-ent"></span><span class="pl-s"></span><span class="pl-ent"></span><span class="pl-s"></span></pre><div class="zeroclipboard-container"></div></div><div class="markdown-heading"><h3></h3><a id="user-content-customize-ui" class="anchor" href="https://github.com/sigoden/dufs#customize-ui"></a></div><p></p><div class="snippet-clipboard-content notranslate position-relative overflow-auto"><pre><code></code></pre><div class="zeroclipboard-container"></div></div><p><code></code></p><p><code></code></p><ul><li><code></code></li><li><code></code></li></ul></details>

## License

Copyright (c) 2022-2024 dufs-developers.

dufs is made available under the terms of either the MIT License or the Apache License 2.0, at your option.

See the LICENSE-APACHE and LICENSE-MIT files for license details.
