diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index 53321d3..928945e 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -8,90 +8,72 @@ on: - backend/** branches: - master - + - dev + jobs: build: runs-on: ubuntu-latest permissions: packages: write - + strategy: + matrix: + component: [frontend, backend] steps: - - name: Checkout code - uses: actions/checkout@v3 - - - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: 23 + - name: Checkout code + uses: actions/checkout@v3 - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 - - - name: Build with Gradle (backend) - run: | - cd backend - ./gradlew bootJar - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 - - name: Log in to GHCR - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Get Commit Hash - id: get_commit_hash - run: echo "hash=$(git rev-parse --short HEAD)" >> $GITHUB_ENV + - name: Log in to GHCR + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push frontend Docker image to GHCR - uses: docker/build-push-action@v3 - with: - context: ./frontend - file: ./frontend/Dockerfile - build-args: | - COMMIT_HASH=${{ env.hash }} - push: true - tags: | - ghcr.io/loudbooks/pastebook-frontend:latest - ghcr.io/loudbooks/pastebook-frontend:${{ env.hash }} + - name: Get Commit Hash + id: get_commit_hash + run: echo "hash=$(git rev-parse --short HEAD)" >> $GITHUB_ENV - - name: Build and push backend Docker image to GHCR - uses: docker/build-push-action@v3 - with: - context: ./backend - file: ./backend/Dockerfile - push: true - tags: | - ghcr.io/loudbooks/pastebook-backend:latest - ghcr.io/loudbooks/pastebook-backend:${{ env.hash }} + - name: Set Docker Tags + id: set_tags + run: | + if [ "${{ github.ref_name }}" == "master" ]; then + echo "tag=latest" >> $GITHUB_ENV + elif [ "${{ github.ref_name }}" == "dev" ]; then + echo "tag=dev" >> $GITHUB_ENV + else + echo "Unknown branch: ${{ github.ref_name }}" + exit 1 + fi - - name: Log in to Docker Hub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_TOKEN }} + - name: Build and push Docker image to GHCR + uses: docker/build-push-action@v3 + with: + context: ./${{ matrix.component }} + file: ./${{ matrix.component }}/Dockerfile + build-args: | + COMMIT_HASH=${{ env.hash }} + push: true + tags: | + ghcr.io/loudbooks/pastebook-${{ matrix.component }}:${{ env.tag }} + ghcr.io/loudbooks/pastebook-${{ matrix.component }}:${{ env.hash }} - - name: Build and push frontend Docker image to Docker Hub - uses: docker/build-push-action@v3 - with: - context: ./frontend - build-args: | - COMMIT_HASH=${{ env.hash }} - file: ./frontend/Dockerfile - push: true - tags: | - ${{ secrets.DOCKER_USERNAME }}/pastebook-frontend:latest - ${{ secrets.DOCKER_USERNAME }}/pastebook-frontend:${{ env.hash }} + - name: Log in to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_TOKEN }} - - name: Build and push backend Docker image to Docker Hub - uses: docker/build-push-action@v3 - with: - context: ./backend - file: ./backend/Dockerfile - push: true - tags: | - ${{ secrets.DOCKER_USERNAME }}/pastebook-backend:latest - ${{ secrets.DOCKER_USERNAME }}/pastebook-backend:${{ env.hash }} + - name: Build and push Docker image to Docker Hub + uses: docker/build-push-action@v3 + with: + context: ./${{ matrix.component }} + file: ./${{ matrix.component }}/Dockerfile + build-args: | + COMMIT_HASH=${{ env.hash }} + push: true + tags: | + ${{ secrets.DOCKER_USERNAME }}/pastebook-${{ matrix.component }}:${{ env.tag }} + ${{ secrets.DOCKER_USERNAME }}/pastebook-${{ matrix.component }}:${{ env.hash }} diff --git a/.gitignore b/.gitignore index b89a4cd..c3f7faf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .* !.gitignore docker-compose.yml -.idea \ No newline at end of file +.idea +/backend/target diff --git a/README.md b/README.md index dd6af11..e9e5504 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # PasteBook -An easy on the eyes, portable, lightning fast pastebin written in Svelte and Kotlin. +An easy on the eyes, portable, lightning fast pastebin written in Svelte and Rust. ### Prerequisites Docker. Both the frontend and backend are to be installed with Docker. You can learn more [here](https://www.docker.com). @@ -73,9 +73,11 @@ systemctl restart nginx Run the following commands in succession. ```bash docker compose stop +docker compose pull docker compose up -d ``` # Final Notes Wow. There was a lot that can go wrong there. I'm not an expert. If you need help, you can email me at contact@loudbook.dev or find me elsewhere. + 💜 diff --git a/backend/.gitignore b/backend/.gitignore deleted file mode 100644 index 44d2e86..0000000 --- a/backend/.gitignore +++ /dev/null @@ -1,40 +0,0 @@ -HELP.md -.gradle -build/ -!gradle/wrapper/gradle-wrapper.jar -!**/src/main/**/build/ -!**/src/test/**/build/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache -bin/ -!**/src/main/**/bin/ -!**/src/test/**/bin/ - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr -out/ -!**/src/main/**/out/ -!**/src/test/**/out/ - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ - -### VS Code ### -.vscode/ - -### Kotlin ### -.kotlin \ No newline at end of file diff --git a/backend/Cargo.lock b/backend/Cargo.lock new file mode 100644 index 0000000..bce6f24 --- /dev/null +++ b/backend/Cargo.lock @@ -0,0 +1,3357 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "actix-codec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" +dependencies = [ + "bitflags 2.6.0", + "bytes", + "futures-core", + "futures-sink", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "actix-cors" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9e772b3bcafe335042b5db010ab7c09013dad6eac4915c91d8d50902769f331" +dependencies = [ + "actix-utils", + "actix-web", + "derive_more", + "futures-util", + "log", + "once_cell", + "smallvec", +] + +[[package]] +name = "actix-http" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d48f96fc3003717aeb9856ca3d02a8c7de502667ad76eeacd830b48d2e91fac4" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "ahash", + "base64 0.22.1", + "bitflags 2.6.0", + "brotli", + "bytes", + "bytestring", + "derive_more", + "encoding_rs", + "flate2", + "futures-core", + "h2", + "http 0.2.12", + "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.90", +] + +[[package]] +name = "actix-router" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8" +dependencies = [ + "bytestring", + "cfg-if", + "http 0.2.12", + "regex", + "regex-lite", + "serde", + "tracing", +] + +[[package]] +name = "actix-rt" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" +dependencies = [ + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca2549781d8dd6d75c40cf6b6051260a2cc2f3c62343d761a969a0640646894" +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-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.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9180d76e5cc7ccbc4d60a506f2c727730b154010262df5b910eb17dbe4b8cb38" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "ahash", + "bytes", + "bytestring", + "cfg-if", + "cookie", + "derive_more", + "encoding_rs", + "futures-core", + "futures-util", + "impl-more", + "itoa", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "regex-lite", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2", + "time", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8" +dependencies = [ + "actix-router", + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +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 = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[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 = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + +[[package]] +name = "anyhow" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" + +[[package]] +name = "async-trait" +version = "0.1.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "aws-credential-types" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60e8f6b615cb5fc60a98132268508ad104310f0cfb25a1c22eee76efdf9154da" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "zeroize", +] + +[[package]] +name = "aws-runtime" +version = "1.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5ac934720fbb46206292d2c75b57e67acfc56fe7dfd34fb9a02334af08409ea" +dependencies = [ + "aws-credential-types", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.12", + "http-body 0.4.6", + "once_cell", + "percent-encoding", + "pin-project-lite", + "tracing", + "uuid", +] + +[[package]] +name = "aws-sdk-s3" +version = "1.65.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3ba2c5c0f2618937ce3d4a5ad574b86775576fa24006bcb3128c6e2cbf3c34e" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-checksums", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "bytes", + "fastrand", + "hex", + "hmac", + "http 0.2.12", + "http-body 0.4.6", + "lru", + "once_cell", + "percent-encoding", + "regex-lite", + "sha2", + "tracing", + "url", +] + +[[package]] +name = "aws-sigv4" +version = "1.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d3820e0c08d0737872ff3c7c1f21ebbb6693d832312d6152bf18ef50a5471c2" +dependencies = [ + "aws-credential-types", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "crypto-bigint 0.5.5", + "form_urlencoded", + "hex", + "hmac", + "http 0.2.12", + "http 1.2.0", + "once_cell", + "p256", + "percent-encoding", + "ring", + "sha2", + "subtle", + "time", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-async" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62220bc6e97f946ddd51b5f1361f78996e704677afc518a4ff66b7a72ea1378c" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "aws-smithy-checksums" +version = "0.60.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1a71073fca26775c8b5189175ea8863afb1c9ea2cceb02a5de5ad9dfbaa795" +dependencies = [ + "aws-smithy-http", + "aws-smithy-types", + "bytes", + "crc32c", + "crc32fast", + "hex", + "http 0.2.12", + "http-body 0.4.6", + "md-5", + "pin-project-lite", + "sha1", + "sha2", + "tracing", +] + +[[package]] +name = "aws-smithy-eventstream" +version = "0.60.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cef7d0a272725f87e51ba2bf89f8c21e4df61b9e49ae1ac367a6d69916ef7c90" +dependencies = [ + "aws-smithy-types", + "bytes", + "crc32fast", +] + +[[package]] +name = "aws-smithy-http" +version = "0.60.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8bc3e8fdc6b8d07d976e301c02fe553f72a39b7a9fea820e023268467d7ab6" +dependencies = [ + "aws-smithy-eventstream", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http-body 0.4.6", + "once_cell", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.61.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4e69cc50921eb913c6b662f8d909131bb3e6ad6cb6090d3a39b66fc5c52095" +dependencies = [ + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-runtime" +version = "1.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f20685047ca9d6f17b994a07f629c813f08b5bce65523e47124879e60103d45" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "fastrand", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "http-body 1.0.1", + "httparse", + "hyper", + "hyper-rustls", + "once_cell", + "pin-project-lite", + "pin-utils", + "rustls", + "tokio", + "tracing", +] + +[[package]] +name = "aws-smithy-runtime-api" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92165296a47a812b267b4f41032ff8069ab7ff783696d217f0994a0d7ab585cd" +dependencies = [ + "aws-smithy-async", + "aws-smithy-types", + "bytes", + "http 0.2.12", + "http 1.2.0", + "pin-project-lite", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-types" +version = "1.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fbd94a32b3a7d55d3806fe27d98d3ad393050439dd05eb53ece36ec5e3d3510" +dependencies = [ + "base64-simd", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http 1.2.0", + "http-body 0.4.6", + "http-body 1.0.1", + "http-body-util", + "itoa", + "num-integer", + "pin-project-lite", + "pin-utils", + "ryu", + "serde", + "time", + "tokio", + "tokio-util", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.60.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab0b0166827aa700d3dc519f72f8b3a91c35d0b8d042dc5d643a91e6f80648fc" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5221b91b3e441e6675310829fd8984801b772cb1546ef6c0e54dec9f1ac13fef" +dependencies = [ + "aws-credential-types", + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "rustc_version", + "tracing", +] + +[[package]] +name = "backend" +version = "0.1.0" +dependencies = [ + "actix-cors", + "actix-web", + "anyhow", + "aws-credential-types", + "aws-sdk-s3", + "chrono", + "env_logger", + "flate2", + "futures-util", + "log", + "mongodb", + "rand", + "serde", + "tokio", +] + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[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 = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bson" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "068208f2b6fcfa27a7f1ee37488d2bb8ba2640f68f5475d08e1d9130696aba59" +dependencies = [ + "ahash", + "base64 0.13.1", + "bitvec", + "hex", + "indexmap 2.7.0", + "js-sys", + "once_cell", + "rand", + "serde", + "serde_bytes", + "serde_json", + "time", + "uuid", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" + +[[package]] +name = "bytes-utils" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" +dependencies = [ + "bytes", + "either", +] + +[[package]] +name = "bytestring" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e465647ae23b2823b0753f50decb2d5a86d2bb2cac04788fafd1f80e45378e5f" +dependencies = [ + "bytes", +] + +[[package]] +name = "cc" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[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.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.52.6", +] + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[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" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32c" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a47af21622d091a8f0fb295b88bc886ac74efcc613efc19f5d0b21de5c89e47" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "rand_core", + "subtle", +] + +[[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 = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.90", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.90", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint 0.4.9", + "der", + "digest", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "enum-as-inner" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "libz-sys", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + +[[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 = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-task", + "memchr", + "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.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap 2.7.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hickory-proto" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07698b8420e2f0d6447a436ba999ec85d8fbf2a398bbd737b82cac4a2e96e512" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna 0.4.0", + "ipnet", + "once_cell", + "rand", + "thiserror", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "hickory-resolver" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28757f23aa75c98f254cf0405e6d8c25b831b32921b050a66692427679b1f243" +dependencies = [ + "cfg-if", + "futures-util", + "hickory-proto", + "ipconfig", + "lru-cache", + "once_cell", + "parking_lot", + "rand", + "resolv-conf", + "smallvec", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.2.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + +[[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 = "hyper" +version = "0.14.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper", + "log", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +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 = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "impl-more" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae21c3177a27788957044151cc2800043d127acaa460a47ebb9b84dfa2c6aa0" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +dependencies = [ + "equivalent", + "hashbrown 0.15.2", + "serde", +] + +[[package]] +name = "ipconfig" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" +dependencies = [ + "socket2", + "widestring", + "windows-sys 0.48.0", + "winreg", +] + +[[package]] +name = "ipnet" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +dependencies = [ + "once_cell", + "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.168" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" + +[[package]] +name = "libz-sys" +version = "1.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + +[[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.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.2", +] + +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[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.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "mongodb" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c857d71f918b38221baf2fdff7207fec9984b4504901544772b1edf0302d669f" +dependencies = [ + "async-trait", + "base64 0.13.1", + "bitflags 1.3.2", + "bson", + "chrono", + "derivative", + "derive_more", + "futures-core", + "futures-executor", + "futures-io", + "futures-util", + "hex", + "hickory-proto", + "hickory-resolver", + "hmac", + "md-5", + "mongodb-internal-macros", + "once_cell", + "pbkdf2", + "percent-encoding", + "rand", + "rustc_version_runtime", + "rustls", + "rustls-pemfile", + "serde", + "serde_bytes", + "serde_with", + "sha-1", + "sha2", + "socket2", + "stringprep", + "strsim", + "take_mut", + "thiserror", + "tokio", + "tokio-rustls", + "tokio-util", + "typed-builder", + "uuid", + "webpki-roots", +] + +[[package]] +name = "mongodb-internal-macros" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6dbc533e93429a71c44a14c04547ac783b56d3f22e6c4f12b1b994cf93844e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "outref" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" + +[[package]] +name = "p256" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +dependencies = [ + "ecdsa", + "elliptic-curve", + "sha2", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest", +] + +[[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.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[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.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[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.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error", +] + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint 0.4.9", + "hmac", + "zeroize", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustc_version_runtime" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dd18cd2bae1820af0b6ad5e54f4a51d0f3fcc53b05f845675074efcc7af071d" +dependencies = [ + "rustc_version", + "semver", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[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.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[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 = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "serde_json" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +dependencies = [ + "indexmap 2.7.0", + "itoa", + "memchr", + "ryu", + "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_with" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.7.0", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "sha-1" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[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 = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest", + "rand_core", +] + +[[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.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[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.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "take_mut" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "time" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "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.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +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.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[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.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +dependencies = [ + "bytes", + "futures-core", + "futures-io", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typed-builder" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89851716b67b937e393b3daa8423e67ddfc4bbbf1654bcf05488e95e0828db0c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[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.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + +[[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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna 1.0.3", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +dependencies = [ + "getrandom", + "serde", +] + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[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.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.90", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + +[[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-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.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[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.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "zstd" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.13+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/backend/Cargo.toml b/backend/Cargo.toml new file mode 100644 index 0000000..2ab4c49 --- /dev/null +++ b/backend/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "backend" +version = "0.1.0" +edition = "2021" + +[dependencies] +actix-web = "4" +actix-cors = "0.7.0" +mongodb = "3.1.0" +aws-sdk-s3 = { version = "1.65.0" } +tokio = { version = "1", features = ["rt"] } +anyhow = { version = "1.0.40", default-features = false } +serde = { version = "1.0.215", features = ["derive"] } +flate2 = { version = "1.0.19", features = ["zlib"] } +chrono = { version = "0.4.19", features = ["serde"] } +rand = "0.8.5" +futures-util = "0.3.31" +aws-credential-types = { version = "1.2.1", features = ["hardcoded-credentials"] } +env_logger = "0.11.5" +log = "0.4.22" + +[[bin]] +name = "backend" +path = "src/main.rs" \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile index 86a4f1b..aa5067b 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,10 +1,12 @@ +FROM rust:1.83.0-alpine AS builder -FROM openjdk:23-slim +COPY . . -WORKDIR /pastebook-backend +RUN apk add --no-cache clang lld musl-dev git +RUN cargo build --release --locked -COPY build/libs/pastebook.jar pastebook.jar +FROM alpine:3.18 AS final -EXPOSE 8080 +COPY --from=builder target/release/backend /usr/local/bin/backend -ENTRYPOINT ["java", "-jar", "pastebook.jar"] \ No newline at end of file +CMD ["/usr/local/bin/backend"] \ No newline at end of file diff --git a/backend/build.gradle.kts b/backend/build.gradle.kts deleted file mode 100644 index 0ffff89..0000000 --- a/backend/build.gradle.kts +++ /dev/null @@ -1,62 +0,0 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - -plugins { - id("org.springframework.boot") version "3.4.0" - id("io.spring.dependency-management") version "1.1.6" - kotlin("jvm") version "2.1.0" - kotlin("plugin.spring") version "2.1.0" -} -val springCloudVersion by extra("2023.0.1") - -group = "dev.loudbook" -version = "0.0.1-SNAPSHOT" - -java { - sourceCompatibility = JavaVersion.VERSION_23 - targetCompatibility = JavaVersion.VERSION_23 -} - -repositories { - mavenCentral() -} - -dependencies { - implementation("org.springframework.boot:spring-boot-starter-data-mongodb") - implementation("org.springframework.boot:spring-boot-starter-web") - implementation("com.fasterxml.jackson.module:jackson-module-kotlin") - implementation("org.jetbrains.kotlin:kotlin-reflect") - implementation("com.github.vladimir-bukhtoyarov:bucket4j-core:8.0.1") - testImplementation("org.springframework.boot:spring-boot-starter-test") - implementation("com.google.code.gson:gson:2.11.0") - implementation("me.paulschwarz:spring-dotenv:4.0.0") - implementation("commons-validator:commons-validator:1.8.0") - implementation("software.amazon.awssdk:s3:2.29.29") -} - -dependencyManagement { - imports { - mavenBom("org.springframework.cloud:spring-cloud-dependencies:$springCloudVersion") - } -} - -tasks.withType { - compilerOptions { - freeCompilerArgs.add("-Xjsr305=strict") - jvmTarget.set(JvmTarget.JVM_23) - } -} - -tasks.bootJar { - archiveBaseName.set("pastebook") - archiveVersion.set("") - archiveClassifier.set("") -} - -tasks.withType { - useJUnitPlatform() -} - -configurations.implementation { - exclude(group = "commons-logging", module = "commons-logging") -} \ No newline at end of file diff --git a/backend/gradle/wrapper/gradle-wrapper.jar b/backend/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index a4b76b9..0000000 Binary files a/backend/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/backend/gradle/wrapper/gradle-wrapper.properties b/backend/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index e2847c8..0000000 --- a/backend/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,7 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/backend/gradlew b/backend/gradlew deleted file mode 100755 index f5feea6..0000000 --- a/backend/gradlew +++ /dev/null @@ -1,252 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/backend/gradlew.bat b/backend/gradlew.bat deleted file mode 100644 index 9d21a21..0000000 --- a/backend/gradlew.bat +++ /dev/null @@ -1,94 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem -@rem SPDX-License-Identifier: Apache-2.0 -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/backend/settings.gradle.kts b/backend/settings.gradle.kts deleted file mode 100644 index 0c750f3..0000000 --- a/backend/settings.gradle.kts +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = "PasteBook" diff --git a/backend/src/controllers/get_controller.rs b/backend/src/controllers/get_controller.rs new file mode 100644 index 0000000..3264802 --- /dev/null +++ b/backend/src/controllers/get_controller.rs @@ -0,0 +1,84 @@ +use crate::database::aws_service::AWSService; +use actix_web::{ + web, HttpRequest, HttpResponse, Responder, +}; +use serde::Deserialize; +use std::sync::Arc; +use crate::database::mongodb_service::MongoService; + +#[derive(Deserialize)] +pub struct ContentQuery { + pub compress: Option, +} + +pub async fn get_metadata_handler( + mongo_service: web::Data>, + request: HttpRequest, + path: web::Path, +) -> impl Responder { + let ip = extract_ip(&request); + if mongo_service.is_user_banned(&ip).await.expect("Failed to check if user is banned") { + return HttpResponse::Forbidden().body("Prohibited"); + } + + match mongo_service.get_paste_metadata(&path).await { + Ok(Some(metadata)) => { + let user = mongo_service.get_user(&metadata.creator_ip).await.unwrap(); + + let public_dto = metadata.to_public_dto(user.unwrap().to_dto()); + HttpResponse::Ok().json(public_dto) + } + Ok(None) => HttpResponse::NotFound().body("Not Found"), + Err(_) => HttpResponse::InternalServerError().body("Internal Server Error"), + } +} + +pub async fn get_content_handler( + aws_service: web::Data>, + mongo_service: web::Data>, + request: HttpRequest, + path: web::Path, + query: web::Query, +) -> impl Responder { + let compress = query.compress.unwrap_or(true); + let ip = extract_ip(&request); + + if mongo_service.is_user_banned(&ip).await.expect("Failed to check if user is banned") { + return HttpResponse::Forbidden().body("Prohibited"); + } + + mongo_service.increment_requests(&ip).await.expect("Failed to increment requests"); + + match aws_service.get_file(&path).await { + Ok(data) => { + if compress { + let compressed = compress_data(&data); + HttpResponse::Ok() + .content_type("text/plain; charset=utf-8") + .append_header(("Content-Encoding", "gzip")) + .body(compressed) + } else { + HttpResponse::Ok() + .content_type("text/plain; charset=utf-8") + .body(data) + } + } + Err(_) => HttpResponse::NotFound().body("File not found"), + } +} + +fn compress_data(data: &[u8]) -> Vec { + use flate2::write::GzEncoder; + use flate2::Compression; + use std::io::Write; + + let mut encoder = GzEncoder::new(Vec::new(), Compression::default()); + encoder.write_all(data).expect("Failed to write data"); + encoder.finish().expect("Failed to finish compression") +} + +fn extract_ip(req: &HttpRequest) -> String { + req.peer_addr() + .map(|addr| addr.ip().to_string()) + .unwrap_or_else(|| "unknown".to_string()) +} diff --git a/backend/src/controllers/mod.rs b/backend/src/controllers/mod.rs new file mode 100644 index 0000000..a2b619e --- /dev/null +++ b/backend/src/controllers/mod.rs @@ -0,0 +1,2 @@ +pub mod get_controller; +pub mod upload_controller; \ No newline at end of file diff --git a/backend/src/controllers/upload_controller.rs b/backend/src/controllers/upload_controller.rs new file mode 100644 index 0000000..7f178c0 --- /dev/null +++ b/backend/src/controllers/upload_controller.rs @@ -0,0 +1,103 @@ +use crate::database::aws_service::AWSService; +use crate::models::paste::Paste; +use crate::database::mongodb_service::MongoService; +use crate::utils::iputils::IPUtils; +use actix_web::{web, HttpRequest, HttpResponse, Responder}; +use rand::{distributions::Alphanumeric, thread_rng, Rng}; +use std::sync::Arc; +use std::time::{SystemTime, UNIX_EPOCH}; + +pub async fn upload_handler( + aws_service: web::Data>, + mongo_service: web::Data>, + req: HttpRequest, + body: String, +) -> impl Responder { + let title = req + .headers() + .get("title") + .and_then(|v| v.to_str().ok()) + .unwrap_or(""); + if title.is_empty() { + return HttpResponse::BadRequest().body("Title is required"); + } + + let report_book = req + .headers() + .get("reportBook") + .map(|v| v.to_str().unwrap_or("false") == "true") + .unwrap_or(false); + + let wrap = req + .headers() + .get("wrap") + .map(|v| v.to_str().unwrap_or("false") == "true") + .unwrap_or(false); + + let mut expires = req + .headers() + .get("expires") + .and_then(|v| v.to_str().ok()?.parse::().ok()) + .unwrap_or_else(|| 86_400_000); + + let since_the_epoch = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis() as u64; + + if expires < 60_000 { + return HttpResponse::BadRequest().body("Expire time too short"); + } + if expires < since_the_epoch { + expires += since_the_epoch; + } + if expires > since_the_epoch + 2_765_000_000 { + return HttpResponse::BadRequest().body("Expire time too long"); + } + + let ip = match IPUtils::get_ip_from_request(&req) { + Some(ip) => ip, + None => return HttpResponse::BadRequest().body("Failed to get IP"), + }; + + let file_id = generate_random_string(5); + + let paste = Paste { + id: file_id.clone(), + title: title.to_string(), + created: since_the_epoch, + report_book, + wrap, + creator_ip: ip.clone(), + expires_at: expires, + }; + + if let Err(e) = aws_service.put_file(&file_id, (&body).as_ref()).await { + return HttpResponse::InternalServerError().body(format!("Failed to upload file: {:?}", e)); + } + if let Err(e) = mongo_service.put_paste(paste).await { + return HttpResponse::InternalServerError().body(format!("Failed to save to database: {:?}", e)); + } + + mongo_service.increment_requests(&ip).await.expect("Failed to increment requests"); + + let host_domain = req + .headers() + .get("X-Domain-Name") + .and_then(|v| v.to_str().ok()); + let response_body = if let Some(domain) = host_domain { + format!("https://{}/p/{}", domain, file_id) + } else { + file_id.clone() + }; + + HttpResponse::Ok().body(response_body) +} + +fn generate_random_string(length: usize) -> String { + thread_rng() + .sample_iter(&Alphanumeric) + .take(length) + .map(char::from) + .collect() +} diff --git a/backend/src/database/aws_service.rs b/backend/src/database/aws_service.rs new file mode 100644 index 0000000..7a5073f --- /dev/null +++ b/backend/src/database/aws_service.rs @@ -0,0 +1,100 @@ +use anyhow::Result; +use aws_sdk_s3::config::{Credentials, Region, SharedCredentialsProvider}; +use aws_sdk_s3::primitives::ByteStream; +use aws_sdk_s3::{Client, Config}; + +pub struct AWSService { + client: Client, + bucket_name: String, +} + +impl AWSService { + pub async fn new(endpoint: &str, bucket_name: &str, access_key: &str, secret_key: &str) -> Result { + println!("Connecting to AWS S3..."); + let region = Region::new("auto"); + let credentials = Credentials::from_keys(access_key, secret_key, None); + let shared_credentials = SharedCredentialsProvider::new(credentials); + + let config = Config::builder() + .force_path_style(true) + .region(region) + .credentials_provider(shared_credentials) + .endpoint_url(endpoint) + .behavior_version_latest() + .build(); + + let client = Client::from_conf(config); + + println!("Connected to AWS S3."); + + Ok(Self { + client, + bucket_name: bucket_name.to_string(), + }) + } + + pub async fn get_file(&self, key: &str) -> Result> { + let response = self + .client + .get_object() + .bucket(&self.bucket_name) + .key(key) + .send() + .await?; + + let data = response.body.collect().await?; + Ok(data.into_bytes().to_vec()) + } + + pub async fn put_file(&self, key: &str, data: &[u8]) -> Result<()> { + self.client + .put_object() + .bucket(&self.bucket_name) + .key(key) + .body(ByteStream::from(data.to_vec())) + .send() + .await?; + Ok(()) + } + + pub async fn delete_file(&self, key: &str) -> Result<()> { + self.client + .delete_object() + .bucket(&self.bucket_name) + .key(key) + .send() + .await?; + Ok(()) + } + + pub async fn list_files(&self) -> Result> { + let response = self + .client + .list_objects_v2() + .bucket(&self.bucket_name) + .send() + .await?; + + let mut keys = Vec::new(); + for object in response.contents.unwrap_or_default() { + if let Some(key) = object.key { + keys.push(key); + } + } + + Ok(keys) + } + + pub async fn create_bucket(&self, bucket_name: &str) -> Result<()> { + if self.client.head_bucket().bucket(bucket_name).send().await.is_ok() { + return Ok(()); + } + + self.client + .create_bucket() + .bucket(bucket_name) + .send() + .await?; + Ok(()) + } +} diff --git a/backend/src/database/migration_service.rs b/backend/src/database/migration_service.rs new file mode 100644 index 0000000..65d404b --- /dev/null +++ b/backend/src/database/migration_service.rs @@ -0,0 +1,137 @@ +use crate::models::paste::Paste; +use crate::models::user::User; +use futures_util::StreamExt; +use mongodb::bson::{doc, Document}; +use mongodb::{Collection, Database}; + +pub struct MigrationService { + database: Database, + admin_database: Database, + users_collection: Collection, + pastes_collection: Collection, +} + +impl MigrationService { + pub fn new(database: &Database, admin_database: &Database, users_collection: &Collection, pastes_collection: &Collection) -> Self { + Self { + database: database.clone(), + admin_database: admin_database.clone(), + users_collection: users_collection.clone(), + pastes_collection: pastes_collection.clone(), + } + } + + pub async fn run_migrations(&self) { + self.user_migration_12_11_24().await.map_err(|e| println!("Error migrating users: {}", e)).ok(); + self.paste_migration_12_11_24().await.map_err(|e| println!("Error migrating pastes: {}", e)).ok(); + } + + pub async fn user_migration_12_11_24(&self) -> Result<(), mongodb::error::Error> { + if !self.database.list_collection_names().await?.contains(&"user".to_string()) { + println!("No users to migrate."); + return Ok(()); + } + + println!("Migrating users..."); + let old_user_collection = self.database.collection::("user"); + + let mut amount = 0; + let target_amount = old_user_collection.count_documents(doc! {}).await?; + let mut cursor = old_user_collection.find(doc! {}).await?; + + while let Some(old_user) = cursor.next().await { + let old_user = old_user?; + let user_ip = old_user.get_str("_id").unwrap_or_default(); + let user_id = old_user.get_str("id").unwrap_or_default(); + let user_requests = old_user.get_i32("requests").unwrap_or_default(); + let user_banned = old_user.get_bool("banned").unwrap_or_default(); + let last_visit = old_user.get_i64("lastVisit").unwrap_or_default(); + + if user_ip.is_empty() || user_id.is_empty() { + println!("Skipping user with empty IP or ID."); + continue; + } + + let user = User { + ip: user_ip.to_string(), + id: user_id.to_string(), + requests: user_requests as u64, + created_at: last_visit, + banned: user_banned, + }; + + self.users_collection.insert_one(user).await?; + + amount += 1; + } + + println!("Migrated {} out of {} users", amount, target_amount); + + let qualified_old_database_name = self.database.name().to_owned() + ".user"; + let qualified_new_database_name = self.database.name().to_owned() + ".user_migrated_12_11_24"; + + self.admin_database.run_command(doc! { + "renameCollection": qualified_old_database_name, + "to": qualified_new_database_name, + "dropTarget": true + }).await?; + + Ok(()) + } + + pub async fn paste_migration_12_11_24(&self) -> Result<(), mongodb::error::Error> { + if !self.database.list_collection_names().await?.contains(&"pastePrivateDTO".to_string()) { + println!("No pastes to migrate."); + return Ok(()); + } + + println!("Migrating pastes..."); + let old_paste_collection = self.database.collection::("pastePrivateDTO"); + + let mut amount = 0; + let target_amount = old_paste_collection.count_documents(doc! {}).await?; + let mut cursor = old_paste_collection.find(doc! {}).await?; + + while let Some(old_paste) = cursor.next().await { + let old_paste = old_paste?; + let paste_id = old_paste.get_str("_id").unwrap_or_default(); + let paste_expires_at = old_paste.get_i64("expires").unwrap_or_default(); + let paste_created_at = old_paste.get_i64("created").unwrap_or_default(); + let paste_creator_ip = old_paste.get_str("creatorIP").unwrap_or_default(); + let paste_report_book = old_paste.get_bool("reportBook").unwrap_or_default(); + let paste_wrap = old_paste.get_bool("wrap").unwrap_or_default(); + + if paste_id.is_empty() { + println!("Skipping paste with empty ID."); + continue; + } + + let paste = Paste { + id: paste_id.to_string(), + title: "".to_string(), + created: paste_created_at as u64, + report_book: paste_report_book, + wrap: paste_wrap, + creator_ip: paste_creator_ip.to_string(), + expires_at: paste_expires_at as u64, + }; + + self.pastes_collection.insert_one(paste).await?; + + amount += 1; + } + + println!("Migrated {} out of {} pastes", amount, target_amount); + + let qualified_old_database_name = self.database.name().to_owned() + ".pastePrivateDTO"; + let qualified_new_database_name = self.database.name().to_owned() + ".pastePrivateDTO_migrated_12_11_24"; + + self.admin_database.run_command(doc! { + "renameCollection": qualified_old_database_name, + "to": qualified_new_database_name, + "dropTarget": true + }).await?; + + Ok(()) + } +} \ No newline at end of file diff --git a/backend/src/database/mod.rs b/backend/src/database/mod.rs new file mode 100644 index 0000000..ffa89f5 --- /dev/null +++ b/backend/src/database/mod.rs @@ -0,0 +1,5 @@ +pub mod aws_service; +pub mod mongodb_service; +pub mod mongoresult; + +mod migration_service; \ No newline at end of file diff --git a/backend/src/database/mongodb_service.rs b/backend/src/database/mongodb_service.rs new file mode 100644 index 0000000..771ec2c --- /dev/null +++ b/backend/src/database/mongodb_service.rs @@ -0,0 +1,86 @@ +use mongodb::bson::{doc, uuid}; +use mongodb::{Client, Collection, Cursor}; +use mongodb::options::ClientOptions; +use crate::database::migration_service::MigrationService; +use crate::models::paste::Paste; +use crate::models::user::User; +use crate::database::mongoresult::MongoResult; + +pub struct MongoService { + user_collection: Collection, + paste_collection: Collection, +} + +impl MongoService { + pub async fn new(uri: &str, db_name: &str) -> MongoResult { + println!("Connecting to MongoDB..."); + let client_options = ClientOptions::parse(uri).await?; + let client = Client::with_options(client_options)?; + + let database = client.database(db_name); + let admin_database = client.database("admin"); + let user_collection = database.collection::("users"); + let paste_collection = database.collection::("pastes"); + + println!("Connected to MongoDB."); + + let migration_service = MigrationService::new(&database, &admin_database, &user_collection, &paste_collection); + migration_service.run_migrations().await; + + Ok(Self { + user_collection, + paste_collection, + }) + } + + pub async fn increment_requests(&self, ip: &str) -> MongoResult<()> { + let filter = doc! { "ip": ip }; + let update = doc! { + "$inc": { "requests": 1 }, + "$setOnInsert": { + "_id": uuid::Uuid::new().to_string(), + "created_at": chrono::Utc::now().timestamp_millis(), + "banned": false + } + }; + + self.user_collection.update_one(filter, update).upsert(true).await?; + Ok(()) + } + + pub async fn put_paste(&self, paste: Paste) -> MongoResult<()> { + self.paste_collection.insert_one(paste).await?; + Ok(()) + } + + pub async fn delete_paste(&self, id: &str) -> MongoResult<()> { + self.paste_collection.delete_one(doc! { "id": id }).await?; + Ok(()) + } + + pub async fn is_user_banned(&self, ip: &str) -> MongoResult { + if let Some(user) = self.user_collection.find_one(doc! { "ip": ip }).await? { + Ok(user.banned) + } else { + Ok(false) + } + } + + pub async fn get_user(&self, ip: &str) -> MongoResult> { + let user = self.user_collection.find_one(doc! { "ip": ip }).await?; + Ok(user) + } + + pub async fn get_paste_metadata(&self, id: &str) -> MongoResult> { + let paste = self.paste_collection.find_one(doc! { "id": id }).await?; + Ok(paste) + } + + pub async fn get_all_pastes_metadata(&self) -> MongoResult> { + let cursor = self + .paste_collection + .find(doc! {}) + .await?; + Ok(cursor) + } +} \ No newline at end of file diff --git a/backend/src/database/mongoresult.rs b/backend/src/database/mongoresult.rs new file mode 100644 index 0000000..434e7c6 --- /dev/null +++ b/backend/src/database/mongoresult.rs @@ -0,0 +1,3 @@ +use mongodb::error::Error; + +pub type MongoResult = Result; \ No newline at end of file diff --git a/backend/src/delete_service.rs b/backend/src/delete_service.rs new file mode 100644 index 0000000..aa02abc --- /dev/null +++ b/backend/src/delete_service.rs @@ -0,0 +1,90 @@ +use std::sync::Arc; +use std::time::Duration; +use chrono::Utc; +use futures_util::StreamExt; +use tokio::spawn; +use tokio::time::interval; +use log::{error, warn}; +use crate::database::aws_service::AWSService; +use crate::database::mongodb_service::MongoService; + +pub struct DeleteHandler { + aws_service: Arc, + mongo_service: Arc, +} + +impl DeleteHandler { + pub fn new(aws_service: Arc, mongo_service: Arc) -> Self { + let handler = Self { + aws_service, + mongo_service, + }; + + handler.start_delete_loop(); + handler + } + + pub(crate) fn start_delete_loop(&self) { + let aws_service = Arc::clone(&self.aws_service); + let mongo_service = Arc::clone(&self.mongo_service); + + spawn(async move { + let mut interval = interval(Duration::from_secs(600)); + loop { + interval.tick().await; + if let Err(err) = Self::delete_files(&aws_service, &mongo_service).await { + error!("Error during deletion process: {}", err); + } + } + }); + } + + async fn delete_files( + aws_service: &Arc, + mongo_service: &Arc, + ) -> Result<(), String> { + let now = Utc::now().timestamp_millis(); + let mut deletable_pastes = Vec::new(); + let mut all_pastes = Vec::new(); + + if let Ok(mut pastes_cursor) = mongo_service.get_all_pastes_metadata().await { + while let Some(paste) = pastes_cursor.next().await { + let paste = paste.expect("Failed to get paste"); + + if paste.expires_at > 0 && paste.expires_at < now as u64 { + deletable_pastes.push(paste.clone()); + } + + all_pastes.push(paste); + } + } + + for paste in &deletable_pastes { + let paste_id = &paste.id; + if let Err(err) = mongo_service.delete_paste(paste_id).await { + error!("Failed to delete paste from database: {:?}", err); + } + let id = &paste.id; + + if let Err(err) = mongo_service.delete_paste(id).await { + error!("Failed to delete paste file: {}", err); + } else { + warn!("Deleted paste file: {}", id); + } + } + + if let Ok(file_names) = aws_service.list_files().await { + for file_name in file_names { + if all_pastes.iter().all(|paste| &paste.id != &file_name) { + if let Err(err) = aws_service.delete_file(&file_name).await { + error!("Failed to delete invalid file {}: {}", file_name, err); + } else { + warn!("Deleted invalid file: {}", file_name); + } + } + } + } + + Ok(()) + } +} diff --git a/backend/src/main.rs b/backend/src/main.rs new file mode 100644 index 0000000..5ebf303 --- /dev/null +++ b/backend/src/main.rs @@ -0,0 +1,66 @@ +mod models; +mod controllers; +mod utils; +mod delete_service; +mod database; + +use database::aws_service::AWSService; +use crate::controllers::get_controller::{get_content_handler, get_metadata_handler}; +use crate::controllers::upload_controller::upload_handler; +use crate::delete_service::DeleteHandler; +use database::mongodb_service::MongoService; +use actix_cors::Cors; +use actix_web::{web, App, HttpServer}; +use std::env; +use std::sync::Arc; + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + env_logger::init(); + + let database_url = env::var("S3_ENDPOINT").expect("S3_ENDPOINT must be set"); + let aws_access_key = env::var("S3_ACCESS_KEY_ID").expect("S3_ACCESS_KEY_ID must be set"); + let aws_secret_key = env::var("S3_SECRET_ACCESS_KEY").expect("S3_SECRET_ACCESS_KEY must be set"); + let bucket_name = env::var("S3_BUCKET").expect("S3_BUCKET must be set"); + let mongo_url = env::var("SPRING_DATA_MONGODB_URI").expect("SPRING_DATA_MONGODB_URI must be set"); + + let aws_service = Arc::new( + AWSService::new( + &database_url, + &bucket_name, + &aws_access_key, + &aws_secret_key + ) + .await + .expect("Failed to initialize AWSService"), + ); + + aws_service.create_bucket(&bucket_name).await.expect("Failed to create bucket"); + + let mongo_service = Arc::new( + MongoService::new( + &mongo_url, + &bucket_name, + ) + .await + .expect("Failed to initialize MongoService") + ); + + let delete_handler = DeleteHandler::new(Arc::clone(&aws_service), Arc::clone(&mongo_service)); + delete_handler.start_delete_loop(); + + HttpServer::new(move || { + let cors = Cors::permissive(); + + App::new() + .wrap(cors) + .app_data(web::Data::new(Arc::clone(&aws_service))) + .app_data(web::Data::new(Arc::clone(&mongo_service))) + .route("/get/{id}/content", web::get().to(get_content_handler)) + .route("/get/{id}/metadata", web::get().to(get_metadata_handler)) + .route("/upload", web::post().to(upload_handler)) + }) + .bind(("0.0.0.0", 8080))? + .run() + .await +} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/loudbook/pastebook/BucketUtils.kt b/backend/src/main/kotlin/dev/loudbook/pastebook/BucketUtils.kt deleted file mode 100644 index 522de61..0000000 --- a/backend/src/main/kotlin/dev/loudbook/pastebook/BucketUtils.kt +++ /dev/null @@ -1,23 +0,0 @@ -package dev.loudbook.pastebook - -import io.github.bucket4j.Bandwidth -import io.github.bucket4j.Bucket -import io.github.bucket4j.Refill -import java.time.Duration - - -object BucketUtils { - fun getBucketPerSeconds(perSeconds: Long): Bucket { - val limit = Bandwidth.classic(perSeconds, Refill.greedy(perSeconds, Duration.ofSeconds(1))) - return Bucket.builder() - .addLimit(limit) - .build() - } - - fun getBucketPerMinutes(perMinutes: Long): Bucket { - val limit = Bandwidth.classic(perMinutes, Refill.greedy(perMinutes, Duration.ofMinutes(1))) - return Bucket.builder() - .addLimit(limit) - .build() - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/loudbook/pastebook/ContentScanner.kt b/backend/src/main/kotlin/dev/loudbook/pastebook/ContentScanner.kt deleted file mode 100644 index 4721da5..0000000 --- a/backend/src/main/kotlin/dev/loudbook/pastebook/ContentScanner.kt +++ /dev/null @@ -1,25 +0,0 @@ -package dev.loudbook.pastebook - -object ContentScanner { - fun scanContent(content: String): String { -/* val lines = content.split("\n").toMutableList() - - for ((index, line) in lines.withIndex()) { - val words = line.split(" ").toMutableList() - - for ((wordIndex, word) in words.withIndex()) { - if (InetAddressValidator.getInstance().isValidInet4Address(word)) { - words[wordIndex] = "***.***.***.***" - } else if (InetAddressValidator.getInstance().isValidInet6Address(word)) { - words[wordIndex] = "****:****:****:****:****:****:****:****" - } - } - - lines[index] = words.joinToString(" ") - } - - return lines.joinToString("\n")*/ - - return content - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/loudbook/pastebook/DeleteHandler.kt b/backend/src/main/kotlin/dev/loudbook/pastebook/DeleteHandler.kt deleted file mode 100644 index 24f9a11..0000000 --- a/backend/src/main/kotlin/dev/loudbook/pastebook/DeleteHandler.kt +++ /dev/null @@ -1,63 +0,0 @@ -package dev.loudbook.pastebook - -import dev.loudbook.pastebook.data.PastePrivateDTO -import dev.loudbook.pastebook.data.R2Service -import dev.loudbook.pastebook.mongo.PasteRepository -import jakarta.annotation.PostConstruct -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.stereotype.Component -import kotlin.concurrent.fixedRateTimer - -@Component -class DeleteHandler { - @Autowired - lateinit var r2Service: R2Service - - @Autowired - lateinit var pasteRepository: PasteRepository - - private val logger: Logger = LoggerFactory.getLogger(DeleteHandler::class.java) - - @PostConstruct - fun init() { - beginLoop() - } - - private final fun beginLoop() { - fixedRateTimer("timer", true, 1000, 1000 * 60 * 10) { - deleteFiles() - } - } - - private fun deleteFiles() { - val deletablePastes = mutableListOf() - val allPastes = mutableListOf() - - var index = 0 - - pasteRepository.findAllDTO().forEach { - val expires = it.expires - index++ - - allPastes.add(it) - - if (System.currentTimeMillis() > expires && expires != 0L) { - deletablePastes.add(it) - } - } - - for (paste in deletablePastes) { - pasteRepository.delete(paste) - paste.id?.let { r2Service.deleteFile(it) } ?: logger.error("Failed to delete paste; $paste") - } - - for (listFileName in r2Service.listFileNames()) { - if (allPastes.none{ it.id == listFileName}) { - r2Service.deleteFile(listFileName) - logger.warn("Deleted invalid file $listFileName") - } - } - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/loudbook/pastebook/IPUtils.kt b/backend/src/main/kotlin/dev/loudbook/pastebook/IPUtils.kt deleted file mode 100644 index 5def367..0000000 --- a/backend/src/main/kotlin/dev/loudbook/pastebook/IPUtils.kt +++ /dev/null @@ -1,19 +0,0 @@ -package dev.loudbook.pastebook - -import jakarta.servlet.http.HttpServletRequest - -object IPUtils { - fun getIPFromRequest(request: HttpServletRequest): String? { - var xRealIP = request.getHeader("Cf-Connecting-IP") - - if (xRealIP == null) { - xRealIP = request.getHeader("CF-Connecting-IPv6") - } - - if (xRealIP == null) { - return request.remoteAddr - } - - return xRealIP.split(",")[0] - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/loudbook/pastebook/PasteBookApplication.kt b/backend/src/main/kotlin/dev/loudbook/pastebook/PasteBookApplication.kt deleted file mode 100644 index 8a42001..0000000 --- a/backend/src/main/kotlin/dev/loudbook/pastebook/PasteBookApplication.kt +++ /dev/null @@ -1,14 +0,0 @@ -package dev.loudbook.pastebook - -import org.springframework.boot.SpringApplication -import org.springframework.boot.autoconfigure.SpringBootApplication -import org.springframework.data.mongodb.repository.config.EnableMongoRepositories - -@SpringBootApplication -@EnableMongoRepositories -class PasteBookApplication - -fun main(args: Array) { - val application = SpringApplication(PasteBookApplication::class.java) - application.run(*args) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/loudbook/pastebook/config/Configuration.kt b/backend/src/main/kotlin/dev/loudbook/pastebook/config/Configuration.kt deleted file mode 100644 index 7a39ce4..0000000 --- a/backend/src/main/kotlin/dev/loudbook/pastebook/config/Configuration.kt +++ /dev/null @@ -1,11 +0,0 @@ -package dev.loudbook.pastebook.config - -import dev.loudbook.pastebook.DeleteHandler -import org.springframework.context.annotation.Bean -import org.springframework.stereotype.Component - -@Component -class Configuration { - @Bean - fun deleteHandler() = DeleteHandler() -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/loudbook/pastebook/config/WebConfig.kt b/backend/src/main/kotlin/dev/loudbook/pastebook/config/WebConfig.kt deleted file mode 100644 index d8cb93a..0000000 --- a/backend/src/main/kotlin/dev/loudbook/pastebook/config/WebConfig.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.loudbook.pastebook.config -import org.springframework.context.annotation.Configuration -import org.springframework.web.servlet.config.annotation.CorsRegistry -import org.springframework.web.servlet.config.annotation.EnableWebMvc -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer - -@Configuration -@EnableWebMvc -class WebConfig : WebMvcConfigurer { - override fun addCorsMappings(registry: CorsRegistry) { - registry.addMapping("/**") - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/loudbook/pastebook/controllers/GetController.kt b/backend/src/main/kotlin/dev/loudbook/pastebook/controllers/GetController.kt deleted file mode 100644 index 6239b0c..0000000 --- a/backend/src/main/kotlin/dev/loudbook/pastebook/controllers/GetController.kt +++ /dev/null @@ -1,97 +0,0 @@ -package dev.loudbook.pastebook.controllers - -import com.google.gson.Gson -import dev.loudbook.pastebook.BucketUtils -import dev.loudbook.pastebook.data.R2Service -import dev.loudbook.pastebook.data.PastePrivateDTO -import dev.loudbook.pastebook.mongo.PasteRepository -import dev.loudbook.pastebook.mongo.UserService -import io.github.bucket4j.Bucket -import jakarta.servlet.http.HttpServletRequest -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.http.HttpHeaders -import org.springframework.http.MediaType -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.* -import java.io.ByteArrayOutputStream -import java.util.zip.GZIPOutputStream - -@RestController -class GetController { - @Autowired - lateinit var r2Service: R2Service - - @Autowired - lateinit var pasteRepository: PasteRepository - - @Autowired - lateinit var userService: UserService - - private val bucket: Bucket = BucketUtils.getBucketPerSeconds(4) - - @GetMapping("/get/{id}/metadata") - fun get(@PathVariable id: String, request: HttpServletRequest): ResponseEntity { - if (!userService.processRequest(request)) { - return ResponseEntity.status(403).body("Prohibited") - } - - if (!bucket.tryConsume(1)) { - return ResponseEntity.status(429).body("Rate limit exceeded") - } - - val paste: PastePrivateDTO = pasteRepository.findDTOByID(id) ?: return ResponseEntity.notFound().build() - - val json = Gson().toJson(userService.getUser(paste.creatorIP)?.toDTO()?.let { paste.toPublicDTO(it) }) ?: return ResponseEntity.notFound().build() - return ResponseEntity.ok().body(json) - } - - @GetMapping("/get/{id}/content") - fun getContent( - @RequestParam(required = false, defaultValue = "true") compress: Boolean, - @PathVariable id: String, - request: HttpServletRequest - ): ResponseEntity { - if (!userService.processRequest(request)) { - return ResponseEntity.status(403).body("Prohibited") - } - - if (!bucket.tryConsume(1)) { - return ResponseEntity.status(429).body(null) - } - - val headers = HttpHeaders() - - if (compress) { - headers.add("Content-Encoding", "gzip") - } else { - headers.add("Content-Type", "text/plain; charset=utf-8") - } - - val paste = r2Service.getFile(id) ?: return ResponseEntity.notFound().build() - val pasteData = paste.toByteArray() - - return if (compress) { - val compressed = compressData(pasteData) - ResponseEntity.ok() - .contentType(MediaType.TEXT_PLAIN) - .headers(headers) - .body(compressed) - } else { - ResponseEntity.ok() - .contentType(MediaType.TEXT_PLAIN) - .headers(headers) - .body(paste) - } - } - - private fun compressData(data: ByteArray): ByteArray { - val byteArrayOutputStream = ByteArrayOutputStream() - val gzipOutputStream = GZIPOutputStream(byteArrayOutputStream) - - gzipOutputStream.use { outputStream -> - outputStream.write(data) - } - - return byteArrayOutputStream.toByteArray() - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/loudbook/pastebook/controllers/UploadController.kt b/backend/src/main/kotlin/dev/loudbook/pastebook/controllers/UploadController.kt deleted file mode 100644 index b4ec7e5..0000000 --- a/backend/src/main/kotlin/dev/loudbook/pastebook/controllers/UploadController.kt +++ /dev/null @@ -1,89 +0,0 @@ -package dev.loudbook.pastebook.controllers - -import dev.loudbook.pastebook.BucketUtils -import dev.loudbook.pastebook.ContentScanner -import dev.loudbook.pastebook.IPUtils -import dev.loudbook.pastebook.data.PastePrivateDTO -import dev.loudbook.pastebook.data.R2Service -import dev.loudbook.pastebook.mongo.PasteRepository -import dev.loudbook.pastebook.mongo.UserService -import io.github.bucket4j.Bucket -import jakarta.servlet.http.HttpServletRequest -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.http.HttpHeaders -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.PostMapping -import org.springframework.web.bind.annotation.RequestBody -import org.springframework.web.bind.annotation.RestController - -@RestController -class UploadController { - @Autowired - lateinit var r2Service: R2Service - - @Autowired - lateinit var pasteRepository: PasteRepository - - @Autowired - lateinit var userService: UserService - - private val bucket: Bucket = BucketUtils.getBucketPerMinutes(4) - - @PostMapping(value = ["/upload"]) - fun upload(request: HttpServletRequest, @RequestBody body: String): ResponseEntity { - if (!userService.processRequest(request)) { - return ResponseEntity.status(403).body("Prohibited") - } - - if (!bucket.tryConsume(1)) { - return ResponseEntity.status(429).body("Rate limit exceeded") - } - - val header = HttpHeaders() - - val fileID = generateRandomString() - - val sinceTheEpoch = System.currentTimeMillis() - - val title = request.getHeader("title") ?: return ResponseEntity.badRequest().body("Title is required") - val reportBook = request.getHeader("reportBook")?.toBoolean() == true - val wrap = request.getHeader("wrap")?.toBoolean() == true - var expire = request.getHeader("expires")?.toLong() ?: (sinceTheEpoch + 8.64e+7).toLong() - - val hostDomain = request.getHeader("X-Domain-Name") - - if (expire < 60000) { - return ResponseEntity.badRequest().body("Expire time too short") - } - - if (expire < sinceTheEpoch) { - expire += sinceTheEpoch - } - - if (expire > (sinceTheEpoch + 2.765e+9)) { - return ResponseEntity.badRequest().body("Expire time too long") - } - - val filteredBody = ContentScanner.scanContent(body) - - val ip = IPUtils.getIPFromRequest(request) ?: return ResponseEntity.badRequest().body("Failed to get IP") - - val paste = PastePrivateDTO(fileID, title, sinceTheEpoch, reportBook, wrap, ip, expire) - - r2Service.uploadFile(fileID, filteredBody) - pasteRepository.save(paste) - - if (hostDomain != null) { - return ResponseEntity.ok().headers(header).body("https://$hostDomain/p/$fileID") - } - - return ResponseEntity.ok().headers(header).body(fileID) - } - - fun generateRandomString(length: Int = 5): String { - val chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" - return (1..length) - .map { chars.random() } - .joinToString("") - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/loudbook/pastebook/data/PasteDTO.kt b/backend/src/main/kotlin/dev/loudbook/pastebook/data/PasteDTO.kt deleted file mode 100644 index 28d24e8..0000000 --- a/backend/src/main/kotlin/dev/loudbook/pastebook/data/PasteDTO.kt +++ /dev/null @@ -1,6 +0,0 @@ -package dev.loudbook.pastebook.data - -import org.springframework.data.annotation.Id - -@Suppress("unused") -class PasteDTO(@Id var id: String?, val user: UserDTO, val title: String, val created: Long, val reportBook: Boolean = false, val wrap: Boolean = false, val expiresAt: Long) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/loudbook/pastebook/data/PastePrivateDTO.kt b/backend/src/main/kotlin/dev/loudbook/pastebook/data/PastePrivateDTO.kt deleted file mode 100644 index 690dfcb..0000000 --- a/backend/src/main/kotlin/dev/loudbook/pastebook/data/PastePrivateDTO.kt +++ /dev/null @@ -1,8 +0,0 @@ -package dev.loudbook.pastebook.data - -import org.springframework.data.annotation.Id - -@Suppress("unused") -data class PastePrivateDTO(@Id var id: String?, val title: String, val created: Long, val reportBook: Boolean = false, val wrap: Boolean = false, val creatorIP: String, val expires: Long){ - fun toPublicDTO(user: UserDTO) = PasteDTO(id, user, title, created, reportBook, wrap, expires) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/loudbook/pastebook/data/R2Service.kt b/backend/src/main/kotlin/dev/loudbook/pastebook/data/R2Service.kt deleted file mode 100644 index 07a330f..0000000 --- a/backend/src/main/kotlin/dev/loudbook/pastebook/data/R2Service.kt +++ /dev/null @@ -1,72 +0,0 @@ -package dev.loudbook.pastebook.data - -import jakarta.annotation.PostConstruct -import org.springframework.beans.factory.annotation.Value -import org.springframework.stereotype.Service -import software.amazon.awssdk.auth.credentials.AwsBasicCredentials -import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider -import software.amazon.awssdk.core.sync.RequestBody -import software.amazon.awssdk.regions.Region -import software.amazon.awssdk.services.s3.S3Client -import software.amazon.awssdk.services.s3.S3Configuration -import software.amazon.awssdk.services.s3.model.CreateBucketRequest -import software.amazon.awssdk.services.s3.model.GetObjectRequest -import software.amazon.awssdk.services.s3.model.ListObjectsRequest -import software.amazon.awssdk.services.s3.model.PutObjectRequest -import java.net.URI - -@Service -class R2Service { - private var amazonS3: S3Client? = null - - @Value("\${s3.accessKey}") - private val accessKey: String? = null - - @Value("\${s3.secretKey}") - private val secretKey: String? = null - - @Value("\${s3.url}") - private val url: String? = null - - @Value("\${s3.bucket}") - private val bucket: String? = null - - @PostConstruct - fun init() { - try { - amazonS3 = S3Client.builder() - .region(Region.US_EAST_1) - .credentialsProvider(AwsCredentialsProvider { AwsBasicCredentials.create(accessKey, secretKey) }) - .endpointOverride(URI.create(url!!)) - .serviceConfiguration(S3Configuration.builder() - .pathStyleAccessEnabled(true) - .build()) - .build() - - if (!amazonS3?.listBuckets()?.buckets()?.any { it.name() == bucket }!!) { - amazonS3?.createBucket(CreateBucketRequest.builder().bucket(bucket).build()) - } - } catch (e: Exception) { - e.printStackTrace() - } - } - - fun uploadFile(key: String, paste: String) { - amazonS3?.putObject(PutObjectRequest.builder().bucket(bucket).key(key).build(), RequestBody.fromString(paste)) - } - - fun deleteFile(key: String) { - amazonS3?.deleteObject { it.bucket(bucket).key(key) } - } - - fun getFile(key: String): String? { - val str = amazonS3?.getObject(GetObjectRequest.builder().bucket(bucket).key(key).build())?.readAllBytes() - ?.toString(Charsets.UTF_8) - - return str - } - - fun listFileNames(): List { - return amazonS3?.listObjects(ListObjectsRequest.builder().bucket(bucket).build())?.contents()?.map { it.key() } ?: emptyList() - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/loudbook/pastebook/data/User.kt b/backend/src/main/kotlin/dev/loudbook/pastebook/data/User.kt deleted file mode 100644 index 1185c96..0000000 --- a/backend/src/main/kotlin/dev/loudbook/pastebook/data/User.kt +++ /dev/null @@ -1,8 +0,0 @@ -package dev.loudbook.pastebook.data - -import org.springframework.data.annotation.Id - -data class User(@Id val ip: String, val id: String, val requests: Int, val lastVisit: Long, val banned: Boolean) { - fun incrementRequests() = User(ip, id, requests + 1, System.currentTimeMillis(), banned) - fun toDTO() = UserDTO(id) -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/loudbook/pastebook/data/UserDTO.kt b/backend/src/main/kotlin/dev/loudbook/pastebook/data/UserDTO.kt deleted file mode 100644 index a4bf6c3..0000000 --- a/backend/src/main/kotlin/dev/loudbook/pastebook/data/UserDTO.kt +++ /dev/null @@ -1,3 +0,0 @@ -package dev.loudbook.pastebook.data - -data class UserDTO(val id: String) \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/loudbook/pastebook/mongo/MigrationHandler.kt b/backend/src/main/kotlin/dev/loudbook/pastebook/mongo/MigrationHandler.kt deleted file mode 100644 index fce5770..0000000 --- a/backend/src/main/kotlin/dev/loudbook/pastebook/mongo/MigrationHandler.kt +++ /dev/null @@ -1,63 +0,0 @@ -package dev.loudbook.pastebook.mongo - -import org.bson.Document -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.springframework.boot.CommandLineRunner -import org.springframework.data.mongodb.core.MongoTemplate -import org.springframework.data.mongodb.core.query.Criteria -import org.springframework.data.mongodb.core.query.Query -import org.springframework.data.mongodb.core.query.Update -import org.springframework.stereotype.Component -import java.util.UUID - -@Component -class MigrationHandler(private val mongoTemplate: MongoTemplate) : CommandLineRunner { - - private val logger: Logger = LoggerFactory.getLogger(MigrationHandler::class.java) - - override fun run(vararg args: String?) { - idMigration(mongoTemplate) - } - - fun idMigration(mongoTemplate: MongoTemplate) { - val migrationQuery = Query(Criteria.where("migration").`is`("fixMissingIds")) - val existingMigration = mongoTemplate.findOne(migrationQuery, MigrationRecord::class.java) - - if (existingMigration != null) { - logger.info("ID migration already completed.") - return - } - - logger.info("Beginning migration of missing IDs...") - - val users = mongoTemplate.findAll(Document::class.java, "users") - - var result = 0 - - users.forEach { - val id = it.getString("id") - if (id == null) { - val newId = UUID.randomUUID().toString() - val query = Query(Criteria.where("_id").`is`(it.getObjectId("_id"))) - val update = Update().set("id", newId) - mongoTemplate.updateFirst(query, update, "users") - logger.info("Fixed missing ID for user ${it.getString("ip")}") - - result++ - } - } - - - if (result > 0) { - logger.info("Fixed ${result} missing IDs.") - } else { - logger.info("No missing IDs found.") - } - - mongoTemplate.save(MigrationRecord("fixMissingIds")) - logger.info("ID migration complete.") - } -} - -data class MigrationRecord(val migration: String) diff --git a/backend/src/main/kotlin/dev/loudbook/pastebook/mongo/PasteRepository.kt b/backend/src/main/kotlin/dev/loudbook/pastebook/mongo/PasteRepository.kt deleted file mode 100644 index b6fadf1..0000000 --- a/backend/src/main/kotlin/dev/loudbook/pastebook/mongo/PasteRepository.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.loudbook.pastebook.mongo - -import dev.loudbook.pastebook.data.PastePrivateDTO -import org.springframework.data.mongodb.repository.MongoRepository -import org.springframework.data.mongodb.repository.Query - -interface PasteRepository : MongoRepository { - @Query(value = "{}", fields = "{ 'content' : 0 }") - fun findAllDTO(): List - - @Query("{ 'id' : ?0 }", fields = "{ 'content' : 0 }") - fun findDTOByID(id: String): PastePrivateDTO? -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/loudbook/pastebook/mongo/UserService.kt b/backend/src/main/kotlin/dev/loudbook/pastebook/mongo/UserService.kt deleted file mode 100644 index b13a00f..0000000 --- a/backend/src/main/kotlin/dev/loudbook/pastebook/mongo/UserService.kt +++ /dev/null @@ -1,40 +0,0 @@ -package dev.loudbook.pastebook.mongo - -import dev.loudbook.pastebook.IPUtils -import dev.loudbook.pastebook.data.User -import jakarta.servlet.http.HttpServletRequest -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.stereotype.Service -import java.util.UUID - -@Service -class UserService { - @Autowired - private lateinit var usersRepository: UsersRepository - - fun incrementRequests(ip: String) { - val user = usersRepository.findByIp(ip) - if (user != null) { - val newUser = user.incrementRequests() - usersRepository.save(newUser) - } else { - val userID = UUID.randomUUID().toString() - usersRepository.save(User(ip, userID, 1, System.currentTimeMillis(), false)) - } - } - - fun processRequest(request: HttpServletRequest): Boolean { - val ip = IPUtils.getIPFromRequest(request) ?: return false - val user = usersRepository.findByIp(ip) - if (user != null && user.banned) { - return false - } - - incrementRequests(ip) - return true - } - - fun getUser(ip: String): User? { - return usersRepository.findByIp(ip) - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/dev/loudbook/pastebook/mongo/UsersRepository.kt b/backend/src/main/kotlin/dev/loudbook/pastebook/mongo/UsersRepository.kt deleted file mode 100644 index 36fc1cd..0000000 --- a/backend/src/main/kotlin/dev/loudbook/pastebook/mongo/UsersRepository.kt +++ /dev/null @@ -1,10 +0,0 @@ -package dev.loudbook.pastebook.mongo - -import dev.loudbook.pastebook.data.User -import org.springframework.data.mongodb.repository.MongoRepository -import org.springframework.data.mongodb.repository.Query - -interface UsersRepository : MongoRepository { - @Query("{ 'ip' : ?0 }") - fun findByIp(ip: String): User? -} \ No newline at end of file diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml deleted file mode 100644 index 95edac5..0000000 --- a/backend/src/main/resources/application.yml +++ /dev/null @@ -1,15 +0,0 @@ -server: - port: 8080 - -spring: - application: - name: PasteBook - data: - mongodb: - uri: ${SPRING_DATA_MONGODB_URI} - -s3: - url: ${S3_ENDPOINT} - accessKey: ${S3_ACCESS_KEY_ID} - secretKey: ${S3_SECRET_ACCESS_KEY} - bucket: ${S3_BUCKET} \ No newline at end of file diff --git a/backend/src/models/mod.rs b/backend/src/models/mod.rs new file mode 100644 index 0000000..5d536bd --- /dev/null +++ b/backend/src/models/mod.rs @@ -0,0 +1,2 @@ +pub mod user; +pub mod paste; \ No newline at end of file diff --git a/backend/src/models/paste.rs b/backend/src/models/paste.rs new file mode 100644 index 0000000..3f3d316 --- /dev/null +++ b/backend/src/models/paste.rs @@ -0,0 +1,38 @@ +use serde::{Deserialize, Serialize}; +use crate::models::user::UserDTO; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct Paste { + pub id: String, + pub title: String, + pub created: u64, + pub report_book: bool, + pub wrap: bool, + pub creator_ip: String, + pub expires_at: u64, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct PasteDTO { + pub id: String, + pub user: UserDTO, + pub title: String, + pub created: u64, + pub report_book: bool, + pub wrap: bool, + pub expires_at: u64, +} + +impl Paste { + pub fn to_public_dto(&self, user: UserDTO) -> PasteDTO { + PasteDTO { + id: self.id.clone(), + user, + title: self.title.clone(), + created: self.created, + report_book: self.report_book, + wrap: self.wrap, + expires_at: self.expires_at, + } + } +} diff --git a/backend/src/models/user.rs b/backend/src/models/user.rs new file mode 100644 index 0000000..66580a6 --- /dev/null +++ b/backend/src/models/user.rs @@ -0,0 +1,29 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct User { + pub ip: String, + pub id: String, + pub requests: u64, + pub created_at: i64, + pub banned: bool, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct UserDTO { + pub id: String, + pub requests: u64, + pub created_at: i64, + pub banned: bool, +} + +impl User { + pub fn to_dto(&self) -> UserDTO { + UserDTO { + id: self.id.clone(), + requests: self.requests, + created_at: self.created_at, + banned: self.banned, + } + } +} \ No newline at end of file diff --git a/backend/src/test/kotlin/dev/loudbook/pastebook/pastebook/PasteBookApplicationTests.kt b/backend/src/test/kotlin/dev/loudbook/pastebook/pastebook/PasteBookApplicationTests.kt deleted file mode 100644 index d421c14..0000000 --- a/backend/src/test/kotlin/dev/loudbook/pastebook/pastebook/PasteBookApplicationTests.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.loudbook.pastebook.pastebook - -import org.junit.jupiter.api.Test -import org.springframework.boot.test.context.SpringBootTest - -@SpringBootTest -class PasteBookApplicationTests { - - @Test - fun contextLoads() { - } - -} diff --git a/backend/src/utils/iputils.rs b/backend/src/utils/iputils.rs new file mode 100644 index 0000000..017265f --- /dev/null +++ b/backend/src/utils/iputils.rs @@ -0,0 +1,22 @@ +use actix_web::HttpRequest; + +pub struct IPUtils; + +impl IPUtils { + pub fn get_ip_from_request(req: &HttpRequest) -> Option { + if let Some(ip) = req.headers().get("Cf-Connecting-IP").and_then(|v| v.to_str().ok()) { + return Some(Self::extract_first_ip(ip)); + } + + if let Some(ip) = req.headers().get("CF-Connecting-IPv6").and_then(|v| v.to_str().ok()) { + return Some(Self::extract_first_ip(ip)); + } + + req.peer_addr() + .map(|addr| addr.ip().to_string()) + } + + fn extract_first_ip(ip_list: &str) -> String { + ip_list.split(',').next().unwrap_or("").trim().to_string() + } +} diff --git a/backend/src/utils/mod.rs b/backend/src/utils/mod.rs new file mode 100644 index 0000000..e38bd47 --- /dev/null +++ b/backend/src/utils/mod.rs @@ -0,0 +1 @@ +pub mod iputils; \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 24b1aa6..1139491 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,8 +11,8 @@ services: - S3_BUCKET=pastebook - S3_ENDPOINT=http://minio:9000 depends_on: - - mongo - - minio + minio: + condition: service_healthy networks: - pastebook-network @@ -43,7 +43,7 @@ services: networks: - pastebook-network pull_policy: always - + minio: image: quay.io/minio/minio:latest container_name: minio @@ -60,7 +60,12 @@ services: aliases: - pastebook.minio pull_policy: always - + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] + interval: 2s + timeout: 2s + retries: 5 + volumes: mongo-data: minio-data: diff --git a/frontend/src/lib/paste.ts b/frontend/src/lib/paste.ts index 98a486c..105ce2b 100755 --- a/frontend/src/lib/paste.ts +++ b/frontend/src/lib/paste.ts @@ -7,5 +7,5 @@ export type Paste = { user: { id: string; }; - expiresAt: string; + expires_at: string; }; diff --git a/frontend/src/routes/[pastes=paste]/[slug]/+page.svelte b/frontend/src/routes/[pastes=paste]/[slug]/+page.svelte index c672143..6186d8f 100755 --- a/frontend/src/routes/[pastes=paste]/[slug]/+page.svelte +++ b/frontend/src/routes/[pastes=paste]/[slug]/+page.svelte @@ -28,7 +28,7 @@ reportBook = data.reportBook; wrap = data.wrap; userId = data.user.id; - expires = new Date(data.expiresAt) + expires = new Date(data.expires_at); const reloadTime = () => { timeSinceStr = formatTimeSince(created as unknown as number);