mirror of
https://github.com/sigoden/dufs.git
synced 2026-04-09 09:09:03 +03:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f103e15e15 | ||
|
|
9dda55b7c8 | ||
|
|
c3dd0f0ec5 | ||
|
|
4167e5c07e | ||
|
|
f66e129985 | ||
|
|
7c3970480e | ||
|
|
34bc8d411a |
13
.dockerignore
Normal file
13
.dockerignore
Normal file
@@ -0,0 +1,13 @@
|
||||
# Directories
|
||||
/.git/
|
||||
/.github/
|
||||
/target/
|
||||
/examples/
|
||||
/docs/
|
||||
/benches/
|
||||
/tmp/
|
||||
|
||||
# Files
|
||||
.gitignore
|
||||
*.md
|
||||
LICENSE*
|
||||
43
.github/workflows/release.yaml
vendored
43
.github/workflows/release.yaml
vendored
@@ -6,8 +6,10 @@ on:
|
||||
- v[0-9]+.[0-9]+.[0-9]+*
|
||||
|
||||
jobs:
|
||||
all:
|
||||
name: All
|
||||
release:
|
||||
name: Publish to Github Reelases
|
||||
outputs:
|
||||
rc: ${{ steps.check-tag.outputs.rc }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -124,3 +126,40 @@ jobs:
|
||||
prerelease: ${{ steps.check-tag.outputs.rc == 'true' }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
docker:
|
||||
name: Publish to Docker Hub
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
needs: release
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
push: ${{ needs.release.outputs.rc == 'false' }}
|
||||
tags: sigoden/duf:latest, sigoden/duf:${{ github.ref_name }}
|
||||
|
||||
publish-crate:
|
||||
name: Publish to crates.io
|
||||
if: ${{ needs.release.outputs.rc == 'false' }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: release
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
- name: Publish
|
||||
|
||||
env:
|
||||
CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_IO_API_TOKEN }}
|
||||
run: cargo publish
|
||||
50
CHANGELOG.md
50
CHANGELOG.md
@@ -2,6 +2,19 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [0.11.0] - 2022-06-03
|
||||
|
||||
### Features
|
||||
|
||||
- Support gracefully shutdown server
|
||||
- Listen 0.0.0.0 by default
|
||||
|
||||
## [0.10.1] - 2022-06-02
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Panic when bind already used port
|
||||
|
||||
## [0.10.0] - 2022-06-02
|
||||
|
||||
### Bug Fixes
|
||||
@@ -19,10 +32,6 @@ All notable changes to this project will be documented in this file.
|
||||
- Change auth logic/options
|
||||
- Improve ui
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- Insert cli output
|
||||
|
||||
### Refactor
|
||||
|
||||
- Small improvement
|
||||
@@ -52,12 +61,6 @@ All notable changes to this project will be documented in this file.
|
||||
- Add some headers to res
|
||||
- Support render-index/render-spa
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- Move src/assets out of src
|
||||
- Update description
|
||||
- Upgrade version
|
||||
|
||||
## [0.7.0] - 2022-05-31
|
||||
|
||||
### Bug Fixes
|
||||
@@ -71,10 +74,6 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
- Drag and drop uploads, upload folder
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- Upgrade version
|
||||
|
||||
## [0.6.0] - 2022-05-31
|
||||
|
||||
### Features
|
||||
@@ -83,10 +82,6 @@ All notable changes to this project will be documented in this file.
|
||||
- Distinct upload and delete operation
|
||||
- Support range requests
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- Upgrade version
|
||||
|
||||
### Refactor
|
||||
|
||||
- Improve code quality
|
||||
@@ -99,12 +94,6 @@ All notable changes to this project will be documented in this file.
|
||||
- Add no-auth-read options
|
||||
- Unzip zip file when unload
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- Reorganize web static files
|
||||
- Rename src/static to src/assets
|
||||
- Upgrade version
|
||||
|
||||
## [0.4.0] - 2022-05-29
|
||||
|
||||
### Features
|
||||
@@ -112,10 +101,6 @@ All notable changes to this project will be documented in this file.
|
||||
- Replace --static option to --no-edit
|
||||
- Add cors
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- Upgrade version
|
||||
|
||||
## [0.3.0] - 2022-05-29
|
||||
|
||||
### Documentation
|
||||
@@ -162,10 +147,6 @@ All notable changes to this project will be documented in this file.
|
||||
- Add logger
|
||||
- Download folder as zip file
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- Update cargo metadata
|
||||
|
||||
## [0.1.0] - 2022-05-26
|
||||
|
||||
### Bug Fixes
|
||||
@@ -183,11 +164,6 @@ All notable changes to this project will be documented in this file.
|
||||
- Support delete operation
|
||||
- Remove parent path
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- Add readme and license
|
||||
- Update cargo metadata
|
||||
|
||||
### Styling
|
||||
|
||||
- Cargo fmt
|
||||
|
||||
12
Cargo.lock
generated
12
Cargo.lock
generated
@@ -286,7 +286,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "duf"
|
||||
version = "0.10.0"
|
||||
version = "0.11.0"
|
||||
dependencies = [
|
||||
"async-walkdir",
|
||||
"async_zip",
|
||||
@@ -882,6 +882,15 @@ dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.6"
|
||||
@@ -965,6 +974,7 @@ dependencies = [
|
||||
"num_cpus",
|
||||
"once_cell",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"winapi 0.3.9",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "duf"
|
||||
version = "0.10.0"
|
||||
version = "0.11.0"
|
||||
edition = "2021"
|
||||
authors = ["sigoden <sigoden@gmail.com>"]
|
||||
description = "Duf is a fully functional file server."
|
||||
@@ -14,7 +14,7 @@ keywords = ["static", "file", "server", "http", "cli"]
|
||||
[dependencies]
|
||||
clap = { version = "3", default-features = false, features = ["std", "cargo"] }
|
||||
chrono = "0.4"
|
||||
tokio = { version = "1", features = ["rt-multi-thread", "macros", "fs", "io-util"]}
|
||||
tokio = { version = "1", features = ["rt-multi-thread", "macros", "fs", "io-util", "signal"]}
|
||||
tokio-rustls = "0.23"
|
||||
tokio-stream = { version = "0.1", features = ["net"] }
|
||||
tokio-util = { version = "0.7", features = ["codec", "io-util"] }
|
||||
|
||||
10
Dockerfile
Normal file
10
Dockerfile
Normal file
@@ -0,0 +1,10 @@
|
||||
FROM rust:1.61 as builder
|
||||
RUN rustup target add x86_64-unknown-linux-musl
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y musl-tools
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN cargo build --target x86_64-unknown-linux-musl --release
|
||||
|
||||
FROM alpine
|
||||
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/duf /bin/
|
||||
ENTRYPOINT ["/bin/duf"]
|
||||
18
README.md
18
README.md
@@ -28,6 +28,12 @@ Duf is a fully functional file server.
|
||||
cargo install duf
|
||||
```
|
||||
|
||||
### With docker
|
||||
|
||||
```
|
||||
docker run -v /tmp:/tmp -p 5000:5000 --rm -it docker.io/sigoden/duf /tmp
|
||||
```
|
||||
|
||||
### Binaries on macOS, Linux, Windows
|
||||
|
||||
Download from [Github Releases](https://github.com/sigoden/duf/releases), unzip and add duf to your $PATH.
|
||||
@@ -50,7 +56,7 @@ OPTIONS:
|
||||
--allow-delete Allow delete files/folders
|
||||
--allow-symlink Allow symlink to files/folders outside root directory
|
||||
--allow-upload Allow upload files/folders
|
||||
-b, --bind <address> Specify bind address [default: 127.0.0.1]
|
||||
-b, --bind <address> Specify bind address [default: 0.0.0.0]
|
||||
--cors Enable CORS, sets `Access-Control-Allow-Origin: *`
|
||||
-h, --help Print help information
|
||||
-p, --port <port> Specify port to listen on [default: 5000]
|
||||
@@ -76,18 +82,10 @@ duf
|
||||
duf folder_name
|
||||
```
|
||||
|
||||
Listen on all Interfaces and port 3000
|
||||
|
||||
```
|
||||
duf -b 0.0.0.0 -p 3000
|
||||
```
|
||||
|
||||
Allow all operations such as upload, delete
|
||||
|
||||
```sh
|
||||
duf --allow-all
|
||||
# or
|
||||
duf -A
|
||||
```
|
||||
|
||||
Only allow upload operation
|
||||
@@ -102,7 +100,7 @@ Serve a single page application (SPA)
|
||||
duf --render-spa
|
||||
```
|
||||
|
||||
Serve https
|
||||
Use https
|
||||
|
||||
```
|
||||
duf --tls-cert my.crt --tls-key my.key
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
/**
|
||||
* @type Element
|
||||
*/
|
||||
let $pathsTable, $pathsTableBody, $uploadersTable;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
let baseDir;
|
||||
|
||||
class Uploader {
|
||||
|
||||
21
src/args.rs
21
src/args.rs
@@ -16,7 +16,7 @@ fn app() -> clap::Command<'static> {
|
||||
Arg::new("address")
|
||||
.short('b')
|
||||
.long("bind")
|
||||
.default_value("127.0.0.1")
|
||||
.default_value("0.0.0.0")
|
||||
.help("Specify bind address")
|
||||
.value_name("address"),
|
||||
)
|
||||
@@ -173,7 +173,7 @@ impl Args {
|
||||
fn parse_path<P: AsRef<Path>>(path: P) -> BoxResult<PathBuf> {
|
||||
let path = path.as_ref();
|
||||
if !path.exists() {
|
||||
bail!("error: path \"{}\" doesn't exist", path.display());
|
||||
return Err(format!("Path `{}` doesn't exist", path.display()).into());
|
||||
}
|
||||
|
||||
env::current_dir()
|
||||
@@ -181,27 +181,14 @@ impl Args {
|
||||
p.push(path); // If path is absolute, it replaces the current path.
|
||||
std::fs::canonicalize(p)
|
||||
})
|
||||
.or_else(|err| {
|
||||
bail!(
|
||||
"error: failed to access path \"{}\": {}",
|
||||
path.display(),
|
||||
err,
|
||||
)
|
||||
})
|
||||
.map_err(|err| format!("Failed to access path `{}`: {}", path.display(), err,).into())
|
||||
}
|
||||
|
||||
/// Construct socket address from arguments.
|
||||
pub fn address(&self) -> BoxResult<SocketAddr> {
|
||||
format!("{}:{}", self.address, self.port)
|
||||
.parse()
|
||||
.or_else(|err| {
|
||||
bail!(
|
||||
"error: invalid address {}:{} : {}",
|
||||
self.address,
|
||||
self.port,
|
||||
err,
|
||||
)
|
||||
})
|
||||
.map_err(|_| format!("Invalid bind address `{}:{}`", self.address, self.port).into())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
macro_rules! bail {
|
||||
($($tt:tt)*) => {
|
||||
return Err(From::from(format!($($tt)*)))
|
||||
}
|
||||
}
|
||||
|
||||
mod args;
|
||||
mod server;
|
||||
|
||||
@@ -23,6 +17,6 @@ async fn run() -> BoxResult<()> {
|
||||
}
|
||||
|
||||
fn handle_err<T>(err: Box<dyn std::error::Error>) -> T {
|
||||
eprintln!("Server error: {}", err);
|
||||
eprintln!("error: {}", err);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
@@ -53,17 +53,24 @@ macro_rules! status {
|
||||
}
|
||||
|
||||
pub async fn serve(args: Args) -> BoxResult<()> {
|
||||
match args.tls.as_ref() {
|
||||
Some(_) => serve_https(args).await,
|
||||
None => serve_http(args).await,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn serve_https(args: Args) -> BoxResult<()> {
|
||||
let args = Arc::new(args);
|
||||
let socket_addr = args.address()?;
|
||||
let (certs, key) = args.tls.clone().unwrap();
|
||||
let inner = Arc::new(InnerService::new(args.clone()));
|
||||
if let Some((certs, key)) = args.tls.as_ref() {
|
||||
let config = ServerConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_no_client_auth()
|
||||
.with_single_cert(certs.clone(), key.clone())?;
|
||||
.with_single_cert(certs, key)?;
|
||||
let tls_acceptor = TlsAcceptor::from(Arc::new(config));
|
||||
let arc_acceptor = Arc::new(tls_acceptor);
|
||||
let listener = TcpListener::bind(&socket_addr).await.unwrap();
|
||||
let listener = TcpListener::bind(&socket_addr).await?;
|
||||
let incoming = tokio_stream::wrappers::TcpListenerStream::new(listener);
|
||||
let incoming = hyper::server::accept::from_stream(incoming.filter_map(|socket| async {
|
||||
match socket {
|
||||
@@ -84,9 +91,16 @@ pub async fn serve(args: Args) -> BoxResult<()> {
|
||||
}
|
||||
}));
|
||||
print_listening(args.address.as_str(), args.port, true);
|
||||
server.await?;
|
||||
} else {
|
||||
let server = hyper::Server::bind(&socket_addr).serve(make_service_fn(move |_| {
|
||||
let graceful = server.with_graceful_shutdown(shutdown_signal());
|
||||
graceful.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn serve_http(args: Args) -> BoxResult<()> {
|
||||
let args = Arc::new(args);
|
||||
let socket_addr = args.address()?;
|
||||
let inner = Arc::new(InnerService::new(args.clone()));
|
||||
let server = hyper::Server::try_bind(&socket_addr)?.serve(make_service_fn(move |_| {
|
||||
let inner = inner.clone();
|
||||
async move {
|
||||
Ok::<_, Infallible>(service_fn(move |req| {
|
||||
@@ -96,9 +110,8 @@ pub async fn serve(args: Args) -> BoxResult<()> {
|
||||
}
|
||||
}));
|
||||
print_listening(args.address.as_str(), args.port, false);
|
||||
server.await?;
|
||||
}
|
||||
|
||||
let graceful = server.with_graceful_shutdown(shutdown_signal());
|
||||
graceful.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -751,3 +764,9 @@ fn retrive_listening_addrs(address: &str) -> Vec<String> {
|
||||
}
|
||||
vec![address.to_owned()]
|
||||
}
|
||||
|
||||
async fn shutdown_signal() {
|
||||
tokio::signal::ctrl_c()
|
||||
.await
|
||||
.expect("Failed to install CTRL+C signal handler")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user