Compare commits

..

13 Commits

Author SHA1 Message Date
sigoden
936d08545b chore(release): version v0.23.1 2022-07-01 06:47:34 +08:00
sigoden
2e6af671ca fix: permissions of unzipped files (#84) 2022-06-30 19:29:47 +08:00
sigoden
583117c01f fix: safari layout and compatibility (#83) 2022-06-30 10:00:42 +08:00
sigoden
6e1df040b4 chore: update deps 2022-06-29 20:36:18 +08:00
sigoden
f5aa3354e1 chore: add github issule templates 2022-06-29 15:16:04 +08:00
sigoden
3ed0d885fe chore(release): version v0.23.0 2022-06-29 11:01:40 +08:00
sigoden
542e9a4ec5 chore: remove aarch64-linux-android platform 2022-06-29 10:58:43 +08:00
sigoden
5ee2c5504c ci: support more platforms (#76) 2022-06-29 10:51:59 +08:00
sigoden
fd02a53823 chore: replace old get-if-addrs with new if-addrs (#78) 2022-06-29 10:01:01 +08:00
sigoden
6554c1c308 feat: use feature to conditional support tls (#77) 2022-06-29 09:19:09 +08:00
sigoden
fe71600bd2 chore(release): version v0.22.0 2022-06-26 12:43:20 +08:00
sigoden
9cfeee0df0 chore: update args help message and readme 2022-06-25 09:58:39 +08:00
sigoden
eb7a536a3f feat: support hiding folders with --hidden (#73) 2022-06-25 08:15:16 +08:00
24 changed files with 448 additions and 368 deletions

17
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,17 @@
---
name: Bug report
about: Create a report to help us improve
---
**Problem**
<!-- A clear and concise description of what the bug is. -->
**Log**
If applicable, add logs to help explain your problem.
**Environment:**
- Dufs version:
- Browser/Webdav Info:
- OS Info:

View File

@@ -0,0 +1,16 @@
---
name: Feature Request
about: If you have any interesting advice, you can tell us.
---
## Specific Demand
<!--
What feature do you need, please describe it in detail.
-->
## Implement Suggestion
<!--
If you have any suggestion for complete this feature, you can tell us.
-->

6
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "cargo" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "monthly"

View File

@@ -7,33 +7,67 @@ on:
jobs: jobs:
release: release:
name: Publish to Github Reelases name: Publish to Github Relases
outputs: outputs:
rc: ${{ steps.check-tag.outputs.rc }} rc: ${{ steps.check-tag.outputs.rc }}
strategy: strategy:
matrix: matrix:
target:
- aarch64-unknown-linux-musl
- aarch64-apple-darwin
- x86_64-apple-darwin
- x86_64-pc-windows-msvc
- x86_64-unknown-linux-musl
include: include:
- target: aarch64-unknown-linux-musl - target: aarch64-unknown-linux-musl
os: ubuntu-latest os: ubuntu-latest
use-cross: true use-cross: true
cargo-flags: ""
- target: aarch64-apple-darwin - target: aarch64-apple-darwin
os: macos-latest os: macos-latest
use-cross: true use-cross: true
cargo-flags: ""
- target: aarch64-pc-windows-msvc
os: windows-latest
use-cross: true
cargo-flags: "--no-default-features"
- target: x86_64-apple-darwin - target: x86_64-apple-darwin
os: macos-latest os: macos-latest
cargo-flags: ""
- target: x86_64-pc-windows-msvc - target: x86_64-pc-windows-msvc
os: windows-latest os: windows-latest
cargo-flags: ""
- target: x86_64-unknown-linux-musl - target: x86_64-unknown-linux-musl
os: ubuntu-latest os: ubuntu-latest
use-cross: true use-cross: true
cargo-flags: ""
- target: i686-unknown-linux-musl
os: ubuntu-latest
use-cross: true
cargo-flags: ""
- target: i686-pc-windows-msvc
os: windows-latest
use-cross: true
cargo-flags: ""
- target: armv7-unknown-linux-musleabihf
os: ubuntu-latest
use-cross: true
cargo-flags: ""
- target: arm-unknown-linux-musleabihf
os: ubuntu-latest
use-cross: true
cargo-flags: ""
- target: mips-unknown-linux-musl
os: ubuntu-latest
use-cross: true
cargo-flags: "--no-default-features"
- target: mipsel-unknown-linux-musl
os: ubuntu-latest
use-cross: true
cargo-flags: "--no-default-features"
- target: mips64-unknown-linux-gnuabi64
os: ubuntu-latest
use-cross: true
cargo-flags: "--no-default-features"
- target: mips64el-unknown-linux-gnuabi64
os: ubuntu-latest
use-cross: true
cargo-flags: "--no-default-features"
runs-on: ${{matrix.os}} runs-on: ${{matrix.os}}
steps: steps:
@@ -59,14 +93,7 @@ jobs:
target: ${{ matrix.target }} target: ${{ matrix.target }}
toolchain: stable toolchain: stable
profile: minimal # minimal component installation (ie, no documentation) profile: minimal # minimal component installation (ie, no documentation)
- name: Install prerequisites
shell: bash
run: |
case ${{ matrix.target }} in
aarch64-unknown-linux-musl) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;;
esac
- name: Show Version Information (Rust, cargo, GCC) - name: Show Version Information (Rust, cargo, GCC)
shell: bash shell: bash
run: | run: |
@@ -82,7 +109,7 @@ jobs:
with: with:
use-cross: ${{ matrix.use-cross }} use-cross: ${{ matrix.use-cross }}
command: build command: build
args: --locked --release --target=${{ matrix.target }} args: --locked --release --target=${{ matrix.target }} ${{ matrix.cargo-flags }}
- name: Build Archive - name: Build Archive
shell: bash shell: bash
@@ -133,6 +160,8 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: release needs: release
steps: steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1 uses: docker/setup-buildx-action@v1
- name: Login to DockerHub - name: Login to DockerHub
@@ -141,9 +170,16 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push - name: Build and push
id: docker_build
uses: docker/build-push-action@v2 uses: docker/build-push-action@v2
with: with:
build-args: |
REPO=${{ github.repository }}
VER=${{ github.ref_name }}
platforms: |
linux/amd64
linux/arm64
linux/386
linux/arm/v7
push: ${{ needs.release.outputs.rc == 'false' }} push: ${{ needs.release.outputs.rc == 'false' }}
tags: ${{ github.repository }}:latest, ${{ github.repository }}:${{ github.ref_name }} tags: ${{ github.repository }}:latest, ${{ github.repository }}:${{ github.ref_name }}

View File

@@ -2,7 +2,30 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [0.21.0] - 2022-06-21 ## [0.23.1] - 2022-06-30
### Bug Fixes
- Safari layout and compatibility ([#83](https://github.com/sigoden/dufs/issues/83))
- Permissions of unzipped files ([#84](https://github.com/sigoden/dufs/issues/84))
## [0.23.0] - 2022-06-29
### Features
- Use feature to conditional support tls ([#77](https://github.com/sigoden/dufs/issues/77))
### Ci
- Support more platforms ([#76](https://github.com/sigoden/dufs/issues/76))
## [0.22.0] - 2022-06-26
### Features
- Support hiding folders with --hidden ([#73](https://github.com/sigoden/dufs/issues/73))
## [0.21.0] - 2022-06-23
### Bug Fixes ### Bug Fixes

305
Cargo.lock generated
View File

@@ -23,7 +23,7 @@ version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [ dependencies = [
"winapi 0.3.9", "winapi",
] ]
[[package]] [[package]]
@@ -54,16 +54,6 @@ dependencies = [
"tempfile", "tempfile",
] ]
[[package]]
name = "async-attributes"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5"
dependencies = [
"quote",
"syn",
]
[[package]] [[package]]
name = "async-channel" name = "async-channel"
version = "1.6.1" version = "1.6.1"
@@ -92,20 +82,6 @@ dependencies = [
"zstd-safe", "zstd-safe",
] ]
[[package]]
name = "async-executor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965"
dependencies = [
"async-task",
"concurrent-queue",
"fastrand",
"futures-lite",
"once_cell",
"slab",
]
[[package]] [[package]]
name = "async-fs" name = "async-fs"
version = "1.5.0" version = "1.5.0"
@@ -117,40 +93,6 @@ dependencies = [
"futures-lite", "futures-lite",
] ]
[[package]]
name = "async-global-executor"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd8b508d585e01084059b60f06ade4cb7415cd2e4084b71dd1cb44e7d3fb9880"
dependencies = [
"async-channel",
"async-executor",
"async-io",
"async-lock",
"blocking",
"futures-lite",
"once_cell",
]
[[package]]
name = "async-io"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5e18f61464ae81cde0a23e713ae8fd299580c54d697a35820cfd0625b8b0e07"
dependencies = [
"concurrent-queue",
"futures-lite",
"libc",
"log",
"once_cell",
"parking",
"polling",
"slab",
"socket2",
"waker-fn",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "async-lock" name = "async-lock"
version = "2.5.0" version = "2.5.0"
@@ -160,34 +102,6 @@ dependencies = [
"event-listener", "event-listener",
] ]
[[package]]
name = "async-std"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52580991739c5cdb36cde8b2a516371c0a3b70dda36d916cc08b82372916808c"
dependencies = [
"async-attributes",
"async-channel",
"async-global-executor",
"async-io",
"async-lock",
"crossbeam-utils",
"futures-channel",
"futures-core",
"futures-io",
"futures-lite",
"gloo-timers",
"kv-log-macro",
"log",
"memchr",
"num_cpus",
"once_cell",
"pin-project-lite",
"pin-utils",
"slab",
"wasm-bindgen-futures",
]
[[package]] [[package]]
name = "async-stream" name = "async-stream"
version = "0.3.3" version = "0.3.3"
@@ -239,8 +153,7 @@ dependencies = [
[[package]] [[package]]
name = "async_io_utilities" name = "async_io_utilities"
version = "0.1.3" version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/Majored/rs-async-io-utilities#1aa191aa5ff651526e0ac08691b1932724350229"
checksum = "0248112abfeab682c97306bc1e180ee957260107a55a437cedf9a3acca92135e"
dependencies = [ dependencies = [
"tokio", "tokio",
] ]
@@ -248,8 +161,7 @@ dependencies = [
[[package]] [[package]]
name = "async_zip" name = "async_zip"
version = "0.0.7" version = "0.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/sigoden/rs-async-zip?branch=patch01#3bd0e3ff5151c1fca6eea0de2c4d122eb1ce1f5c"
checksum = "f0a5c419dca9559f15d04befbf9ff01c39ca16d4c0abd56f60daaf87a386b929"
dependencies = [ dependencies = [
"async-compression", "async-compression",
"async_io_utilities", "async_io_utilities",
@@ -374,12 +286,6 @@ dependencies = [
"pkg-config", "pkg-config",
] ]
[[package]]
name = "c_linked_list"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4964518bd3b4a8190e832886cdc0da9794f12e8e6c1613a9e90ff331c4c8724b"
[[package]] [[package]]
name = "cache-padded" name = "cache-padded"
version = "1.2.0" version = "1.2.0"
@@ -411,14 +317,14 @@ dependencies = [
"num-integer", "num-integer",
"num-traits", "num-traits",
"time", "time",
"winapi 0.3.9", "winapi",
] ]
[[package]] [[package]]
name = "clap" name = "clap"
version = "3.2.4" version = "3.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d20de3739b4fb45a17837824f40aa1769cc7655d7a83e68739a77fe7b30c87a" checksum = "190814073e85d238f31ff738fcb0bf6910cedeb73376c87cd69291028966fd83"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"clap_lex", "clap_lex",
@@ -429,9 +335,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.2.2" version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5538cd660450ebeb4234cfecf8f2284b844ffc4c50531e66d584ad5b91293613" checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [ dependencies = [
"os_str_bytes", "os_str_bytes",
] ]
@@ -481,12 +387,12 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.8" version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"lazy_static", "once_cell",
] ]
[[package]] [[package]]
@@ -511,9 +417,9 @@ dependencies = [
[[package]] [[package]]
name = "diff" name = "diff"
version = "0.1.12" version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]] [[package]]
name = "difflib" name = "difflib"
@@ -572,7 +478,7 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]] [[package]]
name = "dufs" name = "dufs"
version = "0.21.0" version = "0.23.1"
dependencies = [ dependencies = [
"assert_cmd", "assert_cmd",
"assert_fs", "assert_fs",
@@ -584,9 +490,9 @@ dependencies = [
"clap", "clap",
"diqwest", "diqwest",
"futures", "futures",
"get_if_addrs",
"headers", "headers",
"hyper", "hyper",
"if-addrs",
"lazy_static", "lazy_static",
"log", "log",
"md5", "md5",
@@ -615,9 +521,9 @@ dependencies = [
[[package]] [[package]]
name = "either" name = "either"
version = "1.6.1" version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be"
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
@@ -813,12 +719,6 @@ dependencies = [
"slab", "slab",
] ]
[[package]]
name = "gcc"
version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
[[package]] [[package]]
name = "generic-array" name = "generic-array"
version = "0.14.5" version = "0.14.5"
@@ -829,28 +729,6 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "get_if_addrs"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abddb55a898d32925f3148bd281174a68eeb68bbfd9a5938a57b18f506ee4ef7"
dependencies = [
"c_linked_list",
"get_if_addrs-sys",
"libc",
"winapi 0.2.8",
]
[[package]]
name = "get_if_addrs-sys"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d04f9fb746cf36b191c00f3ede8bde9c8e64f9f4b05ae2694a9ccf5e3f5ab48"
dependencies = [
"gcc",
"libc",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.1.16" version = "0.1.16"
@@ -897,18 +775,6 @@ dependencies = [
"walkdir", "walkdir",
] ]
[[package]]
name = "gloo-timers"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9"
dependencies = [
"futures-channel",
"futures-core",
"js-sys",
"wasm-bindgen",
]
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.3.13" version = "0.3.13"
@@ -930,9 +796,9 @@ dependencies = [
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.11.2" version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3"
[[package]] [[package]]
name = "headers" name = "headers"
@@ -1083,6 +949,16 @@ dependencies = [
"unicode-normalization", "unicode-normalization",
] ]
[[package]]
name = "if-addrs"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbc0fa01ffc752e9dbc72818cdb072cd028b86be5e09dd04c5a643704fe101a9"
dependencies = [
"libc",
"winapi",
]
[[package]] [[package]]
name = "ignore" name = "ignore"
version = "0.4.18" version = "0.4.18"
@@ -1103,9 +979,9 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "1.8.2" version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a" checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"hashbrown", "hashbrown",
@@ -1159,15 +1035,6 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "kv-log-macro"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
dependencies = [
"log",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@@ -1197,7 +1064,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"value-bag",
] ]
[[package]] [[package]]
@@ -1299,9 +1165,9 @@ dependencies = [
[[package]] [[package]]
name = "mio" name = "mio"
version = "0.8.3" version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf"
dependencies = [ dependencies = [
"libc", "libc",
"log", "log",
@@ -1437,7 +1303,7 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66"
dependencies = [ dependencies = [
"winapi 0.3.9", "winapi",
] ]
[[package]] [[package]]
@@ -1550,19 +1416,6 @@ version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
[[package]]
name = "polling"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259"
dependencies = [
"cfg-if",
"libc",
"log",
"wepoll-ffi",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "port_check" name = "port_check"
version = "0.1.5" version = "0.1.5"
@@ -1625,18 +1478,18 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.39" version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.18" version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@@ -1760,7 +1613,7 @@ version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [ dependencies = [
"winapi 0.3.9", "winapi",
] ]
[[package]] [[package]]
@@ -1818,16 +1671,15 @@ dependencies = [
"spin", "spin",
"untrusted", "untrusted",
"web-sys", "web-sys",
"winapi 0.3.9", "winapi",
] ]
[[package]] [[package]]
name = "rstest" name = "rstest"
version = "0.13.0" version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b939295f93cb1d12bc1a83cf9ee963199b133fb8a79832dd51b68bb9f59a04dc" checksum = "e9c9dc66cc29792b663ffb5269be669f1613664e69ad56441fdb895c2347b930"
dependencies = [ dependencies = [
"async-std",
"futures", "futures",
"futures-timer", "futures-timer",
"rstest_macros", "rstest_macros",
@@ -1836,9 +1688,9 @@ dependencies = [
[[package]] [[package]]
name = "rstest_macros" name = "rstest_macros"
version = "0.13.0" version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f78aba848123782ba59340928ec7d876ebe745aa0365d6af8a630f19a5c16116" checksum = "5015e68a0685a95ade3eee617ff7101ab6a3fc689203101ca16ebc16f2b89c66"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"proc-macro2", "proc-macro2",
@@ -1954,9 +1806,9 @@ dependencies = [
[[package]] [[package]]
name = "semver" name = "semver"
version = "1.0.10" version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a41d061efea015927ac527063765e73601444cdc344ba855bc7bd44578b25e1c" checksum = "3d92beeab217753479be2f74e54187a6aed4c125ff0703a866c3147a02f0c6dd"
[[package]] [[package]]
name = "serde" name = "serde"
@@ -1980,9 +1832,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.81" version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",
@@ -2048,9 +1900,9 @@ checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.8.0" version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
[[package]] [[package]]
name = "socket2" name = "socket2"
@@ -2059,7 +1911,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
dependencies = [ dependencies = [
"libc", "libc",
"winapi 0.3.9", "winapi",
] ]
[[package]] [[package]]
@@ -2096,9 +1948,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.96" version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf" checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -2116,7 +1968,7 @@ dependencies = [
"libc", "libc",
"redox_syscall", "redox_syscall",
"remove_dir_all", "remove_dir_all",
"winapi 0.3.9", "winapi",
] ]
[[package]] [[package]]
@@ -2137,7 +1989,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
dependencies = [ dependencies = [
"libc", "libc",
"winapi 0.3.9", "winapi",
] ]
[[package]] [[package]]
@@ -2192,7 +2044,7 @@ checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
dependencies = [ dependencies = [
"libc", "libc",
"wasi 0.10.0+wasi-snapshot-preview1", "wasi 0.10.0+wasi-snapshot-preview1",
"winapi 0.3.9", "winapi",
] ]
[[package]] [[package]]
@@ -2226,7 +2078,7 @@ dependencies = [
"signal-hook-registry", "signal-hook-registry",
"socket2", "socket2",
"tokio-macros", "tokio-macros",
"winapi 0.3.9", "winapi",
] ]
[[package]] [[package]]
@@ -2277,9 +2129,9 @@ dependencies = [
[[package]] [[package]]
name = "tower-service" name = "tower-service"
version = "0.3.1" version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
[[package]] [[package]]
name = "tracing" name = "tracing"
@@ -2294,9 +2146,9 @@ dependencies = [
[[package]] [[package]]
name = "tracing-core" name = "tracing-core"
version = "0.1.27" version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7709595b8878a4965ce5e87ebf880a7d39c9afc6837721b21a5a816a8117d921" checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7"
dependencies = [ dependencies = [
"once_cell", "once_cell",
] ]
@@ -2336,9 +2188,9 @@ checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
[[package]] [[package]]
name = "unicode-normalization" name = "unicode-normalization"
version = "0.1.19" version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" checksum = "81dee68f85cab8cf68dec42158baf3a79a1cdc065a8b103025965d6ccb7f6cbd"
dependencies = [ dependencies = [
"tinyvec", "tinyvec",
] ]
@@ -2383,16 +2235,6 @@ dependencies = [
"rand 0.8.5", "rand 0.8.5",
] ]
[[package]]
name = "value-bag"
version = "1.0.0-alpha.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55"
dependencies = [
"ctor",
"version_check",
]
[[package]] [[package]]
name = "vcpkg" name = "vcpkg"
version = "0.2.15" version = "0.2.15"
@@ -2427,7 +2269,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
dependencies = [ dependencies = [
"same-file", "same-file",
"winapi 0.3.9", "winapi",
"winapi-util", "winapi-util",
] ]
@@ -2554,21 +2396,6 @@ dependencies = [
"webpki", "webpki",
] ]
[[package]]
name = "wepoll-ffi"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb"
dependencies = [
"cc",
]
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"
@@ -2591,7 +2418,7 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [ dependencies = [
"winapi 0.3.9", "winapi",
] ]
[[package]] [[package]]
@@ -2649,7 +2476,7 @@ version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
dependencies = [ dependencies = [
"winapi 0.3.9", "winapi",
] ]
[[package]] [[package]]

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "dufs" name = "dufs"
version = "0.21.0" version = "0.23.1"
edition = "2021" edition = "2021"
authors = ["sigoden <sigoden@gmail.com>"] authors = ["sigoden <sigoden@gmail.com>"]
description = "Dufs is a distinctive utility file server" description = "Dufs is a distinctive utility file server"
@@ -14,7 +14,6 @@ keywords = ["static", "file", "server", "webdav", "cli"]
clap = { version = "3", default-features = false, features = ["std", "wrap_help"] } clap = { version = "3", default-features = false, features = ["std", "wrap_help"] }
chrono = "0.4" chrono = "0.4"
tokio = { version = "1", features = ["rt-multi-thread", "macros", "fs", "io-util", "signal"]} tokio = { version = "1", features = ["rt-multi-thread", "macros", "fs", "io-util", "signal"]}
tokio-rustls = "0.23"
tokio-util = { version = "0.7", features = ["io-util"] } tokio-util = { version = "0.7", features = ["io-util"] }
hyper = { version = "0.14", features = ["http1", "server", "tcp", "stream"] } hyper = { version = "0.14", features = ["http1", "server", "tcp", "stream"] }
percent-encoding = "2.1" percent-encoding = "2.1"
@@ -22,13 +21,14 @@ serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
futures = "0.3" futures = "0.3"
base64 = "0.13" base64 = "0.13"
async_zip = "0.0.7" async_zip = { git = "https://github.com/sigoden/rs-async-zip", branch = "patch01" }
async-walkdir = "0.2" async-walkdir = "0.2"
headers = "0.3" headers = "0.3"
mime_guess = "2.0" mime_guess = "2.0"
get_if_addrs = "0.5" if-addrs = "0.7"
rustls = { version = "0.20", default-features = false, features = ["tls12"] } rustls = { version = "0.20", default-features = false, features = ["tls12"], optional = true }
rustls-pemfile = "1" rustls-pemfile = { version = "1", optional = true }
tokio-rustls = { version = "0.23", optional = true }
md5 = "0.7" md5 = "0.7"
lazy_static = "1.4" lazy_static = "1.4"
uuid = { version = "1.1", features = ["v4", "fast-rng"] } uuid = { version = "1.1", features = ["v4", "fast-rng"] }
@@ -38,13 +38,17 @@ log = "0.4"
socket2 = "0.4" socket2 = "0.4"
async-stream = "0.3" async-stream = "0.3"
[features]
default = ["tls"]
tls = ["rustls", "rustls-pemfile", "tokio-rustls"]
[dev-dependencies] [dev-dependencies]
assert_cmd = "2" assert_cmd = "2"
reqwest = { version = "0.11", features = ["blocking", "multipart", "rustls-tls"], default-features = false } reqwest = { version = "0.11", features = ["blocking", "multipart", "rustls-tls"], default-features = false }
assert_fs = "1" assert_fs = "1"
select = "0.5" select = "0.5"
port_check = "0.1" port_check = "0.1"
rstest = "0.13" rstest = "0.15"
regex = "1" regex = "1"
pretty_assertions = "1.2" pretty_assertions = "1.2"
url = "2" url = "2"

View File

@@ -1,10 +1,18 @@
FROM rust:1.61 as builder FROM alpine as builder
RUN rustup target add x86_64-unknown-linux-musl ARG REPO VER TARGETPLATFORM
RUN apt-get update && apt-get install --no-install-recommends -y musl-tools RUN if [ "$TARGETPLATFORM" = "linux/amd64" ]; then \
WORKDIR /app TARGET="x86_64-unknown-linux-musl"; \
COPY . . elif [ "$TARGETPLATFORM" = "linux/arm64" ]; then \
RUN cargo build --target x86_64-unknown-linux-musl --release TARGET="aarch64-unknown-linux-musl"; \
elif [ "$TARGETPLATFORM" = "linux/386" ]; then \
TARGET="i686-unknown-linux-musl"; \
elif [ "$TARGETPLATFORM" = "linux/arm/v7" ]; then \
TARGET="armv7-unknown-linux-musleabihf"; \
fi && \
wget https://github.com/${REPO}/releases/download/${VER}/dufs-${VER}-${TARGET}.tar.gz && \
tar -xf dufs-${VER}-${TARGET}.tar.gz && \
mv dufs /bin/
FROM scratch FROM scratch
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/dufs /bin/ COPY --from=builder /bin/dufs /bin/dufs
ENTRYPOINT ["/bin/dufs"] ENTRYPOINT ["/bin/dufs"]

View File

@@ -1,4 +1,4 @@
# Dufs (Old Name: Duf) # Dufs
[![CI](https://github.com/sigoden/dufs/actions/workflows/ci.yaml/badge.svg)](https://github.com/sigoden/dufs/actions/workflows/ci.yaml) [![CI](https://github.com/sigoden/dufs/actions/workflows/ci.yaml/badge.svg)](https://github.com/sigoden/dufs/actions/workflows/ci.yaml)
[![Crates](https://img.shields.io/crates/v/dufs.svg)](https://crates.io/crates/dufs) [![Crates](https://img.shields.io/crates/v/dufs.svg)](https://crates.io/crates/dufs)
@@ -52,6 +52,7 @@ OPTIONS:
-b, --bind <addr>... Specify bind address -b, --bind <addr>... Specify bind address
-p, --port <port> Specify port to listen on [default: 5000] -p, --port <port> Specify port to listen on [default: 5000]
--path-prefix <path> Specify an path prefix --path-prefix <path> Specify an path prefix
--hidden <value> Hide directories from directory listings, separated by `,`
-a, --auth <rule>... Add auth for path -a, --auth <rule>... Add auth for path
--auth-method <value> Select auth method [default: digest] [possible values: basic, digest] --auth-method <value> Select auth method [default: digest] [possible values: basic, digest]
-A, --allow-all Allow all operations -A, --allow-all Allow all operations
@@ -61,7 +62,7 @@ OPTIONS:
--allow-symlink Allow symlink to files/folders outside root directory --allow-symlink Allow symlink to files/folders outside root directory
--enable-cors Enable CORS, sets `Access-Control-Allow-Origin: *` --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-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 file listing 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) --render-spa Serve SPA(Single Page Application)
--tls-cert <path> Path to an SSL/TLS certificate to serve with HTTPS --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 --tls-key <path> Path to the SSL/TLS certificate's private key
@@ -125,6 +126,12 @@ Listen on a specific port
dufs -p 80 dufs -p 80
``` ```
Hide directories from directory listings
```
dufs --hidden .git,.DS_Store
```
Use https Use https
``` ```
@@ -161,23 +168,23 @@ curl -X DELETE http://127.0.0.1:5000/path-to-file
Dufs supports path level access control. You can control who can do what on which path with `--auth`/`-a`. Dufs supports path level access control. You can control who can do what on which path with `--auth`/`-a`.
``` ```
dufs -a <path>@<readwrite>[@<readonly>] dufs -a <path>@<readwrite>[@<readonly>|@*]
``` ```
- `<path>`: Path to protected - `<path>`: Protected url path
- `<readwrite>`: Account with readwrite permission, required - `<readwrite>`: Account with upload/delete/view/download permission, required
- `<readonly>`: Account with readonly permission, optional - `<readonly>`: Account with view/download permission, optional
> `<readonly>` can be `*` means `<path>` is public, everyone can access/download it. > `*` means `<path>` is public, everyone can view/download it.
For example: For example:
``` ```
dufs -a /@admin:pass@* -a /ui@designer:pass1 -A dufs -a /@admin:pass1@* -a /ui@designer:pass2 -A
``` ```
- All files/folders are public to access/download. - All files/folders are public to view/download.
- Account `admin:pass` can upload/delete/download any files/folders. - Account `admin:pass1` can upload/delete/view/download any files/folders.
- Account `designer:pass1` can upload/delete/download any files/folders in the `ui` folder. - Account `designer:pass2` can upload/delete/view/download any files/folders in the `ui` folder.
## License ## License

View File

@@ -149,7 +149,7 @@ body {
} }
.path svg { .path svg {
height: 100%; height: 16px;
fill: rgba(3,47,98,0.5); fill: rgba(3,47,98,0.5);
padding-right: 0.5em; padding-right: 0.5em;
vertical-align: text-top; vertical-align: text-top;

View File

@@ -32,31 +32,17 @@ let $emptyFolder;
class Uploader { class Uploader {
/** /**
* @type number *
* @param {File} file
* @param {string[]} dirs
*/ */
idx;
/**
* @type File
*/
file;
/**
* @type string
*/
name;
/**
* @type Element
*/
$uploadStatus;
/**
* @type number
*/
uploaded = 0;
/**
* @type number
*/
lastUptime = 0;
static globalIdx = 0;
constructor(file, dirs) { constructor(file, dirs) {
/**
* @type Element
*/
this.$uploadStatus = null
this.uploaded = 0;
this.lastUptime = 0;
this.name = [...dirs, file.name].join("/"); this.name = [...dirs, file.name].join("/");
this.idx = Uploader.globalIdx++; this.idx = Uploader.globalIdx++;
this.file = file; this.file = file;
@@ -70,7 +56,7 @@ class Uploader {
$uploadersTable.insertAdjacentHTML("beforeend", ` $uploadersTable.insertAdjacentHTML("beforeend", `
<tr id="upload${idx}" class="uploader"> <tr id="upload${idx}" class="uploader">
<td class="path cell-icon"> <td class="path cell-icon">
${getSvg(file.path_type)} ${getSvg()}
</td> </td>
<td class="path cell-name"> <td class="path cell-name">
<a href="${encodedUrl}">${encodedName}</a> <a href="${encodedUrl}">${encodedName}</a>
@@ -120,6 +106,8 @@ class Uploader {
} }
} }
Uploader.globalIdx = 0;
/** /**
* Add breadcrumb * Add breadcrumb
* @param {string} href * @param {string} href
@@ -292,12 +280,12 @@ function getSvg(path_type) {
switch (path_type) { switch (path_type) {
case "Dir": case "Dir":
return `<svg height="16" viewBox="0 0 14 16" width="14"><path fill-rule="evenodd" d="M13 4H7V3c0-.66-.31-1-1-1H1c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1V5c0-.55-.45-1-1-1zM6 4H1V3h5v1z"></path></svg>`; return `<svg height="16" viewBox="0 0 14 16" width="14"><path fill-rule="evenodd" d="M13 4H7V3c0-.66-.31-1-1-1H1c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1V5c0-.55-.45-1-1-1zM6 4H1V3h5v1z"></path></svg>`;
case "File": case "SymlinkFile":
return `<svg height="16" viewBox="0 0 12 16" width="12"><path fill-rule="evenodd" d="M6 5H2V4h4v1zM2 8h7V7H2v1zm0 2h7V9H2v1zm0 2h7v-1H2v1zm10-7.5V14c0 .55-.45 1-1 1H1c-.55 0-1-.45-1-1V2c0-.55.45-1 1-1h7.5L12 4.5zM11 5L8 2H1v12h10V5z"></path></svg>`; return `<svg height="16" viewBox="0 0 12 16" width="12"><path fill-rule="evenodd" d="M8.5 1H1c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h10c.55 0 1-.45 1-1V4.5L8.5 1zM11 14H1V2h7l3 3v9zM6 4.5l4 3-4 3v-2c-.98-.02-1.84.22-2.55.7-.71.48-1.19 1.25-1.45 2.3.02-1.64.39-2.88 1.13-3.73.73-.84 1.69-1.27 2.88-1.27v-2H6z"></path></svg>`;
case "SymlinkDir": case "SymlinkDir":
return `<svg height="16" viewBox="0 0 14 16" width="14"><path fill-rule="evenodd" d="M13 4H7V3c0-.66-.31-1-1-1H1c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1V5c0-.55-.45-1-1-1zM1 3h5v1H1V3zm6 9v-2c-.98-.02-1.84.22-2.55.7-.71.48-1.19 1.25-1.45 2.3.02-1.64.39-2.88 1.13-3.73C4.86 8.43 5.82 8 7.01 8V6l4 3-4 3H7z"></path></svg>`; return `<svg height="16" viewBox="0 0 14 16" width="14"><path fill-rule="evenodd" d="M13 4H7V3c0-.66-.31-1-1-1H1c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1V5c0-.55-.45-1-1-1zM1 3h5v1H1V3zm6 9v-2c-.98-.02-1.84.22-2.55.7-.71.48-1.19 1.25-1.45 2.3.02-1.64.39-2.88 1.13-3.73C4.86 8.43 5.82 8 7.01 8V6l4 3-4 3H7z"></path></svg>`;
default: default:
return `<svg height="16" viewBox="0 0 12 16" width="12"><path fill-rule="evenodd" d="M8.5 1H1c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h10c.55 0 1-.45 1-1V4.5L8.5 1zM11 14H1V2h7l3 3v9zM6 4.5l4 3-4 3v-2c-.98-.02-1.84.22-2.55.7-.71.48-1.19 1.25-1.45 2.3.02-1.64.39-2.88 1.13-3.73.73-.84 1.69-1.27 2.88-1.27v-2H6z"></path></svg>`; return `<svg height="16" viewBox="0 0 12 16" width="12"><path fill-rule="evenodd" d="M6 5H2V4h4v1zM2 8h7V7H2v1zm0 2h7V9H2v1zm0 2h7v-1H2v1zm10-7.5V14c0 .55-.45 1-1 1H1c-.55 0-1-.45-1-1V2c0-.55.45-1 1-1h7.5L12 4.5zM11 5L8 2H1v12h10V5z"></path></svg>`;
} }
} }

View File

@@ -1,4 +1,5 @@
use clap::{AppSettings, Arg, ArgMatches, Command}; use clap::{AppSettings, Arg, ArgMatches, Command};
#[cfg(feature = "tls")]
use rustls::{Certificate, PrivateKey}; use rustls::{Certificate, PrivateKey};
use std::env; use std::env;
use std::net::IpAddr; use std::net::IpAddr;
@@ -6,11 +7,12 @@ use std::path::{Path, PathBuf};
use crate::auth::AccessControl; use crate::auth::AccessControl;
use crate::auth::AuthMethod; use crate::auth::AuthMethod;
#[cfg(feature = "tls")]
use crate::tls::{load_certs, load_private_key}; use crate::tls::{load_certs, load_private_key};
use crate::BoxResult; use crate::BoxResult;
fn app() -> Command<'static> { fn app() -> Command<'static> {
Command::new(env!("CARGO_CRATE_NAME")) let app = Command::new(env!("CARGO_CRATE_NAME"))
.version(env!("CARGO_PKG_VERSION")) .version(env!("CARGO_PKG_VERSION"))
.author(env!("CARGO_PKG_AUTHORS")) .author(env!("CARGO_PKG_AUTHORS"))
.about(concat!( .about(concat!(
@@ -48,6 +50,12 @@ fn app() -> Command<'static> {
.value_name("path") .value_name("path")
.help("Specify an path prefix"), .help("Specify an path prefix"),
) )
.arg(
Arg::new("hidden")
.long("hidden")
.help("Hide directories from directory listings, separated by `,`")
.value_name("value"),
)
.arg( .arg(
Arg::new("auth") Arg::new("auth")
.short('a') .short('a')
@@ -104,13 +112,16 @@ fn app() -> Command<'static> {
.arg( .arg(
Arg::new("render-try-index") Arg::new("render-try-index")
.long("render-try-index") .long("render-try-index")
.help("Serve index.html when requesting a directory, returns file listing if not found index.html"), .help("Serve index.html when requesting a directory, returns directory listing if not found index.html"),
) )
.arg( .arg(
Arg::new("render-spa") Arg::new("render-spa")
.long("render-spa") .long("render-spa")
.help("Serve SPA(Single Page Application)"), .help("Serve SPA(Single Page Application)"),
) );
#[cfg(feature = "tls")]
let app = app
.arg( .arg(
Arg::new("tls-cert") Arg::new("tls-cert")
.long("tls-cert") .long("tls-cert")
@@ -122,7 +133,9 @@ fn app() -> Command<'static> {
.long("tls-key") .long("tls-key")
.value_name("path") .value_name("path")
.help("Path to the SSL/TLS certificate's private key"), .help("Path to the SSL/TLS certificate's private key"),
) );
app
} }
pub fn matches() -> ArgMatches { pub fn matches() -> ArgMatches {
@@ -137,6 +150,7 @@ pub struct Args {
pub path_is_file: bool, pub path_is_file: bool,
pub path_prefix: String, pub path_prefix: String,
pub uri_prefix: String, pub uri_prefix: String,
pub hidden: String,
pub auth_method: AuthMethod, pub auth_method: AuthMethod,
pub auth: AccessControl, pub auth: AccessControl,
pub allow_upload: bool, pub allow_upload: bool,
@@ -147,7 +161,10 @@ pub struct Args {
pub render_spa: bool, pub render_spa: bool,
pub render_try_index: bool, pub render_try_index: bool,
pub enable_cors: bool, pub enable_cors: bool,
#[cfg(feature = "tls")]
pub tls: Option<(Vec<Certificate>, PrivateKey)>, pub tls: Option<(Vec<Certificate>, PrivateKey)>,
#[cfg(not(feature = "tls"))]
pub tls: Option<()>,
} }
impl Args { impl Args {
@@ -173,6 +190,10 @@ impl Args {
} else { } else {
format!("/{}/", &path_prefix) format!("/{}/", &path_prefix)
}; };
let hidden: String = matches
.value_of("hidden")
.map(|v| format!(",{},", v))
.unwrap_or_default();
let enable_cors = matches.is_present("enable-cors"); let enable_cors = matches.is_present("enable-cors");
let auth: Vec<&str> = matches let auth: Vec<&str> = matches
.values_of("auth") .values_of("auth")
@@ -190,6 +211,7 @@ impl Args {
let render_index = matches.is_present("render-index"); let render_index = matches.is_present("render-index");
let render_try_index = matches.is_present("render-try-index"); let render_try_index = matches.is_present("render-try-index");
let render_spa = matches.is_present("render-spa"); let render_spa = matches.is_present("render-spa");
#[cfg(feature = "tls")]
let tls = match (matches.value_of("tls-cert"), matches.value_of("tls-key")) { let tls = match (matches.value_of("tls-cert"), matches.value_of("tls-key")) {
(Some(certs_file), Some(key_file)) => { (Some(certs_file), Some(key_file)) => {
let certs = load_certs(certs_file)?; let certs = load_certs(certs_file)?;
@@ -198,6 +220,8 @@ impl Args {
} }
_ => None, _ => None,
}; };
#[cfg(not(feature = "tls"))]
let tls = None;
Ok(Args { Ok(Args {
addrs, addrs,
@@ -206,6 +230,7 @@ impl Args {
path_is_file, path_is_file,
path_prefix, path_prefix,
uri_prefix, uri_prefix,
hidden,
auth_method, auth_method,
auth, auth,
enable_cors, enable_cors,

View File

@@ -3,6 +3,7 @@ mod auth;
mod logger; mod logger;
mod server; mod server;
mod streamer; mod streamer;
#[cfg(feature = "tls")]
mod tls; mod tls;
mod utils; mod utils;
@@ -11,6 +12,7 @@ extern crate log;
use crate::args::{matches, Args}; use crate::args::{matches, Args};
use crate::server::{Request, Server}; use crate::server::{Request, Server};
#[cfg(feature = "tls")]
use crate::tls::{TlsAcceptor, TlsStream}; use crate::tls::{TlsAcceptor, TlsStream};
use std::net::{IpAddr, SocketAddr, TcpListener as StdTcpListener}; use std::net::{IpAddr, SocketAddr, TcpListener as StdTcpListener};
@@ -22,6 +24,7 @@ use tokio::task::JoinHandle;
use hyper::server::conn::{AddrIncoming, AddrStream}; use hyper::server::conn::{AddrIncoming, AddrStream};
use hyper::service::{make_service_fn, service_fn}; use hyper::service::{make_service_fn, service_fn};
#[cfg(feature = "tls")]
use rustls::ServerConfig; use rustls::ServerConfig;
pub type BoxResult<T> = Result<T, Box<dyn std::error::Error>>; pub type BoxResult<T> = Result<T, Box<dyn std::error::Error>>;
@@ -70,12 +73,13 @@ fn serve(args: Arc<Args>) -> BoxResult<Vec<JoinHandle<Result<(), hyper::Error>>>
})) }))
} }
}; };
match args.tls.clone() { match args.tls.as_ref() {
#[cfg(feature = "tls")]
Some((certs, key)) => { Some((certs, key)) => {
let config = ServerConfig::builder() let config = ServerConfig::builder()
.with_safe_defaults() .with_safe_defaults()
.with_no_client_auth() .with_no_client_auth()
.with_single_cert(certs, key)?; .with_single_cert(certs.clone(), key.clone())?;
let config = Arc::new(config); let config = Arc::new(config);
let accepter = TlsAcceptor::new(config.clone(), incoming); let accepter = TlsAcceptor::new(config.clone(), incoming);
let new_service = make_service_fn(move |socket: &TlsStream| { let new_service = make_service_fn(move |socket: &TlsStream| {
@@ -85,6 +89,10 @@ fn serve(args: Arc<Args>) -> BoxResult<Vec<JoinHandle<Result<(), hyper::Error>>>
let server = tokio::spawn(hyper::Server::builder(accepter).serve(new_service)); let server = tokio::spawn(hyper::Server::builder(accepter).serve(new_service));
handles.push(server); handles.push(server);
} }
#[cfg(not(feature = "tls"))]
Some(_) => {
unreachable!()
}
None => { None => {
let new_service = make_service_fn(move |socket: &AddrStream| { let new_service = make_service_fn(move |socket: &AddrStream| {
let remote_addr = socket.remote_addr(); let remote_addr = socket.remote_addr();
@@ -128,7 +136,7 @@ fn print_listening(args: Arc<Args>) -> BoxResult<()> {
} }
} }
if ipv4 || ipv6 { if ipv4 || ipv6 {
let ifaces = get_if_addrs::get_if_addrs() let ifaces = if_addrs::get_if_addrs()
.map_err(|e| format!("Failed to get local interface addresses: {}", e))?; .map_err(|e| format!("Failed to get local interface addresses: {}", e))?;
for iface in ifaces.into_iter() { for iface in ifaces.into_iter() {
let local_ip = iface.ip(); let local_ip = iface.ip();

View File

@@ -1,9 +1,9 @@
use crate::streamer::Streamer; use crate::streamer::Streamer;
use crate::utils::{decode_uri, encode_uri}; use crate::utils::{decode_uri, encode_uri, get_file_name, try_get_file_name};
use crate::{Args, BoxResult}; use crate::{Args, BoxResult};
use async_walkdir::{Filtering, WalkDir};
use xml::escape::escape_str_pcdata; use xml::escape::escape_str_pcdata;
use async_walkdir::WalkDir;
use async_zip::write::{EntryOptions, ZipFileWriter}; use async_zip::write::{EntryOptions, ZipFileWriter};
use async_zip::Compression; use async_zip::Compression;
use chrono::{TimeZone, Utc}; use chrono::{TimeZone, Utc};
@@ -162,7 +162,8 @@ impl Server {
self.handle_zip_dir(path, head_only, &mut res).await?; self.handle_zip_dir(path, head_only, &mut res).await?;
} else if allow_search && query.starts_with("q=") { } else if allow_search && query.starts_with("q=") {
let q = decode_uri(&query[2..]).unwrap_or_default(); let q = decode_uri(&query[2..]).unwrap_or_default();
self.handle_query_dir(path, &q, head_only, &mut res).await?; self.handle_search_dir(path, &q, head_only, &mut res)
.await?;
} else { } else {
self.handle_ls_dir(path, true, head_only, &mut res).await?; self.handle_ls_dir(path, true, head_only, &mut res).await?;
} }
@@ -322,28 +323,39 @@ impl Server {
self.send_index(path, paths, exist, head_only, res) self.send_index(path, paths, exist, head_only, res)
} }
async fn handle_query_dir( async fn handle_search_dir(
&self, &self,
path: &Path, path: &Path,
query: &str, search: &str,
head_only: bool, head_only: bool,
res: &mut Response, res: &mut Response,
) -> BoxResult<()> { ) -> BoxResult<()> {
let mut paths: Vec<PathItem> = vec![]; let mut paths: Vec<PathItem> = vec![];
let mut walkdir = WalkDir::new(path); let hidden = self.args.hidden.to_string();
while let Some(entry) = walkdir.next().await { let search = search.to_string();
if let Ok(entry) = entry { let mut walkdir = WalkDir::new(path).filter(move |entry| {
if !entry let hidden_cloned = hidden.clone();
.file_name() let search_cloned = search.clone();
.to_string_lossy() async move {
let entry_path = entry.path();
let base_name = get_file_name(&entry_path);
if is_hidden(&hidden_cloned, base_name) {
return Filtering::IgnoreDir;
}
if !base_name
.to_lowercase() .to_lowercase()
.contains(&query.to_lowercase()) .contains(&search_cloned.to_lowercase())
{ {
continue; return Filtering::Ignore;
} }
if fs::symlink_metadata(entry.path()).await.is_err() { if fs::symlink_metadata(entry.path()).await.is_err() {
continue; return Filtering::Ignore;
} }
Filtering::Continue
}
});
while let Some(entry) = walkdir.next().await {
if let Ok(entry) = entry {
if let Ok(Some(item)) = self.to_pathitem(entry.path(), path.to_path_buf()).await { if let Ok(Some(item)) = self.to_pathitem(entry.path(), path.to_path_buf()).await {
paths.push(item); paths.push(item);
} }
@@ -359,7 +371,7 @@ impl Server {
res: &mut Response, res: &mut Response,
) -> BoxResult<()> { ) -> BoxResult<()> {
let (mut writer, reader) = tokio::io::duplex(BUF_SIZE); let (mut writer, reader) = tokio::io::duplex(BUF_SIZE);
let filename = get_file_name(path)?; let filename = try_get_file_name(path)?;
res.headers_mut().insert( res.headers_mut().insert(
CONTENT_DISPOSITION, CONTENT_DISPOSITION,
HeaderValue::from_str(&format!( HeaderValue::from_str(&format!(
@@ -374,8 +386,9 @@ impl Server {
return Ok(()); return Ok(());
} }
let path = path.to_owned(); let path = path.to_owned();
let hidden = self.args.hidden.clone();
tokio::spawn(async move { tokio::spawn(async move {
if let Err(e) = zip_dir(&mut writer, &path).await { if let Err(e) = zip_dir(&mut writer, &path, &hidden).await {
error!("Failed to zip {}, {}", path.display(), e); error!("Failed to zip {}, {}", path.display(), e);
} }
}); });
@@ -513,7 +526,7 @@ impl Server {
); );
} }
let filename = get_file_name(path)?; let filename = try_get_file_name(path)?;
res.headers_mut().insert( res.headers_mut().insert(
CONTENT_DISPOSITION, CONTENT_DISPOSITION,
HeaderValue::from_str(&format!("inline; filename=\"{}\"", encode_uri(filename),)) HeaderValue::from_str(&format!("inline; filename=\"{}\"", encode_uri(filename),))
@@ -802,6 +815,10 @@ DATA = {}
let mut rd = fs::read_dir(entry_path).await?; let mut rd = fs::read_dir(entry_path).await?;
while let Ok(Some(entry)) = rd.next_entry().await { while let Ok(Some(entry)) = rd.next_entry().await {
let entry_path = entry.path(); let entry_path = entry.path();
let base_name = get_file_name(&entry_path);
if is_hidden(&self.args.hidden, base_name) {
continue;
}
if let Ok(Some(item)) = self.to_pathitem(entry_path.as_path(), base_path).await { if let Ok(Some(item)) = self.to_pathitem(entry_path.as_path(), base_path).await {
paths.push(item); paths.push(item);
} }
@@ -910,11 +927,8 @@ impl PathItem {
), ),
} }
} }
fn base_name(&self) -> &str { pub fn base_name(&self) -> &str {
Path::new(&self.name) self.name.split('/').last().unwrap_or_default()
.file_name()
.and_then(|v| v.to_str())
.unwrap_or_default()
} }
} }
@@ -978,24 +992,36 @@ fn res_multistatus(res: &mut Response, content: &str) {
)); ));
} }
async fn zip_dir<W: AsyncWrite + Unpin>(writer: &mut W, dir: &Path) -> BoxResult<()> { async fn zip_dir<W: AsyncWrite + Unpin>(writer: &mut W, dir: &Path, hidden: &str) -> BoxResult<()> {
let mut writer = ZipFileWriter::new(writer); let mut writer = ZipFileWriter::new(writer);
let mut walkdir = WalkDir::new(dir); let hidden = hidden.to_string();
let mut walkdir = WalkDir::new(dir).filter(move |entry| {
let hidden = hidden.clone();
async move {
let entry_path = entry.path();
let base_name = get_file_name(&entry_path);
if is_hidden(&hidden, base_name) {
return Filtering::IgnoreDir;
}
let meta = match fs::symlink_metadata(entry.path()).await {
Ok(meta) => meta,
Err(_) => return Filtering::Ignore,
};
if !meta.is_file() {
return Filtering::Ignore;
}
Filtering::Continue
}
});
while let Some(entry) = walkdir.next().await { while let Some(entry) = walkdir.next().await {
if let Ok(entry) = entry { if let Ok(entry) = entry {
let entry_path = entry.path(); let entry_path = entry.path();
let meta = match fs::symlink_metadata(entry.path()).await {
Ok(meta) => meta,
Err(_) => continue,
};
if !meta.is_file() {
continue;
}
let filename = match entry_path.strip_prefix(dir).ok().and_then(|v| v.to_str()) { let filename = match entry_path.strip_prefix(dir).ok().and_then(|v| v.to_str()) {
Some(v) => v, Some(v) => v,
None => continue, None => continue,
}; };
let entry_options = EntryOptions::new(filename.to_owned(), Compression::Deflate); let entry_options = EntryOptions::new(filename.to_owned(), Compression::Deflate)
.unix_permissions(0o644);
let mut file = File::open(&entry_path).await?; let mut file = File::open(&entry_path).await?;
let mut file_writer = writer.write_entry_stream(entry_options).await?; let mut file_writer = writer.write_entry_stream(entry_options).await?;
io::copy(&mut file, &mut file_writer).await?; io::copy(&mut file, &mut file_writer).await?;
@@ -1061,10 +1087,8 @@ fn status_no_content(res: &mut Response) {
*res.status_mut() = StatusCode::NO_CONTENT; *res.status_mut() = StatusCode::NO_CONTENT;
} }
fn get_file_name(path: &Path) -> BoxResult<&str> { fn is_hidden(hidden: &str, file_name: &str) -> bool {
path.file_name() hidden.contains(&format!(",{},", file_name))
.and_then(|v| v.to_str())
.ok_or_else(|| format!("Failed to get file name of `{}`", path.display()).into())
} }
fn set_webdav_headers(res: &mut Response) { fn set_webdav_headers(res: &mut Response) {

View File

@@ -1,4 +1,5 @@
use std::borrow::Cow; use crate::BoxResult;
use std::{borrow::Cow, path::Path};
pub fn encode_uri(v: &str) -> String { pub fn encode_uri(v: &str) -> String {
let parts: Vec<_> = v.split('/').map(urlencoding::encode).collect(); let parts: Vec<_> = v.split('/').map(urlencoding::encode).collect();
@@ -10,3 +11,15 @@ pub fn decode_uri(v: &str) -> Option<Cow<str>> {
.decode_utf8() .decode_utf8()
.ok() .ok()
} }
pub fn get_file_name(path: &Path) -> &str {
path.file_name()
.and_then(|v| v.to_str())
.unwrap_or_default()
}
pub fn try_get_file_name(path: &Path) -> BoxResult<&str> {
path.file_name()
.and_then(|v| v.to_str())
.ok_or_else(|| format!("Failed to get file name of `{}`", path.display()).into())
}

View File

@@ -67,7 +67,7 @@ fn allow_search(#[with(&["--allow-search"])] server: TestServer) -> Result<(), E
let paths = utils::retrive_index_paths(&resp.text()?); let paths = utils::retrive_index_paths(&resp.text()?);
assert!(!paths.is_empty()); assert!(!paths.is_empty());
for p in paths { for p in paths {
assert!(p.contains(&"test.html")); assert!(p.contains("test.html"));
} }
Ok(()) Ok(())
} }

View File

@@ -10,7 +10,7 @@ use std::process::{Command, Stdio};
#[rstest] #[rstest]
fn path_prefix_index(#[with(&["--path-prefix", "xyz"])] server: TestServer) -> Result<(), Error> { fn path_prefix_index(#[with(&["--path-prefix", "xyz"])] server: TestServer) -> Result<(), Error> {
let resp = reqwest::blocking::get(format!("{}{}", server.url(), "xyz"))?; let resp = reqwest::blocking::get(format!("{}{}", server.url(), "xyz"))?;
assert_index_resp!(resp); assert_resp_paths!(resp);
Ok(()) Ok(())
} }

View File

@@ -59,3 +59,35 @@ fn asset_ico(server: TestServer) -> Result<(), Error> {
assert_eq!(resp.headers().get("content-type").unwrap(), "image/x-icon"); assert_eq!(resp.headers().get("content-type").unwrap(), "image/x-icon");
Ok(()) Ok(())
} }
#[rstest]
fn assets_with_prefix(#[with(&["--path-prefix", "xyz"])] server: TestServer) -> Result<(), Error> {
let ver = env!("CARGO_PKG_VERSION");
let resp = reqwest::blocking::get(format!("{}xyz/", server.url()))?;
let index_js = format!("/xyz/__dufs_v{}_index.js", ver);
let index_css = format!("/xyz/__dufs_v{}_index.css", ver);
let favicon_ico = format!("/xyz/__dufs_v{}_favicon.ico", ver);
let text = resp.text()?;
assert!(text.contains(&format!(r#"href="{}""#, index_css)));
assert!(text.contains(&format!(r#"href="{}""#, favicon_ico)));
assert!(text.contains(&format!(r#"src="{}""#, index_js)));
Ok(())
}
#[rstest]
fn asset_js_with_prefix(
#[with(&["--path-prefix", "xyz"])] server: TestServer,
) -> Result<(), Error> {
let url = format!(
"{}xyz/__dufs_v{}_index.js",
server.url(),
env!("CARGO_PKG_VERSION")
);
let resp = reqwest::blocking::get(url)?;
assert_eq!(resp.status(), 200);
assert_eq!(
resp.headers().get("content-type").unwrap(),
"application/javascript"
);
Ok(())
}

View File

@@ -23,9 +23,13 @@ pub static DIR_NO_FOUND: &str = "dir-no-found/";
#[allow(dead_code)] #[allow(dead_code)]
pub static DIR_NO_INDEX: &str = "dir-no-index/"; pub static DIR_NO_INDEX: &str = "dir-no-index/";
/// Directory names for testing hidden
#[allow(dead_code)]
pub static DIR_GIT: &str = ".git/";
/// Directory names for testing purpose /// Directory names for testing purpose
#[allow(dead_code)] #[allow(dead_code)]
pub static DIRECTORIES: &[&str] = &["dira/", "dirb/", "dirc/", DIR_NO_INDEX]; pub static DIRECTORIES: &[&str] = &["dira/", "dirb/", "dirc/", DIR_NO_INDEX, DIR_GIT];
/// Test fixture which creates a temporary directory with a few files and directories inside. /// Test fixture which creates a temporary directory with a few files and directories inside.
/// The directories also contain files. /// The directories also contain files.

42
tests/hidden.rs Normal file
View File

@@ -0,0 +1,42 @@
mod fixtures;
mod utils;
use fixtures::{server, Error, TestServer};
use rstest::rstest;
#[rstest]
#[case(server(&[] as &[&str]), true)]
#[case(server(&["--hidden", ".git,index.html"]), false)]
fn hidden_get_dir(#[case] server: TestServer, #[case] exist: bool) -> Result<(), Error> {
let resp = reqwest::blocking::get(server.url())?;
assert_eq!(resp.status(), 200);
let paths = utils::retrive_index_paths(&resp.text()?);
assert_eq!(paths.contains(".git/"), exist);
assert_eq!(paths.contains("index.html"), exist);
Ok(())
}
#[rstest]
#[case(server(&[] as &[&str]), true)]
#[case(server(&["--hidden", ".git,index.html"]), false)]
fn hidden_propfind_dir(#[case] server: TestServer, #[case] exist: bool) -> Result<(), Error> {
let resp = fetch!(b"PROPFIND", server.url()).send()?;
assert_eq!(resp.status(), 207);
let body = resp.text()?;
assert_eq!(body.contains("<D:href>/.git/</D:href>"), exist);
assert_eq!(body.contains("<D:href>/index.html</D:href>"), exist);
Ok(())
}
#[rstest]
#[case(server(&["--allow-search"] as &[&str]), true)]
#[case(server(&["--allow-search", "--hidden", ".git,test.html"]), false)]
fn hidden_search_dir(#[case] server: TestServer, #[case] exist: bool) -> Result<(), Error> {
let resp = reqwest::blocking::get(format!("{}?q={}", server.url(), "test.html"))?;
assert_eq!(resp.status(), 200);
let paths = utils::retrive_index_paths(&resp.text()?);
for p in paths {
assert_eq!(p.contains("test.html"), exist);
}
Ok(())
}

View File

@@ -7,7 +7,7 @@ use rstest::rstest;
#[rstest] #[rstest]
fn get_dir(server: TestServer) -> Result<(), Error> { fn get_dir(server: TestServer) -> Result<(), Error> {
let resp = reqwest::blocking::get(server.url())?; let resp = reqwest::blocking::get(server.url())?;
assert_index_resp!(resp); assert_resp_paths!(resp);
Ok(()) Ok(())
} }
@@ -69,7 +69,7 @@ fn get_dir_search(#[with(&["-A"])] server: TestServer) -> Result<(), Error> {
let paths = utils::retrive_index_paths(&resp.text()?); let paths = utils::retrive_index_paths(&resp.text()?);
assert!(!paths.is_empty()); assert!(!paths.is_empty());
for p in paths { for p in paths {
assert!(p.contains(&"test.html")); assert!(p.contains("test.html"));
} }
Ok(()) Ok(())
} }
@@ -81,7 +81,7 @@ fn get_dir_search2(#[with(&["-A"])] server: TestServer) -> Result<(), Error> {
let paths = utils::retrive_index_paths(&resp.text()?); let paths = utils::retrive_index_paths(&resp.text()?);
assert!(!paths.is_empty()); assert!(!paths.is_empty());
for p in paths { for p in paths {
assert!(p.contains(&"😀.bin")); assert!(p.contains("😀.bin"));
} }
Ok(()) Ok(())
} }

View File

@@ -1,7 +1,7 @@
mod fixtures; mod fixtures;
mod utils; mod utils;
use fixtures::{server, Error, TestServer, DIR_NO_FOUND, DIR_NO_INDEX}; use fixtures::{server, Error, TestServer, DIR_NO_FOUND, DIR_NO_INDEX, FILES};
use rstest::rstest; use rstest::rstest;
#[rstest] #[rstest]
@@ -30,12 +30,12 @@ fn render_try_index(#[with(&["--render-try-index"])] server: TestServer) -> Resu
#[rstest] #[rstest]
fn render_try_index2(#[with(&["--render-try-index"])] server: TestServer) -> Result<(), Error> { fn render_try_index2(#[with(&["--render-try-index"])] server: TestServer) -> Result<(), Error> {
let resp = reqwest::blocking::get(format!("{}{}", server.url(), DIR_NO_INDEX))?; let resp = reqwest::blocking::get(format!("{}{}", server.url(), DIR_NO_INDEX))?;
let files: Vec<&str> = self::fixtures::FILES let files: Vec<&str> = FILES
.iter() .iter()
.filter(|v| **v != "index.html") .filter(|v| **v != "index.html")
.cloned() .cloned()
.collect(); .collect();
assert_index_resp!(resp, files); assert_resp_paths!(resp, files);
Ok(()) Ok(())
} }

View File

@@ -22,7 +22,7 @@ fn tls_works(#[case] server: TestServer) -> Result<(), Error> {
.danger_accept_invalid_certs(true) .danger_accept_invalid_certs(true)
.build()?; .build()?;
let resp = client.get(server.url()).send()?.error_for_status()?; let resp = client.get(server.url()).send()?.error_for_status()?;
assert_index_resp!(resp); assert_resp_paths!(resp);
Ok(()) Ok(())
} }

View File

@@ -2,9 +2,9 @@ use serde_json::Value;
use std::collections::HashSet; use std::collections::HashSet;
#[macro_export] #[macro_export]
macro_rules! assert_index_resp { macro_rules! assert_resp_paths {
($resp:ident) => { ($resp:ident) => {
assert_index_resp!($resp, self::fixtures::FILES) assert_resp_paths!($resp, self::fixtures::FILES)
}; };
($resp:ident, $files:expr) => { ($resp:ident, $files:expr) => {
assert_eq!($resp.status(), 200); assert_eq!($resp.status(), 200);