Merge branch 'upreb'

This commit is contained in:
Daniel 2023-05-17 08:11:57 -04:00
commit e042821848
19 changed files with 2264 additions and 334 deletions

View file

@ -1,8 +1,8 @@
# Directories # Directories
/.git/ /.git/
/.github/ /.github/
/target/
/upload/ /upload/
/shuttle/
# Files # Files
.gitignore .gitignore

1
.github/FUNDING.yml vendored
View file

@ -1,2 +1,3 @@
github: orhun github: orhun
patreon: orhunp patreon: orhunp
custom: ["https://www.buymeacoffee.com/orhun"]

15
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,15 @@
version: 2
updates:
# Maintain dependencies for Cargo
- package-ecosystem: cargo
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 10
# Maintain dependencies for GitHub Actions
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 10

View file

@ -7,10 +7,10 @@ on:
jobs: jobs:
audit: audit:
name: Audit name: Audit
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@master uses: actions/checkout@v3
- name: Install Rust - name: Install Rust
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:

View file

@ -8,13 +8,13 @@ on:
jobs: jobs:
publish-github: publish-github:
name: Publish on GitHub name: Publish on GitHub
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
strategy: strategy:
matrix: matrix:
TARGET: [x86_64-unknown-linux-gnu, x86_64-unknown-linux-musl] TARGET: [x86_64-unknown-linux-gnu, x86_64-unknown-linux-musl]
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@master uses: actions/checkout@v3
- name: Set the release version - name: Set the release version
run: echo "RELEASE_VERSION=${GITHUB_REF:11}" >> $GITHUB_ENV run: echo "RELEASE_VERSION=${GITHUB_REF:11}" >> $GITHUB_ENV
- name: Install musl-tools - name: Install musl-tools
@ -57,10 +57,10 @@ jobs:
publish-crates-io: publish-crates-io:
name: Publish on crates.io name: Publish on crates.io
needs: publish-github needs: publish-github
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@master uses: actions/checkout@v3
- name: Publish - name: Publish
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:

View file

@ -8,12 +8,12 @@ on:
branches: branches:
- master - master
schedule: schedule:
- cron: '0 0 * * 0' - cron: "0 0 * * 0"
jobs: jobs:
check: check:
name: Check name: Check
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
steps: steps:
- name: Install Rust toolchain - name: Install Rust toolchain
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
@ -22,7 +22,7 @@ jobs:
profile: minimal profile: minimal
override: true override: true
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@master uses: actions/checkout@v3
- name: Check the project files - name: Check the project files
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
@ -31,7 +31,7 @@ jobs:
test: test:
name: Test suite name: Test suite
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
steps: steps:
- name: Install Rust toolchain - name: Install Rust toolchain
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
@ -39,7 +39,7 @@ jobs:
toolchain: stable toolchain: stable
override: true override: true
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@master uses: actions/checkout@v3
- name: Setup cargo-tarpaulin - name: Setup cargo-tarpaulin
run: | run: |
curl -s https://api.github.com/repos/xd009642/tarpaulin/releases/latest | \ curl -s https://api.github.com/repos/xd009642/tarpaulin/releases/latest | \
@ -49,7 +49,7 @@ jobs:
- name: Run tests - name: Run tests
run: cargo tarpaulin --out Xml --verbose -- --test-threads 1 run: cargo tarpaulin --out Xml --verbose -- --test-threads 1
- name: Upload reports to codecov - name: Upload reports to codecov
uses: codecov/codecov-action@v1 uses: codecov/codecov-action@v3
with: with:
name: code-coverage-report name: code-coverage-report
file: cobertura.xml file: cobertura.xml
@ -60,7 +60,7 @@ jobs:
fixtures: fixtures:
name: Test fixtures name: Test fixtures
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
steps: steps:
- name: Install Rust toolchain - name: Install Rust toolchain
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
@ -69,7 +69,7 @@ jobs:
profile: minimal profile: minimal
override: true override: true
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@master uses: actions/checkout@v3
- name: Build the project - name: Build the project
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
@ -84,7 +84,7 @@ jobs:
clippy: clippy:
name: Lints name: Lints
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
steps: steps:
- name: Install Rust toolchain - name: Install Rust toolchain
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
@ -94,7 +94,7 @@ jobs:
components: clippy components: clippy
override: true override: true
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@master uses: actions/checkout@v3
- name: Check the lints - name: Check the lints
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
@ -103,7 +103,7 @@ jobs:
rustfmt: rustfmt:
name: Formatting name: Formatting
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
steps: steps:
- name: Install Rust toolchain - name: Install Rust toolchain
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
@ -113,7 +113,7 @@ jobs:
components: rustfmt components: rustfmt
override: true override: true
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@master uses: actions/checkout@v3
- name: Check the formatting - name: Check the formatting
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
@ -122,10 +122,10 @@ jobs:
lychee: lychee:
name: Links name: Links
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@master uses: actions/checkout@v3
- name: Check the links - name: Check the links
uses: lycheeverse/lychee-action@v1 uses: lycheeverse/lychee-action@v1
with: with:

View file

@ -15,14 +15,14 @@ on:
jobs: jobs:
docker: docker:
name: Docker Build and Push name: Docker Build and Push
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@v3 uses: docker/metadata-action@v4
with: with:
images: | images: |
orhunp/rustypaste orhunp/rustypaste
@ -37,10 +37,10 @@ jobs:
- name: Set up Docker Buildx - name: Set up Docker Buildx
id: buildx id: buildx
uses: docker/setup-buildx-action@v1 uses: docker/setup-buildx-action@v2
- name: Cache Docker layers - name: Cache Docker layers
uses: actions/cache@v2 uses: actions/cache@v3
with: with:
path: /tmp/.buildx-cache path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }} key: ${{ runner.os }}-buildx-${{ github.sha }}
@ -49,14 +49,14 @@ jobs:
- name: Login to Docker Hub - name: Login to Docker Hub
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
uses: docker/login-action@v1 uses: docker/login-action@v2
with: with:
username: orhunp username: orhunp
password: ${{ secrets.DOCKER_TOKEN }} password: ${{ secrets.DOCKER_TOKEN }}
- name: Login to GHCR - name: Login to GHCR
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
uses: docker/login-action@v1 uses: docker/login-action@v2
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
@ -64,16 +64,24 @@ jobs:
- name: Build and push - name: Build and push
id: docker_build id: docker_build
uses: docker/build-push-action@v2 uses: docker/build-push-action@v4
with: with:
context: ./ context: ./
file: ./Dockerfile file: ./Dockerfile
platforms: linux/amd64
builder: ${{ steps.buildx.outputs.name }} builder: ${{ steps.buildx.outputs.name }}
push: ${{ github.event_name != 'pull_request' }} push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
sbom: true
provenance: true
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
cache-from: type=local,src=/tmp/.buildx-cache cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache
- name: Scan the image
uses: anchore/sbom-action@v0
with:
image: ghcr.io/${{ github.repository_owner }}/rustypaste/rustypaste
- name: Image digest - name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }} run: echo ${{ steps.docker_build.outputs.digest }}

33
.github/workflows/shuttle.yml vendored Normal file
View file

@ -0,0 +1,33 @@
name: Deploy on Shuttle
on:
push:
tags:
- "v*.*.*"
jobs:
deploy:
name: Deploy
runs-on: ubuntu-22.04
steps:
- name: Checkout the repository
uses: actions/checkout@v3
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- name: Install cargo-binstall
uses: taiki-e/install-action@cargo-binstall
- name: Install cargo-shuttle
run: cargo binstall -y cargo-shuttle
- name: Prepare for deployment
shell: bash
run: sed -i 's|default = \["rustls"\]|default = \["rustls", "shuttle"\]|g' Cargo.toml
- name: Login
run: cargo shuttle login --api-key ${{ secrets.SHUTTLE_TOKEN }}
- name: Restart
run: cargo shuttle project restart
- name: Deploy
run: cargo shuttle deploy --allow-dirty --no-test

View file

@ -1,9 +1,58 @@
# Changelog # Changelog
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.9.0] - 2023-05-17
The public instance is now available at [https://rustypaste.shuttleapp.rs](https://rustypaste.shuttleapp.rs) 🚀
Read the blog post about `rustypaste` and Shuttle deployments: [https://blog.orhun.dev/blazingly-fast-file-sharing](https://blog.orhun.dev/blazingly-fast-file-sharing)
### Added
- Deploy on Shuttle.rs
- Support setting a default expiry time
You can now specify a expiry time for uploaded files. For example, if you want all the files to expire after one hour:
```toml
[paste]
default_expiry = "1h"
```
- Support overriding the server URL
If you are using `rustypaste` with a redirect or reverse proxy, it is now possible to set a different URL for the returned results:
```toml
[server]
url = "https://rustypaste.shuttleapp.rs"
```
- Add instructions for installing on Alpine Linux
`rustypaste` is now available in [testing](https://pkgs.alpinelinux.org/packages?name=rustypaste&branch=edge) repositories.
- Add new crate features
- `shuttle`: enable an entry point for deploying on Shuttle
- `openssl`: use distro OpenSSL (binary size is reduced ~20% in release mode)
- `rustls`: use [rustls](https://github.com/rustls/rustls) (enabled as default)
### Changed
- Make the default landing page fancier
- Generate SBOM attestation for the Docker image
### Updated
- Bump dependencies
- Update the funding options
- Consider donating if you liked `rustypaste`: [https://donate.orhun.dev](https://donate.orhun.dev) 💖
## [0.8.4] - 2023-01-31 ## [0.8.4] - 2023-01-31
### Added ### Added
@ -13,18 +62,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- This is useful when e.g. you want to be able to share the link to a file that would play in the browser (like `.mp4`) but also share a link that will auto-download as well. - This is useful when e.g. you want to be able to share the link to a file that would play in the browser (like `.mp4`) but also share a link that will auto-download as well.
## [0.8.3] - 2023-01-30 ## [0.8.3] - 2023-01-30
### Updated ### Updated
- Bump dependencies - Bump dependencies
- Switch to [Rust](https://hub.docker.com/_/rust) image for the Dockerfile - Switch to [Rust](https://hub.docker.com/_/rust) image for the Dockerfile
- Remove unused `clap` dependency - Remove unused `clap` dependency
## [0.8.2] - 2022-10-04 ## [0.8.2] - 2022-10-04
### Updated ### Updated
- Don't expose version endpoint in default config - Don't expose version endpoint in default config
- Set `expose_version` to `false` in the configuration file - Set `expose_version` to `false` in the configuration file
## [0.8.1] - 2022-10-04 ## [0.8.1] - 2022-10-04
### Added ### Added
- Add `<server_address>/version` endpoint for retrieving the server version - Add `<server_address>/version` endpoint for retrieving the server version
```toml ```toml
@ -35,11 +90,14 @@ expose_version=true
If `expose_version` entry is not present in the configuration file, `/version` is not exposed. It is recommended to use this feature with authorization enabled. If `expose_version` entry is not present in the configuration file, `/version` is not exposed. It is recommended to use this feature with authorization enabled.
### Fixed ### Fixed
- Replace unmaintained `dotenv` crate with `dotenvy` - Replace unmaintained `dotenv` crate with `dotenvy`
- Fixes [RUSTSEC-2021-0141](https://rustsec.org/advisories/RUSTSEC-2021-0141.html) - Fixes [RUSTSEC-2021-0141](https://rustsec.org/advisories/RUSTSEC-2021-0141.html)
## [0.8.0] - 2022-10-03 ## [0.8.0] - 2022-10-03
### Added ### Added
- Support adding a landing page - Support adding a landing page
You can now specify a landing page text in the configuration file as follows: You can now specify a landing page text in the configuration file as follows:
@ -56,6 +114,7 @@ welcome!
If the landing page entry is not present in the configuration file, visiting the index page will redirect to the repository. If the landing page entry is not present in the configuration file, visiting the index page will redirect to the repository.
### Updated ### Updated
- Do not check for duplicate files by default - Do not check for duplicate files by default
- Set `duplicate_files` to `true` in the configuration file - Set `duplicate_files` to `true` in the configuration file
- It is an expensive operation to do on slower hardware and can take an unreasonable amount of time for bigger files - It is an expensive operation to do on slower hardware and can take an unreasonable amount of time for bigger files
@ -63,13 +122,17 @@ If the landing page entry is not present in the configuration file, visiting the
- Consider supporting me for my open-source work 💖 - Consider supporting me for my open-source work 💖
## [0.7.1] - 2022-05-21 ## [0.7.1] - 2022-05-21
### Added ### Added
- Aggressively test everything - Aggressively test everything
- Add the missing unit tests for the server endpoints (code coverage is increased to 84%) - Add the missing unit tests for the server endpoints (code coverage is increased to 84%)
- Create a custom testing framework (written in Bash) for adding [test fixtures](https://github.com/orhun/rustypaste/tree/master/fixtures) - Create a custom testing framework (written in Bash) for adding [test fixtures](https://github.com/orhun/rustypaste/tree/master/fixtures)
## [0.7.0] - 2022-03-26 ## [0.7.0] - 2022-03-26
### Added ### Added
- Support auto-deletion of expired files - Support auto-deletion of expired files
`rustypaste` can now delete the expired files by itself. To enable this feature, add the following line to the `[paste]` section in the configuration file: `rustypaste` can now delete the expired files by itself. To enable this feature, add the following line to the `[paste]` section in the configuration file:
@ -86,26 +149,33 @@ For users who want to have this feature disabled, there is an alternative [shell
- For the installation and usage, see the Arch Linux [PKGBUILD](https://github.com/archlinux/svntogit-community/blob/packages/rustypaste/trunk/PKGBUILD). - For the installation and usage, see the Arch Linux [PKGBUILD](https://github.com/archlinux/svntogit-community/blob/packages/rustypaste/trunk/PKGBUILD).
### Updated ### Updated
- Upgrade Actix dependencies - Upgrade Actix dependencies
- `actix-web` is updated to [`4.0.*`](https://github.com/actix/actix-web/blob/master/actix-web/CHANGES.md#401---2022-02-25) - `actix-web` is updated to [`4.0.*`](https://github.com/actix/actix-web/blob/master/actix-web/CHANGES.md#401---2022-02-25)
- Strip the binaries during automated builds - Strip the binaries during automated builds
- Size of the Docker image is reduced by ~20% - Size of the Docker image is reduced by ~20%
### Fixed ### Fixed
- Prevent invalid attempts of serving directories - Prevent invalid attempts of serving directories
- This fixes an issue where requesting a directory was possible via e.g. `curl --path-as-is 0.0.0.0:8080/.` - This fixes an issue where requesting a directory was possible via e.g. `curl --path-as-is 0.0.0.0:8080/.`
- This issue had no security impact (path traversal wasn't possible) since internal server error was returned. - This issue had no security impact (path traversal wasn't possible) since internal server error was returned.
## [0.6.5] - 2022-03-13 ## [0.6.5] - 2022-03-13
### Added ### Added
- Add instructions for installing [rustypaste](https://archlinux.org/packages/community/x86_64/rustypaste/) on Arch Linux - Add instructions for installing [rustypaste](https://archlinux.org/packages/community/x86_64/rustypaste/) on Arch Linux
- `pacman -S rustypaste` 🎉 - `pacman -S rustypaste` 🎉
### Fixed ### Fixed
- Fix a bug where the use of `CONFIG` environment variable causes a conflict between the configuration file path and `[config]` section - Fix a bug where the use of `CONFIG` environment variable causes a conflict between the configuration file path and `[config]` section
## [0.6.4] - 2022-03-11 ## [0.6.4] - 2022-03-11
### Added ### Added
- Support setting the refresh rate for hot-reloading the configuration file. - Support setting the refresh rate for hot-reloading the configuration file.
```toml ```toml
@ -121,11 +191,14 @@ timeout="30s"
``` ```
### Security ### Security
- Bump [regex crate](https://github.com/rust-lang/regex) to **1.5.5** - Bump [regex crate](https://github.com/rust-lang/regex) to **1.5.5**
- Fixes [CVE-2022-24713](https://github.com/advisories/GHSA-m5pq-gvj9-9vr8) - Fixes [CVE-2022-24713](https://github.com/advisories/GHSA-m5pq-gvj9-9vr8)
## [0.6.3] - 2022-02-24 ## [0.6.3] - 2022-02-24
### Added ### Added
- Support setting the authentication token in the configuration file. - Support setting the authentication token in the configuration file.
- This is an alternative (but not recommended) way of setting up authentication when the use of `AUTH_TOKEN` environment variable is not applicable. - This is an alternative (but not recommended) way of setting up authentication when the use of `AUTH_TOKEN` environment variable is not applicable.
@ -135,28 +208,37 @@ auth_token="hunter2"
``` ```
## [0.6.2] - 2021-12-05 ## [0.6.2] - 2021-12-05
### Updated ### Updated
- Improve the concurrency - Improve the concurrency
- Shrink the scope of non-suspendable types (`#[must_not_suspend]`) for dropping them before reaching a suspend point (`.await` call). This avoids possible deadlocks, delays, and situations where `Future`s not implementing `Send`. - Shrink the scope of non-suspendable types (`#[must_not_suspend]`) for dropping them before reaching a suspend point (`.await` call). This avoids possible deadlocks, delays, and situations where `Future`s not implementing `Send`.
- Reference: https://rust-lang.github.io/rfcs/3014-must-not-suspend-lint.html - Reference: https://rust-lang.github.io/rfcs/3014-must-not-suspend-lint.html
## [0.6.1] - 2021-11-16 ## [0.6.1] - 2021-11-16
### Fixed ### Fixed
- Gracefully handle the hot-reloading errors. - Gracefully handle the hot-reloading errors.
- Errors that may occur while locking the [Mutex](https://doc.rust-lang.org/std/sync/struct.Mutex.html) are handled properly hence a single configuration change cannot take down the whole service due to [poisoning](https://doc.rust-lang.org/std/sync/struct.Mutex.html#poisoning). - Errors that may occur while locking the [Mutex](https://doc.rust-lang.org/std/sync/struct.Mutex.html) are handled properly hence a single configuration change cannot take down the whole service due to [poisoning](https://doc.rust-lang.org/std/sync/struct.Mutex.html#poisoning).
## [0.6.0] - 2021-11-07 ## [0.6.0] - 2021-11-07
### Added ### Added
- Support pasting files from remote URLs (via `remote=` form field) - Support pasting files from remote URLs (via `remote=` form field)
- `{server.max_content_length}` is used for download limit - `{server.max_content_length}` is used for download limit
- See [README.md#paste-file-from-remote-url](https://github.com/orhun/rustypaste#paste-file-from-remote-url) - See [README.md#paste-file-from-remote-url](https://github.com/orhun/rustypaste#paste-file-from-remote-url)
- Hot reload configuration file to apply configuration changes instantly without restarting the server - Hot reload configuration file to apply configuration changes instantly without restarting the server
### Changed ### Changed
- Library: Switch to Rust 2021 edition - Library: Switch to Rust 2021 edition
### Security ### Security
- Prevent serving an already expired file - Prevent serving an already expired file
In the previous versions, it was possible to view an expired file by using the correct extension (timestamp). e.g. `paste.com/expired_file.txt.1630094518049` will serve the file normally although `paste.com/expired_file.txt` says that it is expired. This version fixes this vulnerability by regex-checking the requested file's extension. In the previous versions, it was possible to view an expired file by using the correct extension (timestamp). e.g. `paste.com/expired_file.txt.1630094518049` will serve the file normally although `paste.com/expired_file.txt` says that it is expired. This version fixes this vulnerability by regex-checking the requested file's extension.
@ -164,7 +246,9 @@ In the previous versions, it was possible to view an expired file by using the c
reference: [f078a9afa74f8608ee3f2a6e705159df15915c78](https://github.com/orhun/rustypaste/commit/f078a9afa74f8608ee3f2a6e705159df15915c78) reference: [f078a9afa74f8608ee3f2a6e705159df15915c78](https://github.com/orhun/rustypaste/commit/f078a9afa74f8608ee3f2a6e705159df15915c78)
## [0.5.0] - 2021-10-12 ## [0.5.0] - 2021-10-12
### Added ### Added
- Added an entry in the configuration file to disable "duplicate uploads": - Added an entry in the configuration file to disable "duplicate uploads":
```toml ```toml
@ -176,13 +260,17 @@ duplicate_files = false
Under the hood, it checks the SHA256 digest of the uploaded files. Under the hood, it checks the SHA256 digest of the uploaded files.
## [0.4.1] - 2021-09-19 ## [0.4.1] - 2021-09-19
### Changed ### Changed
- Update README.md: - Update README.md:
- Mention the new standalone tool: [rustypaste-cli](https://github.com/orhun/rustypaste-cli) - Mention the new standalone tool: [rustypaste-cli](https://github.com/orhun/rustypaste-cli)
- Add [installation](https://github.com/orhun/rustypaste#installation) section. - Add [installation](https://github.com/orhun/rustypaste#installation) section.
## [0.4.0] - 2021-08-27 ## [0.4.0] - 2021-08-27
### Added ### Added
- Support [expiring links](README.md#expiration) (via `expire:` header) - Support [expiring links](README.md#expiration) (via `expire:` header)
- Timestamps are used as extension for expiring files - Timestamps are used as extension for expiring files
- Expired files can be cleaned up with [this command](README.md#cleaning-up-expired-files) - Expired files can be cleaned up with [this command](README.md#cleaning-up-expired-files)
@ -190,28 +278,39 @@ Under the hood, it checks the SHA256 digest of the uploaded files.
- `{server.upload_path}/oneshot` is used for storage - `{server.upload_path}/oneshot` is used for storage
## [0.3.1] - 2021-08-10 ## [0.3.1] - 2021-08-10
### Fixed ### Fixed
- Switch to [upload-release-action](https://github.com/svenstaro/upload-release-action) for uploading releases - Switch to [upload-release-action](https://github.com/svenstaro/upload-release-action) for uploading releases
## [0.3.0] - 2021-08-09 ## [0.3.0] - 2021-08-09
### Added ### Added
- Support overriding MIME types (config: `mime_override`) - Support overriding MIME types (config: `mime_override`)
- Support blacklisting MIME types (config: `mime_blacklist`) - Support blacklisting MIME types (config: `mime_blacklist`)
## [0.2.0] - 2021-08-04 ## [0.2.0] - 2021-08-04
### Added ### Added
- Support shortening URLs (via `url=` form field) - Support shortening URLs (via `url=` form field)
- `{server.upload_path}/url` is used for storage - `{server.upload_path}/url` is used for storage
## [0.1.3] - 2021-07-28 ## [0.1.3] - 2021-07-28
### Fixed ### Fixed
- Prevent sending empty file name and zero bytes - Prevent sending empty file name and zero bytes
- Prevent path traversal on upload directory ([#2](https://github.com/orhun/rustypaste/issues/2)) - Prevent path traversal on upload directory ([#2](https://github.com/orhun/rustypaste/issues/2))
- Check the content length while reading bytes for preventing OOM ([#1](https://github.com/orhun/rustypaste/issues/1)) - Check the content length while reading bytes for preventing OOM ([#1](https://github.com/orhun/rustypaste/issues/1))
## [0.1.2] - 2021-07-27 ## [0.1.2] - 2021-07-27
### Changed ### Changed
- Update Continuous Deployment workflow to publish Docker images - Update Continuous Deployment workflow to publish Docker images
## [0.1.1] - 2021-07-27 ## [0.1.1] - 2021-07-27
Initial release. Initial release.

2063
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[package] [package]
name = "rustypaste" name = "rustypaste"
version = "0.8.4" version = "0.9.0"
edition = "2021" edition = "2021"
description = "A minimal file upload/pastebin service" description = "A minimal file upload/pastebin service"
authors = ["Orhun Parmaksız <orhunparmaksiz@gmail.com>"] authors = ["Orhun Parmaksız <orhunparmaksiz@gmail.com>"]
@ -12,28 +12,46 @@ keywords = ["paste", "pastebin", "upload"]
categories = ["web-programming::http-server"] categories = ["web-programming::http-server"]
include = ["src/**/*", "Cargo.*", "LICENSE", "README.md", "CHANGELOG.md"] include = ["src/**/*", "Cargo.*", "LICENSE", "README.md", "CHANGELOG.md"]
[features]
default = ["rustls"]
openssl = ["actix-web/openssl", "awc/openssl"]
rustls = ["actix-web/rustls", "awc/rustls"]
shuttle = [
"dep:shuttle-actix-web",
"dep:shuttle-runtime",
"dep:shuttle-static-folder",
"dep:tokio",
]
[dependencies] [dependencies]
actix-web = { version = "4.3.0", features = ["rustls"] } actix-web = { version = "4.3.1" }
actix-multipart = "0.5.0" actix-multipart = "0.6.0"
actix-files = "0.6.2" actix-files = "0.6.2"
awc = { version = "3.1.0", features = ["rustls"] } awc = { version = "3.1.1" }
env_logger = "0.10.0" env_logger = "0.10.0"
log = "0.4.17" log = "0.4.17"
serde = "1.0.152" serde = "1.0.163"
futures-util = "0.3.26" futures-util = "0.3.28"
petname = { version = "1.1.3", default-features = false, features = ["std_rng", "default_dictionary"] } petname = { version = "1.1.3", default-features = false, features = [
"std_rng",
"default_dictionary",
] }
rand = "0.8.5" rand = "0.8.5"
dotenvy = "0.15.6" dotenvy = "0.15.7"
url = "2.3.1" url = "2.3.1"
mime = "0.3.16" mime = "0.3.17"
regex = "1.6.0" regex = "1.8.1"
serde_regex = "1.1.0" serde_regex = "1.1.0"
lazy-regex = "2.4.1" lazy-regex = "2.5.0"
humantime = "2.1.0" humantime = "2.1.0"
humantime-serde = "1.1.1" humantime-serde = "1.1.1"
glob = "0.3.1" glob = "0.3.1"
ring = "0.16.20" ring = "0.16.20"
hotwatch = "0.4.5" hotwatch = "0.4.5"
shuttle-actix-web = { version = "0.16.0", optional = true }
shuttle-runtime = { version = "0.16.0", optional = true }
shuttle-static-folder = { version = "0.16.0", optional = true }
tokio = { version = "1.28.1", optional = true }
text-template = "0.1.0" text-template = "0.1.0"
[dependencies.config] [dependencies.config]
@ -42,11 +60,11 @@ default-features = false
features = ["toml", "yaml"] features = ["toml", "yaml"]
[dependencies.byte-unit] [dependencies.byte-unit]
version = "4.0.18" version = "4.0.19"
features = ["serde"] features = ["serde"]
[dependencies.infer] [dependencies.infer]
version = "0.12.0" version = "0.13.0"
default-features = false default-features = false
[dev-dependencies] [dev-dependencies]

View file

@ -14,9 +14,7 @@ RUN cp target/release/rustypaste build-out/
FROM scratch FROM scratch
WORKDIR /app WORKDIR /app
COPY --from=builder \ COPY --from=builder /app/build-out/rustypaste .
/app/build-out/rustypaste \
/app/config.toml ./
ENV SERVER__ADDRESS=0.0.0.0:8000 ENV SERVER__ADDRESS=0.0.0.0:8000
EXPOSE 8000 EXPOSE 8000
USER 1000:1000 USER 1000:1000

View file

@ -1,5 +1,13 @@
<a href="https://github.com/orhun/rustypaste"><img src="img/rustypaste_logo.png" width="500"></a> <a href="https://github.com/orhun/rustypaste"><img src="img/rustypaste_logo.png" width="500"></a>
[![GitHub Release](https://img.shields.io/github/v/release/orhun/rustypaste?style=flat&labelColor=823213&color=2c2c2c&logo=GitHub&logoColor=white)](https://github.com/orhun/rustypaste/releases)
[![Crate Release](https://img.shields.io/crates/v/rustypaste?style=flat&labelColor=823213&color=2c2c2c&logo=Rust&logoColor=white)](https://crates.io/crates/rustypaste/)
[![Coverage](https://img.shields.io/codecov/c/gh/orhun/rustypaste?style=flat&labelColor=823213&color=2c2c2c&logo=Codecov&logoColor=white)](https://codecov.io/gh/orhun/rustypaste)
[![Continuous Integration](https://img.shields.io/github/actions/workflow/status/orhun/rustypaste/ci.yml?branch=master&style=flat&labelColor=823213&color=2c2c2c&logo=GitHub%20Actions&logoColor=white)](https://github.com/orhun/rustypaste/actions?query=workflow%3A%22Continuous+Integration%22)
[![Continuous Deployment](https://img.shields.io/github/actions/workflow/status/orhun/rustypaste/cd.yml?style=flat&labelColor=823213&color=2c2c2c&logo=GitHub%20Actions&logoColor=white&label=deploy)](https://github.com/orhun/rustypaste/actions?query=workflow%3A%22Continuous+Deployment%22)
[![Docker Builds](https://img.shields.io/github/actions/workflow/status/orhun/rustypaste/docker.yml?style=flat&labelColor=823213&color=2c2c2c&label=docker&logo=Docker&logoColor=white)](https://hub.docker.com/r/orhunp/rustypaste)
[![Documentation](https://img.shields.io/docsrs/rustypaste?style=flat&labelColor=823213&color=2c2c2c&logo=Rust&logoColor=white)](https://docs.rs/rustypaste/)
**Rustypaste-pretty** is a minimal file upload/pastebin service with client side highlighting provided by highlight.js. **Rustypaste-pretty** is a minimal file upload/pastebin service with client side highlighting provided by highlight.js.
Just add `?pretty` to the end of a link to highlight! Just add `?pretty` to the end of a link to highlight!
@ -14,6 +22,8 @@ $ curl https://paste.site.com/safe-toad.txt
some text some text
``` ```
The public instance is available at [https://rustypaste.shuttleapp.rs](https://rustypaste.shuttleapp.rs) 🚀
## Features ## Features
- File upload & URL shortening & upload from URL - File upload & URL shortening & upload from URL
@ -22,6 +32,7 @@ some text
- pet name (e.g. `capital-mosquito.txt`) - pet name (e.g. `capital-mosquito.txt`)
- alphanumeric string (e.g. `yB84D2Dv.txt`) - alphanumeric string (e.g. `yB84D2Dv.txt`)
- supports expiring links - supports expiring links
- auto-expiration of files (optional)
- auto-deletion of expired files (optional) - auto-deletion of expired files (optional)
- supports one shot links (can only be viewed once) - supports one shot links (can only be viewed once)
- guesses MIME types - guesses MIME types
@ -55,9 +66,17 @@ cargo install rustypaste
pacman -S rustypaste pacman -S rustypaste
``` ```
### Alpine Linux
`rustypaste` is available for [Alpine Edge](https://pkgs.alpinelinux.org/packages?name=rustypaste&branch=edge). It can be installed via [apk](https://wiki.alpinelinux.org/wiki/Alpine_Package_Keeper) after enabling the [testing repository](https://wiki.alpinelinux.org/wiki/Repositories).
```sh
apk add rustypaste
```
### Binary releases ### Binary releases
See the available binaries on [releases](https://github.com/orhun/rustypaste/releases/) page. See the available binaries on the [releases](https://github.com/orhun/rustypaste/releases/) page.
### Build from source ### Build from source
@ -67,13 +86,34 @@ cd rustypaste/
cargo build --release cargo build --release
``` ```
#### Testing #### Feature flags
- `shuttle`: enable an entry point for deploying on Shuttle
- `openssl`: use distro OpenSSL (binary size is reduced ~20% in release mode)
- `rustls`: use [rustls](https://github.com/rustls/rustls) (enabled as default)
To enable a feature for build, pass `--features` flag to `cargo build` command.
For example, to reuse the OpenSSL present on a distro already:
```sh
cargo build --release --no-default-features --features openssl
```
#### Testing
##### Unit tests
```sh ```sh
# run unit tests
cargo test -- --test-threads 1 cargo test -- --test-threads 1
``` ```
##### Test Fixtures
```sh
./fixtures/test-fixtures.sh
```
## Usage ## Usage
The standalone command line tool (`rpaste`) is available [here](https://github.com/orhun/rustypaste-cli). The standalone command line tool (`rpaste`) is available [here](https://github.com/orhun/rustypaste-cli).

View file

@ -1,50 +1,42 @@
[config] [config]
refresh_rate="1s" refresh_rate = "1s"
[server] [server]
address="127.0.0.1:8020" address="127.0.0.1:8020"
#url = "https://rustypaste.shuttleapp.rs"
#workers=4 #workers=4
max_content_length="10MB" max_content_length = "10MB"
upload_path="./upload" upload_path = "./upload"
timeout="30s" timeout = "30s"
expose_version=false expose_version = false
style="monokai" style="monokai"
landing_page="""Submit files via HTTP POST here: landing_page = """
curl -F 'file=@example.txt' <server>"
This will return the finished URL.
The server administrator might remove any pastes that they do not personally """
want to host.
If you are the server administrator and want to change this page, just go
into your config file and change it! If you change the expiry time, it is
recommended that you do.
Check out the GitHub repository at https://github.com/orhun/rustypaste
By default, pastes expire every hour. The server admin may or may not have
changed this."""
[paste] [paste]
random_url = { enabled = true, type = "petname", words = 2, separator = "-" } random_url = { enabled = true, type = "petname", words = 2, separator = "-" }
#random_url = { enabled = true, type = "alphanumeric", length = 8 } #random_url = { enabled = true, type = "alphanumeric", length = 8 }
default_extension = "txt" default_extension = "txt"
mime_override = [ mime_override = [
{ mime = "image/jpeg", regex = "^.*\\.jpg$" }, { mime = "image/jpeg", regex = "^.*\\.jpg$" },
{ mime = "image/png", regex = "^.*\\.png$" }, { mime = "image/png", regex = "^.*\\.png$" },
{ mime = "image/svg+xml", regex = "^.*\\.svg$" }, { mime = "image/svg+xml", regex = "^.*\\.svg$" },
{ mime = "video/webm", regex = "^.*\\.webm$" }, { mime = "video/webm", regex = "^.*\\.webm$" },
{ mime = "video/x-matroska", regex = "^.*\\.mkv$" }, { mime = "video/x-matroska", regex = "^.*\\.mkv$" },
{ mime = "application/octet-stream", regex = "^.*\\.bin$" }, { mime = "application/octet-stream", regex = "^.*\\.bin$" },
{ mime = "text/plain", regex = "^.*\\.(log|txt|diff)$" }, { mime = "text/plain", regex = "^.*\\.(log|txt|diff|sh|rs|toml)$" },
] ]
mime_blacklist = [ mime_blacklist = [
"application/x-dosexec", "application/x-dosexec",
"application/java-archive", "application/java-archive",
"application/java-vm" "application/java-vm",
] ]
duplicate_files = true duplicate_files = true
# default_expiry = "1h"
delete_expired_files = { enabled = true, interval = "1h" } delete_expired_files = { enabled = true, interval = "1h" }
[paste.highlight_override] [paste.highlight_override]

53
shuttle/config.toml Normal file
View file

@ -0,0 +1,53 @@
[config]
refresh_rate = "1s"
[server]
address = "127.0.0.1:8000"
url = "https://rustypaste.shuttleapp.rs"
#workers=4
max_content_length = "10MB"
upload_path = "./upload"
timeout = "30s"
expose_version = true
landing_page = """
Submit files via HTTP POST here:
curl -F 'file=@example.txt' https://rustypaste.shuttleapp.rs
This will return the URL of the uploaded file.
Pastes expire every hour. Uploaded files might not be persistent.
Check out the GitHub repository: https://github.com/orhun/rustypaste
Command line tool is available : https://github.com/orhun/rustypaste-cli
If you liked this, consider supporting me: https://donate.orhun.dev <3
🦀
"""
[paste]
# random_url = { enabled = true, type = "petname", words = 2, separator = "-" }
random_url = { enabled = true, type = "alphanumeric", length = 6 }
default_extension = "txt"
mime_override = [
{ mime = "image/jpeg", regex = "^.*\\.jpg$" },
{ mime = "image/png", regex = "^.*\\.png$" },
{ mime = "image/svg+xml", regex = "^.*\\.svg$" },
{ mime = "video/webm", regex = "^.*\\.webm$" },
{ mime = "video/x-matroska", regex = "^.*\\.mkv$" },
{ mime = "application/octet-stream", regex = "^.*\\.bin$" },
{ mime = "text/plain", regex = "^.*\\.(log|txt|diff|sh|kt|rs|toml)$" },
]
mime_blacklist = [
"application/x-dosexec",
"application/java-archive",
"application/java-vm",
]
duplicate_files = true
default_expiry = "1h"
delete_expired_files = { enabled = true, interval = "1h" }

View file

@ -31,6 +31,8 @@ pub struct Settings {
pub struct ServerConfig { pub struct ServerConfig {
/// The socket address to bind. /// The socket address to bind.
pub address: String, pub address: String,
/// URL that can be used to access the server externally.
pub url: Option<String>,
/// Number of workers to start. /// Number of workers to start.
pub workers: Option<usize>, pub workers: Option<usize>,
/// Maximum content length. /// Maximum content length.
@ -63,8 +65,11 @@ pub struct PasteConfig {
/// Media type blacklist. /// Media type blacklist.
#[serde(default)] #[serde(default)]
pub mime_blacklist: Vec<String>, pub mime_blacklist: Vec<String>,
/// Allow duplicate uploads /// Allow duplicate uploads.
pub duplicate_files: Option<bool>, pub duplicate_files: Option<bool>,
/// Default expiry time.
#[serde(default, with = "humantime_serde")]
pub default_expiry: Option<Duration>,
/// Delete expired files. /// Delete expired files.
pub delete_expired_files: Option<CleanupConfig>, pub delete_expired_files: Option<CleanupConfig>,
/// Highlight override. /// Highlight override.

View file

@ -1,19 +1,18 @@
use crate::util;
use actix_web::http::header::{ use actix_web::http::header::{
ContentDisposition as ActixContentDisposition, DispositionParam, DispositionType, HeaderMap, ContentDisposition as ActixContentDisposition, DispositionParam, DispositionType, HeaderMap,
}; };
use actix_web::{error, Error as ActixError}; use actix_web::{error, Error as ActixError};
use std::time::Duration;
/// Custom HTTP header for expiry dates. /// Custom HTTP header for expiry dates.
pub const EXPIRE: &str = "expire"; pub const EXPIRE: &str = "expire";
/// Parses the expiry date from the [`custom HTTP header`](EXPIRE). /// Parses the expiry date from the [`custom HTTP header`](EXPIRE).
pub fn parse_expiry_date(headers: &HeaderMap) -> Result<Option<u128>, ActixError> { pub fn parse_expiry_date(headers: &HeaderMap, time: Duration) -> Result<Option<u128>, ActixError> {
if let Some(expire_time) = headers.get(EXPIRE).and_then(|v| v.to_str().ok()) { if let Some(expire_time) = headers.get(EXPIRE).and_then(|v| v.to_str().ok()) {
let timestamp = util::get_system_time()?;
let expire_time = let expire_time =
humantime::parse_duration(expire_time).map_err(error::ErrorInternalServerError)?; humantime::parse_duration(expire_time).map_err(error::ErrorInternalServerError)?;
Ok(timestamp.checked_add(expire_time).map(|t| t.as_millis())) Ok(time.checked_add(expire_time).map(|t| t.as_millis()))
} else { } else {
Ok(None) Ok(None)
} }
@ -62,9 +61,9 @@ impl ContentDisposition {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::util;
use actix_web::http::header::{HeaderName, HeaderValue}; use actix_web::http::header::{HeaderName, HeaderValue};
use std::thread; use std::thread;
use std::time::Duration;
#[test] #[test]
fn test_content_disposition() -> Result<(), ActixError> { fn test_content_disposition() -> Result<(), ActixError> {
@ -97,7 +96,8 @@ mod tests {
HeaderName::from_static(EXPIRE), HeaderName::from_static(EXPIRE),
HeaderValue::from_static("5ms"), HeaderValue::from_static("5ms"),
); );
let expiry_time = parse_expiry_date(&headers)?.unwrap_or_default(); let time = util::get_system_time()?;
let expiry_time = parse_expiry_date(&headers, time)?.unwrap_or_default();
assert!(expiry_time > util::get_system_time()?.as_millis()); assert!(expiry_time > util::get_system_time()?.as_millis());
thread::sleep(Duration::from_millis(10)); thread::sleep(Duration::from_millis(10));
assert!(expiry_time < util::get_system_time()?.as_millis()); assert!(expiry_time < util::get_system_time()?.as_millis());

View file

@ -1,9 +1,10 @@
use actix_web::middleware::Logger; use actix_web::middleware::Logger;
use actix_web::web::Data; use actix_web::web::Data;
#[cfg(not(feature = "shuttle"))]
use actix_web::{App, HttpServer}; use actix_web::{App, HttpServer};
use awc::ClientBuilder; use awc::ClientBuilder;
use hotwatch::{Event, Hotwatch}; use hotwatch::{Event, Hotwatch};
use rustypaste::config::Config; use rustypaste::config::{Config, ServerConfig};
use rustypaste::paste::PasteType; use rustypaste::paste::PasteType;
use rustypaste::server; use rustypaste::server;
use rustypaste::util; use rustypaste::util;
@ -11,17 +12,28 @@ use rustypaste::CONFIG_ENV;
use std::env; use std::env;
use std::fs; use std::fs;
use std::io::Result as IoResult; use std::io::Result as IoResult;
use std::path::PathBuf; use std::path::{Path, PathBuf};
use std::sync::{mpsc, RwLock}; use std::sync::{mpsc, RwLock};
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
#[cfg(feature = "shuttle")]
use {
actix_web::web::{self, ServiceConfig},
shuttle_actix_web::ShuttleActixWeb,
};
#[actix_web::main] /// Sets up the application.
async fn main() -> IoResult<()> { ///
/// * loads the configuration
/// * initializes the logger
/// * creates the necessary directories
/// * spawns the threads
fn setup(config_folder: &Path) -> IoResult<(Data<RwLock<Config>>, ServerConfig, Hotwatch)> {
// Load the .env file. // Load the .env file.
dotenvy::dotenv().ok(); dotenvy::dotenv().ok();
// Initialize logger. // Initialize logger.
#[cfg(not(feature = "shuttle"))]
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
// Parse configuration. // Parse configuration.
@ -30,7 +42,7 @@ async fn main() -> IoResult<()> {
env::remove_var(CONFIG_ENV); env::remove_var(CONFIG_ENV);
PathBuf::from(path) PathBuf::from(path)
} }
None => PathBuf::from("config.toml"), None => config_folder.join("config.toml"),
}; };
let config = Config::parse(&config_path).expect("failed to parse config"); let config = Config::parse(&config_path).expect("failed to parse config");
log::trace!("{:#?}", config); log::trace!("{:#?}", config);
@ -83,6 +95,7 @@ async fn main() -> IoResult<()> {
.unwrap_or_else(|_| panic!("failed to watch {config_path:?}")); .unwrap_or_else(|_| panic!("failed to watch {config_path:?}"));
// Create a thread for cleaning up expired files. // Create a thread for cleaning up expired files.
let upload_path = server_config.upload_path.clone();
thread::spawn(move || loop { thread::spawn(move || loop {
let mut enabled = false; let mut enabled = false;
if let Some(ref cleanup_config) = paste_config if let Some(ref cleanup_config) = paste_config
@ -92,7 +105,7 @@ async fn main() -> IoResult<()> {
{ {
if cleanup_config.enabled { if cleanup_config.enabled {
log::debug!("Running cleanup..."); log::debug!("Running cleanup...");
for file in util::get_expired_files(&server_config.upload_path) { for file in util::get_expired_files(&upload_path) {
match fs::remove_file(&file) { match fs::remove_file(&file) {
Ok(()) => log::info!("Removed expired file: {:?}", file), Ok(()) => log::info!("Removed expired file: {:?}", file),
Err(e) => log::error!("Cannot remove expired file: {}", e), Err(e) => log::error!("Cannot remove expired file: {}", e),
@ -118,6 +131,15 @@ async fn main() -> IoResult<()> {
} }
}); });
Ok((config, server_config, hotwatch))
}
#[cfg(not(feature = "shuttle"))]
#[actix_web::main]
async fn main() -> IoResult<()> {
// Set up the application.
let (config, server_config, _) = setup(&PathBuf::new())?;
// Create an HTTP server. // Create an HTTP server.
let mut http_server = HttpServer::new(move || { let mut http_server = HttpServer::new(move || {
let http_client = ClientBuilder::new() let http_client = ClientBuilder::new()
@ -144,3 +166,33 @@ async fn main() -> IoResult<()> {
// Run the server. // Run the server.
http_server.run().await http_server.run().await
} }
#[cfg(feature = "shuttle")]
#[shuttle_runtime::main]
async fn actix_web(
#[shuttle_static_folder::StaticFolder(folder = "shuttle")] static_folder: PathBuf,
) -> ShuttleActixWeb<impl FnOnce(&mut ServiceConfig) + Send + Clone + 'static> {
// Set up the application.
let (config, server_config, _) = setup(&static_folder)?;
// Create the service.
let service_config = move |cfg: &mut ServiceConfig| {
let http_client = ClientBuilder::new()
.timeout(
server_config
.timeout
.unwrap_or_else(|| Duration::from_secs(30)),
)
.disable_redirects()
.finish();
cfg.service(
web::scope("")
.app_data(Data::clone(&config))
.app_data(Data::new(http_client))
.wrap(Logger::default())
.configure(server::configure_routes),
);
};
Ok(service_config.into())
}

View file

@ -28,7 +28,9 @@ async fn index(config: web::Data<RwLock<Config>>) -> Result<HttpResponse, Error>
.read() .read()
.map_err(|_| error::ErrorInternalServerError("cannot acquire config"))?; .map_err(|_| error::ErrorInternalServerError("cannot acquire config"))?;
match &config.server.landing_page { match &config.server.landing_page {
Some(page) => Ok(HttpResponse::Ok().body(page.clone())), Some(page) => Ok(HttpResponse::Ok()
.content_type("text/plain; charset=\"UTF-8\"")
.body(page.clone())),
None => Ok(HttpResponse::Found() None => Ok(HttpResponse::Found()
.append_header(("Location", env!("CARGO_PKG_HOMEPAGE"))) .append_header(("Location", env!("CARGO_PKG_HOMEPAGE")))
.finish()), .finish()),
@ -158,6 +160,18 @@ async fn upload(
) -> Result<HttpResponse, Error> { ) -> Result<HttpResponse, Error> {
let connection = request.connection_info().clone(); let connection = request.connection_info().clone();
let host = connection.peer_addr().unwrap_or("unknown host"); let host = connection.peer_addr().unwrap_or("unknown host");
let server_url = match config
.read()
.map_err(|_| error::ErrorInternalServerError("cannot acquire config"))?
.server
.url
.clone()
{
Some(v) => v,
None => {
format!("{}://{}", connection.scheme(), connection.host(),)
}
};
auth::check( auth::check(
host, host,
request.headers(), request.headers(),
@ -169,7 +183,16 @@ async fn upload(
.as_ref() .as_ref()
.cloned()), .cloned()),
)?; )?;
let expiry_date = header::parse_expiry_date(request.headers())?; let time = util::get_system_time()?;
let mut expiry_date = header::parse_expiry_date(request.headers(), time)?;
if expiry_date.is_none() {
expiry_date = config
.read()
.map_err(|_| error::ErrorInternalServerError("cannot acquire config"))?
.paste
.default_expiry
.and_then(|v| time.checked_add(v).map(|t| t.as_millis()));
}
let mut urls: Vec<String> = Vec::new(); let mut urls: Vec<String> = Vec::new();
while let Some(item) = payload.next().await { while let Some(item) = payload.next().await {
let mut field = item?; let mut field = item?;
@ -208,9 +231,8 @@ async fn upload(
.get_file(bytes_checksum) .get_file(bytes_checksum)
{ {
urls.push(format!( urls.push(format!(
"{}://{}/{}\n", "{}/{}\n",
connection.scheme(), server_url,
connection.host(),
file.path file.path
.file_name() .file_name()
.map(|v| v.to_string_lossy()) .map(|v| v.to_string_lossy())
@ -248,12 +270,7 @@ async fn upload(
Byte::from_bytes(paste.data.len() as u128).get_appropriate_unit(false), Byte::from_bytes(paste.data.len() as u128).get_appropriate_unit(false),
host host
); );
urls.push(format!( urls.push(format!("{}/{}\n", server_url, file_name));
"{}://{}/{}\n",
connection.scheme(),
connection.host(),
file_name
));
} else { } else {
log::warn!("{} sent an invalid form field", host); log::warn!("{} sent an invalid form field", host);
return Err(error::ErrorBadRequest("invalid form field")); return Err(error::ErrorBadRequest("invalid form field"));