mirror of
https://github.com/sigoden/dufs.git
synced 2026-04-09 17:13:02 +03:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb1f3cddea | ||
|
|
05dbcfb2df | ||
|
|
76e967fa59 | ||
|
|
140a360e37 | ||
|
|
604cbb7412 | ||
|
|
c6541b1c36 | ||
|
|
b6729a3d64 | ||
|
|
4f1a35de5d | ||
|
|
2ffdcdf106 | ||
|
|
1e0cdafbcf | ||
|
|
0a03941e05 | ||
|
|
07a7322748 | ||
|
|
936d08545b | ||
|
|
2e6af671ca | ||
|
|
583117c01f | ||
|
|
6e1df040b4 | ||
|
|
f5aa3354e1 | ||
|
|
3ed0d885fe | ||
|
|
542e9a4ec5 | ||
|
|
5ee2c5504c | ||
|
|
fd02a53823 | ||
|
|
6554c1c308 | ||
|
|
fe71600bd2 | ||
|
|
9cfeee0df0 | ||
|
|
eb7a536a3f | ||
|
|
c1c6dbc356 | ||
|
|
e29cf4c752 | ||
|
|
7f062b6705 | ||
|
|
ea8b9e9cce |
17
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal 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:
|
||||||
16
.github/ISSUE_TEMPLATE/feature_requst.md
vendored
Normal file
16
.github/ISSUE_TEMPLATE/feature_requst.md
vendored
Normal 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
6
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "cargo" # See documentation for possible values
|
||||||
|
directory: "/" # Location of package manifests
|
||||||
|
schedule:
|
||||||
|
interval: "monthly"
|
||||||
72
.github/workflows/release.yaml
vendored
72
.github/workflows/release.yaml
vendored
@@ -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 }}
|
||||||
|
|
||||||
|
|||||||
57
CHANGELOG.md
57
CHANGELOG.md
@@ -2,6 +2,63 @@
|
|||||||
|
|
||||||
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.25.0] - 2022-07-06
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Ui supports creating folder ([#91](https://github.com/sigoden/dufs/issues/91))
|
||||||
|
- Ui supports move folder/file to new path ([#92](https://github.com/sigoden/dufs/issues/92))
|
||||||
|
- Check permission on move/copy destination ([#93](https://github.com/sigoden/dufs/issues/93))
|
||||||
|
- Add completions ([#97](https://github.com/sigoden/dufs/issues/97))
|
||||||
|
- Limit the number of concurrent uploads ([#98](https://github.com/sigoden/dufs/issues/98))
|
||||||
|
|
||||||
|
## [0.24.0] - 2022-07-02
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Unexpect stack overflow when searching a lot ([#87](https://github.com/sigoden/dufs/issues/87))
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Allow search with --render-try-index ([#88](https://github.com/sigoden/dufs/issues/88))
|
||||||
|
|
||||||
|
## [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
|
||||||
|
|
||||||
|
- Escape name contains html escape code ([#65](https://github.com/sigoden/dufs/issues/65))
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Use custom logger with timestamp in rfc3339 ([#67](https://github.com/sigoden/dufs/issues/67))
|
||||||
|
|
||||||
|
### Refactor
|
||||||
|
|
||||||
|
- Split css/js from index.html ([#68](https://github.com/sigoden/dufs/issues/68))
|
||||||
|
|
||||||
## [0.20.0] - 2022-06-20
|
## [0.20.0] - 2022-06-20
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
551
Cargo.lock
generated
551
Cargo.lock
generated
@@ -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,138 +54,17 @@ 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]]
|
|
||||||
name = "async-channel"
|
|
||||||
version = "1.6.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319"
|
|
||||||
dependencies = [
|
|
||||||
"concurrent-queue",
|
|
||||||
"event-listener",
|
|
||||||
"futures-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-compression"
|
name = "async-compression"
|
||||||
version = "0.3.14"
|
version = "0.3.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "345fd392ab01f746c717b1357165b76f0b67a60192007b234058c9045fdcf695"
|
checksum = "345fd392ab01f746c717b1357165b76f0b67a60192007b234058c9045fdcf695"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bzip2",
|
|
||||||
"flate2",
|
"flate2",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"memchr",
|
"memchr",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"tokio",
|
"tokio",
|
||||||
"xz2",
|
|
||||||
"zstd",
|
|
||||||
"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]]
|
|
||||||
name = "async-fs"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8b3ca4f8ff117c37c278a2f7415ce9be55560b846b5bc4412aaa5d29c1c3dae2"
|
|
||||||
dependencies = [
|
|
||||||
"async-lock",
|
|
||||||
"blocking",
|
|
||||||
"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]]
|
|
||||||
name = "async-lock"
|
|
||||||
version = "2.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6"
|
|
||||||
dependencies = [
|
|
||||||
"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]]
|
||||||
@@ -209,12 +88,6 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "async-task"
|
|
||||||
version = "4.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.56"
|
version = "0.1.56"
|
||||||
@@ -226,16 +99,6 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "async-walkdir"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "826d88d73e87e7504b635b6e427561faa6a65f4a2f59e75efcbfa51a0876bb90"
|
|
||||||
dependencies = [
|
|
||||||
"async-fs",
|
|
||||||
"futures-lite",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async_io_utilities"
|
name = "async_io_utilities"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
@@ -247,9 +110,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async_zip"
|
name = "async_zip"
|
||||||
version = "0.0.7"
|
version = "0.0.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f0a5c419dca9559f15d04befbf9ff01c39ca16d4c0abd56f60daaf87a386b929"
|
checksum = "76c7255e8cf3719aaed04f4cb181f3a79ae53bd45c0aba1866b8969ac888ed80"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-compression",
|
"async-compression",
|
||||||
"async_io_utilities",
|
"async_io_utilities",
|
||||||
@@ -259,12 +122,6 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "atomic-waker"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@@ -316,20 +173,6 @@ dependencies = [
|
|||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "blocking"
|
|
||||||
version = "1.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc"
|
|
||||||
dependencies = [
|
|
||||||
"async-channel",
|
|
||||||
"async-task",
|
|
||||||
"atomic-waker",
|
|
||||||
"fastrand",
|
|
||||||
"futures-lite",
|
|
||||||
"once_cell",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bstr"
|
name = "bstr"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
@@ -353,47 +196,11 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
|
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bzip2"
|
|
||||||
version = "0.4.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0"
|
|
||||||
dependencies = [
|
|
||||||
"bzip2-sys",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bzip2-sys"
|
|
||||||
version = "0.1.11+1.0.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"libc",
|
|
||||||
"pkg-config",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "c_linked_list"
|
|
||||||
version = "1.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4964518bd3b4a8190e832886cdc0da9794f12e8e6c1613a9e90ff331c4c8724b"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cache-padded"
|
|
||||||
version = "1.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.73"
|
version = "1.0.73"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||||
dependencies = [
|
|
||||||
"jobserver",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
@@ -411,14 +218,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",
|
||||||
@@ -428,21 +235,21 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_lex"
|
name = "clap_complete"
|
||||||
version = "0.2.2"
|
version = "3.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5538cd660450ebeb4234cfecf8f2284b844ffc4c50531e66d584ad5b91293613"
|
checksum = "ead064480dfc4880a10764488415a97fdd36a4cf1bb022d372f02e8faf8386e1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"os_str_bytes",
|
"clap",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "concurrent-queue"
|
name = "clap_lex"
|
||||||
version = "1.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 = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3"
|
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cache-padded",
|
"os_str_bytes",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -481,19 +288,19 @@ 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]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.1.3"
|
version = "0.1.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8"
|
checksum = "5999502d32b9c48d492abe66392408144895020ec4709e549e840799f3bb74c0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
"typenum",
|
"typenum",
|
||||||
@@ -511,9 +318,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,22 +379,21 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dufs"
|
name = "dufs"
|
||||||
version = "0.20.0"
|
version = "0.25.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assert_cmd",
|
"assert_cmd",
|
||||||
"assert_fs",
|
"assert_fs",
|
||||||
"async-stream",
|
"async-stream",
|
||||||
"async-walkdir",
|
|
||||||
"async_zip",
|
"async_zip",
|
||||||
"base64",
|
"base64",
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
|
"clap_complete",
|
||||||
"diqwest",
|
"diqwest",
|
||||||
"env_logger",
|
|
||||||
"futures",
|
"futures",
|
||||||
"get_if_addrs",
|
|
||||||
"headers",
|
"headers",
|
||||||
"hyper",
|
"hyper",
|
||||||
|
"if-addrs",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
"md5",
|
"md5",
|
||||||
@@ -611,14 +417,15 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
"urlencoding",
|
"urlencoding",
|
||||||
"uuid",
|
"uuid",
|
||||||
|
"walkdir",
|
||||||
"xml-rs",
|
"xml-rs",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[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"
|
||||||
@@ -629,22 +436,6 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "env_logger"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
|
|
||||||
dependencies = [
|
|
||||||
"humantime",
|
|
||||||
"log",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "event-listener"
|
|
||||||
version = "2.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "1.7.0"
|
version = "1.7.0"
|
||||||
@@ -762,21 +553,6 @@ version = "0.3.21"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
|
checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-lite"
|
|
||||||
version = "1.12.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48"
|
|
||||||
dependencies = [
|
|
||||||
"fastrand",
|
|
||||||
"futures-core",
|
|
||||||
"futures-io",
|
|
||||||
"memchr",
|
|
||||||
"parking",
|
|
||||||
"pin-project-lite",
|
|
||||||
"waker-fn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-macro"
|
name = "futures-macro"
|
||||||
version = "0.3.21"
|
version = "0.3.21"
|
||||||
@@ -824,12 +600,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"
|
||||||
@@ -840,28 +610,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"
|
||||||
@@ -908,18 +656,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"
|
||||||
@@ -941,9 +677,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"
|
||||||
@@ -1033,12 +769,6 @@ version = "1.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
|
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "humantime"
|
|
||||||
version = "2.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "0.14.19"
|
version = "0.14.19"
|
||||||
@@ -1100,6 +830,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"
|
||||||
@@ -1120,9 +860,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",
|
||||||
@@ -1158,15 +898,6 @@ version = "1.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
|
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jobserver"
|
|
||||||
version = "0.1.24"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.58"
|
version = "0.3.58"
|
||||||
@@ -1176,15 +907,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"
|
||||||
@@ -1214,18 +936,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"value-bag",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lzma-sys"
|
|
||||||
version = "0.1.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e06754c4acf47d49c727d5665ca9fb828851cda315ed3bd51edd148ef78a8772"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"libc",
|
|
||||||
"pkg-config",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1316,9 +1026,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",
|
||||||
@@ -1387,9 +1097,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.12.0"
|
version = "1.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
|
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opaque-debug"
|
name = "opaque-debug"
|
||||||
@@ -1454,15 +1164,9 @@ 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]]
|
|
||||||
name = "parking"
|
|
||||||
version = "2.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
@@ -1567,19 +1271,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"
|
||||||
@@ -1642,18 +1333,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",
|
||||||
]
|
]
|
||||||
@@ -1750,9 +1441,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.5.6"
|
version = "1.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
|
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -1767,9 +1458,9 @@ checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.6.26"
|
version = "0.6.27"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
|
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "remove_dir_all"
|
name = "remove_dir_all"
|
||||||
@@ -1777,7 +1468,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]]
|
||||||
@@ -1835,16 +1526,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",
|
||||||
@@ -1853,9 +1543,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",
|
||||||
@@ -1971,24 +1661,24 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "1.0.10"
|
version = "1.0.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a41d061efea015927ac527063765e73601444cdc344ba855bc7bd44578b25e1c"
|
checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.137"
|
version = "1.0.138"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
|
checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.137"
|
version = "1.0.138"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
|
checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -1997,9 +1687,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",
|
||||||
@@ -2065,9 +1755,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"
|
||||||
@@ -2076,7 +1766,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]]
|
||||||
@@ -2113,9 +1803,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",
|
||||||
@@ -2133,7 +1823,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
"remove_dir_all",
|
"remove_dir_all",
|
||||||
"winapi 0.3.9",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2154,7 +1844,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]]
|
||||||
@@ -2209,7 +1899,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]]
|
||||||
@@ -2243,7 +1933,7 @@ dependencies = [
|
|||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
"winapi 0.3.9",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2294,9 +1984,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"
|
||||||
@@ -2311,9 +2001,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",
|
||||||
]
|
]
|
||||||
@@ -2353,9 +2043,9 @@ checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-normalization"
|
name = "unicode-normalization"
|
||||||
version = "0.1.19"
|
version = "0.1.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9"
|
checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
]
|
]
|
||||||
@@ -2400,16 +2090,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"
|
||||||
@@ -2431,12 +2111,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "waker-fn"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "walkdir"
|
name = "walkdir"
|
||||||
version = "2.3.2"
|
version = "2.3.2"
|
||||||
@@ -2444,7 +2118,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",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2571,21 +2245,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"
|
||||||
@@ -2608,7 +2267,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]]
|
||||||
@@ -2666,7 +2325,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]]
|
||||||
@@ -2686,41 +2345,3 @@ dependencies = [
|
|||||||
"markup5ever",
|
"markup5ever",
|
||||||
"time",
|
"time",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "xz2"
|
|
||||||
version = "0.1.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2"
|
|
||||||
dependencies = [
|
|
||||||
"lzma-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zstd"
|
|
||||||
version = "0.11.2+zstd.1.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4"
|
|
||||||
dependencies = [
|
|
||||||
"zstd-safe",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zstd-safe"
|
|
||||||
version = "5.0.2+zstd.1.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"zstd-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zstd-sys"
|
|
||||||
version = "2.0.1+zstd.1.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9fd07cbbc53846d9145dbffdf6dd09a7a0aa52be46741825f5c97bdd4f73f12b"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|||||||
22
Cargo.toml
22
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "dufs"
|
name = "dufs"
|
||||||
version = "0.20.0"
|
version = "0.25.0"
|
||||||
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"
|
||||||
@@ -12,9 +12,9 @@ keywords = ["static", "file", "server", "webdav", "cli"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "3", default-features = false, features = ["std", "wrap_help"] }
|
clap = { version = "3", default-features = false, features = ["std", "wrap_help"] }
|
||||||
|
clap_complete = "3"
|
||||||
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,22 +22,26 @@ 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 = { version = "0.0.8", default-features = false, features = ["deflate"] }
|
||||||
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"] }
|
||||||
urlencoding = "2.1"
|
urlencoding = "2.1"
|
||||||
xml-rs = "0.8"
|
xml-rs = "0.8"
|
||||||
env_logger = { version = "0.9", default-features = false, features = ["humantime"] }
|
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
socket2 = "0.4"
|
socket2 = "0.4"
|
||||||
async-stream = "0.3"
|
async-stream = "0.3"
|
||||||
|
walkdir = "2.3"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["tls"]
|
||||||
|
tls = ["rustls", "rustls-pemfile", "tokio-rustls"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
assert_cmd = "2"
|
assert_cmd = "2"
|
||||||
@@ -45,7 +49,7 @@ reqwest = { version = "0.11", features = ["blocking", "multipart", "rustls-tls"]
|
|||||||
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"
|
||||||
|
|||||||
24
Dockerfile
24
Dockerfile
@@ -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"]
|
||||||
|
|||||||
40
README.md
40
README.md
@@ -1,11 +1,11 @@
|
|||||||
# Dufs (Old Name: Duf)
|
# Dufs
|
||||||
|
|
||||||
[](https://github.com/sigoden/dufs/actions/workflows/ci.yaml)
|
[](https://github.com/sigoden/dufs/actions/workflows/ci.yaml)
|
||||||
[](https://crates.io/crates/dufs)
|
[](https://crates.io/crates/dufs)
|
||||||
|
|
||||||
Dufs is a distinctive utility file server that supports static serving, uploading, searching, accessing control, webdav...
|
Dufs is a distinctive utility file server that supports static serving, uploading, searching, accessing control, webdav...
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@@ -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,8 +62,9 @@ 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)
|
||||||
|
--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-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
|
||||||
-h, --help Print help information
|
-h, --help Print help information
|
||||||
@@ -77,7 +79,7 @@ Serve current working directory
|
|||||||
dufs
|
dufs
|
||||||
```
|
```
|
||||||
|
|
||||||
Explicitly allow all operations including upload/delete
|
Explicitly allow all operations including download/upload/delete
|
||||||
|
|
||||||
```
|
```
|
||||||
dufs -A
|
dufs -A
|
||||||
@@ -89,13 +91,13 @@ Only allow upload operation
|
|||||||
dufs --allow-upload
|
dufs --allow-upload
|
||||||
```
|
```
|
||||||
|
|
||||||
Serve a directory
|
Serve a specific directory
|
||||||
|
|
||||||
```
|
```
|
||||||
dufs Downloads
|
dufs Downloads
|
||||||
```
|
```
|
||||||
|
|
||||||
Serve a single file
|
Serve a specific file
|
||||||
|
|
||||||
```
|
```
|
||||||
dufs linux-distro.iso
|
dufs linux-distro.iso
|
||||||
@@ -107,7 +109,7 @@ Serve index.html when requesting a directory
|
|||||||
dufs --render-index
|
dufs --render-index
|
||||||
```
|
```
|
||||||
|
|
||||||
Serve SPA(Single Page Application)
|
Serve single-page application like react
|
||||||
|
|
||||||
```
|
```
|
||||||
dufs --render-spa
|
dufs --render-spa
|
||||||
@@ -125,6 +127,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 +169,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
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,15 @@ body {
|
|||||||
height: 1.1rem;
|
height: 1.1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toolbox .control {
|
||||||
|
cursor: pointer;
|
||||||
|
padding-left: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-file input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.searchbar {
|
.searchbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
@@ -90,15 +99,6 @@ body {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.upload-control {
|
|
||||||
cursor: pointer;
|
|
||||||
padding-left: 0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upload-control input {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upload-status span {
|
.upload-status span {
|
||||||
width: 70px;
|
width: 70px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -132,7 +132,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.paths-table .cell-actions {
|
.paths-table .cell-actions {
|
||||||
width: 60px;
|
width: 75px;
|
||||||
display: flex;
|
display: flex;
|
||||||
padding-left: 0.6em;
|
padding-left: 0.6em;
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
@@ -175,7 +175,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.action-btn {
|
.action-btn {
|
||||||
padding-left: 0.4em;
|
padding-right: 0.3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.uploaders-table {
|
.uploaders-table {
|
||||||
|
|||||||
@@ -15,12 +15,18 @@
|
|||||||
<svg width="16" height="16" viewBox="0 0 16 16"><path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/><path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/></svg>
|
<svg width="16" height="16" viewBox="0 0 16 16"><path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/><path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/></svg>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="upload-control hidden" title="Upload files">
|
<div class="control upload-file hidden" title="Upload files">
|
||||||
<label for="file">
|
<label for="file">
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16"><path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/><path d="M7.646 1.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 2.707V11.5a.5.5 0 0 1-1 0V2.707L5.354 4.854a.5.5 0 1 1-.708-.708l3-3z"/></svg>
|
<svg width="16" height="16" viewBox="0 0 16 16"><path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/><path d="M7.646 1.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 2.707V11.5a.5.5 0 0 1-1 0V2.707L5.354 4.854a.5.5 0 1 1-.708-.708l3-3z"/></svg>
|
||||||
</label>
|
</label>
|
||||||
<input type="file" id="file" name="file" multiple>
|
<input type="file" id="file" name="file" multiple>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="control new-folder hidden" title="New folder">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<path d="m.5 3 .04.87a1.99 1.99 0 0 0-.342 1.311l.637 7A2 2 0 0 0 2.826 14H9v-1H2.826a1 1 0 0 1-.995-.91l-.637-7A1 1 0 0 1 2.19 4h11.62a1 1 0 0 1 .996 1.09L14.54 8h1.005l.256-2.819A2 2 0 0 0 13.81 3H9.828a2 2 0 0 1-1.414-.586l-.828-.828A2 2 0 0 0 6.172 1H2.5a2 2 0 0 0-2 2zm5.672-1a1 1 0 0 1 .707.293L7.586 3H2.19c-.24 0-.47.042-.683.12L1.5 2.98a1 1 0 0 1 1-.98h3.672z"/>
|
||||||
|
<path d="M13.5 10a.5.5 0 0 1 .5.5V12h1.5a.5.5 0 1 1 0 1H14v1.5a.5.5 0 1 1-1 0V13h-1.5a.5.5 0 0 1 0-1H13v-1.5a.5.5 0 0 1 .5-.5z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form class="searchbar hidden">
|
<form class="searchbar hidden">
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
|
|||||||
199
assets/index.js
199
assets/index.js
@@ -29,57 +29,56 @@ let $uploadersTable;
|
|||||||
* @type Element
|
* @type Element
|
||||||
*/
|
*/
|
||||||
let $emptyFolder;
|
let $emptyFolder;
|
||||||
|
/**
|
||||||
|
* @type Element
|
||||||
|
*/
|
||||||
|
let $newFolder;
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
upload() {
|
upload() {
|
||||||
const { file, idx, name } = this;
|
const { idx, name } = this;
|
||||||
let url = getUrl(name);
|
const url = getUrl(name);
|
||||||
|
const encodedUrl = encodedStr(url);
|
||||||
|
const encodedName = encodedStr(name);
|
||||||
$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="${url}">${name}</a>
|
<a href="${encodedUrl}">${encodedName}</a>
|
||||||
</td>
|
</td>
|
||||||
<td class="cell-status upload-status" id="uploadStatus${idx}"></td>
|
<td class="cell-status upload-status" id="uploadStatus${idx}"></td>
|
||||||
</tr>`);
|
</tr>`);
|
||||||
$uploadersTable.classList.remove("hidden");
|
$uploadersTable.classList.remove("hidden");
|
||||||
$emptyFolder.classList.add("hidden");
|
$emptyFolder.classList.add("hidden");
|
||||||
this.$uploadStatus = document.getElementById(`uploadStatus${idx}`);
|
this.$uploadStatus = document.getElementById(`uploadStatus${idx}`);
|
||||||
this.lastUptime = Date.now();
|
this.$uploadStatus.innerHTML = '-';
|
||||||
|
Uploader.queues.push(this);
|
||||||
|
Uploader.runQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
ajax() {
|
||||||
|
Uploader.runings += 1;
|
||||||
|
const url = getUrl(this.name);
|
||||||
|
this.lastUptime = Date.now();
|
||||||
const ajax = new XMLHttpRequest();
|
const ajax = new XMLHttpRequest();
|
||||||
ajax.upload.addEventListener("progress", e => this.progress(e), false);
|
ajax.upload.addEventListener("progress", e => this.progress(e), false);
|
||||||
ajax.addEventListener("readystatechange", () => {
|
ajax.addEventListener("readystatechange", () => {
|
||||||
@@ -94,13 +93,14 @@ class Uploader {
|
|||||||
ajax.addEventListener("error", () => this.fail(), false);
|
ajax.addEventListener("error", () => this.fail(), false);
|
||||||
ajax.addEventListener("abort", () => this.fail(), false);
|
ajax.addEventListener("abort", () => this.fail(), false);
|
||||||
ajax.open("PUT", url);
|
ajax.open("PUT", url);
|
||||||
ajax.send(file);
|
ajax.send(this.file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
progress(event) {
|
progress(event) {
|
||||||
let now = Date.now();
|
const now = Date.now();
|
||||||
let speed = (event.loaded - this.uploaded) / (now - this.lastUptime) * 1000;
|
const speed = (event.loaded - this.uploaded) / (now - this.lastUptime) * 1000;
|
||||||
let [speedValue, speedUnit] = formatSize(speed);
|
const [speedValue, speedUnit] = formatSize(speed);
|
||||||
const speedText = `${speedValue}${speedUnit.toLowerCase()}/s`;
|
const speedText = `${speedValue}${speedUnit.toLowerCase()}/s`;
|
||||||
const progress = formatPercent((event.loaded / event.total) * 100);
|
const progress = formatPercent((event.loaded / event.total) * 100);
|
||||||
const duration = formatDuration((event.total - event.loaded) / speed)
|
const duration = formatDuration((event.total - event.loaded) / speed)
|
||||||
@@ -111,13 +111,34 @@ class Uploader {
|
|||||||
|
|
||||||
complete() {
|
complete() {
|
||||||
this.$uploadStatus.innerHTML = `✓`;
|
this.$uploadStatus.innerHTML = `✓`;
|
||||||
|
Uploader.runings -= 1;
|
||||||
|
Uploader.runQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
fail() {
|
fail() {
|
||||||
this.$uploadStatus.innerHTML = `✗`;
|
this.$uploadStatus.innerHTML = `✗`;
|
||||||
|
Uploader.runings -= 1;
|
||||||
|
Uploader.runQueue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Uploader.globalIdx = 0;
|
||||||
|
|
||||||
|
Uploader.runings = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type Uploader[]
|
||||||
|
*/
|
||||||
|
Uploader.queues = [];
|
||||||
|
|
||||||
|
|
||||||
|
Uploader.runQueue = () => {
|
||||||
|
if (Uploader.runings > 2) return;
|
||||||
|
let uploader = Uploader.queues.shift();
|
||||||
|
if (!uploader) return;
|
||||||
|
uploader.ajax();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add breadcrumb
|
* Add breadcrumb
|
||||||
* @param {string} href
|
* @param {string} href
|
||||||
@@ -141,12 +162,14 @@ function addBreadcrumb(href, uri_prefix) {
|
|||||||
}
|
}
|
||||||
path += encodeURI(name);
|
path += encodeURI(name);
|
||||||
}
|
}
|
||||||
|
const encodedPath = encodedStr(path);
|
||||||
|
const encodedName = encodedStr(name);
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
$breadcrumb.insertAdjacentHTML("beforeend", `<a href="${path}"><svg width="16" height="16" viewBox="0 0 16 16"><path d="M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5z"/></svg></a>`);
|
$breadcrumb.insertAdjacentHTML("beforeend", `<a href="${encodedPath}"><svg width="16" height="16" viewBox="0 0 16 16"><path d="M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5z"/></svg></a>`);
|
||||||
} else if (i === len - 1) {
|
} else if (i === len - 1) {
|
||||||
$breadcrumb.insertAdjacentHTML("beforeend", `<b>${name}</b>`);
|
$breadcrumb.insertAdjacentHTML("beforeend", `<b>${encodedName}</b>`);
|
||||||
} else {
|
} else {
|
||||||
$breadcrumb.insertAdjacentHTML("beforeend", `<a href="${path}">${name}</a>`);
|
$breadcrumb.insertAdjacentHTML("beforeend", `<a href="${encodedPath}">${encodedName}</a>`);
|
||||||
}
|
}
|
||||||
if (i !== len - 1) {
|
if (i !== len - 1) {
|
||||||
$breadcrumb.insertAdjacentHTML("beforeend", `<span class="separator">/</span>`);
|
$breadcrumb.insertAdjacentHTML("beforeend", `<span class="separator">/</span>`);
|
||||||
@@ -160,34 +183,45 @@ function addBreadcrumb(href, uri_prefix) {
|
|||||||
* @param {number} index
|
* @param {number} index
|
||||||
*/
|
*/
|
||||||
function addPath(file, index) {
|
function addPath(file, index) {
|
||||||
|
const encodedName = encodedStr(file.name);
|
||||||
let url = getUrl(file.name)
|
let url = getUrl(file.name)
|
||||||
|
let encodedUrl = encodedStr(url);
|
||||||
let actionDelete = "";
|
let actionDelete = "";
|
||||||
let actionDownload = "";
|
let actionDownload = "";
|
||||||
|
let actionMove = "";
|
||||||
if (file.path_type.endsWith("Dir")) {
|
if (file.path_type.endsWith("Dir")) {
|
||||||
url += "/";
|
url += "/";
|
||||||
|
encodedUrl += "/";
|
||||||
actionDownload = `
|
actionDownload = `
|
||||||
<div class="action-btn">
|
<div class="action-btn">
|
||||||
<a href="${url}?zip" title="Download folder as a .zip file">
|
<a href="${encodedUrl}?zip" title="Download folder as a .zip file">
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16"><path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/><path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/></svg>
|
<svg width="16" height="16" viewBox="0 0 16 16"><path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/><path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/></svg>
|
||||||
</a>
|
</a>
|
||||||
</div>`;
|
</div>`;
|
||||||
} else {
|
} else {
|
||||||
actionDownload = `
|
actionDownload = `
|
||||||
<div class="action-btn" >
|
<div class="action-btn" >
|
||||||
<a href="${url}" title="Download file" download>
|
<a href="${encodedUrl}" title="Download file" download>
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16"><path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/><path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/></svg>
|
<svg width="16" height="16" viewBox="0 0 16 16"><path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/><path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/></svg>
|
||||||
</a>
|
</a>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
if (DATA.allow_delete) {
|
if (DATA.allow_delete) {
|
||||||
|
if (DATA.allow_upload) {
|
||||||
|
actionMove = `
|
||||||
|
<div onclick="movePath(${index})" class="action-btn" id="moveBtn${index}" title="Move to new path">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M1.5 1.5A.5.5 0 0 0 1 2v4.8a2.5 2.5 0 0 0 2.5 2.5h9.793l-3.347 3.346a.5.5 0 0 0 .708.708l4.2-4.2a.5.5 0 0 0 0-.708l-4-4a.5.5 0 0 0-.708.708L13.293 8.3H3.5A1.5 1.5 0 0 1 2 6.8V2a.5.5 0 0 0-.5-.5z"/></svg>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
actionDelete = `
|
actionDelete = `
|
||||||
<div onclick="deletePath(${index})" class="action-btn" id="deleteBtn${index}" title="Delete ${file.name}">
|
<div onclick="deletePath(${index})" class="action-btn" id="deleteBtn${index}" title="Delete">
|
||||||
<svg width="16" height="16" fill="currentColor"viewBox="0 0 16 16"><path d="M6.854 7.146a.5.5 0 1 0-.708.708L7.293 9l-1.147 1.146a.5.5 0 0 0 .708.708L8 9.707l1.146 1.147a.5.5 0 0 0 .708-.708L8.707 9l1.147-1.146a.5.5 0 0 0-.708-.708L8 8.293 6.854 7.146z"/><path d="M14 14V4.5L9.5 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2zM9.5 3A1.5 1.5 0 0 0 11 4.5h2V14a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h5.5v2z"/></svg>
|
<svg width="16" height="16" fill="currentColor"viewBox="0 0 16 16"><path d="M6.854 7.146a.5.5 0 1 0-.708.708L7.293 9l-1.147 1.146a.5.5 0 0 0 .708.708L8 9.707l1.146 1.147a.5.5 0 0 0 .708-.708L8.707 9l1.147-1.146a.5.5 0 0 0-.708-.708L8 8.293 6.854 7.146z"/><path d="M14 14V4.5L9.5 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2zM9.5 3A1.5 1.5 0 0 0 11 4.5h2V14a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h5.5v2z"/></svg>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
let actionCell = `
|
let actionCell = `
|
||||||
<td class="cell-actions">
|
<td class="cell-actions">
|
||||||
${actionDownload}
|
${actionDownload}
|
||||||
|
${actionMove}
|
||||||
${actionDelete}
|
${actionDelete}
|
||||||
</td>`
|
</td>`
|
||||||
|
|
||||||
@@ -197,7 +231,7 @@ function addPath(file, index) {
|
|||||||
${getSvg(file.path_type)}
|
${getSvg(file.path_type)}
|
||||||
</td>
|
</td>
|
||||||
<td class="path cell-name">
|
<td class="path cell-name">
|
||||||
<a href="${url}" title="${file.name}">${file.name}</a>
|
<a href="${encodedUrl}">${encodedName}</a>
|
||||||
</td>
|
</td>
|
||||||
<td class="cell-mtime">${formatMtime(file.mtime)}</td>
|
<td class="cell-mtime">${formatMtime(file.mtime)}</td>
|
||||||
<td class="cell-size">${formatSize(file.size).join(" ")}</td>
|
<td class="cell-size">${formatSize(file.size).join(" ")}</td>
|
||||||
@@ -206,7 +240,7 @@ function addPath(file, index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete pathitem
|
* Delete path
|
||||||
* @param {number} index
|
* @param {number} index
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
@@ -236,6 +270,44 @@ async function deletePath(index) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move path
|
||||||
|
* @param {number} index
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async function movePath(index) {
|
||||||
|
const file = DATA.paths[index];
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
const fileUrl = getUrl(file.name);
|
||||||
|
const fileUrlObj = new URL(fileUrl)
|
||||||
|
|
||||||
|
const prefix = DATA.uri_prefix.slice(0, -1);
|
||||||
|
|
||||||
|
const filePath = decodeURIComponent(fileUrlObj.pathname.slice(prefix.length));
|
||||||
|
|
||||||
|
const newPath = prompt("Enter new path", filePath)
|
||||||
|
if (!newPath || filePath === newPath) return;
|
||||||
|
const newFileUrl = fileUrlObj.origin + prefix + encodeURI(newPath);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(getUrl(file.name), {
|
||||||
|
method: "MOVE",
|
||||||
|
headers: {
|
||||||
|
"Destination": newFileUrl,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (res.status >= 200 && res.status < 300) {
|
||||||
|
location.href = newFileUrl.split("/").slice(0, -1).join("/")
|
||||||
|
} else {
|
||||||
|
throw new Error(await res.text())
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
alert(`Cannot move \`${filePath}\` to \`${newPath}\`, ${err.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function dropzone() {
|
function dropzone() {
|
||||||
["drag", "dragstart", "dragend", "dragover", "dragenter", "dragleave", "drop"].forEach(name => {
|
["drag", "dragstart", "dragend", "dragover", "dragenter", "dragleave", "drop"].forEach(name => {
|
||||||
document.addEventListener(name, e => {
|
document.addEventListener(name, e => {
|
||||||
@@ -260,6 +332,24 @@ function dropzone() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a folder
|
||||||
|
* @param {string} name
|
||||||
|
*/
|
||||||
|
async function createFolder(name) {
|
||||||
|
const url = getUrl(name);
|
||||||
|
try {
|
||||||
|
const res = await fetch(url, {
|
||||||
|
method: "MKCOL",
|
||||||
|
});
|
||||||
|
if (res.status >= 200 && res.status < 300) {
|
||||||
|
location.href = url;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
alert(`Cannot create folder \`${name}\`, ${err.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function addFileEntries(entries, dirs) {
|
async function addFileEntries(entries, dirs) {
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
if (entry.isFile) {
|
if (entry.isFile) {
|
||||||
@@ -285,12 +375,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>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,9 +409,9 @@ function formatSize(size) {
|
|||||||
|
|
||||||
function formatDuration(seconds) {
|
function formatDuration(seconds) {
|
||||||
seconds = Math.ceil(seconds);
|
seconds = Math.ceil(seconds);
|
||||||
let h = Math.floor(seconds / 3600);
|
const h = Math.floor(seconds / 3600);
|
||||||
let m = Math.floor((seconds - h * 3600) / 60);
|
const m = Math.floor((seconds - h * 3600) / 60);
|
||||||
let s = seconds - h * 3600 - m * 60
|
const s = seconds - h * 3600 - m * 60
|
||||||
return `${padZero(h, 2)}:${padZero(m, 2)}:${padZero(s, 2)}`;
|
return `${padZero(h, 2)}:${padZero(m, 2)}:${padZero(s, 2)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,11 +423,19 @@ function formatPercent(precent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function encodedStr(rawStr) {
|
||||||
|
return rawStr.replace(/[\u00A0-\u9999<>\&]/g, function(i) {
|
||||||
|
return '&#'+i.charCodeAt(0)+';';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function ready() {
|
function ready() {
|
||||||
|
document.title = `Index of ${DATA.href} - Dufs`;
|
||||||
$pathsTable = document.querySelector(".paths-table")
|
$pathsTable = document.querySelector(".paths-table")
|
||||||
$pathsTableBody = document.querySelector(".paths-table tbody");
|
$pathsTableBody = document.querySelector(".paths-table tbody");
|
||||||
$uploadersTable = document.querySelector(".uploaders-table");
|
$uploadersTable = document.querySelector(".uploaders-table");
|
||||||
$emptyFolder = document.querySelector(".empty-folder");
|
$emptyFolder = document.querySelector(".empty-folder");
|
||||||
|
$newFolder = document.querySelector(".new-folder");
|
||||||
|
|
||||||
if (DATA.allow_search) {
|
if (DATA.allow_search) {
|
||||||
document.querySelector(".searchbar").classList.remove("hidden");
|
document.querySelector(".searchbar").classList.remove("hidden");
|
||||||
@@ -363,7 +461,14 @@ function ready() {
|
|||||||
}
|
}
|
||||||
if (DATA.allow_upload) {
|
if (DATA.allow_upload) {
|
||||||
dropzone();
|
dropzone();
|
||||||
document.querySelector(".upload-control").classList.remove("hidden");
|
if (DATA.allow_delete) {
|
||||||
|
$newFolder.classList.remove("hidden");
|
||||||
|
$newFolder.addEventListener("click", () => {
|
||||||
|
const name = prompt("Enter name of new folder");
|
||||||
|
if (name) createFolder(name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
document.querySelector(".upload-file").classList.remove("hidden");
|
||||||
document.getElementById("file").addEventListener("change", e => {
|
document.getElementById("file").addEventListener("change", e => {
|
||||||
const files = e.target.files;
|
const files = e.target.files;
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
|
|||||||
47
src/args.rs
47
src/args.rs
@@ -1,4 +1,6 @@
|
|||||||
use clap::{AppSettings, Arg, ArgMatches, Command};
|
use clap::{value_parser, AppSettings, Arg, ArgMatches, Command};
|
||||||
|
use clap_complete::{generate, Generator, Shell};
|
||||||
|
#[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 +8,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> {
|
pub fn build_cli() -> 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 +51,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 +113,23 @@ 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)"),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("completions")
|
||||||
|
.long("completions")
|
||||||
|
.value_name("shell")
|
||||||
|
.value_parser(value_parser!(Shell))
|
||||||
|
.help("Print shell completion script for <shell>"),
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(feature = "tls")]
|
||||||
|
let app = app
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("tls-cert")
|
Arg::new("tls-cert")
|
||||||
.long("tls-cert")
|
.long("tls-cert")
|
||||||
@@ -122,11 +141,13 @@ 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 print_completions<G: Generator>(gen: G, cmd: &mut Command) {
|
||||||
app().get_matches()
|
generate(gen, cmd, cmd.get_name().to_string(), &mut std::io::stdout());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -137,6 +158,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 +169,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 +198,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 +219,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 +228,8 @@ impl Args {
|
|||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
#[cfg(not(feature = "tls"))]
|
||||||
|
let tls = None;
|
||||||
|
|
||||||
Ok(Args {
|
Ok(Args {
|
||||||
addrs,
|
addrs,
|
||||||
@@ -206,6 +238,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,
|
||||||
|
|||||||
30
src/logger.rs
Normal file
30
src/logger.rs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
use chrono::{Local, SecondsFormat};
|
||||||
|
use log::{Level, Metadata, Record};
|
||||||
|
use log::{LevelFilter, SetLoggerError};
|
||||||
|
|
||||||
|
struct SimpleLogger;
|
||||||
|
|
||||||
|
impl log::Log for SimpleLogger {
|
||||||
|
fn enabled(&self, metadata: &Metadata) -> bool {
|
||||||
|
metadata.level() <= Level::Info
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(&self, record: &Record) {
|
||||||
|
if self.enabled(record.metadata()) {
|
||||||
|
let timestamp = Local::now().to_rfc3339_opts(SecondsFormat::Secs, true);
|
||||||
|
if record.level() < Level::Info {
|
||||||
|
eprintln!("{} {} - {}", timestamp, record.level(), record.args());
|
||||||
|
} else {
|
||||||
|
println!("{} {} - {}", timestamp, record.level(), record.args());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
static LOGGER: SimpleLogger = SimpleLogger;
|
||||||
|
|
||||||
|
pub fn init() -> Result<(), SetLoggerError> {
|
||||||
|
log::set_logger(&LOGGER).map(|()| log::set_max_level(LevelFilter::Info))
|
||||||
|
}
|
||||||
51
src/main.rs
51
src/main.rs
@@ -1,27 +1,32 @@
|
|||||||
mod args;
|
mod args;
|
||||||
mod auth;
|
mod auth;
|
||||||
|
mod logger;
|
||||||
mod server;
|
mod server;
|
||||||
mod streamer;
|
mod streamer;
|
||||||
|
#[cfg(feature = "tls")]
|
||||||
mod tls;
|
mod tls;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
use crate::args::{matches, Args};
|
use crate::args::{build_cli, print_completions, 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::io::Write;
|
|
||||||
use std::net::{IpAddr, SocketAddr, TcpListener as StdTcpListener};
|
use std::net::{IpAddr, SocketAddr, TcpListener as StdTcpListener};
|
||||||
use std::{env, sync::Arc};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use clap_complete::Shell;
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use tokio::task::JoinHandle;
|
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>>;
|
||||||
@@ -32,19 +37,18 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn run() -> BoxResult<()> {
|
async fn run() -> BoxResult<()> {
|
||||||
if env::var("RUST_LOG").is_err() {
|
logger::init().map_err(|e| format!("Failed to init logger, {}", e))?;
|
||||||
env::set_var("RUST_LOG", "info")
|
let cmd = build_cli();
|
||||||
|
let matches = cmd.get_matches();
|
||||||
|
if let Some(generator) = matches.get_one::<Shell>("completions") {
|
||||||
|
let mut cmd = build_cli();
|
||||||
|
print_completions(*generator, &mut cmd);
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
env_logger::builder()
|
let args = Args::parse(matches)?;
|
||||||
.format(|buf, record| {
|
|
||||||
let timestamp = buf.timestamp_millis();
|
|
||||||
writeln!(buf, "[{} {}] {}", timestamp, record.level(), record.args())
|
|
||||||
})
|
|
||||||
.init();
|
|
||||||
|
|
||||||
let args = Args::parse(matches())?;
|
|
||||||
let args = Arc::new(args);
|
let args = Arc::new(args);
|
||||||
let handles = serve(args.clone())?;
|
let running = Arc::new(AtomicBool::new(true));
|
||||||
|
let handles = serve(args.clone(), running.clone())?;
|
||||||
print_listening(args)?;
|
print_listening(args)?;
|
||||||
|
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
@@ -57,13 +61,17 @@ async fn run() -> BoxResult<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
_ = shutdown_signal() => {
|
_ = shutdown_signal() => {
|
||||||
|
running.store(false, Ordering::SeqCst);
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serve(args: Arc<Args>) -> BoxResult<Vec<JoinHandle<Result<(), hyper::Error>>>> {
|
fn serve(
|
||||||
let inner = Arc::new(Server::new(args.clone()));
|
args: Arc<Args>,
|
||||||
|
running: Arc<AtomicBool>,
|
||||||
|
) -> BoxResult<Vec<JoinHandle<Result<(), hyper::Error>>>> {
|
||||||
|
let inner = Arc::new(Server::new(args.clone(), running));
|
||||||
let mut handles = vec![];
|
let mut handles = vec![];
|
||||||
let port = args.port;
|
let port = args.port;
|
||||||
for ip in args.addrs.iter() {
|
for ip in args.addrs.iter() {
|
||||||
@@ -79,12 +87,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| {
|
||||||
@@ -94,6 +103,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();
|
||||||
@@ -137,7 +150,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();
|
||||||
|
|||||||
297
src/server.rs
297
src/server.rs
@@ -1,13 +1,12 @@
|
|||||||
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 walkdir::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};
|
||||||
use futures::stream::StreamExt;
|
|
||||||
use futures::TryStreamExt;
|
use futures::TryStreamExt;
|
||||||
use headers::{
|
use headers::{
|
||||||
AcceptRanges, AccessControlAllowCredentials, AccessControlAllowHeaders,
|
AcceptRanges, AccessControlAllowCredentials, AccessControlAllowHeaders,
|
||||||
@@ -24,6 +23,7 @@ use std::fs::Metadata;
|
|||||||
use std::io::SeekFrom;
|
use std::io::SeekFrom;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
use tokio::fs::File;
|
use tokio::fs::File;
|
||||||
@@ -44,11 +44,18 @@ const BUF_SIZE: usize = 65536;
|
|||||||
|
|
||||||
pub struct Server {
|
pub struct Server {
|
||||||
args: Arc<Args>,
|
args: Arc<Args>,
|
||||||
|
assets_prefix: String,
|
||||||
|
running: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
pub fn new(args: Arc<Args>) -> Self {
|
pub fn new(args: Arc<Args>, running: Arc<AtomicBool>) -> Self {
|
||||||
Self { args }
|
let assets_prefix = format!("{}__dufs_v{}_", args.uri_prefix, env!("CARGO_PKG_VERSION"));
|
||||||
|
Self {
|
||||||
|
args,
|
||||||
|
running,
|
||||||
|
assets_prefix,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn call(
|
pub async fn call(
|
||||||
@@ -58,12 +65,15 @@ impl Server {
|
|||||||
) -> Result<Response, hyper::Error> {
|
) -> Result<Response, hyper::Error> {
|
||||||
let method = req.method().clone();
|
let method = req.method().clone();
|
||||||
let uri = req.uri().clone();
|
let uri = req.uri().clone();
|
||||||
|
let assets_prefix = self.assets_prefix.clone();
|
||||||
let enable_cors = self.args.enable_cors;
|
let enable_cors = self.args.enable_cors;
|
||||||
|
|
||||||
let mut res = match self.handle(req).await {
|
let mut res = match self.handle(req).await {
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
let status = res.status().as_u16();
|
let status = res.status().as_u16();
|
||||||
info!(r#"{} "{} {}" - {}"#, addr.ip(), method, uri, status,);
|
if !uri.path().starts_with(&assets_prefix) {
|
||||||
|
info!(r#"{} "{} {}" - {}"#, addr.ip(), method, uri, status,);
|
||||||
|
}
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@@ -89,8 +99,7 @@ impl Server {
|
|||||||
let headers = req.headers();
|
let headers = req.headers();
|
||||||
let method = req.method().clone();
|
let method = req.method().clone();
|
||||||
|
|
||||||
if req_path == "/favicon.ico" && method == Method::GET {
|
if method == Method::GET && self.handle_embed_assets(req_path, &mut res).await? {
|
||||||
self.handle_send_favicon(headers, &mut res).await?;
|
|
||||||
return Ok(res);
|
return Ok(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,16 +155,26 @@ impl Server {
|
|||||||
match method {
|
match method {
|
||||||
Method::GET | Method::HEAD => {
|
Method::GET | Method::HEAD => {
|
||||||
if is_dir {
|
if is_dir {
|
||||||
if render_try_index && query == "zip" {
|
if render_try_index {
|
||||||
self.handle_zip_dir(path, head_only, &mut res).await?;
|
if query == "zip" {
|
||||||
} else if render_index || render_spa || render_try_index {
|
self.handle_zip_dir(path, head_only, &mut res).await?;
|
||||||
|
} else if allow_search && query.starts_with("q=") {
|
||||||
|
let q = decode_uri(&query[2..]).unwrap_or_default();
|
||||||
|
self.handle_search_dir(path, &q, head_only, &mut res)
|
||||||
|
.await?;
|
||||||
|
} else {
|
||||||
|
self.handle_render_index(path, headers, head_only, &mut res)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
} else if render_index || render_spa {
|
||||||
self.handle_render_index(path, headers, head_only, &mut res)
|
self.handle_render_index(path, headers, head_only, &mut res)
|
||||||
.await?;
|
.await?;
|
||||||
} else if query == "zip" {
|
} else if query == "zip" {
|
||||||
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?;
|
||||||
}
|
}
|
||||||
@@ -220,7 +239,7 @@ impl Server {
|
|||||||
} else if is_miss {
|
} else if is_miss {
|
||||||
status_not_found(&mut res);
|
status_not_found(&mut res);
|
||||||
} else {
|
} else {
|
||||||
self.handle_copy(path, headers, &mut res).await?
|
self.handle_copy(path, &req, &mut res).await?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"MOVE" => {
|
"MOVE" => {
|
||||||
@@ -229,7 +248,7 @@ impl Server {
|
|||||||
} else if is_miss {
|
} else if is_miss {
|
||||||
status_not_found(&mut res);
|
status_not_found(&mut res);
|
||||||
} else {
|
} else {
|
||||||
self.handle_move(path, headers, &mut res).await?
|
self.handle_move(path, &req, &mut res).await?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"LOCK" => {
|
"LOCK" => {
|
||||||
@@ -315,31 +334,48 @@ 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 path_buf = path.to_path_buf();
|
||||||
while let Some(entry) = walkdir.next().await {
|
let hidden = self.args.hidden.to_string();
|
||||||
if let Ok(entry) = entry {
|
let running = self.running.clone();
|
||||||
if !entry
|
let search = search.to_lowercase();
|
||||||
.file_name()
|
let search_paths = tokio::task::spawn_blocking(move || {
|
||||||
.to_string_lossy()
|
let mut it = WalkDir::new(&path_buf).into_iter();
|
||||||
.to_lowercase()
|
let mut paths: Vec<PathBuf> = vec![];
|
||||||
.contains(&query.to_lowercase())
|
while let Some(Ok(entry)) = it.next() {
|
||||||
{
|
if !running.load(Ordering::SeqCst) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let entry_path = entry.path();
|
||||||
|
let base_name = get_file_name(entry_path);
|
||||||
|
let file_type = entry.file_type();
|
||||||
|
if is_hidden(&hidden, base_name) {
|
||||||
|
if file_type.is_dir() {
|
||||||
|
it.skip_current_dir();
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if fs::symlink_metadata(entry.path()).await.is_err() {
|
if !base_name.to_lowercase().contains(&search) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Ok(Some(item)) = self.to_pathitem(entry.path(), path.to_path_buf()).await {
|
if entry.path().symlink_metadata().is_err() {
|
||||||
paths.push(item);
|
continue;
|
||||||
}
|
}
|
||||||
|
paths.push(entry_path.to_path_buf());
|
||||||
|
}
|
||||||
|
paths
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
for search_path in search_paths.into_iter() {
|
||||||
|
if let Ok(Some(item)) = self.to_pathitem(search_path, path.to_path_buf()).await {
|
||||||
|
paths.push(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.send_index(path, paths, true, head_only, res)
|
self.send_index(path, paths, true, head_only, res)
|
||||||
@@ -352,7 +388,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!(
|
||||||
@@ -367,8 +403,10 @@ impl Server {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let path = path.to_owned();
|
let path = path.to_owned();
|
||||||
|
let hidden = self.args.hidden.clone();
|
||||||
|
let running = self.running.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, running).await {
|
||||||
error!("Failed to zip {}, {}", path.display(), e);
|
error!("Failed to zip {}, {}", path.display(), e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -418,23 +456,38 @@ impl Server {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_send_favicon(
|
async fn handle_embed_assets(&self, req_path: &str, res: &mut Response) -> BoxResult<bool> {
|
||||||
&self,
|
if let Some(name) = req_path.strip_prefix(&self.assets_prefix) {
|
||||||
headers: &HeaderMap<HeaderValue>,
|
match name {
|
||||||
res: &mut Response,
|
"index.js" => {
|
||||||
) -> BoxResult<()> {
|
*res.body_mut() = Body::from(INDEX_JS);
|
||||||
let path = self.args.path.join("favicon.ico");
|
res.headers_mut().insert(
|
||||||
let meta = fs::metadata(&path).await.ok();
|
"content-type",
|
||||||
let is_file = meta.map(|v| v.is_file()).unwrap_or_default();
|
HeaderValue::from_static("application/javascript"),
|
||||||
if is_file {
|
);
|
||||||
self.handle_send_file(path.as_path(), headers, false, res)
|
}
|
||||||
.await?;
|
"index.css" => {
|
||||||
|
*res.body_mut() = Body::from(INDEX_CSS);
|
||||||
|
res.headers_mut()
|
||||||
|
.insert("content-type", HeaderValue::from_static("text/css"));
|
||||||
|
}
|
||||||
|
"favicon.ico" => {
|
||||||
|
*res.body_mut() = Body::from(FAVICON_ICO);
|
||||||
|
res.headers_mut()
|
||||||
|
.insert("content-type", HeaderValue::from_static("image/x-icon"));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.headers_mut().insert(
|
||||||
|
"cache-control",
|
||||||
|
HeaderValue::from_static("max-age=2592000, public"),
|
||||||
|
);
|
||||||
|
Ok(true)
|
||||||
} else {
|
} else {
|
||||||
*res.body_mut() = Body::from(FAVICON_ICO);
|
Ok(false)
|
||||||
res.headers_mut()
|
|
||||||
.insert("content-type", HeaderValue::from_static("image/x-icon"));
|
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_send_file(
|
async fn handle_send_file(
|
||||||
@@ -491,7 +544,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),))
|
||||||
@@ -590,16 +643,10 @@ impl Server {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_copy(
|
async fn handle_copy(&self, path: &Path, req: &Request, res: &mut Response) -> BoxResult<()> {
|
||||||
&self,
|
let dest = match self.extract_dest(req, res) {
|
||||||
path: &Path,
|
|
||||||
headers: &HeaderMap<HeaderValue>,
|
|
||||||
res: &mut Response,
|
|
||||||
) -> BoxResult<()> {
|
|
||||||
let dest = match self.extract_dest(headers) {
|
|
||||||
Some(dest) => dest,
|
Some(dest) => dest,
|
||||||
None => {
|
None => {
|
||||||
*res.status_mut() = StatusCode::BAD_REQUEST;
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -618,16 +665,10 @@ impl Server {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_move(
|
async fn handle_move(&self, path: &Path, req: &Request, res: &mut Response) -> BoxResult<()> {
|
||||||
&self,
|
let dest = match self.extract_dest(req, res) {
|
||||||
path: &Path,
|
|
||||||
headers: &HeaderMap<HeaderValue>,
|
|
||||||
res: &mut Response,
|
|
||||||
) -> BoxResult<()> {
|
|
||||||
let dest = match self.extract_dest(headers) {
|
|
||||||
Some(dest) => dest,
|
Some(dest) => dest,
|
||||||
None => {
|
None => {
|
||||||
*res.status_mut() = StatusCode::BAD_REQUEST;
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -692,7 +733,7 @@ impl Server {
|
|||||||
paths.sort_unstable();
|
paths.sort_unstable();
|
||||||
let href = format!("/{}", normalize_path(path.strip_prefix(&self.args.path)?));
|
let href = format!("/{}", normalize_path(path.strip_prefix(&self.args.path)?));
|
||||||
let data = IndexData {
|
let data = IndexData {
|
||||||
href: href.clone(),
|
href,
|
||||||
uri_prefix: self.args.uri_prefix.clone(),
|
uri_prefix: self.args.uri_prefix.clone(),
|
||||||
paths,
|
paths,
|
||||||
allow_upload: self.args.allow_upload,
|
allow_upload: self.args.allow_upload,
|
||||||
@@ -701,18 +742,21 @@ impl Server {
|
|||||||
dir_exists: exist,
|
dir_exists: exist,
|
||||||
};
|
};
|
||||||
let data = serde_json::to_string(&data).unwrap();
|
let data = serde_json::to_string(&data).unwrap();
|
||||||
|
let asset_js = format!("{}index.js", self.assets_prefix);
|
||||||
|
let asset_css = format!("{}index.css", self.assets_prefix);
|
||||||
|
let asset_ico = format!("{}favicon.ico", self.assets_prefix);
|
||||||
let output = INDEX_HTML.replace(
|
let output = INDEX_HTML.replace(
|
||||||
"__SLOT__",
|
"__SLOT__",
|
||||||
&format!(
|
&format!(
|
||||||
r#"
|
r#"
|
||||||
<title>Index of {} - Dufs</title>
|
<link rel="icon" type="image/x-icon" href="{}">
|
||||||
<style>{}</style>
|
<link rel="stylesheet" href="{}">
|
||||||
<script>
|
<script>
|
||||||
const DATA =
|
DATA = {}
|
||||||
{}
|
</script>
|
||||||
{}</script>
|
<script src="{}"></script>
|
||||||
"#,
|
"#,
|
||||||
href, INDEX_CSS, data, INDEX_JS
|
asset_ico, asset_css, data, asset_js
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
res.headers_mut()
|
res.headers_mut()
|
||||||
@@ -743,10 +787,43 @@ const DATA =
|
|||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_dest(&self, headers: &HeaderMap<HeaderValue>) -> Option<PathBuf> {
|
fn extract_dest(&self, req: &Request, res: &mut Response) -> Option<PathBuf> {
|
||||||
|
let headers = req.headers();
|
||||||
|
let dest_path = match self.extract_destination_header(headers) {
|
||||||
|
Some(dest) => dest,
|
||||||
|
None => {
|
||||||
|
*res.status_mut() = StatusCode::BAD_REQUEST;
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let authorization = headers.get(AUTHORIZATION);
|
||||||
|
let guard_type = self.args.auth.guard(
|
||||||
|
&dest_path,
|
||||||
|
req.method(),
|
||||||
|
authorization,
|
||||||
|
self.args.auth_method.clone(),
|
||||||
|
);
|
||||||
|
if guard_type.is_reject() {
|
||||||
|
*res.status_mut() = StatusCode::FORBIDDEN;
|
||||||
|
*res.body_mut() = Body::from("Forbidden");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dest = match self.extract_path(&dest_path) {
|
||||||
|
Some(dest) => dest,
|
||||||
|
None => {
|
||||||
|
*res.status_mut() = StatusCode::BAD_REQUEST;
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(dest)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_destination_header(&self, headers: &HeaderMap<HeaderValue>) -> Option<String> {
|
||||||
let dest = headers.get("Destination")?.to_str().ok()?;
|
let dest = headers.get("Destination")?.to_str().ok()?;
|
||||||
let uri: Uri = dest.parse().ok()?;
|
let uri: Uri = dest.parse().ok()?;
|
||||||
self.extract_path(uri.path())
|
Some(uri.path().to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_path(&self, path: &str) -> Option<PathBuf> {
|
fn extract_path(&self, path: &str) -> Option<PathBuf> {
|
||||||
@@ -777,6 +854,10 @@ const 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);
|
||||||
}
|
}
|
||||||
@@ -885,11 +966,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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -953,29 +1031,54 @@ 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,
|
||||||
|
running: Arc<AtomicBool>,
|
||||||
|
) -> BoxResult<()> {
|
||||||
let mut writer = ZipFileWriter::new(writer);
|
let mut writer = ZipFileWriter::new(writer);
|
||||||
let mut walkdir = WalkDir::new(dir);
|
let hidden = Arc::new(hidden.to_string());
|
||||||
while let Some(entry) = walkdir.next().await {
|
let hidden = hidden.to_string();
|
||||||
if let Ok(entry) = entry {
|
let dir_path_buf = dir.to_path_buf();
|
||||||
|
let zip_paths = tokio::task::spawn_blocking(move || {
|
||||||
|
let mut it = WalkDir::new(&dir_path_buf).into_iter();
|
||||||
|
let mut paths: Vec<PathBuf> = vec![];
|
||||||
|
while let Some(Ok(entry)) = it.next() {
|
||||||
|
if !running.load(Ordering::SeqCst) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
let entry_path = entry.path();
|
let entry_path = entry.path();
|
||||||
let meta = match fs::symlink_metadata(entry.path()).await {
|
let base_name = get_file_name(entry_path);
|
||||||
Ok(meta) => meta,
|
let file_type = entry.file_type();
|
||||||
Err(_) => continue,
|
if is_hidden(&hidden, base_name) {
|
||||||
};
|
if file_type.is_dir() {
|
||||||
if !meta.is_file() {
|
it.skip_current_dir();
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let filename = match entry_path.strip_prefix(dir).ok().and_then(|v| v.to_str()) {
|
if entry.path().symlink_metadata().is_err() {
|
||||||
Some(v) => v,
|
continue;
|
||||||
None => continue,
|
}
|
||||||
};
|
if !file_type.is_file() {
|
||||||
let entry_options = EntryOptions::new(filename.to_owned(), Compression::Deflate);
|
continue;
|
||||||
let mut file = File::open(&entry_path).await?;
|
}
|
||||||
let mut file_writer = writer.write_entry_stream(entry_options).await?;
|
paths.push(entry_path.to_path_buf());
|
||||||
io::copy(&mut file, &mut file_writer).await?;
|
|
||||||
file_writer.close().await?;
|
|
||||||
}
|
}
|
||||||
|
paths
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
for zip_path in zip_paths.into_iter() {
|
||||||
|
let filename = match zip_path.strip_prefix(dir).ok().and_then(|v| v.to_str()) {
|
||||||
|
Some(v) => v,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
let entry_options =
|
||||||
|
EntryOptions::new(filename.to_owned(), Compression::Deflate).unix_permissions(0o644);
|
||||||
|
let mut file = File::open(&zip_path).await?;
|
||||||
|
let mut file_writer = writer.write_entry_stream(entry_options).await?;
|
||||||
|
io::copy(&mut file, &mut file_writer).await?;
|
||||||
|
file_writer.close().await?;
|
||||||
}
|
}
|
||||||
writer.close().await?;
|
writer.close().await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1036,10 +1139,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) {
|
||||||
|
|||||||
15
src/utils.rs
15
src/utils.rs
@@ -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())
|
||||||
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//! Run file server with different args
|
||||||
|
|
||||||
mod fixtures;
|
mod fixtures;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
@@ -10,7 +12,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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +38,6 @@ fn path_prefix_propfind(
|
|||||||
#[case("index.html")]
|
#[case("index.html")]
|
||||||
fn serve_single_file(tmpdir: TempDir, port: u16, #[case] file: &str) -> Result<(), Error> {
|
fn serve_single_file(tmpdir: TempDir, port: u16, #[case] file: &str) -> Result<(), Error> {
|
||||||
let mut child = Command::cargo_bin("dufs")?
|
let mut child = Command::cargo_bin("dufs")?
|
||||||
.env("RUST_LOG", "false")
|
|
||||||
.arg(tmpdir.path().join(file))
|
.arg(tmpdir.path().join(file))
|
||||||
.arg("-p")
|
.arg("-p")
|
||||||
.arg(port.to_string())
|
.arg(port.to_string())
|
||||||
|
|||||||
93
tests/assets.rs
Normal file
93
tests/assets.rs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
mod fixtures;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
|
use fixtures::{server, Error, TestServer};
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn assets(server: TestServer) -> Result<(), Error> {
|
||||||
|
let ver = env!("CARGO_PKG_VERSION");
|
||||||
|
let resp = reqwest::blocking::get(server.url())?;
|
||||||
|
let index_js = format!("/__dufs_v{}_index.js", ver);
|
||||||
|
let index_css = format!("/__dufs_v{}_index.css", ver);
|
||||||
|
let favicon_ico = format!("/__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(server: TestServer) -> Result<(), Error> {
|
||||||
|
let url = format!(
|
||||||
|
"{}__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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn asset_css(server: TestServer) -> Result<(), Error> {
|
||||||
|
let url = format!(
|
||||||
|
"{}__dufs_v{}_index.css",
|
||||||
|
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(), "text/css");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn asset_ico(server: TestServer) -> Result<(), Error> {
|
||||||
|
let url = format!(
|
||||||
|
"{}__dufs_v{}_favicon.ico",
|
||||||
|
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(), "image/x-icon");
|
||||||
|
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(())
|
||||||
|
}
|
||||||
@@ -95,3 +95,29 @@ fn auth_basic(
|
|||||||
assert_eq!(resp.status(), 201);
|
assert_eq!(resp.status(), 201);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn auth_webdav_move(
|
||||||
|
#[with(&["--auth", "/@user:pass@*", "--auth", "/dira@user3:pass3", "-A"])] server: TestServer,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let origin_url = format!("{}dira/test.html", server.url());
|
||||||
|
let new_url = format!("{}test2.html", server.url());
|
||||||
|
let resp = fetch!(b"MOVE", &origin_url)
|
||||||
|
.header("Destination", &new_url)
|
||||||
|
.send_with_digest_auth("user3", "pass3")?;
|
||||||
|
assert_eq!(resp.status(), 403);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn auth_webdav_copy(
|
||||||
|
#[with(&["--auth", "/@user:pass@*", "--auth", "/dira@user3:pass3", "-A"])] server: TestServer,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let origin_url = format!("{}dira/test.html", server.url());
|
||||||
|
let new_url = format!("{}test2.html", server.url());
|
||||||
|
let resp = fetch!(b"COPY", &origin_url)
|
||||||
|
.header("Destination", &new_url)
|
||||||
|
.send_with_digest_auth("user3", "pass3")?;
|
||||||
|
assert_eq!(resp.status(), 403);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,14 +6,13 @@ use assert_cmd::prelude::*;
|
|||||||
use assert_fs::fixture::TempDir;
|
use assert_fs::fixture::TempDir;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
use std::io::{BufRead, BufReader};
|
use std::io::Read;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
#[case(&["-b", "20.205.243.166"])]
|
#[case(&["-b", "20.205.243.166"])]
|
||||||
fn bind_fails(tmpdir: TempDir, port: u16, #[case] args: &[&str]) -> Result<(), Error> {
|
fn bind_fails(tmpdir: TempDir, port: u16, #[case] args: &[&str]) -> Result<(), Error> {
|
||||||
Command::cargo_bin("dufs")?
|
Command::cargo_bin("dufs")?
|
||||||
.env("RUST_LOG", "false")
|
|
||||||
.arg(tmpdir.path())
|
.arg(tmpdir.path())
|
||||||
.arg("-p")
|
.arg("-p")
|
||||||
.arg(port.to_string())
|
.arg(port.to_string())
|
||||||
@@ -51,7 +50,6 @@ fn bind_ipv4_ipv6(
|
|||||||
#[case(&["--path-prefix", "/prefix"])]
|
#[case(&["--path-prefix", "/prefix"])]
|
||||||
fn validate_printed_urls(tmpdir: TempDir, port: u16, #[case] args: &[&str]) -> Result<(), Error> {
|
fn validate_printed_urls(tmpdir: TempDir, port: u16, #[case] args: &[&str]) -> Result<(), Error> {
|
||||||
let mut child = Command::cargo_bin("dufs")?
|
let mut child = Command::cargo_bin("dufs")?
|
||||||
.env("RUST_LOG", "false")
|
|
||||||
.arg(tmpdir.path())
|
.arg(tmpdir.path())
|
||||||
.arg("-p")
|
.arg("-p")
|
||||||
.arg(port.to_string())
|
.arg(port.to_string())
|
||||||
@@ -61,22 +59,23 @@ fn validate_printed_urls(tmpdir: TempDir, port: u16, #[case] args: &[&str]) -> R
|
|||||||
|
|
||||||
wait_for_port(port);
|
wait_for_port(port);
|
||||||
|
|
||||||
// WARN assumes urls list is terminated by an empty line
|
let stdout = child.stdout.as_mut().expect("Failed to get stdout");
|
||||||
let url_lines = BufReader::new(child.stdout.take().unwrap())
|
let mut buf = [0; 1000];
|
||||||
|
let buf_len = stdout.read(&mut buf)?;
|
||||||
|
let output = std::str::from_utf8(&buf[0..buf_len])?;
|
||||||
|
let url_lines = output
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| line.expect("Error reading stdout"))
|
|
||||||
.take_while(|line| !line.is_empty()) /* non-empty lines */
|
.take_while(|line| !line.is_empty()) /* non-empty lines */
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>()
|
||||||
let url_lines = url_lines.join("\n");
|
.join("\n");
|
||||||
|
|
||||||
let urls = Regex::new(r"http://[a-zA-Z0-9\.\[\]:/]+")
|
let urls = Regex::new(r"http://[a-zA-Z0-9\.\[\]:/]+")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.captures_iter(url_lines.as_str())
|
.captures_iter(url_lines.as_str())
|
||||||
.map(|caps| caps.get(0).unwrap().as_str())
|
.filter_map(|caps| caps.get(0).map(|v| v.as_str()))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
assert!(!urls.is_empty());
|
assert!(!urls.is_empty());
|
||||||
|
|
||||||
for url in urls {
|
for url in urls {
|
||||||
reqwest::blocking::get(url)?.error_for_status()?;
|
reqwest::blocking::get(url)?.error_for_status()?;
|
||||||
}
|
}
|
||||||
|
|||||||
32
tests/cli.rs
Normal file
32
tests/cli.rs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
//! Run cli with different args, not starting a server
|
||||||
|
|
||||||
|
mod fixtures;
|
||||||
|
|
||||||
|
use assert_cmd::prelude::*;
|
||||||
|
use clap::ValueEnum;
|
||||||
|
use clap_complete::Shell;
|
||||||
|
use fixtures::Error;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// Show help and exit.
|
||||||
|
fn help_shows() -> Result<(), Error> {
|
||||||
|
Command::cargo_bin("dufs")?.arg("-h").assert().success();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// Print completions and exit.
|
||||||
|
fn print_completions() -> Result<(), Error> {
|
||||||
|
// let shell_enums = EnumValueParser::<Shell>::new();
|
||||||
|
for shell in Shell::value_variants() {
|
||||||
|
Command::cargo_bin("dufs")?
|
||||||
|
.arg("--completions")
|
||||||
|
.arg(shell.to_string())
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
mod fixtures;
|
|
||||||
mod utils;
|
|
||||||
|
|
||||||
use fixtures::{server, Error, TestServer};
|
|
||||||
use rstest::rstest;
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn default_favicon(server: TestServer) -> Result<(), Error> {
|
|
||||||
let resp = reqwest::blocking::get(format!("{}favicon.ico", server.url()))?;
|
|
||||||
assert_eq!(resp.status(), 200);
|
|
||||||
assert_eq!(resp.headers().get("content-type").unwrap(), "image/x-icon");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn exist_favicon(#[with(&["-A"])] server: TestServer) -> Result<(), Error> {
|
|
||||||
let url = format!("{}favicon.ico", server.url());
|
|
||||||
let data = b"abc";
|
|
||||||
let resp = fetch!(b"PUT", &url).body(data.to_vec()).send()?;
|
|
||||||
assert_eq!(resp.status(), 201);
|
|
||||||
let resp = reqwest::blocking::get(url)?;
|
|
||||||
assert_eq!(resp.status(), 200);
|
|
||||||
assert_eq!(resp.bytes()?, data.to_vec());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -13,21 +13,7 @@ pub type Error = Box<dyn std::error::Error>;
|
|||||||
|
|
||||||
/// File names for testing purpose
|
/// File names for testing purpose
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub static FILES: &[&str] = &[
|
pub static FILES: &[&str] = &["test.txt", "test.html", "index.html", "😀.bin"];
|
||||||
"test.txt",
|
|
||||||
"test.html",
|
|
||||||
"index.html",
|
|
||||||
"test.mkv",
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
"test \" \' & < >.csv",
|
|
||||||
"😀.data",
|
|
||||||
"⎙.mp4",
|
|
||||||
"#[]{}()@!$&'`+,;= %20.test",
|
|
||||||
#[cfg(unix)]
|
|
||||||
":?#[]{}<>()@!$&'`|*+,;= %20.test",
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
"foo\\bar.test",
|
|
||||||
];
|
|
||||||
|
|
||||||
/// Directory names for testing diretory don't exist
|
/// Directory names for testing diretory don't exist
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -37,13 +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];
|
||||||
|
|
||||||
/// Name of a deeply nested file
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub static DEEPLY_NESTED_FILE: &str = "very/deeply/nested/test.rs";
|
|
||||||
|
|
||||||
/// 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.
|
||||||
@@ -69,10 +55,6 @@ pub fn tmpdir() -> TempDir {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpdir
|
|
||||||
.child(&DEEPLY_NESTED_FILE)
|
|
||||||
.write_str("File in a deeply nested directory.")
|
|
||||||
.expect("Couldn't write to file");
|
|
||||||
tmpdir
|
tmpdir
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +78,6 @@ where
|
|||||||
let tmpdir = tmpdir();
|
let tmpdir = tmpdir();
|
||||||
let child = Command::cargo_bin("dufs")
|
let child = Command::cargo_bin("dufs")
|
||||||
.expect("Couldn't find test binary")
|
.expect("Couldn't find test binary")
|
||||||
.env("RUST_LOG", "false")
|
|
||||||
.arg(tmpdir.path())
|
.arg(tmpdir.path())
|
||||||
.arg("-p")
|
.arg("-p")
|
||||||
.arg(port.to_string())
|
.arg(port.to_string())
|
||||||
@@ -124,7 +105,6 @@ where
|
|||||||
let tmpdir = tmpdir();
|
let tmpdir = tmpdir();
|
||||||
let child = Command::cargo_bin("dufs")
|
let child = Command::cargo_bin("dufs")
|
||||||
.expect("Couldn't find test binary")
|
.expect("Couldn't find test binary")
|
||||||
.env("RUST_LOG", "false")
|
|
||||||
.arg(tmpdir.path())
|
.arg(tmpdir.path())
|
||||||
.arg("-p")
|
.arg("-p")
|
||||||
.arg(port.to_string())
|
.arg(port.to_string())
|
||||||
|
|||||||
42
tests/hidden.rs
Normal file
42
tests/hidden.rs
Normal 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(())
|
||||||
|
}
|
||||||
@@ -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,19 +69,19 @@ 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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn get_dir_search2(#[with(&["-A"])] server: TestServer) -> Result<(), Error> {
|
fn get_dir_search2(#[with(&["-A"])] server: TestServer) -> Result<(), Error> {
|
||||||
let resp = reqwest::blocking::get(format!("{}?q={}", server.url(), "😀.data"))?;
|
let resp = reqwest::blocking::get(format!("{}?q={}", server.url(), "😀.bin"))?;
|
||||||
assert_eq!(resp.status(), 200);
|
assert_eq!(resp.status(), 200);
|
||||||
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(&"😀.data"));
|
assert!(p.contains("😀.bin"));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,6 +50,18 @@ fn render_try_index3(#[with(&["--render-try-index"])] server: TestServer) -> Res
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case(server(&["--render-try-index"] as &[&str]), false)]
|
||||||
|
#[case(server(&["--render-try-index", "--allow-search"] as &[&str]), true)]
|
||||||
|
fn render_try_index4(#[case] server: TestServer, #[case] searched: bool) -> Result<(), Error> {
|
||||||
|
let resp = reqwest::blocking::get(format!("{}{}?q={}", server.url(), DIR_NO_INDEX, "😀.bin"))?;
|
||||||
|
assert_eq!(resp.status(), 200);
|
||||||
|
let paths = utils::retrive_index_paths(&resp.text()?);
|
||||||
|
assert!(!paths.is_empty());
|
||||||
|
assert_eq!(paths.iter().all(|v| v.contains("😀.bin")), searched);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn render_spa(#[with(&["--render-spa"])] server: TestServer) -> Result<(), Error> {
|
fn render_spa(#[with(&["--render-spa"])] server: TestServer) -> Result<(), Error> {
|
||||||
let resp = reqwest::blocking::get(server.url())?;
|
let resp = reqwest::blocking::get(server.url())?;
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -37,12 +37,8 @@ pub fn encode_uri(v: &str) -> String {
|
|||||||
|
|
||||||
fn retrive_index_paths_impl(index: &str) -> Option<HashSet<String>> {
|
fn retrive_index_paths_impl(index: &str) -> Option<HashSet<String>> {
|
||||||
let lines: Vec<&str> = index.lines().collect();
|
let lines: Vec<&str> = index.lines().collect();
|
||||||
let (i, _) = lines
|
let line = lines.iter().find(|v| v.contains("DATA ="))?;
|
||||||
.iter()
|
let value: Value = line[7..].parse().ok()?;
|
||||||
.enumerate()
|
|
||||||
.find(|(_, v)| v.contains("const DATA"))?;
|
|
||||||
let line = lines.get(i + 1)?;
|
|
||||||
let value: Value = line.parse().ok()?;
|
|
||||||
let paths = value
|
let paths = value
|
||||||
.get("paths")?
|
.get("paths")?
|
||||||
.as_array()?
|
.as_array()?
|
||||||
|
|||||||
Reference in New Issue
Block a user