diff --git a/.all-contributorsrc b/.all-contributorsrc new file mode 100644 index 0000000..ddaa1b8 --- /dev/null +++ b/.all-contributorsrc @@ -0,0 +1,27 @@ +{ + "projectName": "website-screenshot", + "projectOwner": "devtomio", + "repoType": "github", + "repoHost": "https://github.com", + "files": [ + "README.md" + ], + "imageSize": 100, + "commit": false, + "commitConvention": "angular", + "contributors": [ + { + "login": "devtomio", + "name": "Tomio", + "avatar_url": "https://avatars.githubusercontent.com/u/75403863?v=4", + "profile": "https://tomio.codes/", + "contributions": [ + "code", + "doc", + "infra", + "maintenance" + ] + } + ], + "contributorsPerLine": 7 +} diff --git a/.cargo/config.toml b/.cargo/config.toml index 128f924..5291d31 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,2 +1,32 @@ [build] -rustflags = ["-Clink-arg=-fuse-ld=lld"] +rustc-wrapper = 'C:\Users\Jezzu\.cargo\bin\sccache.exe' +[target.x86_64-unknown-linux-gnu] +rustflags = [ + '-Clink-arg=-fuse-ld=lld', + '-Zshare-generics=y', +] +linker = '/usr/bin/clang' + +[target.x86_64-pc-windows-msvc] +rustflags = ['-Zshare-generics=y'] +linker = 'rust-lld.exe' + +[target.x86_64-apple-darwin] +rustflags = [ + '-C', + 'link-arg=-fuse-ld=/usr/local/bin/zld', + '-Zshare-generics=y', + '-Csplit-debuginfo=unpacked', +] +[profile.dev] +opt-level = 0 +debug = 2 +incremental = true +codegen-units = 512 + +[profile.release] +opt-level = 3 +debug = 0 +incremental = false +codegen-units = 256 +split-debuginfo = '...' diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..877aadd --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @devtomio diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 0000000..1e758af --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["config:base", "helpers:pinGitHubActionDigests", "group:allNonMajor"], + "labels": ["Meta: Dependencies"] +} diff --git a/.github/workflows/continuous-delivery.yml b/.github/workflows/continuous-delivery.yml new file mode 100644 index 0000000..2b31259 --- /dev/null +++ b/.github/workflows/continuous-delivery.yml @@ -0,0 +1,182 @@ +name: Continuous Delivery + +on: + push: + branches: + - main + +jobs: + release_please: + name: Release Please + runs-on: ubuntu-latest + if: github.repository == 'devtomio/website-screenshot' + outputs: + release_created: ${{ steps.release.outputs.release_created }} + tag_name: ${{ steps.release.outputs.tag_name }} + steps: + - uses: google-github-actions/release-please-action@v3 + id: release + with: + token: ${{ secrets.GITHUB_TOKEN }} + release-type: rust + + github_build: + name: Build release binaries + needs: release_please + if: ${{ needs.release_please.outputs.release_created == 'true' }} + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-unknown-linux-gnu + os: ubuntu-latest + name: website-screenshot-x86_64-unknown-linux-gnu.tar.gz + + - target: x86_64-unknown-linux-musl + os: ubuntu-latest + name: website-screenshot-x86_64-unknown-linux-musl.tar.gz + + - target: i686-unknown-linux-musl + os: ubuntu-latest + name: website-screenshot-i686-unknown-linux-musl.tar.gz + + - target: aarch64-unknown-linux-musl + os: ubuntu-latest + name: website-screenshot-aarch64-unknown-linux-musl.tar.gz + + - target: arm-unknown-linux-musleabihf + os: ubuntu-latest + name: website-screenshot-arm-unknown-linux-musleabihf.tar.gz + + - target: x86_64-apple-darwin + os: macOS-11 + name: website-screenshot-x86_64-apple-darwin.tar.gz + + - target: aarch64-apple-darwin + os: macOS-11 + name: website-screenshot-aarch64-apple-darwin.tar.gz + + - target: x86_64-pc-windows-msvc + os: windows-latest + name: website-screenshot-x86_64-pc-windows-msvc.zip + + - target: i686-pc-windows-msvc + os: windows-latest + name: website-screenshot-i686-pc-windows-msvc.zip + + - target: aarch64-pc-windows-msvc + os: windows-latest + name: website-screenshot-aarch64-pc-windows-msvc.zip + + - target: x86_64-unknown-freebsd + os: ubuntu-latest + name: website-screenshot-x86_64-unknown-freebsd.tar.gz + + runs-on: ${{ matrix.os }} + continue-on-error: true + steps: + - name: Setup | Checkout + uses: actions/checkout@v3 + + - name: Setup | Rust + uses: actions-rs/toolchain@v1.0.7 + with: + toolchain: stable + override: true + profile: minimal + target: ${{ matrix.target }} + + - name: Build | Build + uses: actions-rs/cargo@v1.0.3 + with: + command: build + args: --release --locked --target ${{ matrix.target }} + use-cross: ${{ matrix.os == 'ubuntu-latest' }} + + - name: Post Build | Prepare artifacts [Windows] + if: matrix.os == 'windows-latest' + run: | + cd target/${{ matrix.target }}/release + strip website-screenshot.exe + 7z a ../../../${{ matrix.name }} website-screenshot.exe + cd - + + - name: Post Build | Prepare artifacts [-nix] + if: matrix.os != 'windows-latest' + run: | + cd target/${{ matrix.target }}/release + strip website-screenshot || true + tar czvf ../../../${{ matrix.name }} website-screenshot + cd - + + - name: Deploy | Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: ${{ matrix.name }} + path: ${{ matrix.name }} + + upload_artifacts: + name: Add Build Artifacts to Release + needs: [release_please, github_build] + runs-on: ubuntu-latest + steps: + - name: Setup | Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Setup | Artifacts + uses: actions/download-artifact@v3 + + - name: Setup | Checksums + run: for file in website-screenshot-*/website-screenshot-*; do openssl dgst -sha256 -r "$file" | awk '{print $1}' > "${file}.sha256"; done + + - name: Build | Add Artifacts to Release + uses: softprops/action-gh-release@v1 + with: + files: website-screenshot-*/website-screenshot-* + tag_name: ${{ needs.release_please.outputs.tag_name }} + + cargo_publish: + name: Publish Cargo Package + runs-on: ubuntu-latest + needs: release_please + if: ${{ needs.release_please.outputs.release_created == 'true' }} + steps: + - name: Setup | Checkout + uses: actions/checkout@v3 + + - name: Setup | Rust + uses: actions-rs/toolchain@v1.0.7 + with: + toolchain: stable + profile: minimal + override: true + + - name: Build | Publish + run: cargo publish --token ${{ secrets.CRATES_IO_TOKEN }} + + publish_docker: + name: Publish website-screenshot image to container registries + runs-on: ubuntu-latest + steps: + - name: Setup | Checkout + uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 # tag=v3 + + - name: Setup | Docker Buildx + uses: docker/setup-buildx-action@94ab11c41e45d028884a99163086648e898eed25 # renovate: tag=v1.6.0 + + - name: Build | Login to GitHub Container Registry + uses: docker/login-action@dd4fa0671be5250ee6f50aedf4cb05514abda2c7 # tag=v1.14.1 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build | Push Docker Image + uses: docker/build-push-action@ac9327eae2b366085ac7f6a2d02df8aa8ead720a # tag=v2.10.0 + with: + push: true + context: ./ + file: Dockerfile + tags: ghcr.io/devtomio/website-screenshot:latest diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml new file mode 100644 index 0000000..ea40f3f --- /dev/null +++ b/.github/workflows/continuous-integration.yml @@ -0,0 +1,71 @@ +name: Continuous Integration + +on: + push: + branches: + - main + pull_request: + +jobs: + msrv: + name: MSRV + runs-on: ubuntu-latest + steps: + - name: Setup | Checkout + uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # tag=v2 + + - name: Setup | Rust + uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # tag=v1 + with: + profile: minimal + toolchain: stable + override: true + + - name: Setup | Install cargo-msrv + run: cargo install cargo-msrv --no-default-features + + - name: Build | Verify the MSRV + run: cargo msrv verify + + fmt: + name: Rustfmt + runs-on: ubuntu-latest + steps: + - name: Setup | Checkout + uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # tag=v2 + + - name: Setup | Rust + uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # tag=v1 + with: + profile: minimal + toolchain: nightly + override: true + + - name: Setup | Install rustfmt + run: rustup component add rustfmt + + - name: Build | Lint + uses: actions-rs/cargo@844f36862e911db73fe0815f00a4a2602c279505 # tag=v1 + with: + command: fmt + args: --all -- --check + + clippy: + name: Clippy + runs-on: ubuntu-latest + steps: + - name: Setup | Checkout + uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # tag=v2 + + - name: Setup | Rust + uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # tag=v1 + with: + profile: minimal + toolchain: stable + override: true + + - name: Setup | Install clippy + run: rustup component add clippy + + - name: Build | Clippy + run: cargo clippy -- -D warnings diff --git a/.github/workflows/label-sync.yml b/.github/workflows/label-sync.yml new file mode 100644 index 0000000..23238d3 --- /dev/null +++ b/.github/workflows/label-sync.yml @@ -0,0 +1,21 @@ +name: Automatic Label Sync + +on: + schedule: + - cron: '0 0 * * *' + workflow_dispatch: + +jobs: + label_sync: + name: Automatic Label Synchronization + runs-on: ubuntu-latest + steps: + - name: Checkout Project + uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 # tag=v3 + with: + repository: 'sapphiredev/readme' + - name: Run Label Sync + uses: crazy-max/ghaction-github-labeler@52525cb66833763f651fc34e244e4f73b6e07ff5 # renovate: tag=v3 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + yaml-file: .github/labels.yml diff --git a/.gitignore b/.gitignore index 101c22b..f232362 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,13 @@ # These are backup files generated by rustfmt **/*.rs.bk +# Fleet Config +.cargo/config.toml +fleet.toml + # Static Files -/screenshots/ +screenshots/* +!screenshots/abcdefghijk.png + +# Sled +.website-screenshot/ diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 0000000..cf5de25 --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,5 @@ +{ + "MD036": false, + "MD013": false, + "MD024": false +} diff --git a/Cargo.lock b/Cargo.lock index 14ec604..3e92ba0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,7 +41,7 @@ dependencies = [ "actix-rt", "actix-service", "actix-utils", - "ahash 0.7.6", + "ahash", "base64 0.13.0", "bitflags", "brotli", @@ -156,7 +156,7 @@ dependencies = [ "actix-service", "actix-utils", "actix-web-codegen", - "ahash 0.7.6", + "ahash", "bytes 1.1.0", "bytestring", "cfg-if", @@ -199,12 +199,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "ahash" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" - [[package]] name = "ahash" version = "0.7.6" @@ -251,9 +245,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" +checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" [[package]] name = "async-trait" @@ -268,9 +262,9 @@ dependencies = [ [[package]] name = "attohttpc" -version = "0.18.0" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e69e13a99a7e6e070bb114f7ff381e58c7ccc188630121fc4c2fe4bcf24cd072" +checksum = "262c3f7f5d61249d8c00e5546e2685cd15ebeeb1bc0f3cc5449350a1cb07319e" dependencies = [ "http", "log", @@ -291,27 +285,27 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "aws-creds" -version = "0.27.1" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460a75eac8f3cb7683e0a9a588a83c3ff039331ea7bfbfbfcecf1dacab276e11" +checksum = "ec6e9e20e9681aeb61c81913fad0dba4ed797b4a113c83f8d2d5ad71430efc92" dependencies = [ - "anyhow", "attohttpc", "dirs", "rust-ini", "serde", "serde-xml-rs", "serde_derive", + "thiserror", "url", ] [[package]] name = "aws-region" -version = "0.23.5" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10110ddbd800fb47e6bef95e88fc13495795d252f585272a4fa3ac4f5b2e0a4d" +checksum = "9bdd1c0f4aa70f72812a2f3ec325d6d6162fb80cff093f847b4c394fd78c3643" dependencies = [ - "anyhow", + "thiserror", ] [[package]] @@ -332,15 +326,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - [[package]] name = "block-buffer" version = "0.10.2" @@ -377,6 +362,12 @@ version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "bytes" version = "0.5.6" @@ -487,6 +478,20 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-epoch" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + [[package]] name = "crossbeam-utils" version = "0.8.8" @@ -507,16 +512,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "crypto-mac" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" -dependencies = [ - "generic-array", - "subtle", -] - [[package]] name = "cuid" version = "1.2.0" @@ -536,7 +531,7 @@ checksum = "4c8858831f7781322e539ea39e72449c46b059638250c14344fec8d0aa6e539c" dependencies = [ "cfg-if", "num_cpus", - "parking_lot", + "parking_lot 0.12.0", ] [[package]] @@ -552,23 +547,15 @@ dependencies = [ "syn", ] -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - [[package]] name = "digest" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" dependencies = [ - "block-buffer 0.10.2", + "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -593,12 +580,9 @@ dependencies = [ [[package]] name = "dlv-list" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68df3f2b690c1b86e65ef7830956aededf3cb0a16f898f79b9a6f421a7b6211b" -dependencies = [ - "rand", -] +checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" [[package]] name = "dotenv" @@ -633,7 +617,7 @@ dependencies = [ "futures-util", "http", "hyper", - "hyper-tls", + "hyper-rustls", "mime", "serde", "serde_json", @@ -643,15 +627,6 @@ dependencies = [ "webdriver", ] -[[package]] -name = "fastrand" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" -dependencies = [ - "instant", -] - [[package]] name = "firestorm" version = "0.5.0" @@ -676,21 +651,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.0.1" @@ -701,6 +661,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "futures" version = "0.3.21" @@ -796,6 +766,15 @@ dependencies = [ "slab", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "generic-array" version = "0.14.5" @@ -828,7 +807,7 @@ dependencies = [ "futures-timer", "no-std-compat", "nonzero_ext", - "parking_lot", + "parking_lot 0.12.0", "quanta", "rand", "smallvec", @@ -855,18 +834,18 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.9.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" -dependencies = [ - "ahash 0.4.7", -] +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" [[package]] name = "hashbrown" -version = "0.11.2" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +checksum = "8c21d40587b92fa6a6c6e3c1bdbf87d75511db5672f9c93175574b3a00df1758" +dependencies = [ + "ahash", +] [[package]] name = "hermit-abi" @@ -885,12 +864,11 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hmac" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "crypto-mac", - "digest 0.9.0", + "digest", ] [[package]] @@ -970,24 +948,13 @@ checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" dependencies = [ "http", "hyper", + "log", "rustls", + "rustls-native-certs", "tokio", "tokio-rustls", ] -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes 1.1.0", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - [[package]] name = "idna" version = "0.2.3" @@ -1074,9 +1041,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.123" +version = "0.2.124" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd" +checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50" [[package]] name = "local-channel" @@ -1168,6 +1135,15 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "mime" version = "0.3.16" @@ -1186,9 +1162,9 @@ dependencies = [ [[package]] name = "minidom" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "332592c2149fc7dd40a64fc9ef6f0d65607284b474cef9817d1fc8c7e7b3608e" +checksum = "30565dc73d2f452e92e07ba1bdc0222efe5d1594586d19292de8ad8e53fdd031" dependencies = [ "quick-xml", ] @@ -1225,24 +1201,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "native-tls" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "no-std-compat" version = "0.4.1" @@ -1289,26 +1247,6 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "openssl" -version = "0.10.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-sys", -] - [[package]] name = "openssl-probe" version = "0.1.5" @@ -1316,26 +1254,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] -name = "openssl-sys" -version = "0.9.72" +name = "ordered-multimap" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb" +checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" dependencies = [ - "autocfg", - "cc", - "libc", - "pkg-config", - "vcpkg", + "dlv-list", + "hashbrown 0.12.0", ] [[package]] -name = "ordered-multimap" -version = "0.3.1" +name = "parking_lot" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c672c7ad9ec066e428c00eb917124a06f08db19e2584de982cc34b1f4c12485" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ - "dlv-list", - "hashbrown 0.9.1", + "instant", + "lock_api", + "parking_lot_core 0.8.5", ] [[package]] @@ -1345,7 +1281,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" dependencies = [ "lock_api", - "parking_lot_core", + "parking_lot_core 0.9.2", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", ] [[package]] @@ -1405,12 +1355,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkg-config" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" - [[package]] name = "portpicker" version = "0.1.1" @@ -1477,9 +1421,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.20.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26aab6b48e2590e4a64d1ed808749ba06257882b461d01ca71baeb747074a6dd" +checksum = "8533f14c8382aaad0d592c812ac3b826162128b65662331e1127b45c3d18536b" dependencies = [ "memchr", ] @@ -1598,15 +1542,6 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - [[package]] name = "reqwest" version = "0.11.10" @@ -1632,7 +1567,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "rustls", - "rustls-pemfile", + "rustls-pemfile 0.3.0", "serde", "serde_json", "serde_urlencoded", @@ -1664,9 +1599,9 @@ dependencies = [ [[package]] name = "rust-ini" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63471c4aa97a1cf8332a5f97709a79a4234698de6a1f5087faf66f2dae810e22" +checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" dependencies = [ "cfg-if", "ordered-multimap", @@ -1674,11 +1609,10 @@ dependencies = [ [[package]] name = "rust-s3" -version = "0.30.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff7c04dc81e5159a1ecc8594361f61cb8a62d4e4d0f5b0a0b4b48c463a55910f" +checksum = "d0a329eb6f0bb3fda88b0bb15495f94450ace48f3369f6bb036cffe03798a537" dependencies = [ - "anyhow", "async-trait", "aws-creds", "aws-region", @@ -1697,6 +1631,7 @@ dependencies = [ "serde-xml-rs", "serde_derive", "sha2", + "thiserror", "time 0.3.9", "tokio", "tokio-stream", @@ -1724,6 +1659,18 @@ dependencies = [ "webpki", ] +[[package]] +name = "rustls-native-certs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +dependencies = [ + "openssl-probe", + "rustls-pemfile 1.0.0", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pemfile" version = "0.3.0" @@ -1733,6 +1680,15 @@ dependencies = [ "base64 0.13.0", ] +[[package]] +name = "rustls-pemfile" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7522c9de787ff061458fe9a829dc790a3f5b22dc571694fc5883f448b94d9a9" +dependencies = [ + "base64 0.13.0", +] + [[package]] name = "ryu" version = "1.0.9" @@ -1857,7 +1813,7 @@ checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.3", + "digest", ] [[package]] @@ -1877,15 +1833,13 @@ checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" [[package]] name = "sha2" -version = "0.9.9" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" dependencies = [ - "block-buffer 0.9.0", "cfg-if", "cpufeatures", - "digest 0.9.0", - "opaque-debug", + "digest", ] [[package]] @@ -1912,6 +1866,22 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" +[[package]] +name = "sled" +version = "0.34.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935" +dependencies = [ + "crc32fast", + "crossbeam-epoch", + "crossbeam-utils", + "fs2", + "fxhash", + "libc", + "log", + "parking_lot 0.11.2", +] + [[package]] name = "smallvec" version = "1.8.0" @@ -1951,20 +1921,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "tempfile" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" -dependencies = [ - "cfg-if", - "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi", -] - [[package]] name = "thiserror" version = "1.0.30" @@ -2049,7 +2005,7 @@ dependencies = [ "mio", "num_cpus", "once_cell", - "parking_lot", + "parking_lot 0.12.0", "pin-project-lite", "signal-hook-registry", "socket2", @@ -2068,16 +2024,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tokio-native-tls" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" -dependencies = [ - "native-tls", - "tokio", -] - [[package]] name = "tokio-process-stream" version = "0.2.0" @@ -2207,9 +2153,9 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" dependencies = [ "lazy_static", "log", @@ -2358,12 +2304,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version_check" version = "0.9.4" @@ -2527,6 +2467,7 @@ dependencies = [ "rust-s3", "serde", "serde_json", + "sled", "tokio", "tokio-process-stream", "tokio-stream", @@ -2534,6 +2475,7 @@ dependencies = [ "tracing-actix-web", "tracing-subscriber", "validator", + "version_check", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 709bf82..e49c1ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,17 +1,31 @@ [package] name = "website-screenshot" version = "0.1.0" +rust-version = "1.60" authors = ["Tomio "] license = "MIT/Apache-2.0" edition = "2021" +build = "build.rs" +repository = "https://github.com/devtomio/website-screenshot" +include = [ + "src/**/*", + "build.rs", + "Cross.toml", + "LICENSE-APACHE", + "LICENSE-MIT", + "/README.md" +] +keywords = ["chrome", "chromedriver", "screenshots", "redis"] +readme = "README.md" +categories = ["caching", "command-line-utilities", "database-implementations", "web-programming"] +description = "📸 website screenshots as a service" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] actix-web = "4.0.1" tracing = "0.1.34" -fantoccini = "0.18.0" -anyhow = "1.0.56" +anyhow = "1.0.57" serde_json = "1.0.79" tokio-process-stream = "0.2.0" tokio-stream = "0.1.8" @@ -27,6 +41,11 @@ portpicker = "0.1.1" version = "1.17.0" features = ["full"] +[dependencies.fantoccini] +version = "0.18.0" +default-features = false +features = ["rustls-tls"] + [dependencies.serde] version = "1.0.136" features = ["derive"] @@ -41,7 +60,7 @@ default-features = false features = ["rustls-tls", "json"] [dependencies.tracing-subscriber] -version = "0.3.1" +version = "0.3.11" features = ["env-filter"] [dependencies.validator] @@ -53,17 +72,25 @@ version = "0.13.0" optional = true [dependencies.rust-s3] -version = "0.30.0" +version = "0.31.0" default-features = false features = ["tokio-rustls-tls", "tags"] optional = true +[dependencies.sled] +version = "0.34.7" +optional = true + +[build-dependencies] +version_check = "0.9.4" + [features] default = ["fs_storage"] fs_storage = [] cloudinary_storage = ["dep:base64"] s3_storage = ["dep:rust-s3"] tixte_storage = ["reqwest/multipart"] +sled_storage = ["dep:sled"] [profile.release] lto = true diff --git a/Cross.toml b/Cross.toml new file mode 100644 index 0000000..7c6d08a --- /dev/null +++ b/Cross.toml @@ -0,0 +1,2 @@ +[target.x86_64-unknown-freebsd] +image = "docker.io/rustembedded/cross:x86_64-unknown-freebsd" diff --git a/Dockerfile b/Dockerfile index 4b85c07..652d7c6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,19 @@ -FROM rust:slim-bullseye as base +FROM rustlang/rust:nightly-bullseye-slim as base WORKDIR /usr/src/app ENV CI=true ENV RUSTFLAGS="-C target-cpu=native" +# Build stuff RUN apt-get update && \ apt-get upgrade -y --no-install-recommends && \ - apt-get install -y --no-install-recommends build-essential python3 gnupg wget curl unzip dumb-init lld libssl-dev pkg-config && \ + apt-get install -y --no-install-recommends build-essential python3 gnupg wget curl unzip dumb-init clang lld libssl-dev pkg-config && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* && \ apt-get autoremove +# Chrome & Chromedriver RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \ sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list' && \ apt-get -y update && \ @@ -19,6 +21,10 @@ RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key wget -O /tmp/chromedriver.zip http://chromedriver.storage.googleapis.com/`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE`/chromedriver_linux64.zip && \ unzip /tmp/chromedriver.zip chromedriver -d /usr/local/bin/ +# Fleet +RUN cargo install fleet-rs && \ + cargo install sccache + ENV DISPLAY=:99 ENTRYPOINT ["dumb-init", "--"] @@ -29,12 +35,12 @@ COPY Cargo.toml . RUN mkdir src && \ echo "// blank" > src/lib.rs && \ - cargo build --release && \ + fleet build --release && \ rm -r src COPY src/ src/ -RUN cargo build --release +RUN fleet build --release FROM base as runner diff --git a/README.md b/README.md index 31c98e3..fd69302 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,225 @@ # website-screenshot [![Rust: 1.60+](https://img.shields.io/badge/rust-1.60+-93450a)](https://blog.rust-lang.org/2022/04/07/Rust-1.60.0.html) +[![Continuous Delivery](https://github.com/devtomio/website-screenshot/actions/workflows/continuous-delivery.yml/badge.svg)](https://github.com/devtomio/website-screenshot/actions/workflows/continuous-delivery.yml) +[![Continuous Integration](https://github.com/devtomio/website-screenshot/actions/workflows/continuous-integration.yml/badge.svg)](https://github.com/devtomio/website-screenshot/actions/workflows/continuous-integration.yml) + +**📸 website screenshots as a service** + +## Features + +- 💫 powered by [Rust] +- 🚀 blazing fast +- 👮​​​‍‍​ built-in [ratelimiter](https://github.com/antifuchs/governor) +- 👜 built-in [storage providers](#storage-providers) +- 🗼 configurable + +## Deployment + +### Prerequisites + +- [Rust] 1.60+ or greater +- [Redis] 6 or greater +- [Chrome] browser +- [Chromedriver] (must match with the version your [Chrome] browser) + +### Environment Variables + +- `PORT` - the port that the application will run (optional, defaults to `3000`) +- `REDIS_URL` - the address of your redis database (required) + +### Railway + +[![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/new/template/3ZVgSw?referralCode=tomio) + +### Pre-built Binaries + +**⚠️- You can't change the provider when using the pre-built binaries.** + +#### Linux + +```sh +curl -fsSL https://raw.githubusercontent.com/devtomio/website-screenshot/main/install.sh +``` + +#### Other + +See the [Releases page](https://github.com/devtomio/website-screenshot/releases) of this repository and download the appropriate binary. + +### Docker + +A dockerized version of this application is available [here](https://github.com/devtomio/website-screenshot/pkgs/container/website-screenshot). + +**⚠️- You can't change the provider when using the docker image.** + +### Building from source + +1. Clone this repository. e.g. `git clone https://github.com/devtomio/website-screenshot` +2. Build the binary `cargo build --release` + +## Storage Providers + +### Fs (Filesystem) Provider + +*📝 - This is the default provider.* + +#### Build Command + +```sh +cargo build --release +``` + +### [Cloudinary] Provider + +*📝 - You must make an unsigned upload preset.* + +#### Environment Variables + +- `CLOUDINARY_API_KEY` - your api key (required) +- `CLOUDINARY_UPLOAD_PRESET` - the name of your unsigned upload preset (required) +- `CLOUDINARY_CLOUD_NAME` - the cloud name that you set during registration (required) + +#### Build Command + +```sh +cargo build --release --no-default-features --features cloudinary_provider +``` + +### Amazon AWS [S3] Provider + +*⚠️ - This is untested. If you encounter a bug please don't hesitate to open an issue.* + +#### Environment Variables + +- `S3_BUCKET_NAME` - the name of your s3 bucket (required) +- `S3_REGION` - the region of your s3 bucket (required) +- `S3_ACCESS_KEY` - your access key (required) +- `S3_SECRET_KEY` - your secret key (required) +- `S3_SECURITY_TOKEN` - your security token (optional) +- `S3_SESSION_TOKEN` - your session token (optional) + +#### Build Command + +```sh +cargo build --release --no-default-features --features s3_provider +``` + +### [Tixte] Provider + +#### Environment Variables + +- `TIXTE_UPLOAD_KEY` - your upload key, can be found on the integrations tab (required) +- `TIXTE_DOMAIN_CONFIG` - whether to use random domains or a specific domain (required, can only be `standard`, or `random`) +- `TIXTE_CUSTOM_DOMAIN` - the specific domain to use (only required when `TIXTE_DOMAIN_CONFIG` is set to `standard`) + +#### Build Command + +```sh +cargo build --release --no-default-features --features tixte_provider +``` + +### [Sled] Provider + +#### Environment Variables + +- `SLED_PATH` - the path to provide when opening the sled database (optional, defaults to `.website-screenshot`) + +#### Build Command + +```sh +cargo build --release --no-default-features --features sled_provider +``` + +## Endpoints + +### **GET** `/` + +Hello, world! + +Example Response: + +```text +Hello, world! +``` + +### **POST** `/screenshot` + +Creates a screenshot. + +JSON payload with the `url` key. + +Example Payload: + +```json +{ + "url": "https://rust-lang.org" +} +``` + +Example Response + +```json +{ + "slug": "abcdefghijk", + "path": "/s/abcdefghijk", + "metadata": { + "url": "https://rust-lang.org" + } +} +``` + +### **GET** `/s/:slug` + +Gets the screenshot that is corresponding to the slug. + +Example URL + +```text +http://localhost:3000/s/abcdefghijk +``` + +Example Response + +![Rust Website](screenshots/abcdefghijk.png "Rust Website") + +## License + +Licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. + +## Contributors ✨ + +Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): + + + + + + + + +

Tomio

💻 📖 🚇 🚧
+ + + + + + +This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! + +[Rust]: https://rust-lang.org +[Redis]: https://redis.io +[Chrome]: https://google.com/chrome +[Chromedriver]: https://chromedriver.chromium.org +[Cloudinary]: https://cloudinary.com +[S3]: https://aws.amazon.com/s3 +[Tixte]: https://tixte.com +[Sled]: https://sled.rs diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..4c135a9 --- /dev/null +++ b/build.rs @@ -0,0 +1,46 @@ +use version_check::Version; + +/// Get a list of features enabled. +macro_rules! get_features { + ($($feature:expr),*) => { + vec![ + $( + #[cfg(feature = $feature)] + $feature, + )* + ] + } +} + +fn main() { + let version = match version_check::triple() { + Some((ver, ..)) => ver.to_mmp(), + None => Version::parse("1.0.0").unwrap().to_mmp(), + }; + + if version.0 != 1 && version.1 < 60 { + panic!("Minimum rust version required is 1.60, please update your rust version via `rustup update`."); + } + + let features = get_features!( + "fs_storage", + "cloudinary_storage", + "s3_storage", + "tixte_storage", + "sled_storage" + ); + + if features.is_empty() { + panic!("You must set a storage provider."); + } + + if features.len() > 1 { + panic!( + "\ + You can only have one storage provider. + Provided Features: {} + ", + features.join(", ") + ); + } +} diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 0000000..5e114ea --- /dev/null +++ b/clippy.toml @@ -0,0 +1,4 @@ +msrv = "1.60" +cognitive-complexity-threshold = 20 +enum-variant-name-threshold = 1 +single-char-binding-names-threshold = 3 diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..bf97594 --- /dev/null +++ b/install.sh @@ -0,0 +1,474 @@ +#!/usr/bin/env sh + +# Adapted from https://github.com/Milo123459/glitter/blob/master/install.sh + +help_text="# Options +# +# -V, --verbose +# Enable verbose output for the installer +# +# -f, -y, --force, --yes +# Skip the confirmation prompt during installation +# +# -p, --platform +# Override the platform identified by the installer +# +# -b, --bin-dir +# Override the bin installation directory +# +# -a, --arch +# Override the architecture identified by the installer +# +# -B, --base-url +# Override the base URL used for downloading releases +# -r, --remove +# Uninstall website-screenshot +# -h, --help +# Get some help +# +" + +set -eu +printf '\n' + +BOLD="$(tput bold 2>/dev/null || printf '')" +GREY="$(tput setaf 0 2>/dev/null || printf '')" +UNDERLINE="$(tput smul 2>/dev/null || printf '')" +RED="$(tput setaf 1 2>/dev/null || printf '')" +GREEN="$(tput setaf 2 2>/dev/null || printf '')" +YELLOW="$(tput setaf 3 2>/dev/null || printf '')" +BLUE="$(tput setaf 4 2>/dev/null || printf '')" +MAGENTA="$(tput setaf 5 2>/dev/null || printf '')" +NO_COLOR="$(tput sgr0 2>/dev/null || printf '')" + +SUPPORTED_TARGETS="x86_64-unknown-linux-gnu x86_64-unknown-linux-musl \ + i686-unknown-linux-musl aarch64-unknown-linux-musl \ + arm-unknown-linux-musleabihf x86_64-apple-darwin \ + aarch64-apple-darwin x86_64-pc-windows-msvc \ + i686-pc-windows-msvc aarch64-pc-windows-msvc \ + x86_64-unknown-freebsd" + +info() { + printf '%s\n' "${BOLD}${GREY}>${NO_COLOR} $*" +} + +warn() { + printf '%s\n' "${YELLOW}! $*${NO_COLOR}" +} + +error() { + printf '%s\n' "${RED}x $*${NO_COLOR}" >&2 +} + +completed() { + printf '%s\n' "${GREEN}✓${NO_COLOR} $*" +} + +has() { + command -v "$1" 1>/dev/null 2>&1 +} + +# Gets path to a temporary file, even if +get_tmpfile() { + local suffix + suffix="$1" + if has mktemp; then + printf "%s%s.%s.%s" "$(mktemp)" "-website-screenshot" "${RANDOM}" "${suffix}" + else + # No really good options here--let's pick a default + hope + printf "/tmp/website-screenshot.%s" "${suffix}" + fi +} + +# Test if a location is writeable by trying to write to it. Windows does not let +# you test writeability other than by writing: https://stackoverflow.com/q/1999988 +test_writeable() { + local path + path="${1:-}/test.txt" + if touch "${path}" 2>/dev/null; then + rm "${path}" + return 0 + else + return 1 + fi +} + +download() { + file="$1" + url="$2" + touch "$file" + printf "%s" "$file" + + if has curl; then + cmd="curl --fail --silent --location --output $file $url" + elif has wget; then + cmd="wget --quiet --output-document=$file $url" + elif has fetch; then + cmd="fetch --quiet --output=$file $url" + else + error "No HTTP download program (curl, wget, fetch) found, exiting…" + return 1 + fi + + $cmd && return 0 || rc=$? + + error "Command failed (exit code $rc): ${BLUE}${cmd}${NO_COLOR}" + printf "\n" >&2 + info "This is likely due to website-screenshot not yet supporting your configuration." + info "If you would like to see a build for your configuration," + info "please create an issue requesting a build for ${MAGENTA}${TARGET}${NO_COLOR}:" + info "${BOLD}${UNDERLINE}https://github.com/devtomio/website-screenshot/issues/new/${NO_COLOR}" + return $rc +} + +unpack() { + local archive=$1 + local bin_dir=$2 + local sudo=${3-} + + case "$archive" in + *.tar.gz) + flags=$(test -n "${VERBOSE-}" && echo "-v" || echo "") + ${sudo} tar "${flags}" -xzf "${archive}" -C "${bin_dir}" + return 0 + ;; + *.zip) + flags=$(test -z "${VERBOSE-}" && echo "-qq" || echo "") + UNZIP="${flags}" ${sudo} unzip "${archive}" -d "${bin_dir}" + return 0 + ;; + esac + + error "Unknown package extension." + printf "\n" + info "This almost certainly results from a bug in this script--please file a" + info "bug report at https://github.com/devtomio/website-screenshot/issues" + return 1 +} + +elevate_priv() { + if ! has sudo; then + error 'Could not find the command "sudo", needed to get permissions for install.' + info "If you are on Windows, please run your shell as an administrator, then" + info "rerun this script. Otherwise, please run this script as root, or install" + info "sudo." + exit 1 + fi + if ! sudo -v; then + error "Superuser not granted, aborting installation" + exit 1 + fi +} + +install() { + local msg + local sudo + local archive + local ext="$1" + + if test_writeable "${BIN_DIR}"; then + sudo="" + msg="Installing website-screenshot, please wait…" + else + warn "Escalated permissions are required to install to ${BIN_DIR}" + elevate_priv + sudo="sudo" + msg="Installing website-screenshot as root, please wait…" + fi + info "$msg" + + archive=$(get_tmpfile "$ext") + + # download to the temp file + download "${archive}" "${URL}" + + # unpack the temp file to the bin dir, using sudo if required + unpack "${archive}" "${BIN_DIR}" "${sudo}" + + # remove tempfile + + rm "${archive}" +} + +# Currently supporting: +# - win (Git Bash) +# - darwin +# - linux +# - linux_musl (Alpine) +# - freebsd +detect_platform() { + local platform + platform="$(uname -s | tr '[:upper:]' '[:lower:]')" + + case "${platform}" in + msys_nt*) platform="pc-windows-msvc" ;; + cygwin_nt*) platform="pc-windows-msvc";; + # mingw is Git-Bash + mingw*) platform="pc-windows-msvc" ;; + # use the statically compiled musl bins on linux to avoid linking issues. + linux) platform="unknown-linux-musl" ;; + darwin) platform="apple-darwin" ;; + freebsd) platform="unknown-freebsd" ;; + esac + + printf '%s' "${platform}" +} + +# Currently supporting: +# - x86_64 +# - i386 +detect_arch() { + local arch + arch="$(uname -m | tr '[:upper:]' '[:lower:]')" + + case "${arch}" in + amd64) arch="x86_64" ;; + armv*) arch="arm" ;; + arm64) arch="aarch64" ;; + esac + + # `uname -m` in some cases mis-reports 32-bit OS as 64-bit, so double check + if [ "${arch}" = "x86_64" ] && [ "$(getconf LONG_BIT)" -eq 32 ]; then + arch=i686 + elif [ "${arch}" = "aarch64" ] && [ "$(getconf LONG_BIT)" -eq 32 ]; then + arch=arm + fi + + printf '%s' "${arch}" +} + +detect_target() { + local arch="$1" + local platform="$2" + local target="$arch-$platform" + + if [ "${target}" = "arm-unknown-linux-musl" ]; then + target="${target}eabihf" + fi + + printf '%s' "${target}" +} + + +confirm() { + if [ -z "${FORCE-}" ]; then + printf "%s " "${MAGENTA}?${NO_COLOR} $* ${BOLD}[y/N]${NO_COLOR}" + set +e + read -r yn &2 + info "If you would like to see a build for your configuration," + info "please create an issue requesting a build for ${MAGENTA}${target}${NO_COLOR}:" + info "${BOLD}${UNDERLINE}https://github.com/devtomio/website-screenshot/issues/new/${NO_COLOR}" + printf "\n" + exit 1 + fi +} +UNINSTALL=0 +HELP=0 +CARGOTOML="$(curl -fsSL https://raw.githubusercontent.com/devtomio/website-screenshot/main/Cargo.toml)" +ALL_VERSIONS="$(sed -n 's/.*version = "\([^"]*\)".*/\1/p' <<< "$CARGOTOML")" +IFS=$'\n' read -r -a VERSION <<< "$ALL_VERSIONS" +# defaults +if [ -z "${PLATFORM-}" ]; then + PLATFORM="$(detect_platform)" +fi + +if [ -z "${BIN_DIR-}" ]; then + BIN_DIR=/usr/local/bin +fi + +if [ -z "${ARCH-}" ]; then + ARCH="$(detect_arch)" +fi + +if [ -z "${BASE_URL-}" ]; then + BASE_URL="https://github.com/devtomio/website-screenshot/releases" +fi + +# parse argv variables +while [ "$#" -gt 0 ]; do + case "$1" in + -p | --platform) + PLATFORM="$2" + shift 2 + ;; + -b | --bin-dir) + BIN_DIR="$2" + shift 2 + ;; + -a | --arch) + ARCH="$2" + shift 2 + ;; + -B | --base-url) + BASE_URL="$2" + shift 2 + ;; + + -V | --verbose) + VERBOSE=1 + shift 1 + ;; + -f | -y | --force | --yes) + FORCE=1 + shift 1 + ;; + -r | --remove | --uninstall) + UNINSTALL=1 + shift 1 + ;; + -h | --help) + HELP=1 + shift 1 + ;; + -p=* | --platform=*) + PLATFORM="${1#*=}" + shift 1 + ;; + -b=* | --bin-dir=*) + BIN_DIR="${1#*=}" + shift 1 + ;; + -a=* | --arch=*) + ARCH="${1#*=}" + shift 1 + ;; + -B=* | --base-url=*) + BASE_URL="${1#*=}" + shift 1 + ;; + -V=* | --verbose=*) + VERBOSE="${1#*=}" + shift 1 + ;; + -f=* | -y=* | --force=* | --yes=*) + FORCE="${1#*=}" + shift 1 + ;; + + *) + error "Unknown option: $1" + exit 1 + ;; + esac +done +if [ $UNINSTALL == 1 ]; then + confirm "Are you sure you want to uninstall website-screenshot?" + + msg="" + sudo="" + + info "REMOVING website-screenshot" + + if test_writeable "$(dirname "$(which website-screenshot)")"; then + sudo="" + msg="Removing website-screenshot, please wait…" + else + warn "Escalated permissions are required to install to ${BIN_DIR}" + elevate_priv + sudo="sudo" + msg="Removing website-screenshot as root, please wait…" + fi + + info "$msg" + ${sudo} rm "$(which website-screenshot)" + ${sudo} rm /tmp/website-screenshot + + info "Removed website-screenshot" + exit 0 + + fi +if [ $HELP == 1 ]; then + echo "${help_text}" + exit 0 +fi +TARGET="$(detect_target "${ARCH}" "${PLATFORM}")" + +is_build_available "${ARCH}" "${PLATFORM}" "${TARGET}" + +printf " %s\n" "${UNDERLINE}Configuration${NO_COLOR}" +info "${BOLD}Bin directory${NO_COLOR}: ${GREEN}${BIN_DIR}${NO_COLOR}" +info "${BOLD}Platform${NO_COLOR}: ${GREEN}${PLATFORM}${NO_COLOR}" +info "${BOLD}Arch${NO_COLOR}: ${GREEN}${ARCH}${NO_COLOR}" +info "${BOLD}Version${NO_COLOR}: ${GREEN}${VERSION[0]}${NO_COLOR}" + +# non-empty VERBOSE enables verbose untarring +if [ -n "${VERBOSE-}" ]; then + VERBOSE=v + info "${BOLD}Verbose${NO_COLOR}: yes" +else + VERBOSE= +fi + +printf '\n' + +EXT=tar.gz +if [ "${PLATFORM}" = "pc-windows-msvc" ]; then + EXT=zip +fi + +URL="${BASE_URL}/latest/download/website-screenshot-${TARGET}.${EXT}" +info "Tarball URL: ${UNDERLINE}${BLUE}${URL}${NO_COLOR}" +confirm "Install website-screenshot ${GREEN}${VERSION[0]}${NO_COLOR} to ${BOLD}${GREEN}${BIN_DIR}${NO_COLOR}?" +check_bin_dir "${BIN_DIR}" + +install "${EXT}" +completed "website-screenshot installed" diff --git a/screenshots/abcdefghijk.png b/screenshots/abcdefghijk.png new file mode 100644 index 0000000..47a7367 Binary files /dev/null and b/screenshots/abcdefghijk.png differ diff --git a/src/main.rs b/src/main.rs index 2a6a812..e997851 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,12 +37,11 @@ async fn main() -> anyhow::Result<()> { initialize_tracing(); let driver_port = pick_unused_port().expect("No port available"); - let driver_port_2 = driver_port.clone(); tokio::spawn(async move { let mut chromedriver = Command::new("chromedriver"); chromedriver.stdout(Stdio::piped()).stderr(Stdio::piped()); - chromedriver.arg(format!("--port={driver_port_2}")); + chromedriver.arg(format!("--port={driver_port}")); let mut stream = ProcessLineStream::try_from(chromedriver).expect("Failed to convert command to stream"); @@ -68,7 +67,7 @@ async fn main() -> anyhow::Result<()> { capabilities.insert("goog:chromeOptions".to_owned(), chrome_opts); - let mut client = ClientBuilder::native() + let mut client = ClientBuilder::rustls() .capabilities(capabilities) .connect(&format!("http://localhost:{driver_port}")) .await?; diff --git a/src/providers/mod.rs b/src/providers/mod.rs index ea42357..4ddc80d 100644 --- a/src/providers/mod.rs +++ b/src/providers/mod.rs @@ -28,10 +28,11 @@ cfg_if! { mod tixte; pub use tixte::TixteProvider as Storage; - } else { - // Catch-all - mod fs; + } else if #[cfg(feature = "sled_storage")] { + mod sled; - pub use fs::FsProvider as Storage; + pub use self::sled::SledProvider as Storage; + } else { + panic!("Please provide a storage provider."); } } diff --git a/src/providers/sled.rs b/src/providers/sled.rs new file mode 100644 index 0000000..95721ee --- /dev/null +++ b/src/providers/sled.rs @@ -0,0 +1,53 @@ +use std::env; +use std::sync::Arc; + +use anyhow::Result; +use async_trait::async_trait; +use cuid::cuid; +use redis::{AsyncCommands, Client}; +use sled::Db; + +use super::Provider; + +#[derive(Debug)] +pub struct SledProvider(Arc, Arc); + +#[async_trait] +impl Provider for SledProvider { + fn new() -> Self { + let redis = Arc::new( + Client::open(env::var("REDIS_URL").expect("Failed to load redis url")) + .expect("Failed to open redis client"), + ); + + let db = Arc::new( + sled::open(env::var("SLED_PATH").unwrap_or_else(|_| ".website-screenshot".to_owned())) + .expect("Failed to open sled database"), + ); + + Self(redis, db) + } + + #[inline] + fn prefix() -> String { + "sled".to_owned() + } + + async fn get(&self, slug: String) -> Result> { + let mut con = self.0.get_async_connection().await?; + let key: String = con.get(format!("{}:{slug}", SledProvider::prefix())).await?; + let data = self.1.get(key)?.expect("Failed to get data").as_ref().to_vec(); + + Ok(data) + } + + async fn set(&self, slug: String, data: Vec) -> Result<()> { + let mut con = self.0.get_async_connection().await?; + let key = cuid()?; + + con.set(format!("{}:{slug}", SledProvider::prefix()), &key).await?; + self.1.insert(key, data)?; + + Ok(()) + } +} diff --git a/src/routes/index.rs b/src/routes/index.rs index 3e5082e..c47d3f4 100644 --- a/src/routes/index.rs +++ b/src/routes/index.rs @@ -2,5 +2,5 @@ use actix_web::{get, HttpRequest}; #[get("/")] pub async fn index(_req: HttpRequest) -> &'static str { - "Hello World!" + "Hello, world!" }