diff --git a/.github/workflows/rtabby.yml b/.github/workflows/rtabby.yml index bd02b30..ababc2e 100644 --- a/.github/workflows/rtabby.yml +++ b/.github/workflows/rtabby.yml @@ -17,13 +17,6 @@ env: IMAGE_NAME: ${{ github.repository }} jobs: - clippy: - name: Rust Clippy - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Run Clippy - run: cargo clippy --all-targets --all-features build: name: Rust Build runs-on: ubuntu-latest @@ -35,9 +28,13 @@ jobs: run: cargo test --verbose docker: + strategy: + matrix: + db: [mysql, sqlite] + minimal: [true, false] name: Docker if: ${{ always() && contains(join(needs.*.result, ','), 'success') }} - needs: [clippy, build] + needs: [build] runs-on: ubuntu-latest permissions: contents: read @@ -53,83 +50,14 @@ jobs: # Install the cosign tool except on PR # https://github.com/sigstore/cosign-installer - name: Install cosign + if: ${{ github.event_name == 'release' }} uses: sigstore/cosign-installer@6e04d228eb30da1757ee4e1dd75a0ec73a653e06 #v3.1.1 with: cosign-release: 'v2.1.1' - # Set up BuildKit Docker container builder to be able to build - # multi-platform images and export cache - # https://github.com/docker/setup-buildx-action - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 - - # Login against a Docker registry except on PR - # https://github.com/docker/login-action - - name: Log into registry ${{ env.REGISTRY }} - uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - # Extract metadata (tags, labels) for Docker - # https://github.com/docker/metadata-action - - name: Extract Docker metadata - id: meta - uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: latest - - # Build and push Docker image with Buildx - # https://github.com/docker/build-push-action - - name: Build and push Docker image - id: build-and-push - uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 - with: - context: . - push: ${{ github.event_name == 'release' }} - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max - - # Sign the resulting Docker image digest except on PRs. - # This will only write to the public Rekor transparency log when the Docker - # repository is public to avoid leaking data. If you would like to publish - # transparency data even for private images, pass --force to cosign below. - # https://github.com/sigstore/cosign - - name: Sign the published Docker image + - name: Set up QEMU if: ${{ github.event_name == 'release' }} - env: - # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable - TAGS: ${{ steps.meta.outputs.tags }} - DIGEST: ${{ steps.build-and-push.outputs.digest }} - # This step uses the identity token to provision an ephemeral certificate - # against the sigstore community Fulcio instance. - run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} - docker-sqlite: - name: Docker (Sqlite) - if: ${{ always() && contains(join(needs.*.result, ','), 'success') }} - needs: [clippy, build] - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - # This is used to complete the identity challenge - # with sigstore/fulcio when running outside of PRs. - id-token: write - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - # Install the cosign tool except on PR - # https://github.com/sigstore/cosign-installer - - name: Install cosign - uses: sigstore/cosign-installer@6e04d228eb30da1757ee4e1dd75a0ec73a653e06 #v3.1.1 - with: - cosign-release: 'v2.1.1' + uses: docker/setup-qemu-action@v3 # Set up BuildKit Docker container builder to be able to build # multi-platform images and export cache @@ -140,12 +68,27 @@ jobs: # Login against a Docker registry except on PR # https://github.com/docker/login-action - name: Log into registry ${{ env.REGISTRY }} + if: ${{ github.event_name == 'release' }} uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Prepare + id: prep + env: + REF: ${{ github.ref }} + run: | + if [[ "$REF" == "refs/tags/v"* ]]; then + tag=$(git describe --tags $(git rev-list --tags --max-count=1)) + tag=${tag:1} + else + tag=$(git log -1 --format="%cd" --date=short | sed s/-//g) + fi + echo "TAG=$tag" >> $GITHUB_OUTPUT + echo "GIT_COMMIT=$(echo $(git rev-parse HEAD) | cut -c1-7)" >> $GITHUB_OUTPUT + # Extract metadata (tags, labels) for Docker # https://github.com/docker/metadata-action - name: Extract Docker metadata @@ -153,7 +96,13 @@ jobs: uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: sqlite + tags: | + type=semver, pattern={{version}}, enable=${{ matrix.db == 'mysql' }}, suffix=${{ matrix.minimal && '-minimal' || '' }} + type=semver, pattern={{version}}, suffix=-${{ matrix.db }}${{ matrix.minimal && '-minimal' || '' }} + type=sha, format=short + type=raw, value=latest, enable=${{ matrix.db == 'mysql' }}, suffix=${{ matrix.minimal && '-minimal' || '' }} + type=raw, value=latest, suffix=-${{ matrix.db }}${{ matrix.minimal && '-minimal' || '' }} + type=raw, value=${{ matrix.db }}, suffix=${{ matrix.minimal && '-minimal' || '' }} # Build and push Docker image with Buildx # https://github.com/docker/build-push-action @@ -162,12 +111,15 @@ jobs: uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 with: context: . - file: Dockerfile-sqlite + platforms: linux/amd64${{ github.event_name == 'release' && ',linux/arm64' || '' }} push: ${{ github.event_name == 'release' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max + build-args: | + GIT_COMMIT=${{ steps.prep.outputs.GIT_COMMIT }} + FEATURE_FLAGS=-F|${{ matrix.db }}-bundle${{ !matrix.minimal && '|-F|all-login' || '' }} # Sign the resulting Docker image digest except on PRs. # This will only write to the public Rekor transparency log when the Docker @@ -182,4 +134,4 @@ jobs: DIGEST: ${{ steps.build-and-push.outputs.digest }} # This step uses the identity token to provision an ephemeral certificate # against the sigstore community Fulcio instance. - run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} + run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} \ No newline at end of file diff --git a/.github/workflows/rust-clippy.yml b/.github/workflows/rust-clippy.yml new file mode 100644 index 0000000..c9ec2d0 --- /dev/null +++ b/.github/workflows/rust-clippy.yml @@ -0,0 +1,55 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# rust-clippy is a tool that runs a bunch of lints to catch common +# mistakes in your Rust code and help improve your Rust code. +# More details at https://github.com/rust-lang/rust-clippy +# and https://rust-lang.github.io/rust-clippy/ + +name: rust-clippy analyze + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '31 7 * * 3' + +jobs: + rust-clippy-analyze: + name: Run rust-clippy analyzing + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Install Rust toolchain + uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af #@v1 + with: + profile: minimal + toolchain: stable + components: clippy + override: true + + - name: Install required cargo + run: cargo install clippy-sarif sarif-fmt + + - name: Run rust-clippy + run: + cargo clippy + --all-features + --message-format=json | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt + continue-on-error: true + + - name: Upload analysis results to GitHub + uses: github/codeql-action/upload-sarif@v1 + with: + sarif_file: rust-clippy-results.sarif + wait-for-processing: true \ No newline at end of file diff --git a/.gitignore b/.gitignore index 6c712bb..a8f14bc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,18 @@ -/target +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + .env /vendor data.db diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index f9e0986..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,1911 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "actix-codec" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "617a8268e3537fe1d8c9ead925fca49ef6400927ee7bc26750e90ecee14ce4b8" -dependencies = [ - "bitflags 1.3.2", - "bytes", - "futures-core", - "futures-sink", - "memchr", - "pin-project-lite", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "actix-http" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92ef85799cba03f76e4f7c10f533e66d87c9a7e7055f3391f09000ad8351bc9" -dependencies = [ - "actix-codec", - "actix-rt", - "actix-service", - "actix-tls", - "actix-utils", - "ahash", - "base64", - "bitflags 2.4.1", - "brotli", - "bytes", - "bytestring", - "derive_more", - "encoding_rs", - "flate2", - "futures-core", - "h2", - "http", - "httparse", - "httpdate", - "itoa", - "language-tags", - "local-channel", - "mime", - "percent-encoding", - "pin-project-lite", - "rand", - "sha1", - "smallvec", - "tokio", - "tokio-util", - "tracing", - "zstd", -] - -[[package]] -name = "actix-macros" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" -dependencies = [ - "quote", - "syn 2.0.39", -] - -[[package]] -name = "actix-router" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66ff4d247d2b160861fa2866457e85706833527840e4133f8f49aa423a38799" -dependencies = [ - "bytestring", - "http", - "regex", - "serde", - "tracing", -] - -[[package]] -name = "actix-rt" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28f32d40287d3f402ae0028a9d54bef51af15c8769492826a69d28f81893151d" -dependencies = [ - "futures-core", - "tokio", -] - -[[package]] -name = "actix-server" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb13e7eef0423ea6eab0e59f6c72e7cb46d33691ad56a726b3cd07ddec2c2d4" -dependencies = [ - "actix-rt", - "actix-service", - "actix-utils", - "futures-core", - "futures-util", - "mio", - "socket2", - "tokio", - "tracing", -] - -[[package]] -name = "actix-service" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" -dependencies = [ - "futures-core", - "paste", - "pin-project-lite", -] - -[[package]] -name = "actix-tls" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72616e7fbec0aa99c6f3164677fa48ff5a60036d0799c98cab894a44f3e0efc3" -dependencies = [ - "actix-rt", - "actix-service", - "actix-utils", - "futures-core", - "impl-more", - "pin-project-lite", - "rustls", - "rustls-webpki", - "tokio", - "tokio-rustls", - "tokio-util", - "tracing", - "webpki-roots", -] - -[[package]] -name = "actix-utils" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" -dependencies = [ - "local-waker", - "pin-project-lite", -] - -[[package]] -name = "actix-web" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4a5b5e29603ca8c94a77c65cf874718ceb60292c5a5c3e5f4ace041af462b9" -dependencies = [ - "actix-codec", - "actix-http", - "actix-macros", - "actix-router", - "actix-rt", - "actix-server", - "actix-service", - "actix-tls", - "actix-utils", - "actix-web-codegen", - "ahash", - "bytes", - "bytestring", - "cfg-if", - "cookie", - "derive_more", - "encoding_rs", - "futures-core", - "futures-util", - "itoa", - "language-tags", - "log", - "mime", - "once_cell", - "pin-project-lite", - "regex", - "serde", - "serde_json", - "serde_urlencoded", - "smallvec", - "socket2", - "time", - "url", -] - -[[package]] -name = "actix-web-codegen" -version = "4.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1f50ebbb30eca122b188319a4398b3f7bb4a8cdf50ecfb73bfc6a3c3ce54f5" -dependencies = [ - "actix-router", - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "actix-web-httpauth" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d613edf08a42ccc6864c941d30fe14e1b676a77d16f1dbadc1174d065a0a775" -dependencies = [ - "actix-utils", - "actix-web", - "base64", - "futures-core", - "futures-util", - "log", - "pin-project-lite", -] - -[[package]] -name = "addr2line" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" -dependencies = [ - "cfg-if", - "getrandom", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" -dependencies = [ - "memchr", -] - -[[package]] -name = "alloc-no-stdlib" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" - -[[package]] -name = "alloc-stdlib" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" -dependencies = [ - "alloc-no-stdlib", -] - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "backtrace" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base64" -version = "0.21.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "brotli" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor", -] - -[[package]] -name = "brotli-decompressor" -version = "2.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - -[[package]] -name = "bumpalo" -version = "3.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" - -[[package]] -name = "bytestring" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72" -dependencies = [ - "bytes", -] - -[[package]] -name = "cc" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "jobserver", - "libc", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-targets 0.48.5", -] - -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "cookie" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" -dependencies = [ - "percent-encoding", - "time", - "version_check", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" - -[[package]] -name = "cpufeatures" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "deranged" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version", - "syn 1.0.109", -] - -[[package]] -name = "diesel" -version = "2.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62c6fcf842f17f8c78ecf7c81d75c5ce84436b41ee07e03f490fbb5f5a8731d8" -dependencies = [ - "bitflags 2.4.1", - "byteorder", - "chrono", - "diesel_derives", - "libsqlite3-sys", - "mysqlclient-sys", - "percent-encoding", - "r2d2", - "time", - "url", -] - -[[package]] -name = "diesel_derives" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8337737574f55a468005a83499da720f20c65586241ffea339db9ecdfd2b44" -dependencies = [ - "diesel_table_macro_syntax", - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "diesel_migrations" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6036b3f0120c5961381b570ee20a02432d7e2d27ea60de9578799cf9156914ac" -dependencies = [ - "diesel", - "migrations_internals", - "migrations_macros", -] - -[[package]] -name = "diesel_table_macro_syntax" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" -dependencies = [ - "syn 2.0.39", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "dotenvy" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" - -[[package]] -name = "encoding_rs" -version = "0.8.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "env_logger" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" -dependencies = [ - "humantime", - "is-terminal", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "flate2" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futures-core" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" - -[[package]] -name = "futures-sink" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" - -[[package]] -name = "futures-task" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" - -[[package]] -name = "futures-util" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" -dependencies = [ - "futures-core", - "futures-task", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "gimli" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" - -[[package]] -name = "h2" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" - -[[package]] -name = "hermit-abi" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" - -[[package]] -name = "http" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "iana-time-zone" -version = "0.1.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "idna" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "impl-more" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206ca75c9c03ba3d4ace2460e57b189f39f43de612c2f85836e65c929701bb2d" - -[[package]] -name = "indexmap" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" -dependencies = [ - "equivalent", - "hashbrown", -] - -[[package]] -name = "is-terminal" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" -dependencies = [ - "hermit-abi", - "rustix", - "windows-sys 0.48.0", -] - -[[package]] -name = "itoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" - -[[package]] -name = "jobserver" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" -dependencies = [ - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "language-tags" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" - -[[package]] -name = "libc" -version = "0.2.150" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" - -[[package]] -name = "libsqlite3-sys" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linux-raw-sys" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" - -[[package]] -name = "local-channel" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" -dependencies = [ - "futures-core", - "futures-sink", - "local-waker", -] - -[[package]] -name = "local-waker" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" - -[[package]] -name = "lock_api" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" - -[[package]] -name = "memchr" -version = "2.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" - -[[package]] -name = "migrations_internals" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f23f71580015254b020e856feac3df5878c2c7a8812297edd6c0a485ac9dada" -dependencies = [ - "serde", - "toml", -] - -[[package]] -name = "migrations_macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cce3325ac70e67bbab5bd837a31cae01f1a6db64e0e744a33cb03a543469ef08" -dependencies = [ - "migrations_internals", - "proc-macro2", - "quote", -] - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", -] - -[[package]] -name = "mio" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.48.0", -] - -[[package]] -name = "mysqlclient-sys" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61b381528ba293005c42a409dd73d034508e273bf90481f17ec2e964a6e969b" -dependencies = [ - "pkg-config", - "vcpkg", -] - -[[package]] -name = "num-traits" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "object" -version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.48.5", -] - -[[package]] -name = "paste" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "pin-project-lite" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro2" -version = "1.0.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r2d2" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" -dependencies = [ - "log", - "parking_lot", - "scheduled-thread-pool", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "regex" -version = "1.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" - -[[package]] -name = "ring" -version = "0.17.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" -dependencies = [ - "cc", - "getrandom", - "libc", - "spin", - "untrusted", - "windows-sys 0.48.0", -] - -[[package]] -name = "rtabby-web-api" -version = "0.3.0" -dependencies = [ - "actix-web", - "actix-web-httpauth", - "cfg-if", - "chrono", - "diesel", - "diesel_migrations", - "dotenvy", - "env_logger", - "libsqlite3-sys", - "log", - "rustls", - "rustls-pemfile", - "serde", - "serde_yaml", - "uuid", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.38.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a" -dependencies = [ - "bitflags 2.4.1", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustls" -version = "0.21.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" -dependencies = [ - "log", - "ring", - "rustls-webpki", - "sct", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" -dependencies = [ - "base64", -] - -[[package]] -name = "rustls-webpki" -version = "0.101.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "ryu" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[package]] -name = "scheduled-thread-pool" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" -dependencies = [ - "parking_lot", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "semver" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" - -[[package]] -name = "serde" -version = "1.0.193" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.193" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "serde_json" -version = "1.0.108" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_spanned" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_yaml" -version = "0.9.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" -dependencies = [ - "indexmap", - "itoa", - "ryu", - "serde", - "unsafe-libyaml", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" - -[[package]] -name = "socket2" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" -dependencies = [ - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "termcolor" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "time" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" -dependencies = [ - "deranged", - "itoa", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" -dependencies = [ - "time-core", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", -] - -[[package]] -name = "toml" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "log", - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", -] - -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - -[[package]] -name = "unicode-bidi" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unsafe-libyaml" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "uuid" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.39", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" - -[[package]] -name = "webpki-roots" -version = "0.25.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-core" -version = "0.51.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.0", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" -dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" - -[[package]] -name = "winnow" -version = "0.5.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67b5f0a4e7a27a64c651977932b9dc5667ca7fc31ac44b03ed37a0cf42fdfff" -dependencies = [ - "memchr", -] - -[[package]] -name = "zerocopy" -version = "0.7.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "zstd" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "6.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.9+zstd.1.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" -dependencies = [ - "cc", - "pkg-config", -] diff --git a/Cargo.toml b/Cargo.toml index e2fa7eb..427e09d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,29 +4,38 @@ version = "0.3.0" edition = "2021" [features] -default = ["mysql"] +default = ["mysql", "all-login"] dotenv = ["dep:dotenvy"] mysql = ["diesel/mysql"] -mysqlclient-static = ["mysql"] +mysql-bundle = ["mysql"] sqlite = ["diesel/sqlite"] sqlite-bundle = ["sqlite", "libsqlite3-sys/bundled"] +third-party-login = ["dep:actix-session", "dep:tera", "dep:reqwest", "dep:actix-files"] +google-login = ["third-party-login"] +github-login = ["third-party-login"] +gitlab-login = ["third-party-login"] +microsoft-login = ["third-party-login"] +all-login = ["google-login", "github-login", "gitlab-login", "microsoft-login"] [dev-dependencies] dotenvy = "0.15.6" [dependencies] -cfg-if = "1.0.0" -env_logger = "0.10.0" +env_logger = "0.11.3" log = "0.4.16" dotenvy = {version = "0.15.6", optional = true} rustls = "0.21.7" rustls-pemfile = "1.0.0" -actix-web = { version = "4.2.1", features = ["rustls-0_21"] } -actix-web-httpauth = "0.8.0" +actix-web = { version = "4.5.1", features = ["rustls-0_21"] } +actix-web-httpauth = "0.8.1" +actix-session = { version = "0.9.0", features = ["cookie-session"], optional = true } +actix-files = { version = "0.6.5", optional = true } chrono = { version = "0.4.22", features = ["serde"] } libsqlite3-sys = { version = "0", optional = true } -diesel = { version = "2.0.2", features = ["chrono", "r2d2"] } -diesel_migrations = "2.0.0" +diesel = { version = "2.1.4", features = ["chrono", "r2d2"] } +diesel_migrations = "2.1.0" serde = { version = "1.0.152", features = ["derive"] } serde_yaml = "0.9.16" -uuid = "1.2.2" \ No newline at end of file +uuid = { version = "1.6.1", features = ["serde", "v4"] } +tera = { version = "1", optional = true } +reqwest = { version = "0.11", features = ["json", "rustls-tls"], default-features = false, optional = true } \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 5635142..6420817 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,22 +1,40 @@ # syntax=docker/dockerfile:1 -FROM rust:1.73-alpine AS builder +FROM rust:1.76-alpine AS builder +ARG FEATURE_FLAGS="-F|mysql-bundle|-F|all-login" WORKDIR /build COPY . . -RUN apk add --no-cache build-base binutils mariadb-dev musl-dev bash cmake curl && \ - bash scripts/mariadb-static-build.sh && \ - bash scripts/zlib-static-build.sh && \ - ar x lib/libmysqlclient.a && \ - ar x /lib/libz.a && \ - ar x /usr/lib/libc.a && \ - ar rcs /build/lib/libmysqlclient.a *.o *.lo && \ - rm -rf *.o *.lo && \ - cargo build --target=x86_64-unknown-linux-musl --release -F mysqlclient-static + +RUN apk add --no-cache build-base + +RUN if [[ "$FEATURE_FLAGS" == *"mysql-bundle"* ]]; then \ + apk add --no-cache binutils mariadb-dev musl-dev bash cmake curl && \ + bash scripts/mariadb-static-build.sh && \ + bash scripts/zlib-static-build.sh && \ + ar x lib/libmysqlclient.a && \ + ar x /lib/libz.a && \ + ar x /usr/lib/libc.a && \ + ar rcs /build/lib/libmysqlclient.a *.o *.lo && \ + rm -rf *.o *.lo; \ + fi + +RUN if [[ "$FEATURE_FLAGS" == *"login"* ]]; then \ + echo "login enabled"; \ + else \ + rm -rf /build/web/*; \ + fi + + +RUN cargo build --release --no-default-features --target-dir /build/target/docker $(echo "$FEATURE_FLAGS" | sed 's/|/ /g') FROM scratch +ARG GIT_COMMIT WORKDIR /config -COPY --from=builder /build/target/x86_64-unknown-linux-musl/release/rtabby-web-api / +COPY --from=builder /build/target/docker/release/rtabby-web-api / COPY --from=builder /build/users.exemple.yml . +COPY --from=builder /build/web/ /www/web/ +ENV STATIC_FILES_BASE_DIR=/www/web/ +ENV GIT_COMMIT=$GIT_COMMIT CMD ["/rtabby-web-api"] diff --git a/Dockerfile-sqlite b/Dockerfile-sqlite deleted file mode 100644 index a9663dc..0000000 --- a/Dockerfile-sqlite +++ /dev/null @@ -1,15 +0,0 @@ -# syntax=docker/dockerfile:1 -FROM rust:1.73-alpine AS builder -WORKDIR /build -COPY . . -RUN apk add --no-cache build-base && \ - cargo build --target=x86_64-unknown-linux-musl --release --no-default-features -F sqlite-bundle - -FROM scratch - -WORKDIR /config - -COPY --from=builder /build/target/x86_64-unknown-linux-musl/release/rtabby-web-api / -COPY --from=builder /build/users.exemple.yml . - -CMD ["/rtabby-web-api"] diff --git a/README.md b/README.md index b3321b4..e55b4f8 100644 --- a/README.md +++ b/README.md @@ -25,47 +25,43 @@ Run your own instance with docker compose. ### Prerequisites -* Linux (AMD64/x86_64) with docker engine. +* Linux (AMD64/x86_64/Arm64) with docker engine. + * Arm64 -> 0.4.0 and later [#14](https://github.com/Clem-Fern/rtabby-web-api/issues/14) ### Installation -* Using rtabby-web-api image from Github Docker Repository **(recommended)** +Create a directory which will contain your `docker-compose.yml` and `config` volume. +```sh +mkdir -p rtabby-web-api/config +cd rtabby-web-api +``` + +rtabby-web-api store tabby's configuration in a database. You can choose between mysql or sqlite database. Third-party login will also be stored in database. + +* Mysql ```sh - mkdir -p rtabby-web-api/config - cd rtabby-web-api + # pwd /../../rtabby-web-api wget https://raw.githubusercontent.com/Clem-Fern/rtabby-web-api/master/docker-compose.yml ``` -* From source +* Sqlite ```sh - git clone https://github.com/Clem-Fern/rtabby-web-api - cd rtabby-web-api - ``` - - Edit `docker-compose.yml` to use local context build instead of the published image - ```yaml - # Uncomment build line - build: . - # Comment image - #image: ghcr.io/clem-fern/rtabby-web-api + # pwd /../../rtabby-web-api + wget https://raw.githubusercontent.com/Clem-Fern/rtabby-web-api/master/docker-compose-sqlite.yml -O docker-compose.yml ``` - ### Configuration 1. Create `config` directory. It will be used to store your config and certificate(not mandatory) ```sh - # pwd - # ./rtabby-web-api + # pwd /../../rtabby-web-api mkdir config - - # Only from source installation and optional - # users.yml file will be created at first start - # cp users.exemple.yml config/users.yml + touch config/users.yml + # otherwise users.yml file will be created at first start ``` -2. Add some user into `users.yml`. +2. Tabby uses a token to authenticate user. You have to create your own user with his token in `users.yml` to be able to use the sync service. ```yaml users: @@ -76,102 +72,31 @@ Run your own instance with docker compose. ``` Token must be a valid and unique uuid v4. You can create one [here](https://www.uuidgenerator.net/version4). -3. (Optional) SSL/TLS + rTabby supports OAuth2 providers like Github, Gitlab, Google or Microsoft. You can enable them by adding OAuth client and secret through env var in your `docker-compose.yml`. - Place your key and certificate into `config` directory. Then add the following lines in `docker-compose.yml` : - ```yaml - ports: - - "8080:8080" - environment: - - DATABASE_URL=mysql://tabby:tabby@db/tabby - - SSL_CERTIFICATE=cert.pem - - SSL_CERTIFICATE_KEY=cert.key - volumes: - - ./config:/config - ``` + ```yml + environment: + - DATABASE_URL=mysql://tabby:tabby@db/tabby + #- GITHUB_APP_CLIENT_ID= + #- GITHUB_APP_CLIENT_SECRET= + #- GITLAB_APP_CLIENT_ID= + #- GITLAB_APP_CLIENT_SECRET= + #- GOOGLE_APP_CLIENT_ID= + #- GOOGLE_APP_CLIENT_SECRET= + #- MICROSOFT_APP_CLIENT_ID= + #- MICROSOFT_APP_CLIENT_SECRET= + ``` -4. Miscellaneous - - rtabby-web-api get his configurations from env vars. Available tweaks : - - | ENV VAR | DESCRIPTION | EXAMPLE | DEFAULT | - |---------|-------------|---------|---------| - |DATABASE_URL|Url to database|sqlite:///config/db.sqlite|-| - |CONFIG_FILE|Url to configuration file (Optional)|my_config.yml|users.yml| - |BIND_ADDR|Address listening on (Optional)|0.0.0.0|0.0.0.0| - |BIND_PORT|Port listening on (Optional)|8989|8080| - |SSL_CERTIFICATE|Server certificate (Optional)|cert.pem|None| - |SSL_CERTIFICATE_KEY|Server certificate private key(Optional)|private.key|None| - |CLEANUP_USERS|Delete configurations own by unknown user (Be careful)(Optional)|true|false| - - -## Getting Started (Sqlite) - -Run your own instance with docker compose using only one container with Sqlite. - -### Prerequisites - -* Linux (AMD64/x86_64) with docker engine. - -### Installation - -* Using rtabby-web-api image from Github Docker Repository **(recommended)** - ```sh - mkdir -p rtabby-web-api/config - cd rtabby-web-api - wget https://raw.githubusercontent.com/Clem-Fern/rtabby-web-api/master/docker-compose-sqlite.yml - ``` - -* From source - ```sh - git clone https://github.com/Clem-Fern/rtabby-web-api - cd rtabby-web-api - ``` - - Edit `docker-compose-sqlite.yml` to use local context build instead of the published image - ```yaml - # Uncomment build line - build: - context: . - dockerfile: Dockerfile-sqlite - # Comment image - #image: ghcr.io/clem-fern/rtabby-web-api:sqlite - ``` - - -### Configuration - -1. Create `config` directory. It will be used to store your config and certificate(not mandatory) - - ```sh - # pwd - # ./rtabby-web-api - mkdir config - - # Only from source installation and optional - # users.yml file will be created at first start - # cp users.exemple.yml config/users.yml - ``` - -2. Add some user into `users.yml`. - - ```yaml - users: - #... - - name: 'You' - token: 'token' - #... - ``` - Token must be a valid and unique uuid v4. You can create one [here](https://www.uuidgenerator.net/version4). + Browse to `http:///login` to authenticate and create your user and token. 3. (Optional) SSL/TLS - Place your key and certificate into `config` directory. Then add the following lines in `docker-compose-sqlite.yml` : + Place your key and certificate into `config` directory. Then add the following lines in `docker-compose.yml` : ```yaml ports: - "8080:8080" environment: - - DATABASE_URL=sqlite:///config/db.sqlite + - DATABASE_URL=mysql://tabby:tabby@db/tabby - SSL_CERTIFICATE=cert.pem - SSL_CERTIFICATE_KEY=cert.key volumes: @@ -194,8 +119,6 @@ Run your own instance with docker compose using only one container with Sqlite. ## Usage -* Go to [Usage (Sqlite)](https://github.com/Clem-Fern/rtabby-web-api#usage-sqlite) to run rtabby-web-api using only one container with Sqlite. - * To deploy ```sh docker compose up -d @@ -206,18 +129,6 @@ Run your own instance with docker compose using only one container with Sqlite. docker compose down ``` -## Usage (Sqlite) - -* To deploy - ```sh - docker compose -f docker-compose-sqlite.yml up -d - ``` - -* To shut down your deployment: - ```sh - docker compose -f docker-compose-sqlite.yml down - ``` - ## Contributing Build dependencies: diff --git a/build.rs b/build.rs index 2c216f0..21a6b0f 100644 --- a/build.rs +++ b/build.rs @@ -1,9 +1,9 @@ fn main() { - #[cfg(feature = "mysqlclient-static")] + #[cfg(feature = "mysql-bundle")] mysqlclient_static(); } -#[cfg(feature = "mysqlclient-static")] +#[cfg(feature = "mysql-bundle")] fn mysqlclient_static() { println!("cargo:rustc-link-search=native=lib"); println!("cargo:rustc-link-lib=static=mysqlclient"); diff --git a/docker-compose-sqlite.yml b/docker-compose-sqlite.yml index 043e8cc..c7fcfbd 100644 --- a/docker-compose-sqlite.yml +++ b/docker-compose-sqlite.yml @@ -1,10 +1,19 @@ services: rtabby: container_name: rtabby-web-api + + image: ghcr.io/clem-fern/rtabby-web-api:sqlite + # Minimal image without third party login + #image: ghcr.io/clem-fern/rtabby-web-api:sqlite-minimal + + # Build image from local rtabby repository #build: # context: . - # dockerfile: Dockerfile-sqlite - image: ghcr.io/clem-fern/rtabby-web-api:sqlite + # args: + # - FEATURE_FLAGS=-F|sqlite-bundle|-F|all-login + # - GIT_COMMIT=${GIT_COMMIT} + # Optional: Minimal image without third party login + # - FEATURE_FLAGS=-F|sqlite-bundle # If running as root, setup your user/volume owner UID and GID #user: "1000:1000" @@ -18,6 +27,14 @@ services: - "8080:8080" environment: - DATABASE_URL=sqlite:///config/db.sqlite + #- GITHUB_APP_CLIENT_ID= + #- GITHUB_APP_CLIENT_SECRET= + #- GITLAB_APP_CLIENT_ID= + #- GITLAB_APP_CLIENT_SECRET= + #- GOOGLE_APP_CLIENT_ID= + #- GOOGLE_APP_CLIENT_SECRET= + #- MICROSOFT_APP_CLIENT_ID= + #- MICROSOFT_APP_CLIENT_SECRET= volumes: - ./config:/config networks: diff --git a/docker-compose.yml b/docker-compose.yml index 41f2caf..3120b82 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,8 +1,18 @@ services: rtabby: container_name: rtabby-web-api - #build: . - image: ghcr.io/clem-fern/rtabby-web-api + + image: ghcr.io/clem-fern/rtabby-web-api:latest + # Minimal image without third party login + #image: ghcr.io/clem-fern/rtabby-web-api:latest-minimal + + # Build image from local rtabby repository + #build: + # context: . + # args: + # - GIT_COMMIT=${GIT_COMMIT} + # Optional: Minimal image without third party login + # - FEATURE_FLAGS=-F|mysql-bundle # If running as root, setup your user/volume owner UID and GID #user: "1000:1000" @@ -16,6 +26,15 @@ services: - "8080:8080" environment: - DATABASE_URL=mysql://tabby:tabby@db/tabby + #- GITHUB_APP_CLIENT_ID= + #- GITHUB_APP_CLIENT_SECRET= + #- GITLAB_APP_CLIENT_ID= + #- GITLAB_APP_CLIENT_SECRET= + #- GOOGLE_APP_CLIENT_ID= + #- GOOGLE_APP_CLIENT_SECRET= + #- MICROSOFT_APP_CLIENT_ID= + #- MICROSOFT_APP_CLIENT_SECRET= + volumes: - ./config:/config networks: diff --git a/migrations/2023-12-21-031738_create_users/down.sql b/migrations/2023-12-21-031738_create_users/down.sql new file mode 100644 index 0000000..0172a87 --- /dev/null +++ b/migrations/2023-12-21-031738_create_users/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +DROP TABLE users; \ No newline at end of file diff --git a/migrations/2023-12-21-031738_create_users/up.sql b/migrations/2023-12-21-031738_create_users/up.sql new file mode 100644 index 0000000..7d342ca --- /dev/null +++ b/migrations/2023-12-21-031738_create_users/up.sql @@ -0,0 +1,10 @@ +-- Your SQL goes here +CREATE TABLE users ( + id INTEGER auto_increment NOT NULL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + user_id VARCHAR(255) NOT NULL, + platform VARCHAR(255) NOT NULL, + token VARCHAR(255) NOT NULL UNIQUE, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + modified_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP +) AUTO_INCREMENT = 1000; \ No newline at end of file diff --git a/migrations_sqlite/2023-12-09-170536_create_users/down.sql b/migrations_sqlite/2023-12-09-170536_create_users/down.sql new file mode 100644 index 0000000..0172a87 --- /dev/null +++ b/migrations_sqlite/2023-12-09-170536_create_users/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +DROP TABLE users; \ No newline at end of file diff --git a/migrations_sqlite/2023-12-09-170536_create_users/up.sql b/migrations_sqlite/2023-12-09-170536_create_users/up.sql new file mode 100644 index 0000000..21681a4 --- /dev/null +++ b/migrations_sqlite/2023-12-09-170536_create_users/up.sql @@ -0,0 +1,10 @@ +-- Your SQL goes here +CREATE TABLE users ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , + name VARCHAR(255) NOT NULL, + user_id VARCHAR(255) NOT NULL, + platform VARCHAR(255) NOT NULL, + token VARCHAR(255) NOT NULL UNIQUE, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + modified_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file diff --git a/src/app_config.rs b/src/app_config.rs index a804666..0588ed6 100644 --- a/src/app_config.rs +++ b/src/app_config.rs @@ -1,6 +1,6 @@ use log::warn; use serde::Deserialize; -use crate::models::user::{User, UserWithoutToken}; +use crate::models::user::{LocalUser, UserWithoutToken}; use crate::error::ConfigError; use std::collections::HashMap; use std::fs::File; @@ -9,7 +9,7 @@ use std::io::Write; #[derive(Clone, Debug, Deserialize)] pub struct AppConfig { - pub users: Vec, + pub users: Vec, } pub fn load_file(file: &str) -> Result { diff --git a/src/auth.rs b/src/auth.rs index 92f4aab..4f0b5bb 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -1,16 +1,58 @@ -use actix_web::{web, dev::ServiceRequest, Error, error::ErrorUnauthorized}; -use actix_web_httpauth::extractors::{bearer::{BearerAuth}}; -use log::warn; use crate::app_config::MappedAppConfig; +use actix_web::{dev::ServiceRequest, error::ErrorUnauthorized, web, Error}; +use actix_web_httpauth::extractors::bearer::BearerAuth; +use log::warn; -pub async fn bearer_auth_validator(req: ServiceRequest, credentials: BearerAuth) -> Result { +pub async fn bearer_auth_validator( + req: ServiceRequest, + credentials: BearerAuth, +) -> Result { let default = web::Data::new(MappedAppConfig::default()); - let users: &Vec = &req.app_data::>().unwrap_or(&default).users.clone().into_keys().collect(); - let token = credentials.token(); - if users.contains(&String::from(token)) { - Ok(req) - } else { - warn!("Authentification failed for {:?}", req.connection_info().peer_addr()); - Err((ErrorUnauthorized("Invalide authentication token !"), req)) - } -} \ No newline at end of file + let users: &Vec = &req + .app_data::>() + .unwrap_or(&default) + .users + .clone() + .into_keys() + .collect(); + + let token = String::from(credentials.token()); + + if users.contains(&token) { + return Ok(req); + } + + #[cfg(feature = "third-party-login")] + { + use crate::login::models::User; + use crate::storage::DbPool; + use actix_web::error::ErrorInternalServerError; + + let pool = req.app_data::>().unwrap().clone(); + let token = token.clone(); + + let result = web::block(move || { + let mut conn = pool.get()?; + User::get_user_by_token(&mut conn, &token) + }) + .await; + + match result { + Ok(result) => match result { + Ok(result) => { + if let Some(_user) = result { + return Ok(req); + } + } + Err(err) => return Err((ErrorInternalServerError(err), req)), + }, + Err(err) => return Err((ErrorInternalServerError(err), req)), + } + } + + warn!( + "Authentification failed for {:?}", + req.connection_info().peer_addr() + ); + Err((ErrorUnauthorized("Invalide authentication token !"), req)) +} diff --git a/src/env.rs b/src/env.rs index 86af415..f7c077b 100644 --- a/src/env.rs +++ b/src/env.rs @@ -19,5 +19,3 @@ pub fn init() { } pub use std::env::*; - - diff --git a/src/login/env.rs b/src/login/env.rs new file mode 100644 index 0000000..0d2900d --- /dev/null +++ b/src/login/env.rs @@ -0,0 +1,8 @@ +pub const ENV_STATIC_FILES_BASE_DIR: &str = "STATIC_FILES_BASE_DIR"; +pub const ENV_USE_HTTPS: &str = "USE_HTTPS"; + +use crate::env as app_env; + +pub fn static_files_base_dir() -> String { + app_env::var(ENV_STATIC_FILES_BASE_DIR).unwrap_or("./web/".to_string()) +} \ No newline at end of file diff --git a/src/login/error.rs b/src/login/error.rs new file mode 100644 index 0000000..1d6dc91 --- /dev/null +++ b/src/login/error.rs @@ -0,0 +1,36 @@ +use std::error; +use std::fmt; + +#[derive(Debug)] +pub enum ProviderError { + NotFound(String), +} + +impl std::error::Error for ProviderError {} + +impl fmt::Display for ProviderError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Self::NotFound(ref provider) => { + write!(f, "Specified provider does not exist: {provider}") + } + } + } +} + +#[derive(Debug)] +pub enum OauthError { + UserInfo(reqwest::Error), + AccessToken(reqwest::Error), +} + +impl error::Error for OauthError {} + +impl fmt::Display for OauthError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Self::UserInfo(ref err) => write!(f, "Unable to retreive OAuth user info: {err}"), + Self::AccessToken(ref err) => write!(f, "Unable to retreive OAuth user access token: {err}"), + } + } +} diff --git a/src/login/mod.rs b/src/login/mod.rs new file mode 100644 index 0000000..3953783 --- /dev/null +++ b/src/login/mod.rs @@ -0,0 +1,73 @@ +mod env; +pub mod models; +pub mod providers; +pub mod routes; +pub mod services; +pub mod error; +mod tools; + +use crate::env as app_env; + +#[cfg(feature = "github-login")] +use providers::github; +#[cfg(feature = "gitlab-login")] +use providers::gitlab; +#[cfg(feature = "google-login")] +use providers::google; +#[cfg(feature = "microsoft-login")] +use providers::microsoft; + +use self::providers::OauthInfo; + +#[derive(Clone, Debug)] +pub struct ProvidersConfig { + pub available_providers: Vec, +} + +pub fn get_provider_config() -> ProvidersConfig { + let mut available_providers: Vec = vec![]; + + #[cfg(feature = "github-login")] + if app_env::var(github::env::ENV_GITHUB_APP_CLIENT_ID).is_ok() + && app_env::var(github::env::ENV_GITHUB_APP_CLIENT_SECRET).is_ok() + { + available_providers.push(providers::Provider::Github(OauthInfo { + client_id: app_env::var(github::env::ENV_GITHUB_APP_CLIENT_ID).unwrap(), + client_secret: app_env::var(github::env::ENV_GITHUB_APP_CLIENT_SECRET).unwrap(), + })); + } + + #[cfg(feature = "gitlab-login")] + if app_env::var(gitlab::env::ENV_GITLAB_APP_CLIENT_ID).is_ok() + && app_env::var(gitlab::env::ENV_GITLAB_APP_CLIENT_SECRET).is_ok() + { + available_providers.push(providers::Provider::Gitlab(OauthInfo { + client_id: app_env::var(gitlab::env::ENV_GITLAB_APP_CLIENT_ID).unwrap(), + client_secret: app_env::var(gitlab::env::ENV_GITLAB_APP_CLIENT_SECRET).unwrap(), + })); + } + + #[cfg(feature = "google-login")] + if app_env::var(google::env::ENV_GOOGLE_APP_CLIENT_ID).is_ok() + && app_env::var(google::env::ENV_GOOGLE_APP_CLIENT_SECRET).is_ok() + { + available_providers.push(providers::Provider::Google(OauthInfo { + client_id: app_env::var(google::env::ENV_GOOGLE_APP_CLIENT_ID).unwrap(), + client_secret: app_env::var(google::env::ENV_GOOGLE_APP_CLIENT_SECRET).unwrap(), + })); + } + + #[cfg(feature = "microsoft-login")] + if app_env::var(microsoft::env::ENV_MICROSOFT_APP_CLIENT_ID).is_ok() + && app_env::var(microsoft::env::ENV_MICROSOFT_APP_CLIENT_SECRET).is_ok() + { + available_providers.push(providers::Provider::Microsoft(OauthInfo { + client_id: app_env::var(microsoft::env::ENV_MICROSOFT_APP_CLIENT_ID).unwrap(), + client_secret: app_env::var(microsoft::env::ENV_MICROSOFT_APP_CLIENT_SECRET).unwrap(), + })); + } + + ProvidersConfig { + available_providers + } +} diff --git a/src/login/models.rs b/src/login/models.rs new file mode 100644 index 0000000..778c180 --- /dev/null +++ b/src/login/models.rs @@ -0,0 +1,96 @@ +use serde::{Deserialize, Serialize}; +use crate::models::DbError; +use crate::models::user::uuid_validator; + +use diesel::{ + BoolExpressionMethods, ExpressionMethods, Identifiable, Insertable, + OptionalExtension, QueryDsl, Queryable, + RunQueryDsl, +}; +use crate::storage::DbConnection; +use chrono::NaiveDateTime; + +use crate::schema::users; +use crate::schema::users::dsl::users as all_users; + +#[derive(Clone, Debug, Serialize, Deserialize, Identifiable, Queryable)] +#[diesel(table_name = users)] +#[diesel(primary_key(id))] +pub struct User { + pub id: i32, + pub name: String, + pub user_id: String, + pub platform: String, + #[serde(deserialize_with = "uuid_validator")] + pub token: String, + + pub created_at: NaiveDateTime, + pub modified_at: NaiveDateTime, +} + +#[derive(Insertable)] +#[diesel(table_name = users)] +pub struct NewUser { + pub name: String, + pub user_id: String, + pub platform: String, + pub token: String, +} + +impl User { + + pub fn insert_new_user_config( + conn: &mut DbConnection, + new_user: NewUser, + ) -> Result<(), DbError> { + + match conn { + #[cfg(feature = "mysql")] + DbConnection::Mysql(ref mut conn) => { + diesel::insert_into(all_users) + .values(&new_user) + .execute(conn)?; + }, + #[cfg(feature = "sqlite")] + DbConnection::Sqlite(ref mut conn) => { + diesel::insert_into(all_users) + .values(&new_user) + .execute(conn)?; + } + } + + Ok(()) + } + + + pub fn get_user_by_token( + conn: &mut DbConnection, + in_token: &str, + ) -> Result, DbError> { + use crate::schema::users::dsl::*; + + Ok(all_users + .filter(token.eq(in_token)) + .first::(conn) + .optional()?) + } + + pub fn get_user( + conn: &mut DbConnection, + in_user_id: &str, + in_platform: &str, + ) -> Result, DbError> { + use crate::schema::users::dsl::*; + + Ok(all_users + .filter(platform.eq(in_platform).and(user_id.eq(in_user_id))) + .first::(conn) + .optional()?) + } + + pub fn delete_user(conn: &mut DbConnection, user: User) -> Result<(), DbError> { + diesel::delete(&user).execute(conn)?; + + Ok(()) + } +} diff --git a/src/login/providers/github.rs b/src/login/providers/github.rs new file mode 100644 index 0000000..c222662 --- /dev/null +++ b/src/login/providers/github.rs @@ -0,0 +1,18 @@ +use crate::login::error::OauthError; +use crate::login::providers::{get_user_info, get_access_token, OauthInfo, OauthUserInfo}; + +pub mod env { + pub const ENV_GITHUB_APP_CLIENT_ID: &str = "GITHUB_APP_CLIENT_ID"; + pub const ENV_GITHUB_APP_CLIENT_SECRET: &str = "GITHUB_APP_CLIENT_SECRET"; +} + +pub const GITHUB_OAUTH_AUTHORIZE_URL: &str = "https://github.com/login/oauth/authorize"; +pub const GITHUB_OAUTH_ACCESS_TOKEN_URL: &str = "https://github.com/login/oauth/access_token"; +pub const GITHUB_OAUTH_USER_INFO_URL: &str = "https://api.github.com/user"; + +pub type GithubOauthUserInfo = OauthUserInfo; + +pub async fn user_info(oauth: &OauthInfo, token: String) -> Result { + let token = get_access_token(GITHUB_OAUTH_ACCESS_TOKEN_URL, token, oauth.client_id.clone(), oauth.client_secret.clone(), "authorization_code", None).await?; + get_user_info(GITHUB_OAUTH_USER_INFO_URL, token).await.map_err(OauthError::UserInfo)?.json::().await.map_err(OauthError::UserInfo) +} \ No newline at end of file diff --git a/src/login/providers/gitlab.rs b/src/login/providers/gitlab.rs new file mode 100644 index 0000000..b443dfe --- /dev/null +++ b/src/login/providers/gitlab.rs @@ -0,0 +1,20 @@ +use crate::login::error::OauthError; +use crate::login::providers::{get_user_info, get_access_token, OauthInfo, OauthUserInfo}; +use crate::login::tools; + +pub mod env { + pub const ENV_GITLAB_APP_CLIENT_ID: &str = "GITLAB_APP_CLIENT_ID"; + pub const ENV_GITLAB_APP_CLIENT_SECRET: &str = "GITLAB_APP_CLIENT_SECRET"; +} + +pub const GITLAB_OAUTH_AUTHORIZE_URL: &str = "https://gitlab.com/oauth/authorize"; +pub const GITLAB_OAUTH_ACCESS_TOKEN_URL: &str = "https://gitlab.com/oauth/token"; +pub const GITLAB_OAUTH_USER_INFO_URL: &str = "https://gitlab.com/api/v4/user"; + +pub type GitlabOauthUserInfo = OauthUserInfo; + +pub async fn user_info(oauth: &OauthInfo, host: String, token: String) -> Result { + let redirect_uri = format!("{}://{}/login/gitlab/callback", tools::scheme(), host); + let token = get_access_token(GITLAB_OAUTH_ACCESS_TOKEN_URL, token, oauth.client_id.clone(), oauth.client_secret.clone(), "authorization_code", Some(redirect_uri)).await?; + get_user_info(GITLAB_OAUTH_USER_INFO_URL, token).await.map_err(OauthError::UserInfo)?.json::().await.map_err(OauthError::UserInfo) +} diff --git a/src/login/providers/google.rs b/src/login/providers/google.rs new file mode 100644 index 0000000..809a7fe --- /dev/null +++ b/src/login/providers/google.rs @@ -0,0 +1,20 @@ +use crate::login::error::OauthError; +use crate::login::providers::{get_user_info, get_access_token, OauthInfo, OauthUserInfo}; +use crate::login::tools; + +pub mod env { + pub const ENV_GOOGLE_APP_CLIENT_ID: &str = "GOOGLE_APP_CLIENT_ID"; + pub const ENV_GOOGLE_APP_CLIENT_SECRET: &str = "GOOGLE_APP_CLIENT_SECRET"; +} + +pub const GOOGLE_OAUTH_AUTHORIZE_URL: &str = "https://accounts.google.com/o/oauth2/v2/auth"; +pub const GOOGLE_OAUTH_ACCESS_TOKEN_URL: &str = "https://accounts.google.com/o/oauth2/token"; +pub const GOOGLE_OAUTH_USER_INFO_URL: &str = "https://www.googleapis.com/oauth2/v1/userinfo"; + +pub type GoogleOauthUserInfo = OauthUserInfo; + +pub async fn user_info(oauth: &OauthInfo, host: String, code: String) -> Result { + let redirect_uri = format!("{}://{}/login/google/callback", tools::scheme(), host); + let token = get_access_token(GOOGLE_OAUTH_ACCESS_TOKEN_URL, code, oauth.client_id.clone(), oauth.client_secret.clone(), "authorization_code", Some(redirect_uri)).await?; + get_user_info(GOOGLE_OAUTH_USER_INFO_URL, token).await.map_err(OauthError::UserInfo)?.json::().await.map_err(OauthError::UserInfo) +} diff --git a/src/login/providers/microsoft.rs b/src/login/providers/microsoft.rs new file mode 100644 index 0000000..f2f708f --- /dev/null +++ b/src/login/providers/microsoft.rs @@ -0,0 +1,36 @@ +use crate::login::error::OauthError; +use crate::login::providers::{get_user_info, get_access_token, OauthInfo, OauthUserInfo}; +use crate::login::tools; +use serde::Deserialize; + +pub mod env { + pub const ENV_MICROSOFT_APP_CLIENT_ID: &str = "MICROSOFT_APP_CLIENT_ID"; + pub const ENV_MICROSOFT_APP_CLIENT_SECRET: &str = "MICROSOFT_APP_CLIENT_SECRET"; +} + +pub const MICROSOFT_OAUTH_AUTHORIZE_URL: &str = "https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize"; +pub const MICROSOFT_OAUTH_ACCESS_TOKEN_URL: &str = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token"; +pub const MICROSOFT_OAUTH_USER_INFO_URL: &str = "https://graph.microsoft.com/v1.0/me"; + +#[derive(Debug, Deserialize)] +pub struct MicrosoftOauthUserInfo { + id: String, + #[serde(rename = "displayName")] + display_name: String, +} + + +impl From for OauthUserInfo { + fn from(val: MicrosoftOauthUserInfo) -> Self { + OauthUserInfo { + id: val.id, + name: val.display_name, + } + } +} + +pub async fn user_info(oauth: &OauthInfo, host: String, code: String) -> Result { + let redirect_uri = format!("{}://{}/login/microsoft/callback", tools::scheme(), host); + let token = get_access_token(MICROSOFT_OAUTH_ACCESS_TOKEN_URL, code, oauth.client_id.clone(), oauth.client_secret.clone(), "authorization_code", Some(redirect_uri)).await?; + get_user_info(MICROSOFT_OAUTH_USER_INFO_URL, token).await.map_err(OauthError::UserInfo)?.json::().await.map_err(OauthError::UserInfo) +} diff --git a/src/login/providers/mod.rs b/src/login/providers/mod.rs new file mode 100644 index 0000000..d391e62 --- /dev/null +++ b/src/login/providers/mod.rs @@ -0,0 +1,219 @@ +#[cfg(feature = "github-login")] +pub mod github; +#[cfg(feature = "gitlab-login")] +pub mod gitlab; +#[cfg(feature = "google-login")] +pub mod google; +#[cfg(feature = "microsoft-login")] +pub mod microsoft; + +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::fmt; +use super::error::OauthError; +use super::tools; + +#[derive(Clone, Debug)] +pub struct OauthInfo { + pub client_id: String, + pub client_secret: String, +} + +#[derive(Debug, Deserialize)] +pub struct OauthUserInfo { + id: I, + name: N, +} + +// Gitlab / Github +impl From> for OauthUserInfo { + fn from(val: OauthUserInfo) -> Self { + OauthUserInfo { + id: format!("{}", val.id), + name: val.name, + } + } +} + +#[derive(Clone, Debug, Serialize)] +pub struct Platform { + pub name: String, + pub url: String, +} + +#[derive(Clone, Debug)] +pub enum Provider { + #[cfg(feature = "github-login")] + Github(OauthInfo), + #[cfg(feature = "gitlab-login")] + Gitlab(OauthInfo), + #[cfg(feature = "google-login")] + Google(OauthInfo), + #[cfg(feature = "microsoft-login")] + Microsoft(OauthInfo), +} + +impl Provider { + pub fn name(&self) -> String { + self.to_string().to_lowercase() + } + + pub fn get_oauth_info(&self) -> OauthInfo { + match self { + #[cfg(feature = "github-login")] + Self::Github(oauth) => oauth.clone(), + #[cfg(feature = "gitlab-login")] + Self::Gitlab(oauth) => oauth.clone(), + #[cfg(feature = "google-login")] + Self::Google(oauth) => oauth.clone(), + #[cfg(feature = "microsoft-login")] + Self::Microsoft(oauth) => oauth.clone(), + } + } + + fn get_login_url_params(&self, host: String, state: String) -> Vec<(&str, String)> { + let mut params = vec![ + ("client_id", self.get_oauth_info().client_id), + ("state", state), + ("redirect_uri", format!("{}://{}/login/{}/callback", tools::scheme(), host, self.name())), + ]; + + #[cfg(feature = "github-login")] + if !matches!(self, Self::Github(_)) { + params.push(("response_type", "code".to_string())); + } + + match self { + #[cfg(feature = "gitlab-login")] + Self::Gitlab(_) => { + params.push(("scope", "read_user".to_string())); + } + #[cfg(feature = "google-login")] + Self::Google(_) => { + params.push(("scope", "https://www.googleapis.com/auth/userinfo.profile".to_string())); + }, + #[cfg(feature = "microsoft-login")] + Self::Microsoft(_) => { + params.push(("scope", "https://graph.microsoft.com/User.Read".to_string())); + }, + #[cfg(feature = "github-login")] + _ => {}, + } + + params + } + + pub fn get_login_url(&self, host: String, state: String) -> String { + + let params = self.get_login_url_params(host, state); + + let oauth_url = match self { + #[cfg(feature = "github-login")] + Self::Github(_) => github::GITHUB_OAUTH_AUTHORIZE_URL, + #[cfg(feature = "gitlab-login")] + Self::Gitlab(_) => gitlab::GITLAB_OAUTH_AUTHORIZE_URL, + #[cfg(feature = "google-login")] + Self::Google(_) => google::GOOGLE_OAUTH_AUTHORIZE_URL, + #[cfg(feature = "microsoft-login")] + Self::Microsoft(_) => microsoft::MICROSOFT_OAUTH_AUTHORIZE_URL, + }; + + reqwest::Url::parse_with_params(oauth_url, params).unwrap().to_string() + } + + #[allow(unused_variables)] + pub async fn get_user_info(&self, host: String, token: String) -> Result { + let user_info: OauthUserInfo = match self { + #[cfg(feature = "github-login")] + Self::Github(oauth) => github::user_info(oauth, host).await?.into(), + #[cfg(feature = "gitlab-login")] + Self::Gitlab(oauth) => gitlab::user_info(oauth, host, token).await?.into(), + #[cfg(feature = "google-login")] + Self::Google(oauth) => google::user_info(oauth, host, token).await?, + #[cfg(feature = "microsoft-login")] + Self::Microsoft(oauth) => microsoft::user_info(oauth, host, token).await?.into(), + }; + + Ok(ThirdPartyUserInfo { + id: user_info.id, + name: user_info.name, + platform: self.name(), + }) + } + +} + +impl From for Platform { + fn from(provider: Provider) -> Platform { + Platform { + name: provider.to_string(), + url: format!("login/{}", provider.name()), + } + } +} + +impl fmt::Display for Provider { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + #[cfg(feature = "github-login")] + Self::Github(_) => write!(f, "Github"), + #[cfg(feature = "gitlab-login")] + Self::Gitlab(_) => write!(f, "Gitlab"), + #[cfg(feature = "google-login")] + Self::Google(_) => write!(f, "Google"), + #[cfg(feature = "microsoft-login")] + Self::Microsoft(_) => write!(f, "Microsoft"), + } + } +} + +#[derive(Debug, Deserialize)] +pub struct Body { + access_token: String, +} + +pub async fn get_user_info(url: &str, token: String) -> Result { + let client = reqwest::Client::new(); + + client + .get(url) + .header("Authorization", format!("Bearer {}", token)) + .header("User-Agent", "actix-web/3.3.2") + .send() + .await +} + +async fn get_access_token( + url: &str, + code: String, + client_id: String, + client_secret: String, + grant_type: &str, + redirect_uri: Option, +) -> Result { + let client = reqwest::Client::new(); + let mut map = HashMap::new(); + map.insert("code", code); + map.insert("client_id", client_id); + map.insert("client_secret", client_secret); + map.insert("grant_type", String::from(grant_type)); + if let Some(redirect_uri) = redirect_uri { + map.insert("redirect_uri", redirect_uri); + } + + let res = client + .post(url) + .form(&map) + .header("Accept", "application/json") + .send() + .await.map_err(OauthError::AccessToken)?; + + Ok(res.json::().await.map_err(OauthError::AccessToken)?.access_token) +} + +#[derive(Debug, Deserialize)] +pub struct ThirdPartyUserInfo { + pub id: String, + pub name: String, + pub platform: String, +} diff --git a/src/login/routes.rs b/src/login/routes.rs new file mode 100644 index 0000000..fe09455 --- /dev/null +++ b/src/login/routes.rs @@ -0,0 +1,206 @@ +use actix_web::http::header::ContentType; +use actix_web::{get, web, Error, HttpRequest, HttpResponse}; + +use serde::Deserialize; +use tera::Tera; + +use crate::login::error::ProviderError; +use crate::login::providers::Platform; +use crate::login::ProvidersConfig; +use crate::storage::DbPool; +use log::{error, info}; + +use crate::login::models::{NewUser, User}; + +use crate::env as app_; +use crate::login::env; + +use uuid::Uuid; + +#[derive(Debug, Deserialize)] +pub struct Params { + code: String, + state: String, +} + +#[get("")] +async fn home( + req: HttpRequest, + providers_config: web::Data, +) -> Result { + if let Some(token) = req.cookie("token") { + let mut context = tera::Context::new(); + context.insert("token", &token.value()); + let version = env!("CARGO_PKG_VERSION"); + if let Ok(hash) = app_::var("GIT_COMMIT") { + context.insert("version", &format!("{} ({})", version, hash)); + } else { + context.insert("version", &version); + } + let body = Tera::new(&(env::static_files_base_dir() + "templates/**/*")) + .map_err(actix_web::error::ErrorInternalServerError)? + .render("success.html", &context) + .map_err(actix_web::error::ErrorInternalServerError)?; + return Ok(HttpResponse::build(actix_web::http::StatusCode::OK) + .content_type(ContentType::html()) + .body(body)); + } + + let platforms: Vec = providers_config + .available_providers + .clone() + .into_iter() + .map(|p| p.into()) + .collect(); + + let mut context = tera::Context::new(); + context.insert("platforms", &platforms); + let version = env!("CARGO_PKG_VERSION"); + if let Ok(hash) = app_::var("GIT_COMMIT") { + context.insert("version", &format!("{} ({})", version, hash)); + } else { + context.insert("version", &version); + } + let body = Tera::new(&(env::static_files_base_dir() + "templates/**/*")) + .map_err(actix_web::error::ErrorInternalServerError)? + .render("login.html", &context) + .map_err(actix_web::error::ErrorInternalServerError)?; + + Ok(HttpResponse::Ok() + .content_type(ContentType::html()) + .body(body)) +} + +#[get("/{login}")] +async fn login( + provider_name: web::Path, + providers_config: web::Data, + req: HttpRequest, +) -> Result { + let provider_name = provider_name.into_inner(); + let provider = providers_config + .available_providers + .clone() + .into_iter() + .find(|p| p.name().eq(&provider_name)) + .ok_or(ProviderError::NotFound(provider_name)) + .map_err(actix_web::error::ErrorBadRequest)?; + + let host = req.connection_info().host().to_string(); + let state = Uuid::new_v4().to_string(); + + let login_url = provider.get_login_url(host, state.clone()); + + let mut response = HttpResponse::TemporaryRedirect() + .append_header(("Location", login_url)) + .finish(); + + let ret = response.add_cookie( + &actix_web::cookie::Cookie::build("state", &state) + .path("/") + .expires( + actix_web::cookie::time::OffsetDateTime::now_utc() + + actix_web::cookie::time::Duration::minutes(5), + ) + .finish(), + ); + + if let Err(err) = ret { + error!("add cookie failed: {}", err); + return Ok(HttpResponse::InternalServerError().finish()); + } + Ok(response) +} + +#[get("/{login}/callback")] +async fn login_callback( + provider_name: web::Path, + providers_config: web::Data, + info: web::Query, + pool: web::Data, + req: HttpRequest, +) -> Result { + let provider_name = provider_name.into_inner(); + let provider = providers_config + .available_providers + .clone() + .into_iter() + .find(|p| p.name().eq(&provider_name)) + .ok_or(ProviderError::NotFound(provider_name)) + .map_err(actix_web::error::ErrorBadRequest)?; + + if let Some(state) = req.cookie("state") { + if state.value() != info.state { + error!("state not match"); + let rediret = HttpResponse::Found() + .append_header(("Location", "/")) + .finish(); + return Ok(rediret); + } + } else { + error!("state not found"); + let rediret = HttpResponse::Found() + .append_header(("Location", "/login")) + .finish(); + return Ok(rediret); + } + + let host = req.connection_info().host().to_string(); + + let user_info = provider + .get_user_info(host, info.code.clone()) + .await + .map_err(actix_web::error::ErrorInternalServerError)?; + + info!("user id: {}", user_info.id); + let mut context = tera::Context::new(); + + let clone_pool = pool.clone(); + let mid = user_info.id.clone(); + let mplatform = user_info.platform.clone(); + let current_user = web::block(move || { + let mut conn = clone_pool.get()?; + User::get_user(&mut conn, &mid, &mplatform) + }) + .await + .map_err(actix_web::error::ErrorInternalServerError)?; + + let current_user_token: String; + if let Ok(Some(current_user)) = current_user { + current_user_token = current_user.token; + context.insert("token", ¤t_user_token); + } else { + let new_uuid = Uuid::new_v4().to_string(); + let new_user = NewUser { + name: user_info.name, + user_id: user_info.id, + platform: user_info.platform, + token: new_uuid.clone(), + }; + web::block(move || { + let mut conn = pool.get()?; + User::insert_new_user_config(&mut conn, new_user) + }) + .await? + .map_err(actix_web::error::ErrorInternalServerError)?; + + context.insert("token", &new_uuid); + current_user_token = new_uuid; + } + + // redirect to login success page with 302, and set cookie + let redirect = HttpResponse::Found() + .append_header(("Location", "/")) + .cookie( + actix_web::cookie::Cookie::build("token", ¤t_user_token) + .path("/") + .finish(), + ) + .finish(); + Ok(redirect) +} + +pub fn user_login_route_config(cfg: &mut web::ServiceConfig) { + cfg.service(login); + cfg.service(login_callback); +} diff --git a/src/login/services.rs b/src/login/services.rs new file mode 100644 index 0000000..3cae7d2 --- /dev/null +++ b/src/login/services.rs @@ -0,0 +1,21 @@ + +use actix_files as fs; +use actix_web::web; +use crate::login::env; +use crate::login::routes; + +use actix_session::{SessionMiddleware, storage::CookieSessionStore}; +use actix_web::cookie::Key; + +pub fn login_config(cfg: &mut web::ServiceConfig) { + cfg.service(web::scope("/login") + .wrap(SessionMiddleware::new(CookieSessionStore::default(), Key::generate())) + .service(routes::home) + .configure(routes::user_login_route_config) + ) + .configure(static_files_config); +} + +pub fn static_files_config(cfg: &mut web::ServiceConfig) { + cfg.service(fs::Files::new("/static", env::static_files_base_dir() + "static")); +} \ No newline at end of file diff --git a/src/login/tools.rs b/src/login/tools.rs new file mode 100644 index 0000000..a1b3029 --- /dev/null +++ b/src/login/tools.rs @@ -0,0 +1,13 @@ +use crate::login::env; +use crate::env as app_; + +pub fn scheme() -> String { + let scheme = if app_::var(env::ENV_USE_HTTPS).unwrap_or(String::from("0")) == "1" { + "https" + } + else { + "http" + }; + String::from(scheme) +} + diff --git a/src/main.rs b/src/main.rs index 3c35efe..92699a7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,6 +21,9 @@ extern crate serde_yaml; extern crate actix_web; use actix_web::{middleware, web, App, HttpServer}; +#[cfg(feature = "third-party-login")] +mod login; + extern crate actix_web_httpauth; use actix_web_httpauth::middleware::HttpAuthentication; @@ -28,6 +31,16 @@ mod tls; #[actix_web::main] async fn main() -> Result<(), Box> { + + // third-party-login should only be enable by one of the features below (github-login, gitlab-login, google-login, microsoft-login) + #[cfg(feature = "third-party-login")] + { + #[cfg(not(any(feature = "github-login", feature = "gitlab-login", feature = "google-login", feature = "microsoft-login")))] + { + compile_error!("You must enable at least one login provider feature to use the login feature."); + } + } + // INITIALIZE LOGGING env_logger::Builder::from_env(env_logger::Env::default().default_filter_or( if cfg!(debug_assertions) { @@ -74,18 +87,29 @@ async fn run_app() -> Result<(), Box> { storage.cleanup(&config)?; } + #[cfg(feature = "third-party-login")] + let providers_config: login::ProvidersConfig = login::get_provider_config(); - let pool = storage.pool()?; + #[cfg(feature = "third-party-login")] + info!("Third party login enabled: {} providers found.", providers_config.available_providers.len()); + let pool = storage.pool()?; let mut server = HttpServer::new(move || { - App::new() + let app = App::new() .app_data(web::Data::new(config.clone())) // App Config Data .app_data(web::Data::new(pool.clone())) // Database Pool Data .wrap(middleware::Logger::default().log_target(env!("CARGO_PKG_NAME").to_string())) - // AUTH - .wrap(HttpAuthentication::bearer(auth::bearer_auth_validator)) - // - .configure(api_v1_config) + .configure(api_v1_config); + + #[cfg(feature = "third-party-login")] + if !providers_config.available_providers.is_empty() { + return app.app_data(web::Data::new(providers_config.clone())) + .configure(login::services::login_config); + } + + #[allow(clippy::let_and_return)] + app + }); // socket var @@ -124,6 +148,8 @@ fn api_v1_config(cfg: &mut web::ServiceConfig) { web::scope("/1") .configure(routes::user::user_route_config) // USER ROUTE .configure(routes::config::config_route_config), - ), + ) + // AUTH + .wrap(HttpAuthentication::bearer(auth::bearer_auth_validator)) ); } diff --git a/src/models/config.rs b/src/models/config.rs index d334af9..a828445 100644 --- a/src/models/config.rs +++ b/src/models/config.rs @@ -159,6 +159,18 @@ pub struct ConfigWithoutUserAndContent { pub modified_at: NaiveDateTime, } +impl From for ConfigWithoutUserAndContent { + fn from(config: Config) -> Self { + ConfigWithoutUserAndContent { + id: config.id, + name: config.name, + + created_at: config.created_at, + modified_at: config.modified_at, + } + } +} + #[derive( Clone, Debug, Serialize, Deserialize, Identifiable, Queryable, Insertable, AsChangeset, )] diff --git a/src/models/user.rs b/src/models/user.rs index c470c41..2ca422c 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -2,7 +2,7 @@ use serde::{de, Deserialize, Serialize}; use uuid::Uuid; #[derive(Clone, Debug, Deserialize)] -pub struct User { +pub struct LocalUser { pub name: String, #[serde(deserialize_with = "uuid_validator")] pub token: String, @@ -13,15 +13,15 @@ pub struct UserWithoutToken { pub name: String, } -impl From for UserWithoutToken { - fn from(user: User) -> Self { +impl From for UserWithoutToken { + fn from(user: LocalUser) -> Self { UserWithoutToken { name: user.name, } } } -fn uuid_validator<'de, D>(d: D) -> Result +pub fn uuid_validator<'de, D>(d: D) -> Result where D: de::Deserializer<'de>, { diff --git a/src/routes/config.rs b/src/routes/config.rs index 3c71a64..69d422f 100644 --- a/src/routes/config.rs +++ b/src/routes/config.rs @@ -3,7 +3,7 @@ use actix_web_httpauth::extractors::bearer::BearerAuth; use crate::storage::DbPool; -use crate::models::config::{Config, NewConfig, UpdateConfig, ConfigWithoutUser}; +use crate::models::config::{Config, ConfigWithoutUser, ConfigWithoutUserAndContent, NewConfig, UpdateConfig}; #[get("/configs")] async fn show_configs(auth: BearerAuth, pool: web::Data) -> Result { @@ -51,13 +51,14 @@ async fn get_config( let token = String::from(auth.token()); let id = path.into_inner(); - match web::block(move || { + let result = web::block(move || { let mut conn = pool.get()?; Config::get_config_by_id_and_user(&mut conn, id, &token) }) .await? - .map_err(actix_web::error::ErrorInternalServerError)? - { + .map_err(actix_web::error::ErrorInternalServerError)?; + + match result { Some(config) => Ok(HttpResponse::Ok().json(Into::::into(config))), None => Ok(HttpResponse::Unauthorized().finish()), } @@ -88,15 +89,16 @@ async fn update_config( } let config = config.unwrap(); + let c = config.clone(); web::block(move || { // update config content let mut conn = pool.get()?; - Config::update_user_config_content(&mut conn, config, &updated_config.content) + Config::update_user_config_content(&mut conn, c, &updated_config.content) }) .await? .map_err(actix_web::error::ErrorInternalServerError)?; - Ok(HttpResponse::Ok().finish()) + Ok(HttpResponse::Ok().json(Into::::into(config.clone()))) } #[delete("/configs/{id}")] diff --git a/src/routes/user.rs b/src/routes/user.rs index fd2f536..2068160 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -3,12 +3,30 @@ use actix_web_httpauth::extractors::bearer::BearerAuth; use crate::app_config::MappedAppConfig; +#[cfg(feature = "third-party-login")] +use crate::login::models::User; +use crate::storage::DbPool; + +#[allow(unused_variables)] #[get("/user")] async fn get_user( auth: BearerAuth, app_config: web::Data, + pool: web::Data, ) -> Result { let token = String::from(auth.token()); + #[cfg(feature = "third-party-login")] + { + let clone_pool = pool.clone(); + let clone_token = token.clone(); + let current_user = web::block(move || { + let mut conn = clone_pool.get()?; + User::get_user_by_token(&mut conn, &clone_token) + }).await.map_err(actix_web::error::ErrorInternalServerError)?; + if let Ok(Some(current_user)) = current_user { + return Ok(HttpResponse::Ok().json(current_user)) + } + } match app_config.users.get(&token) { Some(user) => Ok(HttpResponse::Ok().json(user)), diff --git a/src/schema.rs b/src/schema.rs index 7c55b7d..d01a474 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -9,4 +9,21 @@ diesel::table! { created_at -> Timestamp, modified_at -> Timestamp, } -} \ No newline at end of file +} + +diesel::table! { + users (id) { + id -> Integer, + name -> Text, + user_id -> Text, + platform -> Text, + token -> Text, + created_at -> Timestamp, + modified_at -> Timestamp, + } +} + +diesel::allow_tables_to_appear_in_same_query!( + configs, + users, +); diff --git a/src/tls.rs b/src/tls.rs index 2560229..491c0c1 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -107,7 +107,7 @@ impl TLSConfigBuilder { ))); } - self.key = Some(keys.get(0).unwrap().to_owned()); + self.key = Some(keys.first().unwrap().to_owned()); Ok(self) } diff --git a/web/static/favicon.svg b/web/static/favicon.svg new file mode 100644 index 0000000..b3330af --- /dev/null +++ b/web/static/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/static/script.js b/web/static/script.js new file mode 100644 index 0000000..143dbee --- /dev/null +++ b/web/static/script.js @@ -0,0 +1,20 @@ +function copyToken() { + var tokenDisplay = document.getElementById('tokenDisplay'); + var tempInput = document.createElement('input'); + tempInput.value = tokenDisplay.innerText; + document.body.appendChild(tempInput); + tempInput.select(); + document.execCommand('copy'); + document.body.removeChild(tempInput); + + // Change button text to "Copied" + var copyBtn = document.querySelector('.copy-btn'); + copyBtn.innerText = 'Copied'; +} + +function logout() { + // Delete token from cookies (assuming you are using cookies for storing the token) + document.cookie = 'token=; Max-Age=0' + // Redirect to the home page or any desired URL + window.location.href = "/"; +} \ No newline at end of file diff --git a/web/static/styles.css b/web/static/styles.css new file mode 100644 index 0000000..73ba80f --- /dev/null +++ b/web/static/styles.css @@ -0,0 +1,74 @@ +body { + font-family: 'Arial', sans-serif; + background-color: #f4f4f4; + margin: 0; + padding: 0; + display: flex; + align-items: center; + justify-content: center; + height: 100vh; + } + + .container { + text-align: center; + max-width: 400px; + width: 100%; + padding: 20px; + background-color: #fff; + border-radius: 8px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + } + + h1 { + color: #333; + } + + .btn { + display: inline-block; + padding: 10px 20px; + font-size: 16px; + background-color: #4CAF50; + color: #fff; + text-decoration: none; + border-radius: 5px; + cursor: pointer; + margin-top: 20px; + } + + .success-container, + .error-container { + text-align: center; + } + + .token-display { + background-color: #dff0d8; + padding: 10px; + border-radius: 5px; + margin-bottom: 20px; + } + + .copy-btn { + background-color: #5bc0de; + margin-left: 10px; + width: 100px; + } + + .logout-btn { + margin-left: 10px; + width: 100px; + background-color: #e74c3c; /* 红色系,可以根据需要选择其他颜色 */ + color: #fff; /* 文本颜色,白色在深色背景上通常更易读 */ + } + + .logout-btn:hover { + background-color: #c0392b; /* 鼠标悬停时的背景颜色,可以选择稍深的色调 */ + } + + .error-container { + color: #a94442; + } + + .version { + margin-top: 20px; + color: #ccc; + } \ No newline at end of file diff --git a/web/templates/login.html b/web/templates/login.html new file mode 100644 index 0000000..ee35c29 --- /dev/null +++ b/web/templates/login.html @@ -0,0 +1,24 @@ + + + + + + Tabby - Login To Get Sync Token + + + + + +
+

Login to get sync token

+ {% for platform in platforms %} + Login with {{ platform.name }} +
+ {% endfor %} +
+
+
{{ version }}
+
+ + + diff --git a/web/templates/success.html b/web/templates/success.html new file mode 100644 index 0000000..551836a --- /dev/null +++ b/web/templates/success.html @@ -0,0 +1,26 @@ + + + + + + Tabby - Login To Get Sync Token + + + + + +
+

Your token is

+
{{ token }}
+ + + +
+
+
{{ version }}
+
+ + + + +