diff --git a/.env.example b/.env.example deleted file mode 100644 index 5fca0004e..000000000 --- a/.env.example +++ /dev/null @@ -1,107 +0,0 @@ -# MongoDB URI -MONGODB=mongodb://localhost -AUTUMN_MONGO_URI=mongodb://database -REDIS_URI=redis://localhost/ - -# URL to where the Revolt app is publicly accessible -REVOLT_APP_URL=http://local.revolt.chat:5000 - -# URL to where the API is publicly accessible -REVOLT_PUBLIC_URL=http://local.revolt.chat:8000 -VITE_API_URL=http://local.revolt.chat:8000 - -# URL to where the WebSocket server is publicly accessible -REVOLT_EXTERNAL_WS_URL=ws://local.revolt.chat:9000 - -# URL to where Autumn is publicly available -AUTUMN_PUBLIC_URL=http://local.revolt.chat:3000 - -# URL to where January is publicly available -JANUARY_PUBLIC_URL=http://local.revolt.chat:7000 - -# URL to where Vortex is publicly available -# VOSO_PUBLIC_URL=https://voso.revolt.chat - - -## -## hCaptcha Settings -## - -# If you are sure that you don't want to use hCaptcha, set to 1. -REVOLT_UNSAFE_NO_CAPTCHA=1 - -# hCaptcha API key -# REVOLT_HCAPTCHA_KEY=0x0000000000000000000000000000000000000000 - -# hCaptcha site key -# REVOLT_HCAPTCHA_SITEKEY=10000000-ffff-ffff-ffff-000000000001 - - -## -## Email Settings -## - -# If you are sure that you don't want to use email verification, set to 1. -REVOLT_UNSAFE_NO_EMAIL=1 - -# SMTP host -# REVOLT_SMTP_HOST=smtp.example.com - -# SMTP username -# REVOLT_SMTP_USERNAME=noreply@example.com - -# SMTP password -# REVOLT_SMTP_PASSWORD=CHANGEME - -# SMTP From header -# REVOLT_SMTP_FROM=Revolt - - -## -## Application Settings -## - -# Whether to enable staging only features -REVOLT_IS_STAGING=1 - -# Whether to only allow users to sign up if they have an invite code -REVOLT_INVITE_ONLY=0 - -# Maximum number of people that can be in a group chat -REVOLT_MAX_GROUP_SIZE=150 - -# VAPID keys for push notifications -# Generate using this guide: https://gitlab.insrt.uk/revolt/delta/-/wikis/vapid -# --> Please replace these keys before going into production! <-- -REVOLT_VAPID_PRIVATE_KEY=LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUJSUWpyTWxLRnBiVWhsUHpUbERvcEliYk1yeVNrNXpKYzVYVzIxSjJDS3hvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFWnkrQkg2TGJQZ2hEa3pEempXOG0rUXVPM3pCajRXT1phdkR6ZU00c0pqbmFwd1psTFE0WAp1ZDh2TzVodU94QWhMQlU3WWRldVovWHlBdFpWZmNyQi9BPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= -REVOLT_VAPID_PUBLIC_KEY=BGcvgR-i2z4IQ5Mw841vJvkLjt8wY-FjmWrw83jOLCY52qcGZS0OF7nfLzuYbjsQISwVO2HXrmf18gLWVX3Kwfw= - - -## -## Vortex configuration -## - -# VOSO_MANAGE_TOKEN=CHANGEME - - -## -## Autumn configuration -## - -# S3 Region -AUTUMN_S3_REGION=minio - -# S3 Endpoint -AUTUMN_S3_ENDPOINT=http://minio:9000 - -# MinIO Root User -MINIO_ROOT_USER=minioautumn - -# MinIO Root Password -MINIO_ROOT_PASSWORD=minioautumn - -# AWS Access Key ID -AWS_ACCESS_KEY_ID=minioautumn - -# AWS Secret Key -AWS_SECRET_ACCESS_KEY=minioautumn diff --git a/.github/workflows/book.yml b/.github/workflows/book.yml new file mode 100644 index 000000000..00f7ef712 --- /dev/null +++ b/.github/workflows/book.yml @@ -0,0 +1,37 @@ +name: Build documentation +on: + push: + branches: + - main + +jobs: + deploy: + runs-on: ubuntu-latest + permissions: + contents: write # To push a branch + pages: write # To push to a GitHub Pages site + id-token: write # To update the deployment status + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Install latest mdbook + run: | + tag=$(curl 'https://api.github.com/repos/rust-lang/mdbook/releases/latest' | jq -r '.tag_name') + url="https://github.com/rust-lang/mdbook/releases/download/${tag}/mdbook-${tag}-x86_64-unknown-linux-gnu.tar.gz" + mkdir mdbook + curl -sSL $url | tar -xz --directory=./mdbook + echo `pwd`/mdbook >> $GITHUB_PATH + - name: Build Book + run: | + cd doc + mdbook build + - name: Setup Pages + uses: actions/configure-pages@v4 + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: "doc/book" + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml deleted file mode 100644 index 8a02c492d..000000000 --- a/.github/workflows/cla.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: "CLA Assistant" -on: - issue_comment: - types: [created] - pull_request_target: - types: [opened,closed,synchronize] - -jobs: - CLAssistant: - runs-on: ubuntu-latest - steps: - - name: "CLA Assistant" - if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' - # Beta Release - uses: cla-assistant/github-action@v2.1.3-beta - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # the below token should have repo scope and must be manually added by you in the repository's secret - PERSONAL_ACCESS_TOKEN : ${{ secrets.PAT }} - with: - path-to-signatures: 'signatures/version1/cla.json' - path-to-document: 'https://github.com/revoltchat/cla/blob/master/CLA.md' # e.g. a CLA or a DCO document - # branch should not be protected - branch: 'master' - allowlist: insertish,bot* - - #below are the optional inputs - If the optional inputs are not given, then default values will be taken - remote-organization-name: revoltchat - remote-repository-name: cla - create-file-commit-message: 'cla(create): creating file for storing CLA Signatures' - signed-commit-message: 'cla(sign): $contributorName has signed the CLA in #$pullRequestNo' - #custom-notsigned-prcomment: 'pull request comment with Introductory message to ask new contributors to sign' - #custom-pr-sign-comment: 'The signature to be committed in order to sign the CLA' - #custom-allsigned-prcomment: 'pull request comment when all contributors has signed, defaults to **CLA Assistant Lite bot** All Contributors have signed the CLA.' - #lock-pullrequest-aftermerge: false - if you don't want this bot to automatically lock the pull request after merging (default - true) - #use-dco-flag: true - If you are using DCO instead of CLA diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 4f941cd26..58e186d0b 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -2,14 +2,15 @@ name: Docker Test & Publish on: push: - branches: - - "main" + # branches: + # - "main" tags: - "*" paths-ignore: - ".github/**" - "!.github/workflows/docker.yml" - ".vscode/**" + - "doc/**" - ".gitignore" - "LICENSE" - "README" @@ -60,7 +61,7 @@ jobs: if: github.event_name != 'pull_request' strategy: matrix: - project: [delta, bonfire] + project: [delta, bonfire, autumn, january, pushd] name: Build ${{ matrix.project }} image steps: # Configure build environment @@ -97,6 +98,18 @@ jobs: "bonfire": { "path": "crates/bonfire", "tag": "${{ github.repository_owner }}/bonfire" + }, + "autumn": { + "path": "crates/services/autumn", + "tag": "${{ github.repository_owner }}/autumn" + }, + "january": { + "path": "crates/services/january", + "tag": "${{ github.repository_owner }}/january" + }, + "pushd": { + "path": "crates/daemons/pushd", + "tag": "${{ github.repository_owner }}/pushd" } } export_to: output diff --git a/.github/workflows/rust.yaml b/.github/workflows/rust.yaml index 34aa403eb..f0bd33894 100644 --- a/.github/workflows/rust.yaml +++ b/.github/workflows/rust.yaml @@ -2,6 +2,14 @@ name: Rust build, test, and generate specification on: push: + paths-ignore: + - ".github/**" + - "!.github/workflows/rust.yaml" + - ".vscode/**" + - "doc/**" + - ".gitignore" + - "LICENSE" + - "README" pull_request: branches: [main] @@ -33,11 +41,7 @@ jobs: - name: Run services in background run: | - docker-compose -f docker-compose.db.yml up -d - - - name: Copy .env.example - run: | - cp .env.example .env + docker compose -f compose.yml up -d - name: Run cargo test env: @@ -63,7 +67,7 @@ jobs: if: github.event_name != 'pull_request' && github.ref_name == 'main' uses: nev7n/wait_for_response@v1 with: - url: "http://localhost:8000/" + url: "http://localhost:14702/" - name: Checkout API repository if: github.event_name != 'pull_request' && github.ref_name == 'main' @@ -75,7 +79,7 @@ jobs: - name: Download OpenAPI specification if: github.event_name != 'pull_request' && github.ref_name == 'main' - run: curl http://localhost:8000/openapi.json -o api/OpenAPI.json + run: curl http://localhost:14702/openapi.json -o api/OpenAPI.json - name: Commit changes if: github.event_name != 'pull_request' && github.ref_name == 'main' diff --git a/.gitignore b/.gitignore index 6293bc546..3699f3fc4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ Rocket.toml -Revolt.toml +Revolt.*.toml target .data @@ -8,4 +8,6 @@ target .vercel .DS_Store -livekit.yml \ No newline at end of file +livekit.yml +.idea +start \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index b2c3c98cf..7f8ecfcd4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,5 +2,5 @@ "editor.formatOnSave": true, "rust-analyzer.checkOnSave.command": "clippy", "nixEnvSelector.suggestion": false, - "nixEnvSelector.nixFile": "${workspaceRoot}/default.nix" -} + "nixEnvSelector.nixFile": "${workspaceFolder}/default.nix" +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index c32da1943..51db43a61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,21 +1,21 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aead" @@ -85,11 +85,42 @@ dependencies = [ "memchr", ] +[[package]] +name = "aligned-vec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" + [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "amqp_serde" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "787581044ca08ad61cdb3a4e21afba087fb8cdba3bb3e23ce69a7d091808014d" +dependencies = [ + "bytes 1.9.0", + "serde", + "serde_bytes_ng", +] + +[[package]] +name = "amqprs" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "3f1b4afcbd862e16c272b7625b6b057930b052d63c720bc90f6afab0d9abe8a8" +dependencies = [ + "amqp_serde", + "async-trait", + "bytes 1.9.0", + "serde", + "serde_bytes_ng", + "tokio 1.43.0", +] [[package]] name = "android-tzdata" @@ -106,11 +137,67 @@ 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.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +dependencies = [ + "anstyle", + "once_cell", + "windows-sys 0.59.0", +] + [[package]] name = "anyhow" -version = "1.0.81" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" [[package]] name = "arc-swap" @@ -118,17 +205,28 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote 1.0.38", + "syn 2.0.96", +] + [[package]] name = "arrayref" -version = "0.3.7" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "async-attributes" @@ -136,7 +234,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" dependencies = [ - "quote 1.0.35", + "quote 1.0.38", "syn 1.0.109", ] @@ -153,28 +251,26 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.2.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" dependencies = [ "concurrent-queue", - "event-listener 5.3.0", - "event-listener-strategy 0.5.1", + "event-listener-strategy", "futures-core", - "pin-project-lite 0.2.14", + "pin-project-lite 0.2.16", ] [[package]] name = "async-executor" -version = "1.10.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f98c37cf288e302c16ef6c8472aad1e034c6c84ce5ea7b8101c98eb4a802fee" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" dependencies = [ - "async-lock 3.3.0", "async-task", "concurrent-queue", - "fastrand 2.0.2", - "futures-lite 2.3.0", + "fastrand 2.3.0", + "futures-lite 2.6.0", "slab", ] @@ -184,54 +280,34 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "async-channel 2.2.0", + "async-channel 2.3.1", "async-executor", - "async-io 2.3.2", - "async-lock 3.3.0", + "async-io", + "async-lock 3.4.0", "blocking", - "futures-lite 2.3.0", + "futures-lite 2.6.0", "once_cell", "tokio 0.2.25", - "tokio 1.37.0", -] - -[[package]] -name = "async-io" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" -dependencies = [ - "async-lock 2.8.0", - "autocfg 1.2.0", - "cfg-if", - "concurrent-queue", - "futures-lite 1.13.0", - "log", - "parking", - "polling 2.8.0", - "rustix 0.37.27", - "slab", - "socket2 0.4.10", - "waker-fn", + "tokio 1.43.0", ] [[package]] name = "async-io" -version = "2.3.2" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" dependencies = [ - "async-lock 3.3.0", + "async-lock 3.4.0", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.3.0", + "futures-lite 2.6.0", "parking", - "polling 3.6.0", - "rustix 0.38.32", + "polling 3.7.4", + "rustix", "slab", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -245,84 +321,86 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 4.0.3", - "event-listener-strategy 0.4.0", - "pin-project-lite 0.2.14", + "event-listener 5.4.0", + "event-listener-strategy", + "pin-project-lite 0.2.16", ] [[package]] name = "async-process" -version = "1.8.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" +checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" dependencies = [ - "async-io 1.13.0", - "async-lock 2.8.0", + "async-channel 2.3.1", + "async-io", + "async-lock 3.4.0", "async-signal", + "async-task", "blocking", "cfg-if", - "event-listener 3.1.0", - "futures-lite 1.13.0", - "rustix 0.38.32", - "windows-sys 0.48.0", + "event-listener 5.4.0", + "futures-lite 2.6.0", + "rustix", + "tracing", ] [[package]] name = "async-recursion" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", - "quote 1.0.35", - "syn 2.0.58", + "quote 1.0.38", + "syn 2.0.96", ] [[package]] name = "async-signal" -version = "0.2.5" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" dependencies = [ - "async-io 2.3.2", - "async-lock 2.8.0", + "async-io", + "async-lock 3.4.0", "atomic-waker", "cfg-if", "futures-core", "futures-io", - "rustix 0.38.32", + "rustix", "signal-hook-registry", "slab", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] name = "async-std" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +checksum = "c634475f29802fde2b8f0b505b1bd00dfe4df7d4a000f0b36f7671197d5c3615" dependencies = [ "async-attributes", "async-channel 1.9.0", "async-global-executor", - "async-io 1.13.0", - "async-lock 2.8.0", + "async-io", + "async-lock 3.4.0", "async-process", "crossbeam-utils", "futures-channel", "futures-core", "futures-io", - "futures-lite 1.13.0", + "futures-lite 2.6.0", "gloo-timers", "kv-log-macro", "log", "memchr", "once_cell", - "pin-project-lite 0.2.14", + "pin-project-lite 0.2.16", "pin-utils", "slab", "wasm-bindgen-futures", @@ -345,41 +423,41 @@ dependencies = [ [[package]] name = "async-stream" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ "async-stream-impl", "futures-core", - "pin-project-lite 0.2.14", + "pin-project-lite 0.2.16", ] [[package]] name = "async-stream-impl" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", - "quote 1.0.35", - "syn 2.0.58", + "quote 1.0.38", + "syn 2.0.96", ] [[package]] name = "async-task" -version = "4.7.0" +version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.79" +version = "0.1.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" +checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" dependencies = [ "proc-macro2", - "quote 1.0.35", - "syn 2.0.58", + "quote 1.0.38", + "syn 2.0.96", ] [[package]] @@ -392,7 +470,7 @@ dependencies = [ "futures-io", "futures-util", "log", - "pin-project-lite 0.2.14", + "pin-project-lite 0.2.16", "tungstenite", ] @@ -430,9 +508,9 @@ dependencies = [ [[package]] name = "authifier" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30269caf0aaf1e1b542b150030e9688bf41d50026e09a51efd9408f332636c9d" +checksum = "9ba4df3b5df5cf1a08d4af71c407fb56a675b6aaf4d1fec704da32595497d73d" dependencies = [ "async-std", "async-trait", @@ -449,7 +527,7 @@ dependencies = [ "nanoid", "rand 0.8.5", "regex", - "reqwest", + "reqwest 0.11.27", "revolt_okapi", "revolt_rocket_okapi", "rocket", @@ -474,170 +552,711 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" dependencies = [ - "autocfg 1.2.0", + "autocfg 1.4.0", ] [[package]] name = "autocfg" -version = "1.2.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] -name = "backtrace" -version = "0.3.71" +name = "av1-grain" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", ] [[package]] -name = "base16ct" -version = "0.2.0" +name = "avif-serialize" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +checksum = "e335041290c43101ca215eed6f43ec437eb5a42125573f600fc3fa42b9bddd62" +dependencies = [ + "arrayvec", +] [[package]] -name = "base32" -version = "0.4.0" +name = "aws-config" +version = "1.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa" +checksum = "dc47e70fc35d054c8fcd296d47a61711f043ac80534a10b4f741904f81e73a90" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-sdk-sso", + "aws-sdk-ssooidc", + "aws-sdk-sts", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes 1.9.0", + "fastrand 2.3.0", + "hex", + "http 0.2.12", + "ring 0.17.8", + "time", + "tokio 1.43.0", + "tracing", + "url", + "zeroize", +] [[package]] -name = "base64" -version = "0.13.1" +name = "aws-credential-types" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "60e8f6b615cb5fc60a98132268508ad104310f0cfb25a1c22eee76efdf9154da" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "zeroize", +] [[package]] -name = "base64" -version = "0.20.0" +name = "aws-runtime" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" +checksum = "bee7643696e7fdd74c10f9eb42848a87fe469d35eae9c3323f80aa98f350baac" +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 1.9.0", + "fastrand 2.3.0", + "http 0.2.12", + "http-body 0.4.6", + "once_cell", + "percent-encoding", + "pin-project-lite 0.2.16", + "tracing", + "uuid", +] [[package]] -name = "base64" -version = "0.21.7" +name = "aws-sdk-s3" +version = "1.71.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "705cb534fb7b620f540d2fac268d378918dc67d25b173f67edcab3851fe19392" +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 1.9.0", + "fastrand 2.3.0", + "hex", + "hmac", + "http 0.2.12", + "http-body 0.4.6", + "lru 0.12.5", + "once_cell", + "percent-encoding", + "regex-lite", + "sha2", + "tracing", + "url", +] [[package]] -name = "base64" -version = "0.22.0" +name = "aws-sdk-sso" +version = "1.56.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" +checksum = "12e057fdcb8842de9b83592a70f5b4da0ee10bc0ad278247da1425a742a444d7" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes 1.9.0", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] [[package]] -name = "base64ct" -version = "1.6.0" +name = "aws-sdk-ssooidc" +version = "1.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +checksum = "a120ade4a44691b3c5c2ff2fa61b14ed331fdc218397f61ab48d66593012ae2a" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes 1.9.0", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] [[package]] -name = "binascii" -version = "0.1.4" +name = "aws-sdk-sts" +version = "1.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" +checksum = "115fd4fb663817ed595a5ee4f1649d7aacd861d47462323cb37576ce89271b93" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-query", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] [[package]] -name = "bincode" -version = "1.3.3" +name = "aws-sigv4" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +checksum = "690118821e46967b3c4501d67d7d52dd75106a9c54cf36cefa1985cedbe94e05" dependencies = [ - "serde", + "aws-credential-types", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes 1.9.0", + "crypto-bigint 0.5.5", + "form_urlencoded", + "hex", + "hmac", + "http 0.2.12", + "http 1.2.0", + "once_cell", + "p256 0.11.1", + "percent-encoding", + "ring 0.17.8", + "sha2", + "subtle", + "time", + "tracing", + "zeroize", ] [[package]] -name = "binstring" -version = "0.1.1" +name = "aws-smithy-async" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e0d60973d9320722cb1206f412740e162a33b8547ea8d6be75d7cff237c7a85" +checksum = "fa59d1327d8b5053c54bf2eaae63bf629ba9e904434d0835a28ed3c0ed0a614e" +dependencies = [ + "futures-util", + "pin-project-lite 0.2.16", + "tokio 1.43.0", +] [[package]] -name = "bitfield" -version = "0.13.2" +name = "aws-smithy-checksums" +version = "0.62.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" +checksum = "f2f45a1c384d7a393026bc5f5c177105aa9fa68e4749653b985707ac27d77295" +dependencies = [ + "aws-smithy-http", + "aws-smithy-types", + "bytes 1.9.0", + "crc32c", + "crc32fast", + "crc64fast-nvme", + "hex", + "http 0.2.12", + "http-body 0.4.6", + "md-5", + "pin-project-lite 0.2.16", + "sha1", + "sha2", + "tracing", +] [[package]] -name = "bitflags" -version = "1.3.2" +name = "aws-smithy-eventstream" +version = "0.60.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "8b18559a41e0c909b77625adf2b8c50de480a8041e5e4a3f5f7d177db70abc5a" +dependencies = [ + "aws-smithy-types", + "bytes 1.9.0", + "crc32fast", +] [[package]] -name = "bitflags" -version = "2.5.0" +name = "aws-smithy-http" +version = "0.60.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "7809c27ad8da6a6a68c454e651d4962479e81472aa19ae99e59f9aba1f9713cc" +dependencies = [ + "aws-smithy-eventstream", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes 1.9.0", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http-body 0.4.6", + "once_cell", + "percent-encoding", + "pin-project-lite 0.2.16", + "pin-utils", + "tracing", +] [[package]] -name = "bitvec" -version = "1.0.1" +name = "aws-smithy-json" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +checksum = "623a51127f24c30776c8b374295f2df78d92517386f77ba30773f15a30ce1422" dependencies = [ - "funty", - "radium", - "tap", - "wyz", + "aws-smithy-types", ] [[package]] -name = "blake2b_simd" -version = "1.0.2" +name = "aws-smithy-query" +version = "0.60.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" +checksum = "f2fbd61ceb3fe8a1cb7352e42689cec5335833cd9f94103a61e98f9bb61c64bb" dependencies = [ - "arrayref", - "arrayvec", - "constant_time_eq", + "aws-smithy-types", + "urlencoding", ] [[package]] -name = "block-buffer" -version = "0.10.4" +name = "aws-smithy-runtime" +version = "1.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +checksum = "865f7050bbc7107a6c98a397a9fcd9413690c27fa718446967cf03b2d3ac517e" dependencies = [ - "generic-array 0.14.7", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes 1.9.0", + "fastrand 2.3.0", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "http-body 1.0.1", + "httparse", + "hyper 0.14.32", + "hyper-rustls 0.24.2", + "once_cell", + "pin-project-lite 0.2.16", + "pin-utils", + "rustls 0.21.12", + "tokio 1.43.0", + "tracing", ] [[package]] -name = "blocking" -version = "1.5.1" +name = "aws-smithy-runtime-api" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" +checksum = "92165296a47a812b267b4f41032ff8069ab7ff783696d217f0994a0d7ab585cd" dependencies = [ - "async-channel 2.2.0", - "async-lock 3.3.0", - "async-task", - "fastrand 2.0.2", - "futures-io", - "futures-lite 2.3.0", - "piper", + "aws-smithy-async", + "aws-smithy-types", + "bytes 1.9.0", + "http 0.2.12", + "http 1.2.0", + "pin-project-lite 0.2.16", + "tokio 1.43.0", "tracing", + "zeroize", ] [[package]] -name = "bson" -version = "2.10.0" +name = "aws-smithy-types" +version = "1.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d43b38e074cc0de2957f10947e376a1d88b9c4dbab340b590800cc1b2e066b2" +checksum = "a28f6feb647fb5e0d5b50f0472c19a7db9462b74e2fec01bb0b44eedcc834e97" +dependencies = [ + "base64-simd", + "bytes 1.9.0", + "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 0.2.16", + "pin-utils", + "ryu", + "serde", + "time", + "tokio 1.43.0", + "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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0df5a18c4f951c645300d365fec53a61418bcf4650f604f85fe2a665bfaa0c2" +dependencies = [ + "aws-credential-types", + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "rustc_version 0.4.1", + "tracing", +] + +[[package]] +name = "axum" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +dependencies = [ + "async-trait", + "axum-core", + "bytes 1.9.0", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.5.2", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "multer", + "percent-encoding", + "pin-project-lite 0.2.16", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper 1.0.2", + "tokio 1.43.0", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes 1.9.0", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite 0.2.16", + "rustversion", + "sync_wrapper 1.0.2", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-extra" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c794b30c904f0a1c2fb7740f7df7f7972dfaa14ef6f57cb6178dc63e5dca2f04" +dependencies = [ + "axum", + "axum-core", + "bytes 1.9.0", + "fastrand 2.3.0", + "futures-util", + "headers", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "multer", + "pin-project-lite 0.2.16", + "serde", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" +dependencies = [ + "proc-macro2", + "quote 1.0.38", + "syn 2.0.96", +] + +[[package]] +name = "axum_typed_multipart" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05679dcdfeccf4cc6a8f45b80318fff8a9908337da1cfeb6acca00562a345b4d" +dependencies = [ + "anyhow", + "axum", + "axum_typed_multipart_macros", + "bytes 1.9.0", + "chrono", + "futures-core", + "futures-util", + "tempfile", + "thiserror 1.0.69", + "tokio 1.43.0", + "uuid", +] + +[[package]] +name = "axum_typed_multipart_macros" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11eeb275b20a4c750c9fe7bf5a750e97e7944563003efd1c82e70c229a612ca1" +dependencies = [ + "darling 0.20.10", + "heck 0.5.0", + "proc-macro-error", + "quote 1.0.38", + "syn 2.0.96", + "ubyte", +] + +[[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 = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base32" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa" + +[[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 = "binascii" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "binstring" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed79c2a8151273c70956b5e3cdfdc1ff6c1a8b9779ba59c6807d281b32ee2f86" + +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" + +[[package]] +name = "bitstream-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" + +[[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 = "blake2b_simd" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel 2.3.1", + "async-task", + "futures-io", + "futures-lite 2.6.0", + "piper", +] + +[[package]] +name = "bson" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "068208f2b6fcfa27a7f1ee37488d2bb8ba2640f68f5475d08e1d9130696aba59" dependencies = [ "ahash 0.8.11", "base64 0.13.1", "bitvec", "hex", - "indexmap 2.2.6", + "indexmap 2.7.1", "js-sys", "once_cell", "rand 0.8.5", @@ -648,6 +1267,12 @@ dependencies = [ "uuid", ] +[[package]] +name = "built" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c360505aed52b7ec96a3636c3f039d99103c37d1d9b4f7a8c743d3ea9ffcd03b" + [[package]] name = "bumpalo" version = "3.16.0" @@ -656,9 +1281,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.15.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" [[package]] name = "byteorder" @@ -666,6 +1291,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "0.5.6" @@ -674,9 +1305,9 @@ checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" [[package]] name = "bytes" -version = "1.6.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "bytes-utils" @@ -684,7 +1315,7 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" dependencies = [ - "bytes 1.6.0", + "bytes 1.9.0", "either", ] @@ -701,8 +1332,8 @@ dependencies = [ "hashbrown 0.13.2", "instant", "once_cell", - "thiserror", - "tokio 1.37.0", + "thiserror 1.0.69", + "tokio 1.43.0", ] [[package]] @@ -714,7 +1345,7 @@ dependencies = [ "cached_proc_macro_types", "darling 0.14.4", "proc-macro2", - "quote 1.0.35", + "quote 1.0.38", "syn 1.0.109", ] @@ -730,14 +1361,55 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" +[[package]] +name = "cbindgen" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fce8dd7fcfcbf3a0a87d8f515194b49d6135acab73e18bd380d1d93bb1a15eb" +dependencies = [ + "clap", + "heck 0.4.1", + "indexmap 2.7.1", + "log", + "proc-macro2", + "quote 1.0.38", + "serde", + "serde_json", + "syn 2.0.96", + "tempfile", + "toml 0.8.19", +] + [[package]] name = "cc" -version = "1.0.92" +version = "1.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41" +checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" dependencies = [ "jobserver", "libc", + "shlex", +] + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", ] [[package]] @@ -748,16 +1420,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.37" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.52.4", + "windows-targets 0.52.6", ] [[package]] @@ -770,6 +1442,33 @@ dependencies = [ "inout", ] +[[package]] +name = "clap" +version = "4.5.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim 0.11.1", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + [[package]] name = "cloudabi" version = "0.0.3" @@ -781,34 +1480,46 @@ dependencies = [ [[package]] name = "coarsetime" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13b3839cf01bb7960114be3ccf2340f541b6d0c81f8690b007b2b39f750f7e5d" +checksum = "4252bf230cb600c19826a575b31c8c9c84c6f11acfab6dfcad2e941b10b6f8e2" dependencies = [ "libc", "wasix", "wasm-bindgen", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + [[package]] name = "combine" -version = "4.6.6" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ - "bytes 1.6.0", + "bytes 1.9.0", "futures-core", "memchr", - "pin-project-lite 0.2.14", - "tokio 1.37.0", + "pin-project-lite 0.2.16", + "tokio 1.43.0", "tokio-util", ] [[package]] name = "concurrent-queue" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", ] @@ -846,9 +1557,9 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "constant_time_eq" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] name = "convert_case" @@ -858,87 +1569,167 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "cookie" -version = "0.16.2" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" dependencies = [ - "aes-gcm", - "base64 0.20.0", - "hkdf", - "hmac", "percent-encoding", - "rand 0.8.5", - "sha2", - "subtle", "time", "version_check", ] [[package]] -name = "cookie" -version = "0.18.1" +name = "cookie-factory" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" dependencies = [ - "percent-encoding", - "time", - "version_check", + "futures", +] + +[[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 = "core_maths" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b02505ccb8c50b0aa21ace0fc08c3e53adebd4e58caa18a36152803c7709a3" +dependencies = [ + "libm", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crc16" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "338089f42c427b86394a5ee60ff321da23a5c89c9d89514c829687b26359fcff" + +[[package]] +name = "crc32c" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a47af21622d091a8f0fb295b88bc886ac74efcc613efc19f5d0b21de5c89e47" +dependencies = [ + "rustc_version 0.4.1", ] [[package]] -name = "cookie-factory" -version = "0.3.3" +name = "crc32fast" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ - "futures", + "cfg-if", ] [[package]] -name = "core-foundation" -version = "0.9.4" +name = "crc64fast-nvme" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +checksum = "d5e2ee08013e3f228d6d2394116c4549a6df77708442c62d887d83f68ef2ee37" dependencies = [ - "core-foundation-sys", - "libc", + "cbindgen", + "crc", ] [[package]] -name = "core-foundation-sys" -version = "0.8.6" +name = "crossbeam-channel" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" +dependencies = [ + "crossbeam-utils", +] [[package]] -name = "cpufeatures" -version = "0.2.12" +name = "crossbeam-deque" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ - "libc", + "crossbeam-epoch", + "crossbeam-utils", ] [[package]] -name = "crc16" -version = "0.4.0" +name = "crossbeam-epoch" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "338089f42c427b86394a5ee60ff321da23a5c89c9d89514c829687b26359fcff" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] [[package]] name = "crossbeam-queue" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" + +[[package]] +name = "crypto-bigint" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle", + "zeroize", +] [[package]] name = "crypto-bigint" @@ -963,11 +1754,34 @@ dependencies = [ "typenum", ] +[[package]] +name = "cssparser" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b3df4f93e5fbbe73ec01ec8d3f68bba73107993a5b1e7519273c32db9b0d5be" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf 0.11.3", + "smallvec", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote 1.0.38", + "syn 2.0.96", +] + [[package]] name = "ct-codecs" -version = "1.1.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3b7eb4404b8195a9abb6356f4ac07d8ba267045c8d6d220ac4dc992e6cc75df" +checksum = "b916ba8ce9e4182696896f015e8a5ae6081b305f74690baa8465e35f5a142ea4" [[package]] name = "ctr" @@ -980,24 +1794,24 @@ dependencies = [ [[package]] name = "curl" -version = "0.4.46" +version = "0.4.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e2161dd6eba090ff1594084e95fd67aeccf04382ffea77999ea94ed42ec67b6" +checksum = "d9fb4d13a1be2b58f14d60adba57c9834b78c62fd86c3e76a148f732686e9265" dependencies = [ "curl-sys", "libc", "openssl-probe", "openssl-sys", "schannel", - "socket2 0.5.6", + "socket2 0.5.8", "windows-sys 0.52.0", ] [[package]] name = "curl-sys" -version = "0.4.72+curl-8.6.0" +version = "0.4.78+curl-8.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29cbdc8314c447d11e8fd156dcdd031d9e02a7a976163e396b548c03153bc9ea" +checksum = "8eec768341c5c7789611ae51cf6c459099f22e64a5d5d0ce4892434e33821eaf" dependencies = [ "cc", "libc", @@ -1029,6 +1843,16 @@ dependencies = [ "darling_macro 0.14.4", ] +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core 0.20.10", + "darling_macro 0.20.10", +] + [[package]] name = "darling_core" version = "0.13.4" @@ -1038,8 +1862,8 @@ dependencies = [ "fnv", "ident_case", "proc-macro2", - "quote 1.0.35", - "strsim", + "quote 1.0.38", + "strsim 0.10.0", "syn 1.0.109", ] @@ -1052,11 +1876,25 @@ dependencies = [ "fnv", "ident_case", "proc-macro2", - "quote 1.0.35", - "strsim", + "quote 1.0.38", + "strsim 0.10.0", "syn 1.0.109", ] +[[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 1.0.38", + "strsim 0.11.1", + "syn 2.0.96", +] + [[package]] name = "darling_macro" version = "0.13.4" @@ -1064,7 +1902,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ "darling_core 0.13.4", - "quote 1.0.35", + "quote 1.0.38", "syn 1.0.109", ] @@ -1075,10 +1913,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ "darling_core 0.14.4", - "quote 1.0.35", + "quote 1.0.38", "syn 1.0.109", ] +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core 0.20.10", + "quote 1.0.38", + "syn 2.0.96", +] + [[package]] name = "dashmap" version = "5.5.3" @@ -1086,7 +1935,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core", @@ -1094,9 +1943,15 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.5.0" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" + +[[package]] +name = "data-url" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" [[package]] name = "deadqueue" @@ -1105,7 +1960,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16a2561fd313df162315935989dceb8c99db4ee1933358270a57a3cfb8c957f3" dependencies = [ "crossbeam-queue", - "tokio 1.37.0", + "tokio 1.43.0", ] [[package]] @@ -1163,9 +2018,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8aed3b3c608dc56cf36c45fe979d04eda51242e6703d8d0bb03426ef7c41db6a" dependencies = [ "proc-macro2", - "quote 1.0.35", + "quote 1.0.38", "syn 1.0.109", - "synstructure", + "synstructure 0.12.6", ] [[package]] @@ -1175,6 +2030,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", + "serde", ] [[package]] @@ -1184,28 +2040,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2", - "quote 1.0.35", + "quote 1.0.38", "syn 1.0.109", ] [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "convert_case", "proc-macro2", - "quote 1.0.35", - "rustc_version 0.4.0", - "syn 1.0.109", + "quote 1.0.38", + "rustc_version 0.4.1", + "syn 2.0.96", ] [[package]] name = "devise" -version = "0.3.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c7580b072f1c8476148f16e0a0d5dedddab787da98d86c5082c5e9ed8ab595" +checksum = "f1d90b0c4c777a2cad215e3c7be59ac7c15adf45cf76317009b7d096d46f651d" dependencies = [ "devise_codegen", "devise_core", @@ -1213,25 +2069,25 @@ dependencies = [ [[package]] name = "devise_codegen" -version = "0.3.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "123c73e7a6e51b05c75fe1a1b2f4e241399ea5740ed810b0e3e6cacd9db5e7b2" +checksum = "71b28680d8be17a570a2334922518be6adc3f58ecc880cbb404eaeb8624fd867" dependencies = [ "devise_core", - "quote 1.0.35", + "quote 1.0.38", ] [[package]] name = "devise_core" -version = "0.3.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841ef46f4787d9097405cac4e70fb8644fc037b526e8c14054247c0263c400d0" +checksum = "b035a542cf7abf01f2e3c4d5a7acbaebfefe120ae4efc7bde3df98186e4b8af7" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.8.0", "proc-macro2", - "proc-macro2-diagnostics 0.9.1", - "quote 1.0.35", - "syn 1.0.109", + "proc-macro2-diagnostics", + "quote 1.0.38", + "syn 2.0.96", ] [[package]] @@ -1246,6 +2102,17 @@ dependencies = [ "subtle", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote 1.0.38", + "syn 2.0.96", +] + [[package]] name = "dlv-list" version = "0.3.0" @@ -1253,10 +2120,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" [[package]] -name = "dotenv" -version = "0.15.0" +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "dtoa-short" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] [[package]] name = "dyn-clone" @@ -1264,6 +2140,18 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + [[package]] name = "ecdsa" version = "0.16.9" @@ -1272,8 +2160,8 @@ checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der 0.7.9", "digest", - "elliptic-curve", - "rfc6979", + "elliptic-curve 0.13.8", + "rfc6979 0.4.0", "signature 2.2.0", "spki 0.7.3", ] @@ -1293,7 +2181,7 @@ dependencies = [ "openssl", "serde", "sha2", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1306,11 +2194,37 @@ dependencies = [ "getrandom", ] +[[package]] +name = "ego-tree" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12a0bb14ac04a9fcf170d0bbbef949b44cc492f4452bd20c095636956f653642" + [[package]] name = "either" -version = "1.10.0" +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 = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest", + "ff 0.12.1", + "generic-array 0.14.7", + "group 0.12.1", + "pkcs8 0.9.0", + "rand_core 0.6.4", + "sec1 0.3.0", + "subtle", + "zeroize", +] [[package]] name = "elliptic-curve" @@ -1318,17 +2232,17 @@ version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ - "base16ct", - "crypto-bigint", + "base16ct 0.2.0", + "crypto-bigint 0.5.5", "digest", - "ff", + "ff 0.13.0", "generic-array 0.14.7", - "group", + "group 0.13.0", "hkdf", "pem-rfc7468 0.7.0", "pkcs8 0.10.2", "rand_core 0.6.4", - "sec1", + "sec1 0.7.3", "subtle", "zeroize", ] @@ -1339,21 +2253,21 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a87260449b06739ee78d6281c68d2a0ff3e3af64a78df63d3a1aeb3c06997c8a" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "memchr", ] [[package]] name = "email_address" -version = "0.2.4" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2153bd83ebc09db15bcbdc3e2194d901804952e3dc96967e1cd3b0c5c32d112" +checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] @@ -1366,7 +2280,7 @@ checksum = "21cdad81446a7f7dc43f6a77409efeb9733d2fa65553efef6018ef257c959b73" dependencies = [ "heck 0.4.1", "proc-macro2", - "quote 1.0.35", + "quote 1.0.38", "syn 1.0.109", ] @@ -1381,13 +2295,13 @@ dependencies = [ [[package]] name = "enum-iterator-derive" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03cdc46ec28bd728e67540c528013c6a10eb69a02eb31078a1bda695438cbfb8" +checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b" dependencies = [ "proc-macro2", - "quote 1.0.35", - "syn 2.0.58", + "quote 1.0.38", + "syn 2.0.96", ] [[package]] @@ -1420,12 +2334,12 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1436,55 +2350,38 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite 0.2.14", -] - -[[package]] -name = "event-listener" -version = "4.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite 0.2.14", -] - -[[package]] -name = "event-listener" -version = "5.3.0" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" dependencies = [ "concurrent-queue", "parking", - "pin-project-lite 0.2.14", + "pin-project-lite 0.2.16", ] [[package]] name = "event-listener-strategy" -version = "0.4.0" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" dependencies = [ - "event-listener 4.0.3", - "pin-project-lite 0.2.14", + "event-listener 5.4.0", + "pin-project-lite 0.2.16", ] [[package]] -name = "event-listener-strategy" -version = "0.5.1" +name = "exr" +version = "1.73.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3" +checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0" dependencies = [ - "event-listener 5.3.0", - "pin-project-lite 0.2.14", + "bit_field", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", ] [[package]] @@ -1498,22 +2395,40 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.2" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] -name = "fcm" -version = "0.9.2" +name = "fcm_v1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8d0da8a6bd63bdec888b6d7a87c5698230005c1800823d28ddd5adb6f2550f" +checksum = "fc9ed4ed7618b2f7581edb6ce28794a10fcc335e196fc2bcf56afee99ad63d10" dependencies = [ - "chrono", - "erased-serde", - "log", - "reqwest", + "async-trait", + "reqwest 0.11.27", "serde", "serde_json", + "yup-oauth2", +] + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", ] [[package]] @@ -1526,16 +2441,26 @@ dependencies = [ "subtle", ] +[[package]] +name = "ffprobe" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ffef835e1f9ac151db5bb2adbb95c9dfe1f315f987f011dd89cd655b4e9a52c" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "figment" -version = "0.10.15" +version = "0.10.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7270677e7067213e04f323b55084586195f18308cd7546cfac9f873344ccceb6" +checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" dependencies = [ "atomic 0.6.0", "pear", "serde", - "toml 0.8.12", + "toml 0.8.19", "uncased", "version_check", ] @@ -1552,18 +2477,22 @@ dependencies = [ "winapi", ] -[[package]] -name = "finl_unicode" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" - [[package]] name = "fixedbitset" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "float-cmp" version = "0.9.0" @@ -1579,6 +2508,35 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + +[[package]] +name = "fontconfig-parser" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1fcfcd44ca6e90c921fee9fa665d530b21ef1327a4c1a6c5250ea44b776ada7" +dependencies = [ + "roxmltree", +] + +[[package]] +name = "fontdb" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3a6f9af55fb97ad673fb7a69533eb2f967648a06fa21f8c9bb2cd6d33975716" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "slotmap", + "tinyvec", + "ttf-parser", +] + [[package]] name = "foreign-types" version = "0.3.2" @@ -1611,7 +2569,7 @@ checksum = "4b8e3a1339ed45ad8fde94530c4bdcbd5f371a3c6bd3bf57682923792830aa37" dependencies = [ "arc-swap", "async-trait", - "bytes 1.6.0", + "bytes 1.9.0", "bytes-utils", "crossbeam-queue", "float-cmp", @@ -1620,9 +2578,9 @@ dependencies = [ "parking_lot", "rand 0.8.5", "redis-protocol", - "semver 1.0.22", - "socket2 0.5.6", - "tokio 1.37.0", + "semver 1.0.25", + "socket2 0.5.8", + "tokio 1.43.0", "tokio-stream", "tokio-util", "url", @@ -1641,11 +2599,21 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -1658,9 +2626,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -1668,15 +2636,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -1685,9 +2653,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" @@ -1700,21 +2668,21 @@ dependencies = [ "futures-io", "memchr", "parking", - "pin-project-lite 0.2.14", + "pin-project-lite 0.2.16", "waker-fn", ] [[package]] name = "futures-lite" -version = "2.3.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" dependencies = [ - "fastrand 2.0.2", + "fastrand 2.3.0", "futures-core", "futures-io", "parking", - "pin-project-lite 0.2.14", + "pin-project-lite 0.2.16", ] [[package]] @@ -1725,31 +2693,31 @@ checksum = "45ec6fe3675af967e67c5536c0b9d44e34e6c52f86bedc4ea49c5317b8e94d06" dependencies = [ "futures-channel", "futures-task", - "tokio 1.37.0", + "tokio 1.43.0", ] [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", - "quote 1.0.35", - "syn 2.0.58", + "quote 1.0.38", + "syn 2.0.96", ] [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-timer" @@ -1759,9 +2727,9 @@ checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -1770,22 +2738,44 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.14", + "pin-project-lite 0.2.16", "pin-utils", "slab", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "generator" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" +dependencies = [ + "cc", + "libc", + "log", + "rustversion", + "windows 0.48.0", +] + [[package]] name = "generator" -version = "0.7.5" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" +checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd" dependencies = [ - "cc", + "cfg-if", "libc", "log", "rustversion", - "windows", + "windows 0.58.0", ] [[package]] @@ -1801,18 +2791,27 @@ dependencies = [ [[package]] name = "generic-array" -version = "1.0.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe739944a5406424e080edccb6add95685130b9f160d5407c639c7df0c5836b0" +checksum = "e8c8444bc9d71b935156cc0ccab7f622180808af7867b1daae6547d773591703" dependencies = [ "typenum", ] +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", @@ -1823,14 +2822,14 @@ dependencies = [ [[package]] name = "getset" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e45727250e75cc04ff2846a66397da8ef2b3db8e40e0cef4df67950a07621eb9" +checksum = "eded738faa0e88d3abc9d1a13cb11adc2073c400969eeb8793cf7132589959fc" dependencies = [ - "proc-macro-error", + "proc-macro-error2", "proc-macro2", - "quote 1.0.35", - "syn 1.0.109", + "quote 1.0.38", + "syn 2.0.96", ] [[package]] @@ -1843,11 +2842,21 @@ dependencies = [ "polyval", ] +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gimli" -version = "0.28.1" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "git2" @@ -1864,15 +2873,15 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "gloo-timers" -version = "0.2.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" dependencies = [ "futures-channel", "futures-core", @@ -1880,13 +2889,24 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff 0.12.1", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "group" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ - "ff", + "ff 0.13.0", "rand_core 0.6.4", "subtle", ] @@ -1897,19 +2917,48 @@ version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ - "bytes 1.6.0", + "bytes 1.9.0", "fnv", "futures-core", "futures-sink", "futures-util", - "http", - "indexmap 2.2.6", + "http 0.2.12", + "indexmap 2.7.1", + "slab", + "tokio 1.43.0", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +dependencies = [ + "atomic-waker", + "bytes 1.9.0", + "fnv", + "futures-core", + "futures-sink", + "http 1.2.0", + "indexmap 2.7.1", "slab", - "tokio 1.37.0", + "tokio 1.43.0", "tokio-util", "tracing", ] +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "handlebars" version = "4.5.0" @@ -1921,7 +2970,7 @@ dependencies = [ "pest_derive", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1941,14 +2990,49 @@ checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash 0.8.11", "allocator-api2", ] +[[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 = "headers" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" +dependencies = [ + "base64 0.21.7", + "bytes 1.9.0", + "headers-core", + "http 1.2.0", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" +dependencies = [ + "http 1.2.0", +] + [[package]] name = "heck" version = "0.4.1" @@ -1976,6 +3060,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "hex" version = "0.4.3" @@ -2002,24 +3092,24 @@ dependencies = [ [[package]] name = "hmac-sha1-compact" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9d405ec732fa3fcde87264e54a32a84956a377b3e3107de96e59b798c84a7" +checksum = "18492c9f6f9a560e0d346369b665ad2bdbc89fa9bceca75796584e79042694c3" [[package]] name = "hmac-sha256" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3688e69b38018fec1557254f64c8dc2cc8ec502890182f395dbb0aa997aa5735" +checksum = "4a8575493d277c9092b988c780c94737fb9fd8651a1001e16bee3eccfc1baedb" dependencies = [ "digest", ] [[package]] name = "hmac-sha512" -version = "1.1.5" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ce1f4656bae589a3fab938f9f09bf58645b7ed01a2c5f8a3c238e01a4ef78a" +checksum = "b0b3a0f572aa8389d325f5852b9e0a333a15b0f86ecccbb3fdb6e97cd86dc67c" dependencies = [ "digest", ] @@ -2035,13 +3125,38 @@ dependencies = [ "winapi", ] +[[package]] +name = "html5ever" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote 1.0.38", + "syn 2.0.96", +] + [[package]] name = "http" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ - "bytes 1.6.0", + "bytes 1.9.0", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +dependencies = [ + "bytes 1.9.0", "fnv", "itoa", ] @@ -2052,90 +3167,338 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ - "bytes 1.6.0", - "http", - "pin-project-lite 0.2.14", + "bytes 1.9.0", + "http 0.2.12", + "pin-project-lite 0.2.16", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes 1.9.0", + "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 1.9.0", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "pin-project-lite 0.2.16", ] [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error 1.2.3", +] + +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes 1.9.0", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite 0.2.16", + "socket2 0.5.8", + "tokio 1.43.0", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +dependencies = [ + "bytes 1.9.0", + "futures-channel", + "futures-util", + "h2 0.4.7", + "http 1.2.0", + "http-body 1.0.1", + "httparse", + "httpdate", + "itoa", + "pin-project-lite 0.2.16", + "smallvec", + "tokio 1.43.0", + "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 0.14.32", + "log", + "rustls 0.21.12", + "rustls-native-certs", + "tokio 1.43.0", + "tokio-rustls 0.24.1", +] + +[[package]] +name = "hyper-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" +dependencies = [ + "futures-util", + "http 1.2.0", + "hyper 1.5.2", + "hyper-util", + "rustls 0.22.4", + "rustls-pki-types", + "tokio 1.43.0", + "tokio-rustls 0.25.0", + "tower-service", + "webpki-roots 0.26.7", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +dependencies = [ + "futures-util", + "http 1.2.0", + "hyper 1.5.2", + "hyper-util", + "rustls 0.23.21", + "rustls-pki-types", + "tokio 1.43.0", + "tokio-rustls 0.26.1", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes 1.9.0", + "hyper 0.14.32", + "native-tls", + "tokio 1.43.0", + "tokio-native-tls", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes 1.9.0", + "http-body-util", + "hyper 1.5.2", + "hyper-util", + "native-tls", + "tokio 1.43.0", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes 1.9.0", + "futures-channel", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "hyper 1.5.2", + "pin-project-lite 0.2.16", + "socket2 0.5.8", + "tokio 1.43.0", + "tower-service", + "tracing", +] + +[[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 0.52.0", +] + +[[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 = "humantime" -version = "1.3.0" +name = "icu_normalizer" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" dependencies = [ - "quick-error", + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", ] [[package]] -name = "hyper" -version = "0.14.28" +name = "icu_normalizer_data" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" -dependencies = [ - "bytes 1.6.0", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite 0.2.14", - "socket2 0.5.6", - "tokio 1.37.0", - "tower-service", - "tracing", - "want", -] +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" [[package]] -name = "hyper-tls" -version = "0.5.0" +name = "icu_properties" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" dependencies = [ - "bytes 1.6.0", - "hyper", - "native-tls", - "tokio 1.37.0", - "tokio-native-tls", + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", ] [[package]] -name = "iana-time-zone" -version = "0.1.60" +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 = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", ] [[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" +name = "icu_provider_macros" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ - "cc", + "proc-macro2", + "quote 1.0.38", + "syn 2.0.96", ] [[package]] @@ -2177,12 +3540,23 @@ dependencies = [ [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "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]] @@ -2191,6 +3565,61 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" +[[package]] +name = "image" +version = "0.25.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "exr", + "gif", + "image-webp 0.2.1", + "num-traits", + "png", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79afb8cbee2ef20f59ccd477a218c12a93943d075b492015ecb1bb81f8ee904" +dependencies = [ + "byteorder-lite", + "quick-error 2.0.1", +] + +[[package]] +name = "image-webp" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b77d01e822461baa8409e156015a1d91735549f0f2c17691bd2d996bef238f7f" +dependencies = [ + "byteorder-lite", + "quick-error 2.0.1", +] + +[[package]] +name = "imagesize" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" + +[[package]] +name = "imgref" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" + [[package]] name = "impl_ops" version = "0.1.1" @@ -2203,19 +3632,29 @@ version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ - "autocfg 1.2.0", + "autocfg 1.4.0", "hashbrown 0.12.3", "serde", ] [[package]] name = "indexmap" -version = "2.2.6" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.15.2", + "serde", +] + +[[package]] +name = "infer" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc150e5ce2330295b8616ce0e3f53250e53af31759a9dbedad1621ba29151847" +dependencies = [ + "cfb", ] [[package]] @@ -2235,22 +3674,22 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", ] [[package]] -name = "io-lifetimes" -version = "1.0.11" +name = "interpolate_name" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ - "hermit-abi 0.3.9", - "libc", - "windows-sys 0.48.0", + "proc-macro2", + "quote 1.0.38", + "syn 2.0.96", ] [[package]] @@ -2259,7 +3698,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2 0.5.6", + "socket2 0.5.8", "widestring", "windows-sys 0.48.0", "winreg", @@ -2267,9 +3706,26 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.9.0" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "is-terminal" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e19b23d53f35ce9f56aebc7d1bb4e6ac1e9c0db7ac85c8d1760c04379edced37" +dependencies = [ + "hermit-abi 0.4.0", + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "isahc" @@ -2285,7 +3741,7 @@ dependencies = [ "encoding_rs", "event-listener 2.5.3", "futures-lite 1.13.0", - "http", + "http 0.2.12", "log", "mime", "once_cell", @@ -2317,7 +3773,7 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d4e5d712dd664b11e778d1cfc06c79ba2700d6bc1771e44fb7b6a4656b487d" dependencies = [ - "generic-array 1.0.0", + "generic-array 1.2.0", "schemars", "serde", "time", @@ -2343,25 +3799,32 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "jobserver" -version = "0.1.28" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" + [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -2404,31 +3867,191 @@ dependencies = [ "hmac-sha256", "hmac-sha512", "k256", - "p256", + "p256 0.13.2", "p384", "rand 0.8.5", "rsa", "serde", "serde_json", "spki 0.6.0", - "thiserror", + "thiserror 1.0.69", "zeroize", ] +[[package]] +name = "jxl-bitstream" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5855ff16398ffbcf81fee52c41ca65326499c8764b21bb9952c367ace98995fb" +dependencies = [ + "tracing", +] + +[[package]] +name = "jxl-coding" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da5b5093904e940bc11ef50e872c7bdf7b6e88653f012b925f8479daf212b5c9" +dependencies = [ + "jxl-bitstream", + "tracing", +] + +[[package]] +name = "jxl-color" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cb1c31e10054079df585633fc14fb9e4c96565c58c05b983c502e2472b57fa0" +dependencies = [ + "jxl-bitstream", + "jxl-coding", + "jxl-grid", + "jxl-threadpool", + "tracing", +] + +[[package]] +name = "jxl-frame" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35b289aa0f24044167d83a1c29ae2b99210c5082ab7e3e90dacbcae818aa0a2" +dependencies = [ + "jxl-bitstream", + "jxl-coding", + "jxl-grid", + "jxl-image", + "jxl-modular", + "jxl-threadpool", + "jxl-vardct", + "tracing", +] + +[[package]] +name = "jxl-grid" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70b96735a85299a6bce8664643fcb759f29ea73ce344a90b6f7de9b92a8e9b2d" +dependencies = [ + "tracing", +] + +[[package]] +name = "jxl-image" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b31b17ed3bd0e3b65e7b06628f5930e009dda6cd17638cf5159a20a3feedec6" +dependencies = [ + "jxl-bitstream", + "jxl-color", + "jxl-grid", + "tracing", +] + +[[package]] +name = "jxl-modular" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3b9fb8f46e63a14ecedefbd0f873b04162aaf8a09676b630c31bc8dadc4638" +dependencies = [ + "jxl-bitstream", + "jxl-coding", + "jxl-grid", + "jxl-threadpool", + "tracing", +] + +[[package]] +name = "jxl-oxide" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba1ee3895e6c62b131994807b1ee6d179013613a86c01c203369af8d1e8d2f0" +dependencies = [ + "jxl-bitstream", + "jxl-color", + "jxl-frame", + "jxl-grid", + "jxl-image", + "jxl-render", + "jxl-threadpool", + "tracing", +] + +[[package]] +name = "jxl-render" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "203a79b3025b86f875cc97a6f39e1fcc6314f915b2f6b69f767fca45fd482a11" +dependencies = [ + "jxl-bitstream", + "jxl-coding", + "jxl-color", + "jxl-frame", + "jxl-grid", + "jxl-image", + "jxl-modular", + "jxl-threadpool", + "jxl-vardct", + "tracing", +] + +[[package]] +name = "jxl-threadpool" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d2860c68899a3c6266044fc26c6a0041e9f27145f58cc69b6eedc1b77f5ee13" +dependencies = [ + "rayon", + "rayon-core", + "tracing", +] + +[[package]] +name = "jxl-vardct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16af82a1ad770887cad720bfd3cc6a6d023faf377036989a24cf2c6538b649e0" +dependencies = [ + "jxl-bitstream", + "jxl-coding", + "jxl-grid", + "jxl-modular", + "jxl-threadpool", + "tracing", +] + [[package]] name = "k256" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if", - "ecdsa", - "elliptic-curve", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", "once_cell", "sha2", "signature 2.2.0", ] +[[package]] +name = "kamadak-exif" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef4fc70d0ab7e5b6bafa30216a6b48705ea964cdfc29c050f2412295eba58077" +dependencies = [ + "mutate_once", +] + +[[package]] +name = "kurbo" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89234b2cc610a7dd927ebde6b41dd1a5d4214cffaef4cf1fb2195d592f92518f" +dependencies = [ + "arrayvec", + "smallvec", +] + [[package]] name = "kv-log-macro" version = "1.0.7" @@ -2440,13 +4063,19 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin 0.5.2", + "spin 0.9.8", ] +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "lettre" version = "0.10.4" @@ -2467,14 +4096,24 @@ dependencies = [ "once_cell", "quoted_printable", "socket2 0.4.10", - "tokio 1.37.0", + "tokio 1.43.0", ] [[package]] name = "libc" -version = "0.2.153" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "9b9569d2f74e257076d8c6bfa73fb505b46b851e51ddaecc825944aa3bed17fa" +dependencies = [ + "arbitrary", + "cc", +] [[package]] name = "libgit2-sys" @@ -2488,27 +4127,47 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "libloading" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] + [[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "libnghttp2-sys" -version = "0.1.9+1.58.0" +version = "0.1.11+1.64.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b57e858af2798e167e709b9d969325b6d8e9d50232fcbc494d7d54f976854a64" +checksum = "1b6c24e48a7167cffa7119da39d577fa482e66c688a4aac016bee862e1a713c4" dependencies = [ "cc", "libc", ] +[[package]] +name = "libwebp-sys" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54cd30df7c7165ce74a456e4ca9732c603e8dc5e60784558c1c6dc047f876733" +dependencies = [ + "cc", + "glob", +] + [[package]] name = "libz-sys" -version = "1.1.16" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" +checksum = "df9b68e50e6e0b26f672573834882eb57759f6db9b3be2ea3c35c91188bb4eaa" dependencies = [ "cc", "libc", @@ -2542,88 +4201,122 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.3.8" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] -name = "linux-raw-sys" -version = "0.4.13" +name = "litemap" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "livekit-api" -version = "0.3.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e20c3fc3de5944ce6b5c8da4084cf828bbae7216671e32e83b767ce61feeb7e0" +checksum = "bccba8f66e9eeef0edd98470bee3212ca4f4468793f18264995feb1ce4ab45dc" dependencies = [ "base64 0.21.7", + "http 0.2.12", "jsonwebtoken", "livekit-protocol", "log", "parking_lot", "prost", - "reqwest", + "reqwest 0.11.27", "scopeguard", "serde", "serde_json", "sha2", - "thiserror", + "thiserror 1.0.69", "url", ] [[package]] name = "livekit-protocol" -version = "0.3.2" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a1bd23257110be29d024d8d816adff70df18ea1d22ceb1aab6f3ad4aab0d523" +checksum = "6a3554533d83fb849cb140641edcbf48628980ef816f50002202368fe908fb85" dependencies = [ "futures-util", + "livekit-runtime", "parking_lot", "pbjson", "pbjson-types", "prost", "prost-types", "serde", - "thiserror", - "tokio 1.37.0", + "thiserror 1.0.69", + "tokio 1.43.0", +] + +[[package]] +name = "livekit-runtime" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ae53eb874eb86e96e8ccffc31b5a00926d46174cbcb1c24ce4e57b1fcc5c8f6" +dependencies = [ + "tokio 1.43.0", + "tokio-stream", ] [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ - "autocfg 1.2.0", + "autocfg 1.4.0", "scopeguard", ] [[package]] name = "log" -version = "0.4.21" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" dependencies = [ "value-bag", ] [[package]] name = "loom" -version = "0.5.6" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" +dependencies = [ + "cfg-if", + "generator 0.7.5", + "scoped-tls", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "loom" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" dependencies = [ "cfg-if", - "generator", + "generator 0.8.4", "scoped-tls", - "serde", - "serde_json", "tracing", "tracing-subscriber", ] +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + [[package]] name = "lru" version = "0.7.8" @@ -2639,7 +4332,16 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a83fb7698b3643a0e34f9ae6f2e8f0178c0fd42f8b59d493aa271ff3a5bf21" dependencies = [ - "hashbrown 0.14.3", + "hashbrown 0.14.5", +] + +[[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]] @@ -2651,6 +4353,32 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "lru_time_cache" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9106e1d747ffd48e6be5bb2d97fa706ed25b144fbee4d5c02eae110cd8d6badd" + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "markup5ever" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45" +dependencies = [ + "log", + "phf 0.11.3", + "phf_codegen 0.11.3", + "string_cache", + "string_cache_codegen", + "tendril", +] + [[package]] name = "match_cfg" version = "0.1.0" @@ -2672,6 +4400,22 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + [[package]] name = "md-5" version = "0.10.6" @@ -2684,29 +4428,27 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] -name = "metrics" -version = "0.18.1" +name = "memmap2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e52eb6380b6d2a10eb3434aec0885374490f5b82c8aaf5cd487a183c98be834" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" dependencies = [ - "ahash 0.7.8", - "metrics-macros", + "libc", ] [[package]] -name = "metrics-macros" -version = "0.5.1" +name = "metrics" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49e30813093f757be5cf21e50389a24dc7dbb22c49f23b7e8f51d69b508a5ffa" +checksum = "884adb57038347dfbaf2d5065887b6cf4312330dc8e94bc30a1a839bd79d3261" dependencies = [ - "proc-macro2", - "quote 1.0.35", - "syn 1.0.109", + "ahash 0.8.11", + "portable-atomic", ] [[package]] @@ -2723,29 +4465,30 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ - "adler", + "adler2", + "simd-adler32", ] [[package]] name = "mio" -version = "0.8.11" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "mobc" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90eb49dc5d193287ff80e72a86f34cfb27aae562299d22fea215e06ea1059dd3" +checksum = "316a7d198b51958a0ab57248bf5f42d8409551203cb3c821d5925819a8d5415f" dependencies = [ "async-std", "async-trait", @@ -2755,8 +4498,8 @@ dependencies = [ "futures-util", "log", "metrics", - "thiserror", - "tokio 1.37.0", + "thiserror 1.0.69", + "tokio 1.43.0", "tracing", "tracing-subscriber", ] @@ -2771,6 +4514,28 @@ dependencies = [ "redis", ] +[[package]] +name = "moka" +version = "0.12.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926" +dependencies = [ + "async-lock 3.4.0", + "crossbeam-channel", + "crossbeam-epoch", + "crossbeam-utils", + "event-listener 5.4.0", + "futures-util", + "loom 0.7.2", + "parking_lot", + "portable-atomic", + "rustc_version 0.4.1", + "smallvec", + "tagptr", + "thiserror 1.0.69", + "uuid", +] + [[package]] name = "mongodb" version = "2.6.1" @@ -2798,43 +4563,42 @@ dependencies = [ "percent-encoding", "rand 0.8.5", "rustc_version_runtime", - "rustls", - "rustls-pemfile", + "rustls 0.20.9", + "rustls-pemfile 1.0.4", "serde", "serde_with", "sha-1", "sha2", "socket2 0.4.10", "stringprep", - "strsim", + "strsim 0.10.0", "take_mut", - "thiserror", - "tokio 1.37.0", - "tokio-rustls", + "thiserror 1.0.69", + "tokio 1.43.0", + "tokio-rustls 0.23.4", "tokio-util", "trust-dns-proto", "trust-dns-resolver", "typed-builder", "uuid", - "webpki-roots", + "webpki-roots 0.22.6", ] [[package]] name = "multer" -version = "2.1.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" +checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" dependencies = [ - "bytes 1.6.0", + "bytes 1.9.0", "encoding_rs", "futures-util", - "http", + "http 1.2.0", "httparse", - "log", "memchr", "mime", "spin 0.9.8", - "tokio 1.37.0", + "tokio 1.43.0", "tokio-util", "version_check", ] @@ -2845,6 +4609,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" +[[package]] +name = "mutate_once" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16cf681a23b4d0a43fc35024c176437f9dcd818db34e0f42ab456a0ee5ad497b" + [[package]] name = "nanoid" version = "0.4.0" @@ -2856,11 +4626,10 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -2872,6 +4641,49 @@ dependencies = [ "tempfile", ] +[[package]] +name = "neon" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d75440242411c87dc39847b0e33e961ec1f10326a9d8ecf9c1ea64a3b3c13dc" +dependencies = [ + "getrandom", + "libloading", + "neon-macros", + "once_cell", + "semver 1.0.25", + "send_wrapper", + "smallvec", +] + +[[package]] +name = "neon-macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6813fde79b646e47e7ad75f480aa80ef76a5d9599e2717407961531169ee38b" +dependencies = [ + "quote 1.0.38", + "syn 2.0.96", + "syn-mid", +] + +[[package]] +name = "neon-serde4" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8778b4afaa506e067da24566623873c38797987be593232a4f9ad82c84bb3eb3" +dependencies = [ + "neon", + "num", + "serde", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "nom" version = "7.1.3" @@ -2882,6 +4694,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + [[package]] name = "ntapi" version = "0.4.1" @@ -2901,6 +4719,29 @@ dependencies = [ "winapi", ] +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-bigint-dig" version = "0.8.4" @@ -2918,12 +4759,32 @@ dependencies = [ "zeroize", ] +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote 1.0.38", + "syn 2.0.96", +] + [[package]] name = "num-integer" version = "0.1.46" @@ -2935,22 +4796,33 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.44" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg 1.4.0", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "autocfg 1.2.0", + "num-bigint", "num-integer", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ - "autocfg 1.2.0", + "autocfg 1.4.0", "libm", ] @@ -2990,7 +4862,7 @@ checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ "proc-macro-crate", "proc-macro2", - "quote 1.0.35", + "quote 1.0.38", "syn 1.0.109", ] @@ -3002,24 +4874,33 @@ checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ "proc-macro-crate", "proc-macro2", - "quote 1.0.35", - "syn 2.0.58", + "quote 1.0.38", + "syn 2.0.96", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", ] [[package]] name = "object" -version = "0.32.2" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "opaque-debug" @@ -3029,11 +4910,11 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.64" +version = "0.10.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +checksum = "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.8.0", "cfg-if", "foreign-types", "libc", @@ -3049,21 +4930,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", - "quote 1.0.35", - "syn 2.0.58", + "quote 1.0.38", + "syn 2.0.96", ] [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.102" +version = "0.9.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" dependencies = [ "cc", "libc", @@ -3083,29 +4964,46 @@ dependencies = [ [[package]] name = "os_info" -version = "3.8.2" +version = "3.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" +checksum = "6e6520c8cc998c5741ee68ec1dc369fc47e5f0ea5320018ecf2a1ccd6328f48b" dependencies = [ "log", "serde", "windows-sys 0.52.0", ] +[[package]] +name = "outref" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" + [[package]] name = "overload" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "p256" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +dependencies = [ + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2", +] + [[package]] name = "p256" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ - "ecdsa", - "elliptic-curve", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", "primeorder", "sha2", ] @@ -3116,23 +5014,23 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" dependencies = [ - "ecdsa", - "elliptic-curve", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", "primeorder", "sha2", ] [[package]] name = "parking" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -3140,28 +5038,28 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pathdiff" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] name = "pbjson" @@ -3191,7 +5089,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18f596653ba4ac51bdecbb4ef6773bc7f56042dc13927910de1684ad3d32aa12" dependencies = [ - "bytes 1.6.0", + "bytes 1.9.0", "chrono", "pbjson", "pbjson-build", @@ -3217,7 +5115,7 @@ checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" dependencies = [ "inlinable_string", "pear_codegen", - "yansi 1.0.1", + "yansi", ] [[package]] @@ -3227,9 +5125,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" dependencies = [ "proc-macro2", - "proc-macro2-diagnostics 0.10.1", - "quote 1.0.35", - "syn 2.0.58", + "proc-macro2-diagnostics", + "quote 1.0.38", + "syn 2.0.96", ] [[package]] @@ -3252,6 +5150,16 @@ dependencies = [ "base64 0.13.1", ] +[[package]] +name = "pem" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +dependencies = [ + "base64 0.22.1", + "serde", +] + [[package]] name = "pem-rfc7468" version = "0.6.0" @@ -3278,20 +5186,20 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.9" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95" +checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror", + "thiserror 2.0.11", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.7.9" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73541b156d32197eecda1a4014d7f868fd2bcb3c550d5386087cfba442bf69c" +checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e" dependencies = [ "pest", "pest_generator", @@ -3299,22 +5207,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.9" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c35eeed0a3fab112f75165fdc026b3913f4183133f19b49be773ac9ea966e8bd" +checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b" dependencies = [ "pest", "pest_meta", "proc-macro2", - "quote 1.0.35", - "syn 2.0.58", + "quote 1.0.38", + "syn 2.0.96", ] [[package]] name = "pest_meta" -version = "2.7.9" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2adbf29bb9776f28caece835398781ab24435585fe0d4dc1374a61db5accedca" +checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea" dependencies = [ "once_cell", "pest", @@ -3323,32 +5231,128 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.2.6", + "indexmap 2.7.1", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_shared 0.10.0", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf_codegen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared 0.11.3", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", + "proc-macro2", + "quote 1.0.38", + "syn 2.0.96", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher 0.3.11", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher 1.0.1", ] +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" dependencies = [ "proc-macro2", - "quote 1.0.35", - "syn 2.0.58", + "quote 1.0.38", + "syn 2.0.96", ] [[package]] @@ -3359,9 +5363,9 @@ checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -3371,12 +5375,12 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "piper" -version = "0.2.1" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" dependencies = [ "atomic-waker", - "fastrand 2.0.2", + "fastrand 2.3.0", "futures-io", ] @@ -3414,9 +5418,22 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] [[package]] name = "polling" @@ -3424,29 +5441,29 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ - "autocfg 1.2.0", + "autocfg 1.4.0", "bitflags 1.3.2", "cfg-if", "concurrent-queue", "libc", "log", - "pin-project-lite 0.2.14", + "pin-project-lite 0.2.16", "windows-sys 0.48.0", ] [[package]] name = "polling" -version = "3.6.0" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c976a60b2d7e99d6f229e414670a9b85d13ac305cc6d1e9c134de58c5aaaf6" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.3.9", - "pin-project-lite 0.2.14", - "rustix 0.38.32", + "hermit-abi 0.4.0", + "pin-project-lite 0.2.16", + "rustix", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3461,6 +5478,12 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "portable-atomic" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" + [[package]] name = "powerfmt" version = "0.2.0" @@ -3469,9 +5492,18 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "pretty_env_logger" @@ -3485,12 +5517,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.17" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", - "syn 2.0.58", + "syn 2.0.96", ] [[package]] @@ -3499,7 +5531,7 @@ version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" dependencies = [ - "elliptic-curve", + "elliptic-curve 0.13.8", ] [[package]] @@ -3520,7 +5552,7 @@ checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", "proc-macro2", - "quote 1.0.35", + "quote 1.0.38", "syn 1.0.109", "version_check", ] @@ -3532,30 +5564,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", - "quote 1.0.35", + "quote 1.0.38", "version_check", ] [[package]] -name = "proc-macro2" -version = "1.0.79" +name = "proc-macro-error-attr2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" dependencies = [ - "unicode-ident", + "proc-macro2", + "quote 1.0.38", ] [[package]] -name = "proc-macro2-diagnostics" -version = "0.9.1" +name = "proc-macro-error2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" dependencies = [ + "proc-macro-error-attr2", "proc-macro2", - "quote 1.0.35", - "syn 1.0.109", - "version_check", - "yansi 0.5.1", + "quote 1.0.38", + "syn 2.0.96", +] + +[[package]] +name = "proc-macro2" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +dependencies = [ + "unicode-ident", ] [[package]] @@ -3565,43 +5606,62 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", - "quote 1.0.35", - "syn 2.0.58", + "quote 1.0.38", + "syn 2.0.96", "version_check", - "yansi 1.0.1", + "yansi", +] + +[[package]] +name = "profiling" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" +dependencies = [ + "quote 1.0.38", + "syn 2.0.96", ] [[package]] name = "prometheus" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" +checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1" dependencies = [ "cfg-if", "fnv", "lazy_static", "memchr", "parking_lot", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "prost" -version = "0.12.4" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" dependencies = [ - "bytes 1.6.0", + "bytes 1.9.0", "prost-derive", ] [[package]] name = "prost-build" -version = "0.12.4" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80b776a1b2dc779f5ee0641f8ade0125bc1298dd41a9a0c16d8bd57b42d222b1" +checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" dependencies = [ - "bytes 1.6.0", + "bytes 1.9.0", "heck 0.5.0", "itertools 0.12.1", "log", @@ -3612,32 +5672,41 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.58", + "syn 2.0.96", "tempfile", ] [[package]] name = "prost-derive" -version = "0.12.4" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", "itertools 0.12.1", "proc-macro2", - "quote 1.0.35", - "syn 2.0.58", + "quote 1.0.38", + "syn 2.0.96", ] [[package]] name = "prost-types" -version = "0.12.4" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3235c33eb02c1f1e212abdbe34c78b264b038fb58ca612664343271e36e55ffe" +checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" dependencies = [ "prost", ] +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + [[package]] name = "querystring" version = "1.1.0" @@ -3650,6 +5719,12 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quote" version = "0.3.15" @@ -3658,9 +5733,9 @@ checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" [[package]] name = "quote" -version = "1.0.35" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -3814,6 +5889,76 @@ dependencies = [ "rand_core 0.3.1", ] +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools 0.12.1", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "once_cell", + "paste", + "profiling", + "rand 0.8.5", + "rand_chacha 0.3.1", + "simd_helpers", + "system-deps", + "thiserror 1.0.69", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2413fd96bd0ea5cdeeb37eaf446a22e6ed7b981d792828721e74ded1980a45c6" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error 2.0.1", + "rav1e", + "rayon", + "rgb", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "rdrand" version = "0.4.0" @@ -3830,15 +5975,15 @@ source = "git+https://github.com/revoltchat/redis-rs?rev=f8ca28ab85da59d2ccde526 dependencies = [ "async-std", "async-trait", - "bytes 1.6.0", + "bytes 1.9.0", "combine", "futures-util", "itoa", "percent-encoding", - "pin-project-lite 0.2.14", + "pin-project-lite 0.2.16", "ryu", "sha1_smol", - "tokio 1.37.0", + "tokio 1.43.0", "tokio-util", "url", ] @@ -3865,7 +6010,7 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c31deddf734dc0a39d3112e73490e88b61a05e83e074d211f348404cee4d2c6" dependencies = [ - "bytes 1.6.0", + "bytes 1.9.0", "bytes-utils", "cookie-factory", "crc16", @@ -3875,43 +6020,43 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.8.0", ] [[package]] name = "ref-cast" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4846d4c50d1721b1a3bef8af76924eef20d5e723647333798c1b519b3a9473f" +checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc" +checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", - "quote 1.0.35", - "syn 2.0.58", + "quote 1.0.38", + "syn 2.0.96", ] [[package]] name = "regex" -version = "1.10.4" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", ] [[package]] @@ -3925,15 +6070,21 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.3", + "regex-syntax 0.8.5", ] +[[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.6.29" @@ -3942,9 +6093,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" @@ -3953,15 +6104,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ "base64 0.21.7", - "bytes 1.6.0", + "bytes 1.9.0", "encoding_rs", "futures-core", "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-tls", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper-tls 0.5.0", "ipnet", "js-sys", "log", @@ -3969,14 +6120,14 @@ dependencies = [ "native-tls", "once_cell", "percent-encoding", - "pin-project-lite 0.2.14", - "rustls-pemfile", + "pin-project-lite 0.2.16", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", - "system-configuration", - "tokio 1.37.0", + "sync_wrapper 0.1.2", + "system-configuration 0.5.1", + "tokio 1.43.0", "tokio-native-tls", "tower-service", "url", @@ -3986,6 +6137,50 @@ dependencies = [ "winreg", ] +[[package]] +name = "reqwest" +version = "0.12.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" +dependencies = [ + "base64 0.22.1", + "bytes 1.9.0", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.4.7", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.5.2", + "hyper-rustls 0.27.5", + "hyper-tls 0.6.0", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite 0.2.16", + "rustls-pemfile 2.2.0", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.2", + "system-configuration 0.6.1", + "tokio 1.43.0", + "tokio-native-tls", + "tower", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + [[package]] name = "resolv-conf" version = "0.7.0" @@ -3993,20 +6188,77 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" dependencies = [ "hostname", - "quick-error", + "quick-error 1.2.3", +] + +[[package]] +name = "resvg" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a325d5e8d1cebddd070b13f44cec8071594ab67d1012797c121f27a669b7958" +dependencies = [ + "gif", + "image-webp 0.1.3", + "log", + "pico-args", + "rgb", + "svgtypes", + "tiny-skia", + "usvg", + "zune-jpeg", +] + +[[package]] +name = "revolt-autumn" +version = "0.8.1" +dependencies = [ + "axum", + "axum-macros", + "axum_typed_multipart", + "ffprobe", + "image", + "imagesize", + "infer", + "jxl-oxide", + "kamadak-exif", + "lazy_static", + "moka", + "nanoid", + "revolt-config", + "revolt-database", + "revolt-files", + "revolt-result", + "revolt_clamav-client", + "serde", + "serde_json", + "sha2", + "simdutf8", + "strum_macros", + "tempfile", + "tokio 1.43.0", + "tower-http", + "tracing", + "tracing-subscriber", + "ulid 1.1.4", + "utoipa", + "utoipa-scalar", + "webp", ] [[package]] name = "revolt-bonfire" -version = "0.7.1" +version = "0.8.1" dependencies = [ + "async-channel 2.3.1", "async-std", "async-tungstenite", + "authifier", "bincode", "fred", "futures", "log", "lru 0.7.8", + "lru_time_cache", "once_cell", "querystring", "redis-kiss", @@ -4026,34 +6278,36 @@ dependencies = [ [[package]] name = "revolt-config" -version = "0.7.1" +version = "0.8.1" dependencies = [ "async-std", "cached", "config", - "dotenv", "futures-locks", "log", "once_cell", "pretty_env_logger", + "revolt-result", "sentry", "serde", ] [[package]] name = "revolt-database" -version = "0.7.1" +version = "0.8.1" dependencies = [ + "amqprs", "async-lock 2.8.0", "async-recursion", "async-std", "async-trait", "authifier", + "axum", "base64 0.21.7", "bson", "deadqueue", "decancer", - "fcm", + "fcm_v1", "futures", "indexmap 1.9.3", "isahc", @@ -4072,6 +6326,7 @@ dependencies = [ "revolt-permissions", "revolt-presence", "revolt-result", + "revolt_a2", "revolt_okapi", "revolt_optional_struct", "revolt_rocket_okapi", @@ -4079,7 +6334,7 @@ dependencies = [ "schemars", "serde", "serde_json", - "ulid 1.1.2", + "ulid 1.1.4", "unicode-segmentation", "url-escape", "validator 0.16.1", @@ -4088,15 +6343,15 @@ dependencies = [ [[package]] name = "revolt-delta" -version = "0.7.1" +version = "0.8.1" dependencies = [ + "amqprs", "async-channel 1.9.0", "async-std", "authifier", "bitfield", "chrono", "dashmap", - "dotenv", "env_logger", "futures", "impl_ops", @@ -4113,11 +6368,12 @@ dependencies = [ "rand 0.8.5", "redis-kiss", "regex", - "reqwest", + "reqwest 0.11.27", "revolt-config", "revolt-database", "revolt-models", "revolt-permissions", + "revolt-presence", "revolt-result", "revolt-voice", "revolt_rocket_okapi", @@ -4135,9 +6391,60 @@ dependencies = [ "vergen", ] +[[package]] +name = "revolt-files" +version = "0.8.1" +dependencies = [ + "aes-gcm", + "aws-config", + "aws-sdk-s3", + "base64 0.22.1", + "ffprobe", + "image", + "imagesize", + "jxl-oxide", + "resvg", + "revolt-config", + "revolt-result", + "tempfile", + "tiny-skia", + "tracing", + "typenum", + "usvg", + "webp", +] + +[[package]] +name = "revolt-january" +version = "0.8.1" +dependencies = [ + "async-recursion", + "axum", + "axum-extra", + "encoding_rs", + "lazy_static", + "mime", + "moka", + "regex", + "reqwest 0.12.12", + "revolt-config", + "revolt-files", + "revolt-models", + "revolt-result", + "scraper", + "serde", + "serde_json", + "tempfile", + "tokio 1.43.0", + "tracing", + "tracing-subscriber", + "utoipa", + "utoipa-scalar", +] + [[package]] name = "revolt-models" -version = "0.7.1" +version = "0.8.1" dependencies = [ "indexmap 1.9.3", "iso8601-timestamp 0.2.17", @@ -4149,12 +6456,26 @@ dependencies = [ "rocket", "schemars", "serde", + "utoipa", "validator 0.16.1", ] +[[package]] +name = "revolt-nodejs-bindings" +version = "0.8.1" +dependencies = [ + "async-std", + "neon", + "neon-serde4", + "revolt-config", + "revolt-database", + "revolt-result", + "serde", +] + [[package]] name = "revolt-permissions" -version = "0.7.1" +version = "0.8.1" dependencies = [ "async-std", "async-trait", @@ -4169,7 +6490,7 @@ dependencies = [ [[package]] name = "revolt-presence" -version = "0.7.1" +version = "0.8.1" dependencies = [ "async-std", "log", @@ -4178,10 +6499,35 @@ dependencies = [ "redis-kiss", ] +[[package]] +name = "revolt-pushd" +version = "0.8.1" +dependencies = [ + "amqprs", + "async-trait", + "authifier", + "base64 0.22.1", + "fcm_v1", + "isahc", + "iso8601-timestamp 0.2.17", + "log", + "revolt-config", + "revolt-database", + "revolt-models", + "revolt_a2", + "revolt_optional_struct", + "serde", + "serde_json", + "tokio 1.43.0", + "ulid 1.1.4", + "web-push", +] + [[package]] name = "revolt-result" -version = "0.7.1" +version = "0.8.1" dependencies = [ + "axum", "log", "revolt_okapi", "revolt_rocket_okapi", @@ -4189,6 +6535,7 @@ dependencies = [ "schemars", "serde", "serde_json", + "utoipa", ] [[package]] @@ -4217,6 +6564,7 @@ dependencies = [ "futures", "livekit-api", "livekit-protocol", + "livekit-runtime", "log", "lru 0.7.8", "redis-kiss", @@ -4235,6 +6583,36 @@ dependencies = [ "ulid 0.5.0", ] +[[package]] +name = "revolt_a2" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edbe1f79cb41271d3cd8f932d75dddeba963c19dc93d1ee6cbe0391b495ab2f5" +dependencies = [ + "base64 0.21.7", + "erased-serde", + "http 1.2.0", + "http-body-util", + "hyper 1.5.2", + "hyper-rustls 0.26.0", + "hyper-util", + "parking_lot", + "pem 3.0.4", + "ring 0.17.8", + "rustls 0.22.4", + "rustls-pemfile 2.2.0", + "serde", + "serde_json", + "thiserror 1.0.69", + "tokio 1.43.0", +] + +[[package]] +name = "revolt_clamav-client" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d719e9fe861e2e05cbf01fd82d310fdeb2160c74fee21689a5c223eeb65cea8" + [[package]] name = "revolt_okapi" version = "0.9.1" @@ -4259,9 +6637,9 @@ dependencies = [ [[package]] name = "revolt_rocket_okapi" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "275e1e9bd3343f75225cafa64f4bfb939c8b21c5f861141180fc0e24769ff6cf" +checksum = "cb113b281380c12c185c8d98c4887627ae6f7add16a510073382518ce34e42db" dependencies = [ "either", "log", @@ -4281,11 +6659,22 @@ checksum = "cc6620569d8ac8f0a1690fcca13f488503807a60e96ebf729749b59aca1dbef9" dependencies = [ "darling 0.13.4", "proc-macro2", - "quote 1.0.35", - "rocket_http 0.5.0", + "quote 1.0.38", + "rocket_http", "syn 1.0.109", ] +[[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 = "rfc6979" version = "0.4.0" @@ -4296,6 +6685,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "rgb" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" +dependencies = [ + "bytemuck", +] + [[package]] name = "ring" version = "0.16.20" @@ -4328,9 +6726,9 @@ dependencies = [ [[package]] name = "rmp" -version = "0.8.12" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9860a6cc38ed1da53456442089b4dfa35e7cedaa326df63017af88385e6b20" +checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" dependencies = [ "byteorder", "num-traits", @@ -4339,9 +6737,9 @@ dependencies = [ [[package]] name = "rmp-serde" -version = "1.1.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bffea85eea980d8a74453e5d02a8d93028f3c34725de143085a844ebe953258a" +checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" dependencies = [ "byteorder", "rmp", @@ -4350,47 +6748,47 @@ dependencies = [ [[package]] name = "rocket" -version = "0.5.0-rc.2" -source = "git+https://github.com/rwf2/Rocket/?rev=4dcd928#4dcd92837f3a4626a28d4b25da8e6e560878d004" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a516907296a31df7dc04310e7043b61d71954d703b603cc6867a026d7e72d73f" dependencies = [ "async-stream", "async-trait", "atomic 0.5.3", - "atty", "binascii", - "bytes 1.6.0", + "bytes 1.9.0", "either", "figment", "futures", - "indexmap 1.9.3", + "indexmap 2.7.1", "log", "memchr", "multer", "num_cpus", "parking_lot", - "pin-project-lite 0.2.14", + "pin-project-lite 0.2.16", "rand 0.8.5", "ref-cast", "rocket_codegen", - "rocket_http 0.5.0-rc.2", + "rocket_http", "serde", "serde_json", - "state 0.5.3", + "state", "tempfile", "time", - "tokio 1.37.0", + "tokio 1.43.0", "tokio-stream", "tokio-util", "ubyte", "version_check", - "yansi 0.5.1", + "yansi", ] [[package]] name = "rocket_authifier" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89a12311f60e9288833fc3ce6029bce5d5c61870ceef74d4a50668a8b520ad" +checksum = "810753b79106c44a4e76247fc7576b660663133a9e8f4b0afeb303589ec51d59" dependencies = [ "authifier", "iso8601-timestamp 0.1.11", @@ -4404,25 +6802,27 @@ dependencies = [ [[package]] name = "rocket_codegen" -version = "0.5.0-rc.2" -source = "git+https://github.com/rwf2/Rocket/?rev=4dcd928#4dcd92837f3a4626a28d4b25da8e6e560878d004" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "575d32d7ec1a9770108c879fc7c47815a80073f96ca07ff9525a94fcede1dd46" dependencies = [ "devise", "glob", - "indexmap 1.9.3", + "indexmap 2.7.1", "proc-macro2", - "quote 1.0.35", - "rocket_http 0.5.0-rc.2", - "syn 1.0.109", - "unicode-xid 0.2.4", + "quote 1.0.38", + "rocket_http", + "syn 2.0.96", + "unicode-xid 0.2.6", + "version_check", ] [[package]] name = "rocket_cors" -version = "0.6.0-alpha1" -source = "git+https://github.com/lawliet89/rocket_cors?rev=c17e8145baa4790319fdb6a473e465b960f55e7c#c17e8145baa4790319fdb6a473e465b960f55e7c" +version = "0.6.0" +source = "git+https://github.com/lawliet89/rocket_cors?rev=072d90359b23e9b291df6b672c07c93de9c46011#072d90359b23e9b291df6b672c07c93de9c46011" dependencies = [ - "http", + "http 0.2.12", "log", "regex", "rocket", @@ -4435,9 +6835,9 @@ dependencies = [ [[package]] name = "rocket_empty" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c0922e47f981204fee38578a8efcf47a5c1a4ac0eb7f59e6bdfa3e61c8e3d69" +checksum = "97a55000e1ef5f4a9b20ae3d9de2a0bd22620c78ebd1aa568776ae12276125a6" dependencies = [ "revolt_okapi", "revolt_rocket_okapi", @@ -4446,61 +6846,36 @@ dependencies = [ [[package]] name = "rocket_http" -version = "0.5.0-rc.2" -source = "git+https://github.com/rwf2/Rocket/?rev=4dcd928#4dcd92837f3a4626a28d4b25da8e6e560878d004" -dependencies = [ - "cookie 0.16.2", - "either", - "futures", - "http", - "hyper", - "indexmap 1.9.3", - "log", - "memchr", - "pear", - "percent-encoding", - "pin-project-lite 0.2.14", - "ref-cast", - "serde", - "smallvec", - "stable-pattern", - "state 0.5.3", - "time", - "tokio 1.37.0", - "uncased", -] - -[[package]] -name = "rocket_http" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a1663694d059fe5f943ea5481363e48050acedd241d46deb2e27f71110389e" +checksum = "e274915a20ee3065f611c044bd63c40757396b6dbc057d6046aec27f14f882b9" dependencies = [ - "cookie 0.18.1", + "cookie", "either", "futures", - "http", - "hyper", - "indexmap 2.2.6", + "http 0.2.12", + "hyper 0.14.32", + "indexmap 2.7.1", "log", "memchr", "pear", "percent-encoding", - "pin-project-lite 0.2.14", + "pin-project-lite 0.2.16", "ref-cast", + "serde", "smallvec", "stable-pattern", - "state 0.6.0", + "state", "time", - "tokio 1.37.0", + "tokio 1.43.0", "uncased", ] [[package]] name = "rocket_prometheus" -version = "0.10.0-rc.3" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3e70efcf6b0234723d0d295b95d64283ed02bb98a8a3f0c8c7ebb5b8da69165" +checksum = "76f429d654a6854abef0a5361235e847b6552d5d989c2fc8a312ba4aebd8837f" dependencies = [ "prometheus", "rocket", @@ -4517,6 +6892,12 @@ dependencies = [ "serde", ] +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + [[package]] name = "rsa" version = "0.7.2" @@ -4562,9 +6943,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc_version" @@ -4577,11 +6958,11 @@ dependencies = [ [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.22", + "semver 1.0.25", ] [[package]] @@ -4596,41 +6977,78 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.27" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.8.0", "errno", - "io-lifetimes", "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", + "linux-raw-sys", + "windows-sys 0.59.0", ] [[package]] -name = "rustix" -version = "0.38.32" +name = "rustls" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" dependencies = [ - "bitflags 2.5.0", - "errno", - "libc", - "linux-raw-sys 0.4.13", - "windows-sys 0.52.0", + "log", + "ring 0.16.20", + "sct", + "webpki", ] [[package]] name = "rustls" -version = "0.20.9" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", - "ring 0.16.20", + "ring 0.17.8", + "rustls-webpki 0.101.7", "sct", - "webpki", +] + +[[package]] +name = "rustls" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +dependencies = [ + "log", + "ring 0.17.8", + "rustls-pki-types", + "rustls-webpki 0.102.8", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls" +version = "0.23.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki 0.102.8", + "subtle", + "zeroize", +] + +[[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 1.0.4", + "schannel", + "security-framework", ] [[package]] @@ -4642,32 +7060,86 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring 0.17.8", + "untrusted 0.9.0", +] + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring 0.17.8", + "rustls-pki-types", + "untrusted 0.9.0", +] + [[package]] name = "rustversion" -version = "1.0.15" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + +[[package]] +name = "rustybuzz" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" +checksum = "c85d1ccd519e61834798eb52c4e886e8c2d7d698dd3d6ce0b1b47eb8557f1181" +dependencies = [ + "bitflags 2.8.0", + "bytemuck", + "core_maths", + "log", + "smallvec", + "ttf-parser", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-properties", + "unicode-script", +] [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "schemars" -version = "0.8.16" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" dependencies = [ "dyn-clone", "indexmap 1.9.3", @@ -4678,14 +7150,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.16" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" dependencies = [ "proc-macro2", - "quote 1.0.35", + "quote 1.0.38", "serde_derive_internals", - "syn 1.0.109", + "syn 2.0.96", ] [[package]] @@ -4700,6 +7172,22 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scraper" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b90460b31bfe1fc07be8262e42c665ad97118d4585869de9345a84d501a9eaf0" +dependencies = [ + "ahash 0.8.11", + "cssparser", + "ego-tree", + "getopts", + "html5ever", + "once_cell", + "selectors", + "tendril", +] + [[package]] name = "sct" version = "0.7.1" @@ -4710,13 +7198,33 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct 0.1.1", + "der 0.6.1", + "generic-array 0.14.7", + "pkcs8 0.9.0", + "subtle", + "zeroize", +] + [[package]] name = "sec1" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ - "base16ct", + "base16ct 0.2.0", "der 0.7.9", "generic-array 0.14.7", "pkcs8 0.10.2", @@ -4732,16 +7240,16 @@ checksum = "b6326ddc956378a0739200b2c30892dccaf198992dfd7323274690b9e188af23" dependencies = [ "der 0.4.5", "pem 0.8.3", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "security-framework" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.8.0", "core-foundation", "core-foundation-sys", "libc", @@ -4750,14 +7258,33 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.10.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", ] +[[package]] +name = "selectors" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb30575f3638fc8f6815f448d50cb1a2e255b0897985c8c59f4d37b72a07b06" +dependencies = [ + "bitflags 2.8.0", + "cssparser", + "derive_more", + "fxhash", + "log", + "new_debug_unreachable", + "phf 0.10.1", + "phf_codegen 0.10.0", + "precomputed-hash", + "servo_arc", + "smallvec", +] + [[package]] name = "semver" version = "0.9.0" @@ -4769,9 +7296,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.22" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" [[package]] name = "semver-parser" @@ -4779,6 +7306,12 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" + [[package]] name = "sentry" version = "0.31.8" @@ -4787,14 +7320,14 @@ checksum = "6ce4b57f1b521f674df7a1d200be8ff5d74e3712020ee25b553146657b5377d5" dependencies = [ "httpdate", "native-tls", - "reqwest", + "reqwest 0.11.27", "sentry-backtrace", "sentry-contexts", "sentry-core", "sentry-debug-images", "sentry-panic", "sentry-tracing", - "tokio 1.37.0", + "tokio 1.43.0", "ureq", ] @@ -4819,7 +7352,7 @@ dependencies = [ "hostname", "libc", "os_info", - "rustc_version 0.4.0", + "rustc_version 0.4.1", "sentry-core", "uname", ] @@ -4881,7 +7414,7 @@ dependencies = [ "rand 0.8.5", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "time", "url", "uuid", @@ -4889,61 +7422,81 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.197" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_bytes" -version = "0.11.14" +version = "0.11.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_bytes_ng" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" +checksum = "bdb0ebce8684e2253f964e8b6ce51f0ccc6666bbb448fb4a6788088bda6544b6" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", - "quote 1.0.35", - "syn 2.0.58", + "quote 1.0.38", + "syn 2.0.96", ] [[package]] name = "serde_derive_internals" -version = "0.26.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", - "quote 1.0.35", - "syn 1.0.109", + "quote 1.0.38", + "syn 2.0.96", ] [[package]] name = "serde_json" -version = "1.0.115" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.7.1", "itoa", + "memchr", "ryu", "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -4978,10 +7531,19 @@ checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" dependencies = [ "darling 0.13.4", "proc-macro2", - "quote 1.0.35", + "quote 1.0.38", "syn 1.0.109", ] +[[package]] +name = "servo_arc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d036d71a959e00c77a63538b90a6c2390969f9772b096ea837205c6bd0491a44" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "sha-1" version = "0.10.1" @@ -5006,9 +7568,9 @@ dependencies = [ [[package]] name = "sha1_smol" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" [[package]] name = "sha2" @@ -5030,11 +7592,17 @@ dependencies = [ "lazy_static", ] +[[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.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -5059,13 +7627,64 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote 1.0.38", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "simplecss" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c" +dependencies = [ + "log", +] + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ - "autocfg 1.2.0", + "autocfg 1.4.0", +] + +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "version_check", ] [[package]] @@ -5097,9 +7716,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.6" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -5147,13 +7766,10 @@ dependencies = [ ] [[package]] -name = "state" -version = "0.5.3" +name = "stable_deref_trait" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b" -dependencies = [ - "loom", -] +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "state" @@ -5161,18 +7777,53 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8" dependencies = [ - "loom", + "loom 0.5.6", +] + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +dependencies = [ + "float-cmp", +] + +[[package]] +name = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote 1.0.38", ] [[package]] name = "stringprep" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" dependencies = [ - "finl_unicode", "unicode-bidi", "unicode-normalization", + "unicode-properties", ] [[package]] @@ -5181,11 +7832,40 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote 1.0.38", + "rustversion", + "syn 2.0.96", +] + [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "svgtypes" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc" +dependencies = [ + "kurbo", + "siphasher 1.0.1", +] [[package]] name = "syn" @@ -5205,27 +7885,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", - "quote 1.0.35", + "quote 1.0.38", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.58" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", - "quote 1.0.35", + "quote 1.0.38", "unicode-ident", ] +[[package]] +name = "syn-mid" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5dc35bb08dd1ca3dfb09dce91fd2d13294d6711c88897d9a9d60acf39bce049" +dependencies = [ + "proc-macro2", + "quote 1.0.38", + "syn 2.0.96", +] + [[package]] name = "sync_wrapper" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + [[package]] name = "synom" version = "0.11.3" @@ -5242,9 +7942,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2", - "quote 1.0.35", + "quote 1.0.38", "syn 1.0.109", - "unicode-xid 0.2.4", + "unicode-xid 0.2.6", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote 1.0.38", + "syn 2.0.96", ] [[package]] @@ -5269,7 +7980,18 @@ checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", "core-foundation", - "system-configuration-sys", + "system-configuration-sys 0.5.0", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.8.0", + "core-foundation", + "system-configuration-sys 0.6.0", ] [[package]] @@ -5282,6 +8004,35 @@ dependencies = [ "libc", ] +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml 0.8.19", + "version-compare", +] + +[[package]] +name = "tagptr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" + [[package]] name = "take_mut" version = "0.2.2" @@ -5294,16 +8045,35 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + [[package]] name = "tempfile" -version = "3.10.1" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if", - "fastrand 2.0.2", - "rustix 0.38.32", - "windows-sys 0.52.0", + "fastrand 2.3.0", + "getrandom", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", ] [[package]] @@ -5317,22 +8087,42 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.58" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.11", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", - "quote 1.0.35", - "syn 2.0.58", + "quote 1.0.38", + "syn 2.0.96", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote 1.0.38", + "syn 2.0.96", ] [[package]] @@ -5345,15 +8135,28 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + [[package]] name = "time" -version = "0.3.34" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", "itoa", + "libc", "num-conv", + "num_threads", "powerfmt", "serde", "time-core", @@ -5368,19 +8171,55 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" dependencies = [ "num-conv", "time-core", ] +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "png", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[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.6.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" dependencies = [ "tinyvec_macros", ] @@ -5404,32 +8243,31 @@ dependencies = [ [[package]] name = "tokio" -version = "1.37.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", - "bytes 1.6.0", + "bytes 1.9.0", "libc", "mio", - "num_cpus", "parking_lot", - "pin-project-lite 0.2.14", + "pin-project-lite 0.2.16", "signal-hook-registry", - "socket2 0.5.6", + "socket2 0.5.8", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", - "quote 1.0.35", - "syn 2.0.58", + "quote 1.0.38", + "syn 2.0.96", ] [[package]] @@ -5439,7 +8277,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", - "tokio 1.37.0", + "tokio 1.43.0", ] [[package]] @@ -5448,35 +8286,65 @@ version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ - "rustls", - "tokio 1.37.0", + "rustls 0.20.9", + "tokio 1.43.0", "webpki", ] +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls 0.21.12", + "tokio 1.43.0", +] + +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls 0.22.4", + "rustls-pki-types", + "tokio 1.43.0", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" +dependencies = [ + "rustls 0.23.21", + "tokio 1.43.0", +] + [[package]] name = "tokio-stream" -version = "0.1.15" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", - "pin-project-lite 0.2.14", - "tokio 1.37.0", + "pin-project-lite 0.2.16", + "tokio 1.43.0", ] [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ - "bytes 1.6.0", + "bytes 1.9.0", "futures-core", "futures-io", "futures-sink", - "pin-project-lite 0.2.14", - "tokio 1.37.0", - "tracing", + "pin-project-lite 0.2.16", + "tokio 1.43.0", ] [[package]] @@ -5490,21 +8358,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.12" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.9", + "toml_edit 0.22.22", ] [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] @@ -5515,22 +8383,22 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.7.1", "toml_datetime", "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.22.9" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.7.1", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.5", + "winnow 0.6.24", ] [[package]] @@ -5545,40 +8413,78 @@ dependencies = [ "sha2", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite 0.2.16", + "sync_wrapper 1.0.2", + "tokio 1.43.0", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +dependencies = [ + "bitflags 2.8.0", + "bytes 1.9.0", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "pin-project-lite 0.2.16", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", - "pin-project-lite 0.2.14", + "pin-project-lite 0.2.16", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", - "quote 1.0.35", - "syn 2.0.58", + "quote 1.0.38", + "syn 2.0.96", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -5607,9 +8513,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", @@ -5642,9 +8548,9 @@ dependencies = [ "log", "rand 0.8.5", "smallvec", - "thiserror", + "thiserror 1.0.69", "tinyvec", - "tokio 1.37.0", + "tokio 1.43.0", "url", ] @@ -5663,8 +8569,8 @@ dependencies = [ "parking_lot", "resolv-conf", "smallvec", - "thiserror", - "tokio 1.37.0", + "thiserror 1.0.69", + "tokio 1.43.0", "trust-dns-proto", ] @@ -5674,6 +8580,15 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "ttf-parser" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be21190ff5d38e8b4a2d3b6a3ae57f612cc39c96e83cedeaf7abc338a8bac4a" +dependencies = [ + "core_maths", +] + [[package]] name = "tungstenite" version = "0.17.3" @@ -5682,13 +8597,13 @@ checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" dependencies = [ "base64 0.13.1", "byteorder", - "bytes 1.6.0", - "http", + "bytes 1.9.0", + "http 0.2.12", "httparse", "log", "rand 0.8.5", "sha-1", - "thiserror", + "thiserror 1.0.69", "url", "utf-8", ] @@ -5700,7 +8615,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89851716b67b937e393b3daa8423e67ddfc4bbbf1654bcf05488e95e0828db0c" dependencies = [ "proc-macro2", - "quote 1.0.35", + "quote 1.0.38", "syn 1.0.109", ] @@ -5721,9 +8636,9 @@ dependencies = [ [[package]] name = "ucd-trie" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "ulid" @@ -5748,11 +8663,10 @@ dependencies = [ [[package]] name = "ulid" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34778c17965aa2a08913b57e1f34db9b4a63f5de31768b55bf20d2795f921259" +checksum = "f294bff79170ed1c5633812aff1e565c35d993a36e757f9bc0accf5eec4e6045" dependencies = [ - "getrandom", "rand 0.8.5", "web-time", ] @@ -5778,12 +8692,9 @@ dependencies = [ [[package]] name = "unicase" -version = "2.7.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicase_serde" @@ -5797,30 +8708,66 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + +[[package]] +name = "unicode-bidi-mirroring" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64af057ad7466495ca113126be61838d8af947f41d93a949980b2389a118082f" + +[[package]] +name = "unicode-ccc" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "260bc6647b3893a9a90668360803a15f96b85a5257b1c3a0c3daf6ae2496de42" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243" + +[[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 = "unicode-script" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb421b350c9aff471779e262955939f565ec18b86c15364e6bdf0d662ca7c1f" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] -name = "unicode-normalization" -version = "0.1.23" +name = "unicode-vo" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] +checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" [[package]] -name = "unicode-segmentation" -version = "1.11.0" +name = "unicode-width" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-xid" @@ -5830,9 +8777,9 @@ checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "universal-hash" @@ -5858,11 +8805,11 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "2.9.6" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11f214ce18d8b2cbe84ed3aa6486ed3f5b285cf8d8fbdbce9f3f767a724adc35" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "log", "native-tls", "once_cell", @@ -5871,12 +8818,12 @@ dependencies = [ [[package]] name = "url" -version = "2.5.0" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna 1.0.3", "percent-encoding", "serde", ] @@ -5896,22 +8843,116 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" +[[package]] +name = "usvg" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447e703d7223b067607655e625e0dbca80822880248937da65966194c4864e6" +dependencies = [ + "base64 0.22.1", + "data-url", + "flate2", + "fontdb", + "imagesize", + "kurbo", + "log", + "pico-args", + "roxmltree", + "rustybuzz", + "simplecss", + "siphasher 1.0.1", + "strict-num", + "svgtypes", + "tiny-skia-path", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "xmlwriter", +] + [[package]] name = "utf-8" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[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 = "utoipa" +version = "4.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5afb1a60e207dca502682537fefcfd9921e71d0b83e9576060f09abc6efab23" +dependencies = [ + "indexmap 2.7.1", + "serde", + "serde_json", + "utoipa-gen", +] + +[[package]] +name = "utoipa-gen" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20c24e8ab68ff9ee746aad22d39b5535601e6416d1b0feeabf78be986a5c4392" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote 1.0.38", + "regex", + "syn 2.0.96", + "ulid 1.1.4", +] + +[[package]] +name = "utoipa-scalar" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3ab4b7269d14d93626b0bfedf212f1b0995cb7d13d35daba21d579511e7fae8" +dependencies = [ + "axum", + "serde", + "serde_json", + "utoipa", +] + [[package]] name = "uuid" -version = "1.8.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b" dependencies = [ "getrandom", "serde", ] +[[package]] +name = "v_frame" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + [[package]] name = "validator" version = "0.15.0" @@ -5953,7 +8994,7 @@ dependencies = [ "lazy_static", "proc-macro-error", "proc-macro2", - "quote 1.0.35", + "quote 1.0.38", "regex", "syn 1.0.109", "validator_types", @@ -5971,15 +9012,15 @@ dependencies = [ [[package]] name = "valuable" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "value-bag" -version = "1.8.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74797339c3b98616c009c7c3eb53a0ce41e85c8ec66bd3db96ed132d20cfdee8" +checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2" [[package]] name = "vcpkg" @@ -5998,24 +9039,36 @@ dependencies = [ "enum-iterator", "getset", "git2", - "rustc_version 0.4.0", + "rustc_version 0.4.1", "rustversion", "sysinfo", - "thiserror", + "thiserror 1.0.69", "time", ] +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + [[package]] name = "version_check" -version = "0.9.4" +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 = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" [[package]] name = "waker-fn" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" [[package]] name = "want" @@ -6043,82 +9096,87 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", + "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", - "quote 1.0.35", - "syn 2.0.58", + "quote 1.0.38", + "syn 2.0.96", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ - "quote 1.0.35", + "quote 1.0.38", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", - "quote 1.0.35", - "syn 2.0.58", + "quote 1.0.38", + "syn 2.0.96", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "web-push" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5c5c6deab45e8820b77c9c8ae168f1ded4595767c746584bb67b9100f2b71d" +checksum = "49214685777c429ac44a92780983f4fadeecddd8c08c463d8196521e17e974bd" dependencies = [ "async-trait", "base64 0.13.1", "chrono", "ece", "futures-lite 1.13.0", - "http", + "http 0.2.12", "isahc", "jwt-simple", "log", @@ -6131,9 +9189,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -6149,6 +9207,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f53152f51fb5af0c08484c33d16cca96175881d1f3dec068c23b31a158c2d99" +dependencies = [ + "image", + "libwebp-sys", +] + [[package]] name = "webpki" version = "0.22.4" @@ -6168,6 +9236,21 @@ dependencies = [ "webpki", ] +[[package]] +name = "webpki-roots" +version = "0.26.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + [[package]] name = "widestring" version = "1.1.0" @@ -6192,11 +9275,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "winapi", + "windows-sys 0.59.0", ] [[package]] @@ -6214,13 +9297,88 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", + "windows-targets 0.52.6", +] + [[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.4", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote 1.0.38", + "syn 2.0.96", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote 1.0.38", + "syn 2.0.96", +] + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", ] [[package]] @@ -6238,7 +9396,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "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]] @@ -6258,17 +9425,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "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]] @@ -6279,9 +9447,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -6291,9 +9459,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -6303,9 +9471,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +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 = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -6315,9 +9489,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -6327,9 +9501,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -6339,9 +9513,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -6351,9 +9525,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" @@ -6366,9 +9540,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.5" +version = "0.6.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" dependencies = [ "memchr", ] @@ -6383,6 +9557,18 @@ dependencies = [ "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" @@ -6392,6 +9578,18 @@ dependencies = [ "tap", ] +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + +[[package]] +name = "xmlwriter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + [[package]] name = "yaml-rust" version = "0.4.5" @@ -6403,43 +9601,164 @@ dependencies = [ [[package]] name = "yansi" -version = "0.5.1" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" +dependencies = [ + "is-terminal", +] [[package]] -name = "yansi" -version = "1.0.1" +name = "yoke" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" +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 1.0.38", + "syn 2.0.96", + "synstructure 0.13.1", +] + +[[package]] +name = "yup-oauth2" +version = "8.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b61da40aeb0907a65f7fb5c1de83c5a224d6a9ebb83bf918588a2bb744d636b8" +dependencies = [ + "anyhow", + "async-trait", + "base64 0.21.7", + "futures", + "http 0.2.12", + "hyper 0.14.32", + "hyper-rustls 0.24.2", + "itertools 0.12.1", + "log", + "percent-encoding", + "rustls 0.22.4", + "rustls-pemfile 1.0.4", + "seahash", + "serde", + "serde_json", + "time", + "tokio 1.43.0", + "tower-service", + "url", +] [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote 1.0.38", + "syn 2.0.96", +] + +[[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 = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", - "quote 1.0.35", - "syn 2.0.58", + "quote 1.0.38", + "syn 2.0.96", + "synstructure 0.13.1", ] [[package]] name = "zeroize" -version = "1.7.0" +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 1.0.38", + "syn 2.0.96", +] + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" +dependencies = [ + "zune-core", +] [[patch.unused]] name = "redis" version = "0.22.3" source = "git+https://github.com/revoltchat/redis-rs?rev=1a41faf356fd21aebba71cea7eb7eb2653e5f0ef#1a41faf356fd21aebba71cea7eb7eb2653e5f0ef" + +[[patch.unused]] +name = "rocket" +version = "0.5.0-rc.2" +source = "git+https://github.com/rwf2/Rocket/?rev=4dcd928#4dcd92837f3a4626a28d4b25da8e6e560878d004" diff --git a/Cargo.toml b/Cargo.toml index 03eb89cb6..2c3013fdf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,22 @@ [workspace] resolver = "2" -members = ["crates/delta", "crates/bonfire", "crates/voice-ingress", "crates/core/*"] + +members = [ + "crates/delta", + "crates/bonfire", + "crates/core/*", + "crates/services/*", + "crates/bindings/*", + "crates/daemons/pushd", + "crates/daemons/voice-ingress" +] [patch.crates-io] -# mobc-redis = { git = "https://github.com/insertish/mobc", rev = "8b880bb59f2ba80b4c7bc40c649c113d8857a186" } redis22 = { package = "redis", version = "0.22.3", git = "https://github.com/revoltchat/redis-rs", rev = "1a41faf356fd21aebba71cea7eb7eb2653e5f0ef" } redis23 = { package = "redis", version = "0.23.1", git = "https://github.com/revoltchat/redis-rs", rev = "f8ca28ab85da59d2ccde526b4d2fb390eff5a5f9" } # authifier = { package = "authifier", version = "1.0.8", path = "../authifier/crates/authifier" } # rocket_authifier = { package = "rocket_authifier", version = "1.0.8", path = "../authifier/crates/rocket_authifier" } -rocket = { git = "https://github.com/rwf2/Rocket/", rev = "4dcd928" } \ No newline at end of file +rocket = { git = "https://github.com/rwf2/Rocket/", rev = "4dcd928" } + +[profile.release] +lto = true diff --git a/Dockerfile b/Dockerfile index 153a7b1b5..10112b667 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Build Stage -FROM --platform="${BUILDPLATFORM}" rust:1.70.0-slim +FROM --platform="${BUILDPLATFORM}" rust:1.77.2-slim-bookworm USER 0:0 WORKDIR /home/rust/src @@ -17,14 +17,19 @@ RUN sh /tmp/build-image-layer.sh tools # Build all dependencies COPY Cargo.toml Cargo.lock ./ +COPY crates/bindings/node/Cargo.toml ./crates/bindings/node/ COPY crates/bonfire/Cargo.toml ./crates/bonfire/ COPY crates/delta/Cargo.toml ./crates/delta/ COPY crates/core/config/Cargo.toml ./crates/core/config/ COPY crates/core/database/Cargo.toml ./crates/core/database/ +COPY crates/core/files/Cargo.toml ./crates/core/files/ COPY crates/core/models/Cargo.toml ./crates/core/models/ COPY crates/core/permissions/Cargo.toml ./crates/core/permissions/ COPY crates/core/presence/Cargo.toml ./crates/core/presence/ COPY crates/core/result/Cargo.toml ./crates/core/result/ +COPY crates/services/autumn/Cargo.toml ./crates/services/autumn/ +COPY crates/services/january/Cargo.toml ./crates/services/january/ +COPY crates/daemons/pushd/Cargo.toml ./crates/daemons/pushd/ RUN sh /tmp/build-image-layer.sh deps # Build all apps diff --git a/Dockerfile.useCurrentArch b/Dockerfile.useCurrentArch index 1d6d3b55e..34a69462e 100644 --- a/Dockerfile.useCurrentArch +++ b/Dockerfile.useCurrentArch @@ -1,5 +1,5 @@ # Build Stage -FROM rust:1.70.0-slim +FROM rust:1.77.2-slim-bookworm USER 0:0 WORKDIR /home/rust/src @@ -13,14 +13,19 @@ COPY scripts/build-image-layer.sh /tmp/ # Build all dependencies COPY Cargo.toml Cargo.lock ./ +COPY crates/bindings/node/Cargo.toml ./crates/bindings/node/ COPY crates/bonfire/Cargo.toml ./crates/bonfire/ COPY crates/delta/Cargo.toml ./crates/delta/ COPY crates/core/config/Cargo.toml ./crates/core/config/ COPY crates/core/database/Cargo.toml ./crates/core/database/ +COPY crates/core/files/Cargo.toml ./crates/core/files/ COPY crates/core/models/Cargo.toml ./crates/core/models/ COPY crates/core/permissions/Cargo.toml ./crates/core/permissions/ COPY crates/core/presence/Cargo.toml ./crates/core/presence/ COPY crates/core/result/Cargo.toml ./crates/core/result/ +COPY crates/services/autumn/Cargo.toml ./crates/services/autumn/ +COPY crates/services/january/Cargo.toml ./crates/services/january/ +COPY crates/daemons/pushd/Cargo.toml ./crates/daemons/pushd/ RUN sh /tmp/build-image-layer.sh deps # Build all apps diff --git a/LICENSE b/LICENSE index eaeac67ce..dad4e57d6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,7 @@ - GNU AFFERO GENERAL PUBLIC LICENSE +With the exception of crates that specify their own LICENSE file, +the following license applies to the source code of this project. + +GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 Copyright (C) 2007 Free Software Foundation, Inc. diff --git a/README.md b/README.md index ee942fa73..ea8cab84b 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,47 @@ -# Revolt Backend - -This is a monorepo for the Revolt backend. - -| Crate | Path | Description | -| ------------------ | -------------------------------------------------- | ----------------------------- | -| `core/config` | [crates/core/config](crates/core/config) | Core: Configuration | -| `core/database` | [crates/core/database](crates/core/database) | Core: Database Implementation | -| `core/models` | [crates/core/models](crates/core/models) | Core: API Models | -| `core/permissions` | [crates/core/permissions](crates/core/permissions) | Core: Permission Logic | -| `core/presence` | [crates/core/presence](crates/core/presence) | Core: User Presence | -| `core/result` | [crates/core/result](crates/core/result) | Core: Result and Error types | -| `delta` | [crates/delta](crates/delta) | REST API server | -| `bonfire` | [crates/bonfire](crates/bonfire) | WebSocket events server | - -Note: `january`, `autumn`, and `vortex` are yet to be moved into this monorepo. +
+

+ Revolt Backend + + [![Stars](https://img.shields.io/github/stars/revoltchat/backend?style=flat-square&logoColor=white)](https://github.com/revoltchat/backend/stargazers) + [![Forks](https://img.shields.io/github/forks/revoltchat/backend?style=flat-square&logoColor=white)](https://github.com/revoltchat/backend/network/members) + [![Pull Requests](https://img.shields.io/github/issues-pr/revoltchat/backend?style=flat-square&logoColor=white)](https://github.com/revoltchat/backend/pulls) + [![Issues](https://img.shields.io/github/issues/revoltchat/backend?style=flat-square&logoColor=white)](https://github.com/revoltchat/backend/issues) + [![Contributors](https://img.shields.io/github/contributors/revoltchat/backend?style=flat-square&logoColor=white)](https://github.com/revoltchat/backend/graphs/contributors) + [![License](https://img.shields.io/github/license/revoltchat/backend?style=flat-square&logoColor=white)](https://github.com/revoltchat/backend/blob/main/LICENSE) +

+The services and libraries that power the Revolt service.
+
+ +| Crate | Path | Description | | +| ------------------ | -------------------------------------------------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `core/config` | [crates/core/config](crates/core/config) | Core: Configuration | ![Crates.io Version](https://img.shields.io/crates/v/revolt-config) ![Crates.io Version](https://img.shields.io/crates/msrv/revolt-config) ![Crates.io Version](https://img.shields.io/crates/size/revolt-config) ![Crates.io License](https://img.shields.io/crates/l/revolt-config) | +| `core/database` | [crates/core/database](crates/core/database) | Core: Database Implementation | ![Crates.io Version](https://img.shields.io/crates/v/revolt-database) ![Crates.io Version](https://img.shields.io/crates/msrv/revolt-database) ![Crates.io Version](https://img.shields.io/crates/size/revolt-database) ![Crates.io License](https://img.shields.io/crates/l/revolt-database) | +| `core/files` | [crates/core/files](crates/core/files) | Core: S3 and encryption subroutines | ![Crates.io Version](https://img.shields.io/crates/v/revolt-files) ![Crates.io Version](https://img.shields.io/crates/msrv/revolt-files) ![Crates.io Version](https://img.shields.io/crates/size/revolt-files) ![Crates.io License](https://img.shields.io/crates/l/revolt-files) | +| `core/models` | [crates/core/models](crates/core/models) | Core: API Models | ![Crates.io Version](https://img.shields.io/crates/v/revolt-models) ![Crates.io Version](https://img.shields.io/crates/msrv/revolt-models) ![Crates.io Version](https://img.shields.io/crates/size/revolt-models) ![Crates.io License](https://img.shields.io/crates/l/revolt-models) | +| `core/permissions` | [crates/core/permissions](crates/core/permissions) | Core: Permission Logic | ![Crates.io Version](https://img.shields.io/crates/v/revolt-permissions) ![Crates.io Version](https://img.shields.io/crates/msrv/revolt-permissions) ![Crates.io Version](https://img.shields.io/crates/size/revolt-permissions) ![Crates.io License](https://img.shields.io/crates/l/revolt-permissions) | +| `core/presence` | [crates/core/presence](crates/core/presence) | Core: User Presence | ![Crates.io Version](https://img.shields.io/crates/v/revolt-presence) ![Crates.io Version](https://img.shields.io/crates/msrv/revolt-presence) ![Crates.io Version](https://img.shields.io/crates/size/revolt-presence) ![Crates.io License](https://img.shields.io/crates/l/revolt-presence) | +| `core/result` | [crates/core/result](crates/core/result) | Core: Result and Error types | ![Crates.io Version](https://img.shields.io/crates/v/revolt-result) ![Crates.io Version](https://img.shields.io/crates/msrv/revolt-result) ![Crates.io Version](https://img.shields.io/crates/size/revolt-result) ![Crates.io License](https://img.shields.io/crates/l/revolt-result) | +| `delta` | [crates/delta](crates/delta) | REST API server | ![License](https://img.shields.io/badge/license-AGPL--3.0--or--later-blue) | +| `bonfire` | [crates/bonfire](crates/bonfire) | WebSocket events server | ![License](https://img.shields.io/badge/license-AGPL--3.0--or--later-blue) | +| `services/january` | [crates/services/january](crates/services/january) | Proxy server | ![License](https://img.shields.io/badge/license-AGPL--3.0--or--later-blue) | +| `services/autumn` | [crates/services/autumn](crates/services/autumn) | File server | ![License](https://img.shields.io/badge/license-AGPL--3.0--or--later-blue) | +| `bindings/node` | [crates/bindings/node](crates/bindings/node) | Node.js bindings for the Revolt software | ![License](https://img.shields.io/badge/license-AGPL--3.0--or--later-blue) | +| `daemons/pushd` | [crates/daemons/pushd](crates/daemons/pushd) | Push notification daemon server | ![License](https://img.shields.io/badge/license-AGPL--3.0--or--later-blue) | + +
+
## Minimum Supported Rust Version -Rust 1.70 or higher. +Rust 1.76 or higher. + +> [!CAUTION] +> The events server has a significant performance regression between Rust 1.77.2 and 1.78.0 onwards, see [issue #341](https://github.com/revoltchat/backend/issues/341). ## Development Guide +Before contributing, make yourself familiar with [our contribution guidelines](https://developers.revolt.chat/contrib.html) and the [technical documentation for this project](https://revoltchat.github.io/backend/). + Before getting started, you'll want to install: - Rust toolchain (rustup recommended) @@ -31,6 +52,21 @@ Before getting started, you'll want to install: > A **default.nix** is available for Nix users! > Just run `nix-shell` and continue. +As a heads-up, the development environment uses the following ports: + +| Service | Port | +| ------------------------- | :------------: | +| MongoDB | 27017 | +| Redis | 6379 | +| MinIO | 14009 | +| Maildev | 14025
14080 | +| Revolt Web App | 14701 | +| RabbitMQ | 5672
15672 | +| `crates/delta` | 14702 | +| `crates/bonfire` | 14703 | +| `crates/services/autumn` | 14704 | +| `crates/services/january` | 14705 | + Now you can clone and build the project: ```bash @@ -39,26 +75,45 @@ cd revolt-backend cargo build ``` -If you want to run the API and event servers: +A default configuration `Revolt.toml` is present in this project that is suited for development. -```bash -# create environment file (will be deprecated in future) -cp .env.example .env +If you'd like to change anything, create a `Revolt.overrides.toml` file and specify relevant variables. -# (optionally) copy the default configuration file -cp crates/core/config/Revolt.toml Revolt.toml -# configure as necessary... +> [!TIP] +> Use Sentry to catch unexpected service errors: +> +> ```toml +> # Revolt.overrides.toml +> [sentry] +> api = "https://abc@your.sentry/1" +> events = "https://abc@your.sentry/1" +> files = "https://abc@your.sentry/1" +> proxy = "https://abc@your.sentry/1" +> ``` +Then continue: + +```bash # start other necessary services docker compose up -d +# run everything together +./scripts/start.sh +# .. or individually # run the API server cargo run --bin revolt-delta # run the events server cargo run --bin revolt-bonfire +# run the file server +cargo run --bin revolt-autumn +# run the proxy server +cargo run --bin revolt-january +# run the push daemon (not usually needed in regular development) +cargo run --bin revolt-pushd # hint: # mold -run +# mold -run ./scripts/start.sh ``` You can start a web client by doing the following: @@ -72,10 +127,13 @@ git clone --recursive https://github.com/revoltchat/revite cd revite yarn yarn build:deps -yarn dev --port 3001 +echo "VITE_API_URL=http://local.revolt.chat:14702" > .env.local +yarn dev --port 14701 ``` -Then go to https://local.revolt.chat:3001 +Then go to http://local.revolt.chat:14701 to create an account/login. + +When signing up, go to http://localhost:14080 to find confirmation/password reset emails. ## Deployment Guide @@ -105,6 +163,23 @@ Tag and push a new release by running: just release ``` +If you have bumped the crate versions, proceed to [GitHub releases](https://github.com/revoltchat/backend/releases/new) to create a changelog. + +## Testing + +First, start the required services: + +```sh +docker compose -f docker-compose.db.yml up -d +``` + +Now run tests for whichever database: + +```sh +TEST_DB=REFERENCE cargo nextest run +TEST_DB=MONGODB cargo nextest run +``` + ## License The Revolt backend is generally licensed under the [GNU Affero General Public License v3.0](https://github.com/revoltchat/backend/blob/master/LICENSE). diff --git a/Revolt.toml b/Revolt.toml new file mode 100644 index 000000000..f9ea06291 --- /dev/null +++ b/Revolt.toml @@ -0,0 +1,61 @@ +# ⚠️ This configuration is intended for development environment. +# If you'd like to override anything, create a Revolt.override.toml + +[database] +# MongoDB connection URL +# Defaults to the container name specified in self-hosted +mongodb = "mongodb://127.0.0.1:14017" +# Redis connection URL +# Defaults to the container name specified in self-hosted +redis = "redis://127.0.0.1:14079/" + +[hosts] +# Web locations of various services +# Defaults assume all services are reverse-proxied +# See https://github.com/revoltchat/self-hosted/blob/master/Caddyfile +# +# Remember to change these to https/wss where appropriate in production! +app = "http://local.revolt.chat:14701" +api = "http://local.revolt.chat:14702" +events = "ws://local.revolt.chat:14703" +autumn = "http://local.revolt.chat:14704" +january = "http://local.revolt.chat:14705" +livekit = "ws://local.revolt.chat:14706" +voso_legacy = "" +voso_legacy_ws = "" + +[api] + +[api.smtp] +# Email server configuration for verification +# Defaults to no email verification (host field is empty) +host = "localhost" +username = "smtp" +password = "smtp" +from_address = "development@revolt.chat" +reply_to = "support@revolt.chat" +port = 14025 +use_tls = false + +[api.livekit] +url = "ws://local.revolt.chat:14706" +key = "" +secret = "" + +[files.s3] +# S3 protocol endpoint +endpoint = "http://127.0.0.1:14009" +# S3 region name +region = "minio" +# S3 protocol key ID +access_key_id = "minioautumn" +# S3 protocol access key +secret_access_key = "minioautumn" +# Bucket to upload to by default +default_bucket = "revolt-uploads" + +[rabbit] +host = "rabbit.backend.orb.local" +port = 5672 +username = "rabbituser" +password = "rabbitpass" diff --git a/compose.yml b/compose.yml new file mode 100644 index 000000000..31af45bad --- /dev/null +++ b/compose.yml @@ -0,0 +1,66 @@ +services: + # Redis + redis: + image: eqalpha/keydb + ports: + - "6379:6379" + + # MongoDB + database: + image: mongo + ports: + - "27017:27017" + volumes: + - ./.data/db:/data/db + + # MinIO + minio: + image: minio/minio + command: server /data + environment: + MINIO_ROOT_USER: minioautumn + MINIO_ROOT_PASSWORD: minioautumn + volumes: + - ./.data/minio:/data + ports: + - "14009:9000" + - "14010:9001" + restart: always + + # Create buckets for minio. + createbuckets: + image: minio/mc + depends_on: + - minio + entrypoint: > + /bin/sh -c "while ! /usr/bin/mc ready minio; do + /usr/bin/mc config host add minio http://minio:9000 minioautumn minioautumn; + echo 'Waiting minio...' && sleep 1; + done; /usr/bin/mc mb minio/revolt-uploads; exit 0;" + + # Rabbit + rabbit: + image: rabbitmq:3-management + environment: + RABBITMQ_DEFAULT_USER: rabbituser + RABBITMQ_DEFAULT_PASS: rabbitpass + volumes: + - ./.data/rabbit:/var/lib/rabbitmq + #- ./rabbit_plugins:/opt/rabbitmq/plugins/ + #- ./rabbit_enabled_plugins:/etc/rabbitmq/enabled_plugins + # uncomment this if you need to enable other plugins + ports: + - "5672:5672" + - "15672:15672" # management UI, for development + + # Mock SMTP server + maildev: + image: soulteary/maildev + ports: + - "14025:25" + - "14080:8080" + environment: + MAILDEV_SMTP_PORT: 25 + MAILDEV_WEB_PORT: 8080 + MAILDEV_INCOMING_USER: smtp + MAILDEV_INCOMING_PASS: smtp diff --git a/crates/bindings/node/.gitignore b/crates/bindings/node/.gitignore new file mode 100644 index 000000000..c76a159c7 --- /dev/null +++ b/crates/bindings/node/.gitignore @@ -0,0 +1,7 @@ +target +index.node +**/node_modules +**/.DS_Store +npm-debug.log* +cargo.log +cross.log diff --git a/crates/bindings/node/Cargo.toml b/crates/bindings/node/Cargo.toml new file mode 100644 index 000000000..55ada675f --- /dev/null +++ b/crates/bindings/node/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "revolt-nodejs-bindings" +version = "0.8.1" +description = "Node.js bindings for the Revolt software" +authors = ["Paul Makles "] +license = "MIT" +edition = "2021" +exclude = ["index.node"] + +[lib] +crate-type = ["cdylib"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +neon = "1.0.0" +neon-serde4 = "1.0.0" + +serde = { version = "1", features = ["derive"] } + +async-std = "1.12.0" + +revolt-config = { version = "0.8.1", path = "../../core/config" } +revolt-result = { version = "0.8.1", path = "../../core/result" } +revolt-database = { version = "0.8.1", path = "../../core/database" } diff --git a/crates/bindings/node/README.md b/crates/bindings/node/README.md new file mode 100644 index 000000000..f2ccee6ee --- /dev/null +++ b/crates/bindings/node/README.md @@ -0,0 +1,92 @@ +# revolt.node + +**revolt.node:** Node.js bindings for the Revolt software + +This project was bootstrapped by [create-neon](https://www.npmjs.com/package/create-neon). + +## Building revolt.node + +Building revolt.node requires a [supported version of Node and Rust](https://github.com/neon-bindings/neon#platform-support). + +To run the build, run: + +```sh +$ npm run build +``` + +This command uses the [@neon-rs/cli](https://www.npmjs.com/package/@neon-rs/cli) utility to assemble the binary Node addon from the output of `cargo`. + +## Exploring revolt.node + +After building revolt.node, you can explore its exports at the Node console: + +```sh +$ npm i +$ npm run build +$ node +> require('.').hello() +'hello node' +``` + +## Available Scripts + +In the project directory, you can run: + +#### `npm install` + +Installs the project, including running `npm run build`. + +#### `npm run build` + +Builds the Node addon (`index.node`) from source, generating a release build with `cargo --release`. + +Additional [`cargo build`](https://doc.rust-lang.org/cargo/commands/cargo-build.html) arguments may be passed to `npm run build` and similar commands. For example, to enable a [cargo feature](https://doc.rust-lang.org/cargo/reference/features.html): + +``` +npm run build -- --feature=beetle +``` + +#### `npm run debug` + +Similar to `npm run build` but generates a debug build with `cargo`. + +#### `npm run cross` + +Similar to `npm run build` but uses [cross-rs](https://github.com/cross-rs/cross) to cross-compile for another platform. Use the [`CARGO_BUILD_TARGET`](https://doc.rust-lang.org/cargo/reference/config.html#buildtarget) environment variable to select the build target. + +#### `npm test` + +Runs the unit tests by calling `cargo test`. You can learn more about [adding tests to your Rust code](https://doc.rust-lang.org/book/ch11-01-writing-tests.html) from the [Rust book](https://doc.rust-lang.org/book/). + +## Project Layout + +The directory structure of this project is: + +``` +revolt.node/ +├── Cargo.toml +├── README.md +├── src/ +| └── lib.rs +├── index.node +├── package.json +└── target/ +``` + +| Entry | Purpose | +|----------------|------------------------------------------------------------------------------------------------------------------------------------------| +| `Cargo.toml` | The Cargo [manifest file](https://doc.rust-lang.org/cargo/reference/manifest.html), which informs the `cargo` command. | +| `README.md` | This file. | +| `src/` | The directory tree containing the Rust source code for the project. | +| `lib.rs` | Entry point for the Rust source code. | +| `index.node` | The main module, a [Node addon](https://nodejs.org/api/addons.html) generated by the build and pointed to by `"main"` in `package.json`. | +| `package.json` | The npm [manifest file](https://docs.npmjs.com/cli/v7/configuring-npm/package-json), which informs the `npm` command. | +| `target/` | Binary artifacts generated by the Rust build. | + +## Learn More + +Learn more about: + +- [Neon](https://neon-bindings.com). +- [Rust](https://www.rust-lang.org). +- [Node](https://nodejs.org). diff --git a/crates/bindings/node/index.d.ts b/crates/bindings/node/index.d.ts new file mode 100644 index 000000000..0417ba4d1 --- /dev/null +++ b/crates/bindings/node/index.d.ts @@ -0,0 +1,84 @@ +import { Channel, User } from "revolt-api"; + +/** + * Opaque type for Revolt database + */ +export declare interface Database {} + +/** + * Opaque type for Revolt database + */ +export declare interface OpaqueUser {} + +/** + * Error type from Revolt backend + */ +export declare interface Err { + type: string; + location: string; +} + +/** + * Initialises background tasks and logging, must be called before anything else! + * Can be called multiple times! + */ +export declare function init(); + +/** + * Gets a new handle to the Revolt database + * @returns {Database} Handle + */ +export declare function database(): Database; + +/** + * Fetch user from database + * @param {string} userId User's ID + * @this {Database} + */ +export declare function database_fetch_user(userId: string): OpaqueUser; + +/** + * Fetch user from database + * @param {string} username Username + * @param {string} discriminator Discriminator + * @this {Database} + */ +export declare function database_fetch_user_by_username( + username: string, + discriminator: string +): OpaqueUser; + +/** + * Gets model data as JSON + * @this {OpaqueUser} + */ +export declare function model_data(): User; + +/** + * Gets error if the model failed to fetch + * @this {OpaqueUser} + */ +export declare function model_error(): Err; + +/** + * Open a direct message channel between two users + * @param {string} userA User A ID + * @param {string} userB User B ID + * @returns Existing or newly created channel + */ +export declare function proc_channels_create_dm( + userA: string, + userB: string +): Promise; + +/** + * Suspend a user + * @param {string} user User + * @param {number} duration Duration (in days), set to 0 for indefinite + * @param {string} reason Pipe-separated list of reasons (e.g. reason1|reason2|reason3) + */ +export declare function proc_users_suspend( + user: OpaqueUser, + duration: number, + reason: string +): Promise<{ error: Err }>; diff --git a/crates/bindings/node/package.json b/crates/bindings/node/package.json new file mode 100644 index 000000000..70768a860 --- /dev/null +++ b/crates/bindings/node/package.json @@ -0,0 +1,36 @@ +{ + "name": "revolt-nodejs-bindings", + "version": "0.7.15-rev0.0.3", + "description": "Node.js bindings for the Revolt software", + "main": "index.node", + "scripts": { + "test": "cargo test", + "cargo-build": "cargo build --message-format=json > cargo.log", + "cross-build": "cross build --message-format=json > cross.log", + "postcargo-build": "neon dist < cargo.log", + "postcross-build": "neon dist -m /target < cross.log", + "debug": "npm run cargo-build --", + "build": "npm run cargo-build -- --release", + "cross": "npm run cross-build -- --release" + }, + "author": "Paul Makles", + "license": "AGPL-3.0", + "devDependencies": { + "@neon-rs/cli": "0.1.73" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/revoltchat/backend" + }, + "keywords": [ + "revolt", + "chat" + ], + "bugs": { + "url": "https://github.com/revoltchat/backend/issues" + }, + "homepage": "https://github.com/revoltchat/backend#readme", + "dependencies": { + "revolt-api": "^0.7.15" + } +} \ No newline at end of file diff --git a/crates/bindings/node/pnpm-lock.yaml b/crates/bindings/node/pnpm-lock.yaml new file mode 100644 index 000000000..1d623ad9c --- /dev/null +++ b/crates/bindings/node/pnpm-lock.yaml @@ -0,0 +1,117 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + revolt-api: + specifier: ^0.7.15 + version: 0.7.15 + +devDependencies: + '@neon-rs/cli': + specifier: 0.1.73 + version: 0.1.73 + +packages: + + /@cargo-messages/android-arm-eabi@0.1.72: + resolution: {integrity: sha512-gGZxIM1mj+Y5x+ULND6ZCNr7f70OJi9wDlycSK8hGONy9wrChN6JAIHryddC5cqcwlYAoQ6IDcDFElnhAYbybA==} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@cargo-messages/darwin-arm64@0.1.72: + resolution: {integrity: sha512-EAzN5MLaXPljZKZDO5qR+aBs44eSq2ZbEnS7AI/FziE3MzeXbrGOS3fLba5+7yWPFXJyZolXzePm8N1EBv8ovg==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@cargo-messages/darwin-x64@0.1.72: + resolution: {integrity: sha512-RLo6j8s3nYbjdd1LDct4wamfChyRit7zokUuxtIYCu9XOlltkN5vnj1vwnrPvoqCMZ/7CbbuHFwSTn9A71de/w==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@cargo-messages/linux-arm-gnueabihf@0.1.72: + resolution: {integrity: sha512-tHsRshuzfjrX6SDW3jg6al8vMNLTMgczGnVYl5RuBZf/yrAUuwe30KxA9ge6w6mW6Ox797DyBchzAc9OLgTgmQ==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@cargo-messages/linux-x64-gnu@0.1.72: + resolution: {integrity: sha512-VGtL6CCnUbhsP4aYuBNT5kfrAL7o0qjrxw97a+ax13t+nJd26tVEEIKHMu5drvvS/Nm/hn7sLT8zMnnCv0pvHg==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@cargo-messages/win32-arm64-msvc@0.1.72: + resolution: {integrity: sha512-V93Cgz39K+yqa3MveNbhh29pYCp8izK5uEavjPoxlNxAbsMCWH+s0verGDdUcfGxjR1H2V7oZ4FszPqR2SqMRQ==} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@cargo-messages/win32-x64-msvc@0.1.72: + resolution: {integrity: sha512-knz3uSrO0OSbq3U5VWfCY8FB4NsM43BOWLZ7x4sfaMOC1XWv+IyvDdkLe6DhJx8KUw46KIAimYs9YROrp6l46Q==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@neon-rs/cli@0.1.73: + resolution: {integrity: sha512-1kv8S/feB6UQWQQwsnGfkSkEBOtlFDNExnioL81E2BwvUWgjQPaseHgpi2EpWVgsPUgur5eBm4QowmlpWkD4/w==} + hasBin: true + optionalDependencies: + '@cargo-messages/android-arm-eabi': 0.1.72 + '@cargo-messages/darwin-arm64': 0.1.72 + '@cargo-messages/darwin-x64': 0.1.72 + '@cargo-messages/linux-arm-gnueabihf': 0.1.72 + '@cargo-messages/linux-x64-gnu': 0.1.72 + '@cargo-messages/win32-arm64-msvc': 0.1.72 + '@cargo-messages/win32-x64-msvc': 0.1.72 + dev: true + + /axios@0.26.1: + resolution: {integrity: sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==} + dependencies: + follow-redirects: 1.15.6 + transitivePeerDependencies: + - debug + dev: false + + /follow-redirects@1.15.6: + resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + dev: false + + /lodash.defaultsdeep@4.6.1: + resolution: {integrity: sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==} + dev: false + + /revolt-api@0.7.15: + resolution: {integrity: sha512-rWRnjBFvoXt/RHRxHg9KuP+x1Jk61KdsOpKOczVJg5tIqGDmqp/6QZHw5rJTtG+wCH3Ah3YKwY+MM5NHfg1W3Q==} + dependencies: + axios: 0.26.1 + lodash.defaultsdeep: 4.6.1 + transitivePeerDependencies: + - debug + dev: false diff --git a/crates/bindings/node/src/lib.rs b/crates/bindings/node/src/lib.rs new file mode 100644 index 000000000..85b391834 --- /dev/null +++ b/crates/bindings/node/src/lib.rs @@ -0,0 +1,221 @@ +#[macro_use] +extern crate serde; + +use std::sync::OnceLock; + +use neon::prelude::*; +use revolt_database::{Database, DatabaseInfo}; + +fn js_init(mut cx: FunctionContext) -> JsResult { + // static INIT: OnceLock<()> = OnceLock::new(); + // if INIT.get().is_none() { + // INIT.get_or_init(|| { + // async_std::task::block_on(async { + // revolt_config::configure!(api); + + // match DatabaseInfo::Auto.connect().await { + // Ok(db) => { + // let authifier_db = db.clone().to_authifier().await.database; + // revolt_database::tasks::start_workers(db, authifier_db); + // Ok(()) + // } + // Err(err) => Err(err), + // } + // }) + // .or_else(|err| cx.throw_error(err)) + // .unwrap(); + // }); + // } + + Ok(cx.undefined()) +} + +struct DatabaseBinding(Database, Channel); +impl Finalize for DatabaseBinding {} +impl DatabaseBinding { + fn take(&self) -> (Database, Channel) { + (self.0.clone(), self.1.clone()) + } +} + +fn js_database(mut cx: FunctionContext) -> JsResult> { + let db = async_std::task::block_on(DatabaseInfo::Auto.connect()) + .or_else(|err| cx.throw_error(err))?; + + let channel = cx.channel(); + Ok(cx.boxed(DatabaseBinding(db, channel))) +} + +// Implementations for models +#[derive(Clone)] +#[allow(clippy::large_enum_variant)] +enum Model { + User(revolt_database::User), + Error(revolt_result::Error), +} + +impl Model { + fn give(&self) -> Model { + self.clone() + } +} + +impl Finalize for Model {} + +macro_rules! shim_boxed { + ($cx: ident, $name: ident, $model: ident, $( $variable: ident $type: ident $id: expr )+, $cmd: ident, $( $arg: expr, )+) => { + fn $name(mut cx: FunctionContext) -> JsResult { + $( + let $variable = cx.argument::<$type>($id)?.value(&mut cx); + )+ + + let (db, channel) = cx.this::>()?.take(); + let (deferred, promise) = cx.promise(); + + async_std::task::spawn(async move { + let result = db.$cmd($($arg,)+).await; + deferred.settle_with(&channel, move |mut cx| { + Ok(cx.boxed(match result { + Ok(value) => Model::$model(value), + Err(error) => Model::Error(error) + })) + }) + }); + + Ok(promise) + } + + $cx.export_function(stringify!($name), $name)?; + }; +} + +fn js_data(mut cx: FunctionContext) -> JsResult { + match cx.this::>()?.give() { + Model::Error(_) => neon_serde4::to_value(&mut cx, &None::<()>), + Model::User(user) => neon_serde4::to_value(&mut cx, &user), + } + .or_else(|e| cx.throw_error(e.to_string())) +} + +fn js_error(mut cx: FunctionContext) -> JsResult { + let value = match cx.this::>()?.give() { + Model::Error(err) => Some(err), + _ => None, + }; + + neon_serde4::to_value(&mut cx, &value).or_else(|e| cx.throw_error(e.to_string())) +} + +// Basic data implementation +#[derive(Serialize, Deserialize)] +struct ResultBinding { + #[serde(flatten)] + value: Option, + error: Option, +} + +macro_rules! shim { + ($cx: ident, $name: ident, $( $variable: ident $type: ident $id: expr )*, $( $model: ident $modelType: ident $modelId: expr )*, | $db: ident | $closure: expr, $( $arg: expr, )+) => { + fn $name(mut cx: FunctionContext) -> JsResult { + $( + let $variable = cx.argument::<$type>($id)?.value(&mut cx); + )* + + $( + let mut $model = if let Model::$modelType(value) = cx.argument::>($modelId)?.give() { + value + } else { + unreachable!() + }; + )* + + let (db, channel) = cx.this::>()?.take(); + let (deferred, promise) = cx.promise(); + + async_std::task::spawn(async move { + #[allow(clippy::redundant_closure_call)] + let result = (|$db: $crate::Database| $closure)(db.clone()).await; + deferred.settle_with(&channel, move |mut cx| { + neon_serde4::to_value( + &mut cx, + &match result { + Ok(value) => ResultBinding { + value: Some(value), + error: None, + }, + Err(error) => ResultBinding { + value: None, + error: Some(error), + }, + }, + ) + .or_else(|e| cx.throw_error(e.to_string())) + }) + }); + + Ok(promise) + } + + $cx.export_function(stringify!($name), $name)?; + }; +} + +#[neon::main] +fn main(mut cx: ModuleContext) -> NeonResult<()> { + // initialise required background stuff + cx.export_function("init", js_init)?; + + // database & model stuff + cx.export_function("database", js_database)?; + cx.export_function("model_data", js_data)?; + cx.export_function("model_error", js_error)?; + + shim_boxed!( + cx, + database_fetch_user, + User, + user_id JsString 0, + fetch_user, + &user_id, + ); + + shim_boxed!( + cx, + database_fetch_user_by_username, + User, + username JsString 0 + discriminator JsString 1, + fetch_user_by_username, + &username, &discriminator, + ); + + // procedure calls + shim!( + cx, + proc_channels_create_dm, + user_a JsString 0 + user_b JsString 1, + , + |db| async move { + let user_a = db.fetch_user(&user_a).await?; + let user_b = db.fetch_user(&user_b).await?; + revolt_database::Channel::create_dm(&db, &user_a, &user_b).await + }, + &userA, &userB, + ); + + shim!( + cx, + proc_users_suspend, + duration JsNumber 1 + reason JsString 2, + user User 0, + |db| async move { + let duration = duration as usize; + user.suspend(&db, if duration == 0 { None } else { Some(duration) }, Some(reason.split('|').map(|x| x.to_owned()).collect())).await + }, + &user, + ); + + Ok(()) +} diff --git a/crates/bindings/node/test.js b/crates/bindings/node/test.js new file mode 100644 index 000000000..d8f601747 --- /dev/null +++ b/crates/bindings/node/test.js @@ -0,0 +1,53 @@ +const Internal = require("."); + +// playing around with class wrapper, not practical +class Model { + constructor(model) { + this.model = model; + } + + data() { + return Internal.model_data.bind(this.model)(); + } + + error() { + return Internal.model_error.bind(this.model)(); + } +} + +class User extends Model { + constructor(db, user) { + super(user); + this.db = db; + } +} + +class Database { + constructor() { + this.db = Internal.database(); + } + + async fetchUser(userId) { + return new User( + this, + await Internal.database_fetch_user.bind(this.db)(userId) + ); + } + + async fetchUserByUsername(username, discriminator) { + return new User( + this, + await Internal.database_fetch_user_by_username.bind(this.db)( + username, + discriminator + ) + ); + } +} + +const db = new Database(); +db.fetchUserByUsername("dos", "7624").then((user) => console.info(user.data())); +db.fetchUserByUsername("dos", "1111").then((user) => console.info(user.data())); +db.fetchUserByUsername("dos", "1111").then((user) => + console.info(user.error()) +); diff --git a/crates/bindings/package-lock.json b/crates/bindings/package-lock.json new file mode 100644 index 000000000..9376d3a95 --- /dev/null +++ b/crates/bindings/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "bindings", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/crates/bonfire/Cargo.toml b/crates/bonfire/Cargo.toml index 4e5a2f0a3..64c6507de 100644 --- a/crates/bonfire/Cargo.toml +++ b/crates/bonfire/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "revolt-bonfire" -version = "0.7.1" +version = "0.8.1" license = "AGPL-3.0-or-later" edition = "2021" @@ -14,6 +14,8 @@ lru = "0.7.6" ulid = "0.5.0" once_cell = "1.9.0" redis-kiss = "0.1.4" +lru_time_cache = "0.11.11" +async-channel = "2.3.1" # parsing querystring = "1.1.0" @@ -34,11 +36,12 @@ async-std = { version = "1.8.0", features = [ ] } # core +authifier = { version = "1.0.9" } revolt-result = { path = "../core/result" } revolt-models = { path = "../core/models" } revolt-config = { path = "../core/config" } revolt-database = { path = "../core/database" } -revolt-permissions = { version = "0.7.1", path = "../core/permissions" } +revolt-permissions = { version = "0.8.1", path = "../core/permissions" } revolt-presence = { path = "../core/presence", features = ["redis-is-patched"] } revolt-voice = { path = "../core/voice" } diff --git a/crates/bonfire/Dockerfile b/crates/bonfire/Dockerfile index fb5d540ff..d1829137b 100644 --- a/crates/bonfire/Dockerfile +++ b/crates/bonfire/Dockerfile @@ -1,11 +1,12 @@ # Build Stage FROM ghcr.io/revoltchat/base:latest AS builder +FROM debian:12 AS debian # Bundle Stage -FROM debian:bullseye-slim -RUN apt-get update && \ - apt-get install -y ca-certificates && \ - apt-get clean +FROM gcr.io/distroless/cc-debian12:nonroot COPY --from=builder /home/rust/src/target/release/revolt-bonfire ./ -EXPOSE 9000 +COPY --from=debian /usr/bin/uname /usr/bin/uname + +EXPOSE 14703 +USER nonroot CMD ["./revolt-bonfire"] diff --git a/crates/bonfire/src/config.rs b/crates/bonfire/src/config.rs index 71b8946a1..5642d3baa 100644 --- a/crates/bonfire/src/config.rs +++ b/crates/bonfire/src/config.rs @@ -1,5 +1,6 @@ use async_tungstenite::tungstenite::{handshake, Message}; use futures::channel::oneshot::Sender; +use revolt_database::events::client::ReadyPayloadFields; use revolt_result::{create_error, Result}; use serde::{Deserialize, Serialize}; @@ -83,6 +84,17 @@ impl ProtocolConfiguration { pub fn get_protocol_format(&self) -> &ProtocolFormat { &self.format } + + /// Get ready payload fields + pub fn get_ready_payload_fields(&self) -> Vec { + vec![ + ReadyPayloadFields::Users, + ReadyPayloadFields::Servers, + ReadyPayloadFields::Channels, + ReadyPayloadFields::Members, + ReadyPayloadFields::Emoji, + ] + } } /// Object holding one side of a channel for receiving the parsed information diff --git a/crates/bonfire/src/events/impl.rs b/crates/bonfire/src/events/impl.rs index 2f0141653..714d9a8d7 100644 --- a/crates/bonfire/src/events/impl.rs +++ b/crates/bonfire/src/events/impl.rs @@ -1,8 +1,9 @@ use std::collections::HashSet; use revolt_database::{ - events::client::EventV1, util::permissions::DatabasePermissionQuery, Channel, Database, Member, - MemberCompositeKey, Presence, RelationshipStatus, + events::client::{EventV1, ReadyPayloadFields}, + util::permissions::DatabasePermissionQuery, + Channel, Database, Member, MemberCompositeKey, Presence, RelationshipStatus, }; use revolt_models::v0; use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; @@ -23,11 +24,11 @@ impl Cache { let server = self.servers.get(server); let mut query = DatabasePermissionQuery::new(db, self.users.get(&self.user_id).unwrap()) - .channel(&channel); + .channel(channel); // let mut perms = perms(self.users.get(&self.user_id).unwrap()).channel(channel); if let Some(member) = member { - query = query.member(&member); + query = query.member(member); } if let Some(server) = server { @@ -92,8 +93,13 @@ impl Cache { /// State Manager impl State { /// Generate a Ready packet for the current user - pub async fn generate_ready_payload(&mut self, db: &Database) -> Result { + pub async fn generate_ready_payload( + &mut self, + db: &Database, + fields: Vec, + ) -> Result { let user = self.clone_user(); + self.cache.is_bot = user.bot.is_some(); // Find all relationships to the user. let mut user_ids: HashSet = user @@ -151,14 +157,36 @@ impl State { .await?; // Fetch customisations. - let emojis = db - .fetch_emoji_by_parent_ids( - &servers - .iter() - .map(|x| x.id.to_string()) - .collect::>(), + let emojis = if fields.contains(&ReadyPayloadFields::Emoji) { + Some( + db.fetch_emoji_by_parent_ids( + &servers + .iter() + .map(|x| x.id.to_string()) + .collect::>(), + ) + .await?, ) - .await?; + } else { + None + }; + + // Fetch user settings + let user_settings = if let Some(ReadyPayloadFields::UserSettings(keys)) = fields + .iter() + .find(|e| matches!(e, ReadyPayloadFields::UserSettings(_))) + { + Some(db.fetch_user_settings(&user.id, &keys).await?) + } else { + None + }; + + // Fetch channel unreads + let channel_unreads = if fields.contains(&ReadyPayloadFields::ChannelUnreads) { + Some(db.fetch_unreads(&user.id).await?) + } else { + None + }; // Copy data into local state cache. self.cache.users = users.iter().cloned().map(|x| (x.id.clone(), x)).collect(); @@ -181,40 +209,69 @@ impl State { .collect(); // Make sure we see our own user correctly. - users.push(user.into_self().await); + users.push(user.into_self(true).await); // Set subscription state internally. - self.reset_state(); - self.insert_subscription(self.private_topic.clone()); + self.reset_state().await; + self.insert_subscription(self.private_topic.clone()).await; for user in &users { - self.insert_subscription(user.id.clone()); + self.insert_subscription(user.id.clone()).await; } for server in &servers { - self.insert_subscription(server.id.clone()); + self.insert_subscription(server.id.clone()).await; + + if self.cache.is_bot { + self.insert_subscription(format!("{}u", server.id)).await; + } } for channel in &channels { - self.insert_subscription(channel.id().to_string()); + self.insert_subscription(channel.id().to_string()).await; } - // fetch voice states for all the channels we can see - let mut voice_states = Vec::new(); + let voice_states = if fields.contains(&ReadyPayloadFields::VoiceStates) { + // fetch voice states for all the channels we can see + let mut voice_states = Vec::new(); - for channel in &channels { - if let Ok(Some(voice_state)) = self.fetch_voice_state(channel).await { - voice_states.push(voice_state) + for channel in &channels { + if let Ok(Some(voice_state)) = self.fetch_voice_state(channel).await { + voice_states.push(voice_state) + } } - } + + Some(voice_states) + } else { + None + }; Ok(EventV1::Ready { - users, - servers: servers.into_iter().map(Into::into).collect(), - channels: channels.into_iter().map(Into::into).collect(), - members: members.into_iter().map(Into::into).collect(), - emojis: emojis.into_iter().map(Into::into).collect(), + users: if fields.contains(&ReadyPayloadFields::Users) { + Some(users) + } else { + None + }, + servers: if fields.contains(&ReadyPayloadFields::Servers) { + Some(servers.into_iter().map(Into::into).collect()) + } else { + None + }, + channels: if fields.contains(&ReadyPayloadFields::Channels) { + Some(channels.into_iter().map(Into::into).collect()) + } else { + None + }, + members: if fields.contains(&ReadyPayloadFields::Members) { + Some(members.into_iter().map(Into::into).collect()) + } else { + None + }, + emojis: emojis.map(|vec| vec.into_iter().map(Into::into).collect()), voice_states, + + user_settings, + channel_unreads: channel_unreads.map(|vec| vec.into_iter().map(Into::into).collect()), }) } @@ -248,11 +305,11 @@ impl State { let mut bulk_events = vec![]; for id in added_channels { - self.insert_subscription(id); + self.insert_subscription(id).await; } for id in removed_channels { - self.remove_subscription(&id); + self.remove_subscription(&id).await; self.cache.channels.remove(&id); bulk_events.push(EventV1::ChannelDelete { id }); @@ -275,7 +332,7 @@ impl State { .channels .insert(channel.id().to_string(), channel.clone()); - self.insert_subscription(channel.id().to_string()); + self.insert_subscription(channel.id().to_string()).await; bulk_events.push(EventV1::ChannelCreate(channel.into())); } } @@ -348,7 +405,7 @@ impl State { match event { EventV1::ChannelCreate(channel) => { let id = channel.id().to_string(); - self.insert_subscription(id.clone()); + self.insert_subscription(id.clone()).await; self.cache.channels.insert(id, channel.clone().into()); } EventV1::ChannelUpdate { @@ -388,17 +445,17 @@ impl State { } } EventV1::ChannelDelete { id } => { - self.remove_subscription(id); + self.remove_subscription(id).await; self.cache.channels.remove(id); } EventV1::ChannelGroupJoin { user, .. } => { - self.insert_subscription(user.clone()); + self.insert_subscription(user.clone()).await; } EventV1::ChannelGroupLeave { id, user, .. } => { if user == &self.cache.user_id { - self.remove_subscription(id); + self.remove_subscription(id).await; } else if !self.cache.can_subscribe_to_user(user) { - self.remove_subscription(user); + self.remove_subscription(user).await; } } @@ -408,7 +465,12 @@ impl State { channels, emojis: _, } => { - self.insert_subscription(id.clone()); + self.insert_subscription(id.clone()).await; + + if self.cache.is_bot { + self.insert_subscription(format!("{}u", id)).await; + } + self.cache.servers.insert(id.clone(), server.clone().into()); let member = Member { id: MemberCompositeKey { @@ -445,13 +507,13 @@ impl State { EventV1::ServerMemberJoin { .. } => { // We will always receive ServerCreate when joining a new server. } - EventV1::ServerMemberLeave { id, user } => { + EventV1::ServerMemberLeave { id, user, .. } => { if user == &self.cache.user_id { - self.remove_subscription(id); + self.remove_subscription(id).await; if let Some(server) = self.cache.servers.remove(id) { for channel in &server.channels { - self.remove_subscription(channel); + self.remove_subscription(channel).await; self.cache.channels.remove(channel); } } @@ -459,11 +521,11 @@ impl State { } } EventV1::ServerDelete { id } => { - self.remove_subscription(id); + self.remove_subscription(id).await; if let Some(server) = self.cache.servers.remove(id) { for channel in &server.channels { - self.remove_subscription(channel); + self.remove_subscription(channel).await; self.cache.channels.remove(channel); } } @@ -536,9 +598,23 @@ impl State { self.cache.users.insert(id.clone(), user.clone().into()); if self.cache.can_subscribe_to_user(id) { - self.insert_subscription(id.clone()); + self.insert_subscription(id.clone()).await; } else { - self.remove_subscription(id); + self.remove_subscription(id).await; + } + } + + EventV1::Message(message) => { + // Since Message events are fanned out to many clients, + // we must reconstruct the relationship value at this end. + if let Some(user) = &mut message.user { + user.relationship = self + .cache + .users + .get(&self.cache.user_id) + .expect("missing self?") + .relationship_with(&message.author) + .into(); } } @@ -552,11 +628,11 @@ impl State { // Sub / unsub accordingly. if let Some(id) = queue_add { - self.insert_subscription(id); + self.insert_subscription(id).await; } if let Some(id) = queue_remove { - self.remove_subscription(&id); + self.remove_subscription(&id).await; } true @@ -582,7 +658,7 @@ impl State { } Ok(Some(v0::ChannelVoiceState { - id: channel.id(), + id: channel.id().to_string(), participants, })) } else { diff --git a/crates/bonfire/src/events/state.rs b/crates/bonfire/src/events/state.rs index ae2e8904b..eae398019 100644 --- a/crates/bonfire/src/events/state.rs +++ b/crates/bonfire/src/events/state.rs @@ -1,6 +1,12 @@ -use std::collections::{HashMap, HashSet}; +use std::{ + collections::{HashMap, HashSet}, + sync::Arc, + time::Duration, +}; +use async_std::sync::{Mutex, RwLock}; use lru::LruCache; +use lru_time_cache::{LruCache as LruTimeCache, TimedEntry}; use revolt_database::{Channel, Member, Server, User}; /// Enumeration representing some change in subscriptions @@ -30,6 +36,7 @@ pub enum SubscriptionStateChange { #[derive(Debug)] pub struct Cache { pub user_id: String, + pub is_bot: bool, pub users: HashMap, pub channels: HashMap, @@ -43,6 +50,7 @@ impl Default for Cache { fn default() -> Self { Cache { user_id: Default::default(), + is_bot: false, users: Default::default(), channels: Default::default(), @@ -58,14 +66,17 @@ impl Default for Cache { pub struct State { pub cache: Cache, + pub session_id: String, pub private_topic: String, - subscribed: HashSet, - state: SubscriptionStateChange, + pub state: SubscriptionStateChange, + + pub subscribed: Arc>>, + pub active_servers: Arc>>, } impl State { /// Create state from User - pub fn from(user: User) -> State { + pub fn from(user: User, session_id: String) -> State { let mut subscribed = HashSet::new(); let private_topic = format!("{}!", user.id); subscribed.insert(private_topic.clone()); @@ -80,22 +91,61 @@ impl State { State { cache, - subscribed, + subscribed: Arc::new(RwLock::new(subscribed)), + active_servers: Arc::new(Mutex::new(LruTimeCache::with_expiry_duration_and_capacity( + Duration::from_secs(900), + 5, + ))), + session_id, private_topic, state: SubscriptionStateChange::Reset, } } /// Apply currently queued state - pub fn apply_state(&mut self) -> SubscriptionStateChange { + pub async fn apply_state(&mut self) -> SubscriptionStateChange { + // Check if we need to change subscriptions to member event topics + if !self.cache.is_bot { + enum Server { + Subscribe(String), + Unsubscribe(String), + } + + let active_server_changes: Vec = { + let mut active_servers = self.active_servers.lock().await; + active_servers + .notify_iter() + .map(|e| match e { + TimedEntry::Valid(k, _) => Server::Subscribe(format!("{}u", k)), + TimedEntry::Expired(k, _) => Server::Unsubscribe(format!("{}u", k)), + }) + .collect() + // It is bad practice to open more than one Mutex at once and could + // lead to a deadlock, so instead we choose to collect the changes. + }; + + for entry in active_server_changes { + match entry { + Server::Subscribe(k) => { + self.insert_subscription(k).await; + } + Server::Unsubscribe(k) => { + self.remove_subscription(&k).await; + } + } + } + } + + // Flush changes to subscriptions let state = std::mem::replace(&mut self.state, SubscriptionStateChange::None); + let mut subscribed = self.subscribed.write().await; if let SubscriptionStateChange::Change { add, remove } = &state { for id in add { - self.subscribed.insert(id.clone()); + subscribed.insert(id.clone()); } for id in remove { - self.subscribed.remove(id); + subscribed.remove(id); } } @@ -107,20 +157,16 @@ impl State { self.cache.users.get(&self.cache.user_id).unwrap().clone() } - /// Iterate through all subscriptions - pub fn iter_subscriptions(&self) -> std::collections::hash_set::Iter<'_, std::string::String> { - self.subscribed.iter() - } - /// Reset the current state - pub fn reset_state(&mut self) { + pub async fn reset_state(&mut self) { self.state = SubscriptionStateChange::Reset; - self.subscribed.clear(); + self.subscribed.write().await.clear(); } /// Add a new subscription - pub fn insert_subscription(&mut self, subscription: String) { - if self.subscribed.contains(&subscription) { + pub async fn insert_subscription(&mut self, subscription: String) { + let mut subscribed = self.subscribed.write().await; + if subscribed.contains(&subscription) { return; } @@ -137,12 +183,13 @@ impl State { SubscriptionStateChange::Reset => {} } - self.subscribed.insert(subscription); + subscribed.insert(subscription); } /// Remove existing subscription - pub fn remove_subscription(&mut self, subscription: &str) { - if !self.subscribed.contains(&subscription.to_string()) { + pub async fn remove_subscription(&mut self, subscription: &str) { + let mut subscribed = self.subscribed.write().await; + if !subscribed.contains(&subscription.to_string()) { return; } @@ -159,6 +206,6 @@ impl State { SubscriptionStateChange::Reset => panic!("Should not remove during a reset!"), } - self.subscribed.remove(subscription); + subscribed.remove(subscription); } } diff --git a/crates/bonfire/src/main.rs b/crates/bonfire/src/main.rs index 54a2b56ea..2cfeac921 100644 --- a/crates/bonfire/src/main.rs +++ b/crates/bonfire/src/main.rs @@ -30,13 +30,16 @@ async fn main() { database::connect().await; // Clean up the current region information. - clear_region(None).await; + let no_clear_region = env::var("NO_CLEAR_PRESENCE").unwrap_or_else(|_| "0".into()) == "1"; + if !no_clear_region { + clear_region(None).await; + } VOICE_CLIENT.set(Arc::new(VoiceClient::from_revolt_config().await)).unwrap(); // Setup a TCP listener to accept WebSocket connections on. - // By default, we bind to port 9000 on all interfaces. - let bind = env::var("HOST").unwrap_or_else(|_| "0.0.0.0:9000".into()); + // By default, we bind to port 14703 on all interfaces. + let bind = env::var("HOST").unwrap_or_else(|_| "0.0.0.0:14703".into()); info!("Listening on host {bind}"); let try_socket = TcpListener::bind(bind).await; let listener = try_socket.expect("Failed to bind"); diff --git a/crates/bonfire/src/websocket.rs b/crates/bonfire/src/websocket.rs index e5517356d..810befbf9 100644 --- a/crates/bonfire/src/websocket.rs +++ b/crates/bonfire/src/websocket.rs @@ -1,24 +1,33 @@ -use std::net::SocketAddr; +use std::{collections::HashSet, net::SocketAddr, sync::Arc}; use async_tungstenite::WebSocketStream; +use authifier::AuthifierEvent; use fred::{ + error::{RedisError, RedisErrorKind}, interfaces::{ClientLike, EventInterface, PubsubInterface}, types::RedisConfig, }; use futures::{ channel::oneshot, - pin_mut, select, + join, pin_mut, select, stream::{SplitSink, SplitStream}, FutureExt, SinkExt, StreamExt, TryStreamExt, }; use redis_kiss::{PayloadType, REDIS_PAYLOAD_TYPE, REDIS_URI}; +use revolt_config::report_internal_error; use revolt_database::{ events::{client::EventV1, server::ClientMessage}, Database, User, UserHint, }; use revolt_presence::{create_session, delete_session}; -use async_std::{net::TcpStream, sync::Mutex}; +use async_std::{ + net::TcpStream, + sync::{Mutex, RwLock}, + task::spawn, +}; +use revolt_result::create_error; +use sentry::Level; use crate::config::{ProtocolConfiguration, WebsocketHandshakeCallback}; use crate::events::state::{State, SubscriptionStateChange}; @@ -42,10 +51,12 @@ pub async fn client(db: &'static Database, stream: TcpStream, addr: SocketAddr) else { return; }; + // Verify we've received a valid config, otherwise we should just drop the connection. let Ok(mut config) = receiver.await else { return; }; + info!( "User {addr:?} provided protocol configuration (version = {}, format = {:?})", config.get_protocol_version(), @@ -57,10 +68,8 @@ pub async fn client(db: &'static Database, stream: TcpStream, addr: SocketAddr) // If the user has not provided authentication, request information. if config.get_session_token().is_none() { - while let Ok(message) = read.try_next().await { - if let Ok(ClientMessage::Authenticate { token }) = - config.decode(message.as_ref().unwrap()) - { + while let Ok(Some(message)) = read.try_next().await { + if let Ok(ClientMessage::Authenticate { token }) = config.decode(&message) { config.set_session_token(token); break; } @@ -69,35 +78,48 @@ pub async fn client(db: &'static Database, stream: TcpStream, addr: SocketAddr) // Try to authenticate the user. let Some(token) = config.get_session_token().as_ref() else { + write + .send(config.encode(&EventV1::Error { + data: create_error!(InvalidSession), + })) + .await + .ok(); return; }; - let user = match User::from_token(db, token, UserHint::Any).await { + + let (user, session_id) = match User::from_token(db, token, UserHint::Any).await { Ok(user) => user, Err(err) => { - write.send(config.encode(&err)).await.ok(); + write + .send(config.encode(&EventV1::Error { data: err })) + .await + .ok(); return; } }; + info!("User {addr:?} authenticated as @{}", user.username); // Create local state. - let mut state = State::from(user); + let mut state = State::from(user, session_id); let user_id = state.cache.user_id.clone(); // Notify socket we have authenticated. - if write - .send(config.encode(&EventV1::Authenticated)) - .await - .is_err() - { + if report_internal_error!(write.send(config.encode(&EventV1::Authenticated)).await).is_err() { return; } // Download required data to local cache and send Ready payload. - let Ok(ready_payload) = state.generate_ready_payload(db).await else { - return; + let ready_payload = match report_internal_error!( + state + .generate_ready_payload(db, config.get_ready_payload_fields()) + .await + ) { + Ok(ready_payload) => ready_payload, + Err(_) => return, }; - if write.send(config.encode(&ready_payload)).await.is_err() { + + if report_internal_error!(write.send(config.encode(&ready_payload)).await).is_err() { return; } @@ -110,20 +132,44 @@ pub async fn client(db: &'static Database, stream: TcpStream, addr: SocketAddr) } { + // Setup channels and mutexes let write = Mutex::new(write); - // Create a PubSub connection to poll on. - let listener = listener(db, &mut state, addr, &config, &write).fuse(); - // Read from WebSocket stream. - let worker = worker(addr, user_id.clone(), &config, read, &write).fuse(); + let subscribed = state.subscribed.clone(); + let active_servers = state.active_servers.clone(); + let (topic_signal_s, topic_signal_r) = async_channel::unbounded(); - // Pin both tasks. - pin_mut!(listener, worker); + // TODO: this needs to be rewritten + // Create channels through which the tasks can signal to each other they need to clean up + let (kill_signal_1_s, kill_signal_1_r) = async_channel::bounded(1); + let (kill_signal_2_s, kill_signal_2_r) = async_channel::bounded(1); - // Wait for either disconnect or for listener to die. - select!( - () = listener => {}, - () = worker => {} + // Create a PubSub connection to poll on. + let listener = listener_with_kill_signal( + db, + &mut state, + addr, + &config, + topic_signal_r, + kill_signal_1_r, + &write, + kill_signal_2_s, ); + + // Read from WebSocket stream. + let worker = worker_with_kill_signal( + addr, + subscribed, + active_servers, + user_id.clone(), + &config, + topic_signal_s, + kill_signal_2_r, + read, + &write, + kill_signal_1_s, + ); + + join!(listener, worker); } // Clean up presence session. let last_session = delete_session(&user_id, session_id).await; @@ -134,29 +180,79 @@ pub async fn client(db: &'static Database, stream: TcpStream, addr: SocketAddr) } } +#[allow(clippy::too_many_arguments)] +async fn listener_with_kill_signal( + db: &'static Database, + state: &mut State, + addr: SocketAddr, + config: &ProtocolConfiguration, + topic_signal_r: async_channel::Receiver<()>, + kill_signal_r: async_channel::Receiver<()>, + write: &Mutex, + kill_signal_s: async_channel::Sender<()>, +) { + listener( + db, + state, + addr, + config, + topic_signal_r, + kill_signal_r, + write, + ) + .await; + kill_signal_s.send(()).await.ok(); +} + async fn listener( db: &'static Database, state: &mut State, addr: SocketAddr, config: &ProtocolConfiguration, + topic_signal_r: async_channel::Receiver<()>, + kill_signal_r: async_channel::Receiver<()>, write: &Mutex, ) { let redis_config = RedisConfig::from_url(&REDIS_URI).unwrap(); - let Ok(subscriber) = fred::types::Builder::from_config(redis_config).build_subscriber_client() - else { - return; + let subscriber = match report_internal_error!( + fred::types::Builder::from_config(redis_config).build_subscriber_client() + ) { + Ok(subscriber) => subscriber, + Err(_) => return, }; - if subscriber.init().await.is_err() { + + if report_internal_error!(subscriber.init().await).is_err() { return; - }; + } + + // Handle Redis connection dropping + let (clean_up_s, clean_up_r) = async_channel::bounded(1); + let clean_up_s = Arc::new(Mutex::new(clean_up_s)); + subscriber.on_error(move |err| { + if let RedisErrorKind::Canceled = err.kind() { + let clean_up_s = clean_up_s.clone(); + spawn(async move { + clean_up_s.lock().await.send(()).await.ok(); + }); + } + + Ok(()) + }); + let mut message_rx = subscriber.message_rx(); - loop { + 'out: loop { // Check for state changes for subscriptions. - match state.apply_state() { + match state.apply_state().await { SubscriptionStateChange::Reset => { - subscriber.unsubscribe_all().await.unwrap(); - for id in state.iter_subscriptions() { - subscriber.subscribe(id).await.unwrap(); + if report_internal_error!(subscriber.unsubscribe_all().await).is_err() { + break 'out; + } + + let subscribed = state.subscribed.read().await; + for id in subscribed.iter() { + if report_internal_error!(subscriber.subscribe(id).await).is_err() { + break 'out; + } } #[cfg(debug_assertions)] @@ -167,112 +263,242 @@ async fn listener( #[cfg(debug_assertions)] info!("{addr:?} unsubscribing from {id}"); - subscriber.unsubscribe(id).await.unwrap(); + if report_internal_error!(subscriber.unsubscribe(id).await).is_err() { + break 'out; + } } for id in add { #[cfg(debug_assertions)] info!("{addr:?} subscribing to {id}"); - subscriber.subscribe(id).await.unwrap(); + if report_internal_error!(subscriber.subscribe(id).await).is_err() { + break 'out; + } } } SubscriptionStateChange::None => {} } - // Handle incoming events. - let Ok(message) = message_rx.recv().await.map_err(|e| { - warn!("Error while consuming pub/sub messages: {e:?}"); - sentry::capture_error(&e); - }) else { - return; - }; - let event = match *REDIS_PAYLOAD_TYPE { - PayloadType::Json => message - .value - .as_str() - .and_then(|s| serde_json::from_str::(s.as_ref()).ok()), - PayloadType::Msgpack => message - .value - .as_bytes() - .and_then(|b| rmp_serde::from_slice::(b).ok()), - PayloadType::Bincode => message - .value - .as_bytes() - .and_then(|b| bincode::deserialize::(b).ok()), - }; - let Some(mut event) = event else { - warn!("Failed to deserialise an event for {}!", message.channel); - return; - }; - let should_send = state.handle_incoming_event_v1(db, &mut event).await; - if !should_send { - continue; - } + let t1 = message_rx.recv().fuse(); + let t2 = topic_signal_r.recv().fuse(); + let t3 = kill_signal_r.recv().fuse(); + let t4 = clean_up_r.recv().fuse(); + + pin_mut!(t1, t2, t3, t4); + + select! { + _ = t4 => { + break 'out; + }, + _ = t3 => { + break 'out; + }, + _ = t2 => {}, + message = t1 => { + // Handle incoming events. + let message = match report_internal_error!(message) { + Ok(message) => message, + Err(_) => break 'out + }; + + let event = match *REDIS_PAYLOAD_TYPE { + PayloadType::Json => message + .value + .as_str() + .and_then(|s| report_internal_error!(serde_json::from_str::(s.as_ref())).ok()), + PayloadType::Msgpack => message + .value + .as_bytes() + .and_then(|b| report_internal_error!(rmp_serde::from_slice::(b)).ok()), + PayloadType::Bincode => message + .value + .as_bytes() + .and_then(|b| report_internal_error!(bincode::deserialize::(b)).ok()), + }; + + let Some(mut event) = event else { + let err = format!( + "Failed to deserialise event for {}: `{:?}`", + message.channel, + message + .value + ); + + error!("{}", err); + sentry::capture_message(&err, Level::Error); + break 'out; + }; + + if let EventV1::Auth(auth) = &event { + if let AuthifierEvent::DeleteSession { session_id, .. } = auth { + if &state.session_id == session_id { + event = EventV1::Logout; + } + } else if let AuthifierEvent::DeleteAllSessions { + exclude_session_id, .. + } = auth + { + if let Some(excluded) = exclude_session_id { + if &state.session_id != excluded { + event = EventV1::Logout; + } + } else { + event = EventV1::Logout; + } + } + } else { + let should_send = state.handle_incoming_event_v1(db, &mut event).await; + if !should_send { + continue; + } + } + + let result = write.lock().await.send(config.encode(&event)).await; + if let Err(e) = result { + use async_tungstenite::tungstenite::Error; + if !matches!(e, Error::AlreadyClosed | Error::ConnectionClosed) { + let err = format!("Error while sending an event to {addr:?}: {e:?}"); + warn!("{}", err); + sentry::capture_message(&err, Level::Warning); + } - let result = write.lock().await.send(config.encode(&event)).await; - if let Err(e) = result { - use async_tungstenite::tungstenite::Error; - if !matches!(e, Error::AlreadyClosed | Error::ConnectionClosed) { - warn!("Error while sending an event to {addr:?}: {e:?}"); + break 'out; + } + + if let EventV1::Logout = event { + info!("User {addr:?} received log out event!"); + break 'out; + } } - return; } } + + report_internal_error!(subscriber.quit().await).ok(); } +#[allow(clippy::too_many_arguments)] +async fn worker_with_kill_signal( + addr: SocketAddr, + subscribed: Arc>>, + active_servers: Arc>>, + user_id: String, + config: &ProtocolConfiguration, + topic_signal_s: async_channel::Sender<()>, + kill_signal_r: async_channel::Receiver<()>, + read: WsReader, + write: &Mutex, + kill_signal_s: async_channel::Sender<()>, +) { + worker( + addr, + subscribed, + active_servers, + user_id, + config, + topic_signal_s, + kill_signal_r, + read, + write, + ) + .await; + kill_signal_s.send(()).await.ok(); +} + +#[allow(clippy::too_many_arguments)] async fn worker( addr: SocketAddr, + subscribed: Arc>>, + active_servers: Arc>>, user_id: String, config: &ProtocolConfiguration, + topic_signal_s: async_channel::Sender<()>, + kill_signal_r: async_channel::Receiver<()>, mut read: WsReader, write: &Mutex, ) { loop { - let result = read.try_next().await; - let msg = match result { - Ok(Some(msg)) => msg, - Ok(None) => return, - Err(e) => { - use async_tungstenite::tungstenite::Error; - if !matches!(e, Error::AlreadyClosed | Error::ConnectionClosed) { - warn!("Error while reading an event from {addr:?}: {e:?}"); - } + let t1 = read.try_next().fuse(); + let t2 = kill_signal_r.recv().fuse(); + + pin_mut!(t1, t2); + + select! { + _ = t2 => { return; - } - }; - - let Ok(payload) = config.decode(&msg) else { - continue; - }; - match payload { - ClientMessage::BeginTyping { channel } => { - EventV1::ChannelStartTyping { - id: channel.clone(), - user: user_id.clone(), - } - .p(channel.clone()) - .await; - } - ClientMessage::EndTyping { channel } => { - EventV1::ChannelStopTyping { - id: channel.clone(), - user: user_id.clone(), - } - .p(channel.clone()) - .await; - } - ClientMessage::Ping { data, responded } => { - if responded.is_none() { - write - .lock() - .await - .send(config.encode(&EventV1::Pong { data })) - .await - .ok(); + }, + result = t1 => { + let msg = match result { + Ok(Some(msg)) => msg, + Ok(None) => { + warn!("Received a None message!"); + sentry::capture_message("Received a None message!", Level::Warning); + return; + } + Err(e) => { + use async_tungstenite::tungstenite::Error; + if !matches!(e, Error::AlreadyClosed | Error::ConnectionClosed) { + let err = format!("Error while reading an event from {addr:?}: {e:?}"); + warn!("{}", err); + sentry::capture_message(&err, Level::Warning); + } + + return; + } + }; + + let Ok(payload) = config.decode(&msg) else { + continue; + }; + + match payload { + ClientMessage::BeginTyping { channel } => { + if !subscribed.read().await.contains(&channel) { + continue; + } + + EventV1::ChannelStartTyping { + id: channel.clone(), + user: user_id.clone(), + } + .p(channel.clone()) + .await; + } + ClientMessage::EndTyping { channel } => { + if !subscribed.read().await.contains(&channel) { + continue; + } + + EventV1::ChannelStopTyping { + id: channel.clone(), + user: user_id.clone(), + } + .p(channel.clone()) + .await; + } + ClientMessage::Subscribe { server_id } => { + let mut servers = active_servers.lock().await; + let has_item = servers.contains_key(&server_id); + servers.insert(server_id, ()); + + if !has_item { + // Poke the listener to adjust subscriptions + topic_signal_s.send(()).await.ok(); + } + } + ClientMessage::Ping { data, responded } => { + if responded.is_none() { + write + .lock() + .await + .send(config.encode(&EventV1::Pong { data })) + .await + .ok(); + } + } + _ => {} } } - _ => {} } } } diff --git a/crates/core/config/Cargo.toml b/crates/core/config/Cargo.toml index 69986f481..1e9c96d6e 100644 --- a/crates/core/config/Cargo.toml +++ b/crates/core/config/Cargo.toml @@ -1,20 +1,20 @@ [package] name = "revolt-config" -version = "0.7.1" +version = "0.8.1" edition = "2021" -license = "AGPL-3.0-or-later" +license = "MIT" authors = ["Paul Makles "] description = "Revolt Backend: Configuration" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] +report-macros = ["revolt-result"] test = ["async-std"] default = ["test"] [dependencies] # Utility -dotenv = "0.15.0" config = "0.13.3" cached = "0.44.0" once_cell = "1.18.0" @@ -32,3 +32,6 @@ pretty_env_logger = "0.4.0" # Sentry sentry = "0.31.5" + +# Core +revolt-result = { version = "0.8.1", path = "../result", optional = true } diff --git a/crates/core/config/LICENSE b/crates/core/config/LICENSE new file mode 100644 index 000000000..7c2815b9f --- /dev/null +++ b/crates/core/config/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2024 Pawel Makles + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/crates/core/config/Revolt.test.toml b/crates/core/config/Revolt.test.toml index 084a46ee0..7ac98ee26 100644 --- a/crates/core/config/Revolt.test.toml +++ b/crates/core/config/Revolt.test.toml @@ -1,3 +1,9 @@ [database] mongodb = "mongodb://localhost" redis = "redis://localhost/" + +[rabbit] +host = "127.0.0.1" +port = 5672 +username = "rabbituser" +password = "rabbitpass" diff --git a/crates/core/config/Revolt.toml b/crates/core/config/Revolt.toml index c6c44f7c1..6b9a4919c 100644 --- a/crates/core/config/Revolt.toml +++ b/crates/core/config/Revolt.toml @@ -1,72 +1,271 @@ [database] +# MongoDB connection URL +# Defaults to the container name specified in self-hosted mongodb = "mongodb://database" +# Redis connection URL +# Defaults to the container name specified in self-hosted redis = "redis://redis/" [hosts] +# Web locations of various services +# Defaults assume all services are reverse-proxied +# See https://github.com/revoltchat/self-hosted/blob/master/Caddyfile +# +# Remember to change these to https/wss where appropriate in production! app = "http://local.revolt.chat" api = "http://local.revolt.chat/api" events = "ws://local.revolt.chat/ws" autumn = "http://local.revolt.chat/autumn" january = "http://local.revolt.chat/january" +livekit = "ws://local.revolt.chat/livekit" voso_legacy = "" voso_legacy_ws = "" +[rabbit] +host = "rabbit" +port = 5672 +username = "rabbituser" +password = "rabbitpass" + [api] [api.registration] +# Whether an invite should be required for registration +# See https://github.com/revoltchat/self-hosted#making-your-instance-invite-only invite_only = false [api.smtp] +# Email server configuration for verification +# Defaults to no email verification (host field is empty) host = "" username = "" password = "" -from_address = "" - -[api.vapid] -private_key = "LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUJSUWpyTWxLRnBiVWhsUHpUbERvcEliYk1yeVNrNXpKYzVYVzIxSjJDS3hvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFWnkrQkg2TGJQZ2hEa3pEempXOG0rUXVPM3pCajRXT1phdkR6ZU00c0pqbmFwd1psTFE0WAp1ZDh2TzVodU94QWhMQlU3WWRldVovWHlBdFpWZmNyQi9BPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo" -public_key = "BGcvgR-i2z4IQ5Mw841vJvkLjt8wY-FjmWrw83jOLCY52qcGZS0OF7nfLzuYbjsQISwVO2HXrmf18gLWVX3Kwfw=" +from_address = "noreply@example.com" +# reply_to = "noreply@example.com" +# port = 587 +# use_tls = true -[api.fcm] -api_key = "" [api.security] +# Authifier Shield API key authifier_shield_key = "" +# Legacy voice server management token voso_legacy_token = "" +# Whether services are behind the Cloudflare network trust_cloudflare = false [api.security.captcha] +# hCaptcha configuration hcaptcha_key = "" hcaptcha_sitekey = "" [api.workers] +# Maximum concurrent connections (to proxy server) max_concurrent_connections = 50 +[api.livekit] +# Livekit server url +url = "ws://livekit:7880" +# Livekit security key name +key = "dev" +# Livekit security secret value +secret = "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxc" + + +[pushd] +# this changes the names of the queues to not overlap +# prod/beta if they happen to be on the same exchange/instance. +# Usually they have to be, so that messages sent from one or the other get sent to everyone +production = true + +# none of these should need changing +exchange = "revolt.notifications" +message_queue = "notifications.origin.message" +fr_accepted_queue = "notifications.ingest.fr_accepted" # friend request accepted +fr_received_queue = "notifications.ingest.fr_received" # friend request received +generic_queue = "notifications.ingest.generic" # generic messages (title + body) +ack_queue = "notifications.process.ack" # updates badges for apple devices + +[pushd.vapid] +queue = "notifications.outbound.vapid" +private_key = "LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUJSUWpyTWxLRnBiVWhsUHpUbERvcEliYk1yeVNrNXpKYzVYVzIxSjJDS3hvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFWnkrQkg2TGJQZ2hEa3pEempXOG0rUXVPM3pCajRXT1phdkR6ZU00c0pqbmFwd1psTFE0WAp1ZDh2TzVodU94QWhMQlU3WWRldVovWHlBdFpWZmNyQi9BPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo" +public_key = "BGcvgR-i2z4IQ5Mw841vJvkLjt8wY-FjmWrw83jOLCY52qcGZS0OF7nfLzuYbjsQISwVO2HXrmf18gLWVX3Kwfw=" + +[pushd.fcm] +queue = "notifications.outbound.fcm" +key_type = "" +project_id = "" +private_key_id = "" +private_key = "" +client_email = "" +client_id = "" +auth_uri = "" +token_uri = "" +auth_provider_x509_cert_url = "" +client_x509_cert_url = "" + +[pushd.apn] +sandbox = false +queue = "notifications.outbound.apn" +pkcs8 = "" +key_id = "" +team_id = "" + + +[files] +# Encryption key for stored files +# Generate your own key using `openssl rand -base64 32` +encryption_key = "qcuMA+ssxhMyKaNAKBGFfryfFtUH8NDlamQyDwGW6fU=" +# Quality used for lossy WebP previews (set to 100 for lossless) +webp_quality = 80.0 +# Mime types that cannot be uploaded or served +# +# Example for Windows executables and Android installation files: +# ["application/vnd.microsoft.portable-executable", "application/vnd.android.package-archive"] +blocked_mime_types = [] +# ClamAV service +# hostname:port +clamd_host = "" +# Mime types that should be virus scanned +# +# Leave empty to scan all file types +scan_mime_types = [ + "application/vnd.microsoft.portable-executable", + "application/vnd.android.package-archive", + "application/zip", +] + +[files.limit] +# Minimum file size (in bytes) +min_file_size = 1 +# Minimum image resolution +min_resolution = [1, 1] +# Maximum MP of images +max_mega_pixels = 40 +# Maximum pixel side of an image +max_pixel_side = 10_000 + +[files.preview] +# Maximum image resolution +attachments = [1280, 1280] +avatars = [128, 128] +backgrounds = [1280, 720] +icons = [128, 128] +banners = [480, 480] +emojis = [128, 128] + +[files.s3] +# Configuration for S3 +# Defaults included for MinIO + self-hosted setup +# +# Backblaze B2: +# - endpoint is listed on the "Buckets" page +# - path_style_buckets is set to true +# - region is `eu-central-003` string from endpoint URL +# - access_key_id is keyID generated on the "Application Keys" page +# - secret_access_key is token generated on the "Application Keys" page +# - default_bucket matches the name of the bucket you've created + +# S3 protocol endpoint +endpoint = "http://minio:9000" +# Whether to use path-style buckets +# Generally true, except for MinIO +path_style_buckets = false +# S3 region name +region = "minio" +# S3 protocol key ID +access_key_id = "minioautumn" +# S3 protocol access key +secret_access_key = "minioautumn" +default_bucket = "revolt-uploads" + + [features] +# Bucket to upload to by default +# Feature gate options webhooks_enabled = false [features.limits] -[features.limits.default] +[features.limits.global] group_size = 100 -bots = 5 -message_length = 2000 message_embeds = 5 message_replies = 5 -message_attachments = 5 message_reactions = 20 -servers = 100 server_emoji = 100 server_roles = 200 server_channels = 200 -attachment_size = 20000000 -avatar_size = 4000000 -background_size = 6000000 -icon_size = 2500000 -banner_size = 6000000 -emoji_size = 500000 +# How many hours since creation a user is considered new +new_user_hours = 72 + +# Maximum permissible body size in bytes for uploads +# (should be greater than any one file upload limit) +body_limit_size = 20_000_000 + +[features.limits.new_user] +# Limits imposed on new users + +# Number of outgoing friend requests permitted at any time +outgoing_friend_requests = 5 + +# Maximum number of owned bots +bots = 2 + +# Message content length +message_length = 2000 + +# Number of attachments that can be included +message_attachments = 5 + +# Maximum number of servers the user can create/join +servers = 50 + +[features.limits.new_user.file_upload_size_limit] +# Maximum file size limits (in bytes) +attachments = 20_000_000 +avatars = 4_000_000 +backgrounds = 6_000_000 +icons = 2_500_000 +banners = 6_000_000 +emojis = 500_000 + +[features.limits.default] +# Limits imposed on users by default + +# Number of outgoing friend requests permitted at any time +outgoing_friend_requests = 10 + +# Maximum number of owned bots +bots = 5 + +# Message content length +message_length = 2000 + +# Number of attachments that can be included +message_attachments = 5 + +# Maximum number of servers the user can create/join +servers = 100 + +[features.limits.default.file_upload_size_limit] +# Maximum file size limits (in bytes) +attachments = 20_000_000 +avatars = 4_000_000 +backgrounds = 6_000_000 +icons = 2_500_000 +banners = 6_000_000 +emojis = 500_000 + +[features.advanced] +# The max amount of messages the rabbitmq provider/db mention adder job will delay for before forcing handling of a channel. +# default: 5 +process_message_delay_limit = 5 [sentry] +# Configuration for Sentry error reporting api = "" events = "" -voice_ingress = "" \ No newline at end of file +voice_ingress = "" +files = "" +proxy = "" diff --git a/crates/core/config/src/lib.rs b/crates/core/config/src/lib.rs index df8c2c884..168788dcc 100644 --- a/crates/core/config/src/lib.rs +++ b/crates/core/config/src/lib.rs @@ -6,9 +6,60 @@ use futures_locks::RwLock; use once_cell::sync::Lazy; use serde::Deserialize; -#[cfg(not(debug_assertions))] -use std::env; +pub use sentry::{capture_error, capture_message, Level}; +#[cfg(feature = "report-macros")] +#[macro_export] +macro_rules! report_error { + ( $expr: expr, $error: ident $( $tt:tt )? ) => { + $expr + .inspect_err(|err| { + $crate::capture_message( + &format!("{err:?} ({}:{}:{})", file!(), line!(), column!()), + $crate::Level::Error, + ); + }) + .map_err(|_| ::revolt_result::create_error!($error)) + }; +} + +#[cfg(feature = "report-macros")] +#[macro_export] +macro_rules! capture_internal_error { + ( $expr: expr ) => { + $crate::capture_message( + &format!("{:?} ({}:{}:{})", $expr, file!(), line!(), column!()), + $crate::Level::Error, + ); + }; +} + +#[cfg(feature = "report-macros")] +#[macro_export] +macro_rules! report_internal_error { + ( $expr: expr ) => { + $expr + .inspect_err(|err| { + $crate::capture_message( + &format!("{err:?} ({}:{}:{})", file!(), line!(), column!()), + $crate::Level::Error, + ); + }) + .map_err(|_| ::revolt_result::create_error!(InternalError)) + }; +} + +/// Paths to search for configuration +static CONFIG_SEARCH_PATHS: [&str; 3] = [ + // current working directory + "Revolt.toml", + // current working directory - overrides file + "Revolt.overrides.toml", + // root directory, for Docker containers + "/Revolt.toml", +]; + +/// Configuration builder static CONFIG_BUILDER: Lazy> = Lazy::new(|| { RwLock::new({ let mut builder = Config::builder().add_source(File::from_str( @@ -21,22 +72,32 @@ static CONFIG_BUILDER: Lazy> = Lazy::new(|| { include_str!("../Revolt.test.toml"), FileFormat::Toml, )); - } else if std::path::Path::new("Revolt.toml").exists() { - builder = builder.add_source(File::new("Revolt.toml", FileFormat::Toml)); + } + + for path in CONFIG_SEARCH_PATHS { + if std::path::Path::new(path).exists() { + builder = builder.add_source(File::new(path, FileFormat::Toml)); + } } builder.build().unwrap() }) }); -// https://gifbox.me/view/gT5mqxYKCZv-twilight-meow - #[derive(Deserialize, Debug, Clone)] pub struct Database { pub mongodb: String, pub redis: String, } +#[derive(Deserialize, Debug, Clone)] +pub struct Rabbit { + pub host: String, + pub port: u16, + pub username: String, + pub password: String, +} + #[derive(Deserialize, Debug, Clone)] pub struct Hosts { pub app: String, @@ -64,14 +125,34 @@ pub struct ApiSmtp { } #[derive(Deserialize, Debug, Clone)] -pub struct ApiVapid { +pub struct PushVapid { + pub queue: String, pub private_key: String, pub public_key: String, } #[derive(Deserialize, Debug, Clone)] -pub struct ApiFcm { - pub api_key: String, +pub struct PushFcm { + pub queue: String, + pub key_type: String, + pub project_id: String, + pub private_key_id: String, + pub private_key: String, + pub client_email: String, + pub client_id: String, + pub auth_uri: String, + pub token_uri: String, + pub auth_provider_x509_cert_url: String, + pub client_x509_cert_url: String, +} + +#[derive(Deserialize, Debug, Clone)] +pub struct PushApn { + pub queue: String, + pub sandbox: bool, + pub pkcs8: String, + pub key_id: String, + pub team_id: String, } #[derive(Deserialize, Debug, Clone)] @@ -104,61 +185,163 @@ pub struct ApiLiveKit { pub struct Api { pub registration: ApiRegistration, pub smtp: ApiSmtp, - pub vapid: ApiVapid, - pub fcm: ApiFcm, pub security: ApiSecurity, pub workers: ApiWorkers, pub livekit: ApiLiveKit, } #[derive(Deserialize, Debug, Clone)] -pub struct FeaturesLimits { +pub struct Pushd { + pub production: bool, + pub exchange: String, + pub message_queue: String, + pub fr_accepted_queue: String, + pub fr_received_queue: String, + pub generic_queue: String, + pub ack_queue: String, + + pub vapid: PushVapid, + pub fcm: PushFcm, + pub apn: PushApn, +} + +impl Pushd { + fn get_routing_key(&self, key: String) -> String { + match self.production { + true => key + "-prd", + false => key + "-tst", + } + } + + pub fn get_ack_routing_key(&self) -> String { + self.get_routing_key(self.ack_queue.clone()) + } + + pub fn get_message_routing_key(&self) -> String { + self.get_routing_key(self.message_queue.clone()) + } + + pub fn get_fr_accepted_routing_key(&self) -> String { + self.get_routing_key(self.fr_accepted_queue.clone()) + } + + pub fn get_fr_received_routing_key(&self) -> String { + self.get_routing_key(self.fr_received_queue.clone()) + } + + pub fn get_generic_routing_key(&self) -> String { + self.get_routing_key(self.generic_queue.clone()) + } +} + +#[derive(Deserialize, Debug, Clone)] +pub struct FilesLimit { + pub min_file_size: usize, + pub min_resolution: [usize; 2], + pub max_mega_pixels: usize, + pub max_pixel_side: usize, +} + +#[derive(Deserialize, Debug, Clone)] +pub struct FilesS3 { + pub endpoint: String, + pub path_style_buckets: bool, + pub region: String, + pub access_key_id: String, + pub secret_access_key: String, + pub default_bucket: String, +} + +#[derive(Deserialize, Debug, Clone)] +pub struct Files { + pub encryption_key: String, + pub webp_quality: f32, + pub blocked_mime_types: Vec, + pub clamd_host: String, + pub scan_mime_types: Vec, + + pub limit: FilesLimit, + pub preview: HashMap, + pub s3: FilesS3, +} + +#[derive(Deserialize, Debug, Clone)] +pub struct GlobalLimits { pub group_size: usize, - pub bots: usize, - pub message_length: usize, - pub message_replies: usize, - pub message_attachments: usize, pub message_embeds: usize, + pub message_replies: usize, pub message_reactions: usize, - pub servers: usize, pub server_emoji: usize, pub server_roles: usize, pub server_channels: usize, - pub attachment_size: usize, - pub avatar_size: usize, - pub background_size: usize, - pub icon_size: usize, - pub banner_size: usize, - pub emoji_size: usize, + pub new_user_hours: usize, + + pub body_limit_size: usize, +} + +#[derive(Deserialize, Debug, Clone)] +pub struct FeaturesLimits { + pub outgoing_friend_requests: usize, + + pub bots: usize, + pub message_length: usize, + pub message_attachments: usize, + pub servers: usize, + + pub file_upload_size_limit: HashMap, } #[derive(Deserialize, Debug, Clone)] pub struct FeaturesLimitsCollection { + pub global: GlobalLimits, + + pub new_user: FeaturesLimits, pub default: FeaturesLimits, #[serde(flatten)] pub roles: HashMap, } +#[derive(Deserialize, Debug, Clone)] +pub struct FeaturesAdvanced { + #[serde(default)] + pub process_message_delay_limit: u16, +} + +impl Default for FeaturesAdvanced { + fn default() -> Self { + Self { + process_message_delay_limit: 5, + } + } +} + #[derive(Deserialize, Debug, Clone)] pub struct Features { pub limits: FeaturesLimitsCollection, pub webhooks_enabled: bool, + #[serde(default)] + pub advanced: FeaturesAdvanced, } #[derive(Deserialize, Debug, Clone)] pub struct Sentry { pub api: String, pub events: String, - pub voice_ingress: String + pub voice_ingress: String, + pub files: String, + pub proxy: String, } #[derive(Deserialize, Debug, Clone)] pub struct Settings { pub database: Database, + pub rabbit: Rabbit, pub hosts: Hosts, pub api: Api, + pub pushd: Pushd, + pub files: Files, pub features: Features, pub sentry: Sentry, } @@ -166,22 +349,10 @@ pub struct Settings { impl Settings { pub fn preflight_checks(&self) { if self.api.smtp.host.is_empty() { - #[cfg(not(debug_assertions))] - if !env::var("REVOLT_UNSAFE_NO_EMAIL").map_or(false, |v| v == *"1") { - panic!("Running in production without email is not recommended, set REVOLT_UNSAFE_NO_EMAIL=1 to override."); - } - - #[cfg(debug_assertions)] log::warn!("No SMTP settings specified! Remember to configure email."); } if self.api.security.captcha.hcaptcha_key.is_empty() { - #[cfg(not(debug_assertions))] - if !env::var("REVOLT_UNSAFE_NO_CAPTCHA").map_or(false, |v| v == *"1") { - panic!("Running in production without CAPTCHA is not recommended, set REVOLT_UNSAFE_NO_CAPTCHA=1 to override."); - } - - #[cfg(debug_assertions)] log::warn!("No Captcha key specified! Remember to add hCaptcha key."); } } @@ -205,8 +376,6 @@ pub async fn config() -> Settings { /// Configure logging and common Rust variables pub async fn setup_logging(release: &'static str, dsn: String) -> Option { - dotenv::dotenv().ok(); - if std::env::var("RUST_LOG").is_err() { std::env::set_var("RUST_LOG", "info"); } @@ -215,6 +384,12 @@ pub async fn setup_logging(release: &'static str, dsn: String) -> Option"] @@ -16,6 +16,7 @@ mongodb = ["dep:mongodb", "bson"] tasks = ["isahc", "linkify", "url-escape"] async-std-runtime = ["async-std"] rocket-impl = ["rocket", "schemars", "revolt_okapi", "revolt_rocket_okapi"] +axum-impl = ["axum"] redis-is-patched = ["revolt-presence/redis-is-patched"] # Default Features @@ -23,13 +24,15 @@ default = ["mongodb", "async-std-runtime", "tasks"] [dependencies] # Core -revolt-config = { version = "0.7.1", path = "../config" } -revolt-result = { version = "0.7.1", path = "../result" } -revolt-models = { version = "0.7.1", path = "../models", features = [ +revolt-config = { version = "0.8.1", path = "../config", features = [ + "report-macros", +] } +revolt-result = { version = "0.8.1", path = "../result" } +revolt-models = { version = "0.8.1", path = "../models", features = [ "validator", ] } -revolt-presence = { version = "0.7.1", path = "../presence" } -revolt-permissions = { version = "0.7.1", path = "../permissions", features = [ +revolt-presence = { version = "0.8.1", path = "../presence" } +revolt-permissions = { version = "0.8.1", path = "../permissions", features = [ "serde", "bson", ] } @@ -76,17 +79,24 @@ async-recursion = "1.0.4" # Async async-std = { version = "1.8.0", features = ["attributes"], optional = true } +# Axum Impl +axum = { version = "0.7.5", optional = true } + # Rocket Impl schemars = { version = "0.8.8", optional = true } -rocket = { version = "0.5.0-rc.2", default-features = false, features = [ +rocket = { version = "0.5.1", default-features = false, features = [ "json", ], optional = true } revolt_okapi = { version = "0.9.1", optional = true } -revolt_rocket_okapi = { version = "0.9.1", optional = true } +revolt_rocket_okapi = { version = "0.10.0", optional = true } # Notifications -fcm = "0.9.2" +fcm_v1 = "0.3.0" web-push = "0.10.0" +revolt_a2 = { version = "0.10", default-features = false, features = ["ring"] } # Authifier -authifier = { version = "1.0.8" } +authifier = { version = "1.0.9", features = ["rocket_impl"] } + +# RabbitMQ +amqprs = { version = "1.7.0" } diff --git a/crates/core/database/src/amqp/amqp.rs b/crates/core/database/src/amqp/amqp.rs new file mode 100644 index 000000000..39b4820eb --- /dev/null +++ b/crates/core/database/src/amqp/amqp.rs @@ -0,0 +1,211 @@ +use std::collections::HashSet; + +use crate::events::rabbit::*; +use crate::User; +use amqprs::channel::BasicPublishArguments; +use amqprs::{channel::Channel, connection::Connection, error::Error as AMQPError}; +use amqprs::{BasicProperties, FieldTable}; +use revolt_models::v0::PushNotification; +use revolt_presence::filter_online; + +use serde_json::to_string; + +#[derive(Clone)] +pub struct AMQP { + #[allow(unused)] + connection: Connection, + channel: Channel, +} + +impl AMQP { + pub fn new(connection: Connection, channel: Channel) -> AMQP { + AMQP { + connection, + channel, + } + } + + pub async fn friend_request_accepted( + &self, + accepted_request_user: &User, + sent_request_user: &User, + ) -> Result<(), AMQPError> { + let config = revolt_config::config().await; + let payload = FRAcceptedPayload { + accepted_user: accepted_request_user.to_owned(), + user: sent_request_user.id.clone(), + }; + let payload = to_string(&payload).unwrap(); + + debug!( + "Sending friend request accept payload on channel {}: {}", + config.pushd.get_fr_accepted_routing_key(), + payload + ); + self.channel + .basic_publish( + BasicProperties::default() + .with_content_type("application/json") + .with_persistence(true) + .finish(), + payload.into(), + BasicPublishArguments::new( + &config.pushd.exchange, + &config.pushd.get_fr_accepted_routing_key(), + ), + ) + .await + } + + pub async fn friend_request_received( + &self, + received_request_user: &User, + sent_request_user: &User, + ) -> Result<(), AMQPError> { + let config = revolt_config::config().await; + let payload = FRReceivedPayload { + from_user: sent_request_user.to_owned(), + user: received_request_user.id.clone(), + }; + let payload = to_string(&payload).unwrap(); + + debug!( + "Sending friend request received payload on channel {}: {}", + config.pushd.get_fr_received_routing_key(), + payload + ); + + self.channel + .basic_publish( + BasicProperties::default() + .with_content_type("application/json") + .with_persistence(true) + .finish(), + payload.into(), + BasicPublishArguments::new( + &config.pushd.exchange, + &config.pushd.get_fr_received_routing_key(), + ), + ) + .await + } + + pub async fn generic_message( + &self, + user: &User, + title: String, + body: String, + icon: Option, + ) -> Result<(), AMQPError> { + let config = revolt_config::config().await; + let payload = GenericPayload { + title, + body, + icon, + user: user.to_owned(), + }; + let payload = to_string(&payload).unwrap(); + + debug!( + "Sending generic payload on channel {}: {}", + config.pushd.get_generic_routing_key(), + payload + ); + + self.channel + .basic_publish( + BasicProperties::default() + .with_content_type("application/json") + .with_persistence(true) + .finish(), + payload.into(), + BasicPublishArguments::new( + &config.pushd.exchange, + &config.pushd.get_generic_routing_key(), + ), + ) + .await + } + + pub async fn message_sent( + &self, + recipients: Vec, + payload: PushNotification, + ) -> Result<(), AMQPError> { + if recipients.is_empty() { + return Ok(()); + } + + let config = revolt_config::config().await; + + let online_ids = filter_online(&recipients).await; + let recipients = (&recipients.into_iter().collect::>() - &online_ids) + .into_iter() + .collect::>(); + + let payload = MessageSentPayload { + notification: payload, + users: recipients, + }; + let payload = to_string(&payload).unwrap(); + + debug!( + "Sending message payload on channel {}: {}", + config.pushd.get_message_routing_key(), + payload + ); + + self.channel + .basic_publish( + BasicProperties::default() + .with_content_type("application/json") + .with_persistence(true) + .finish(), + payload.into(), + BasicPublishArguments::new( + &config.pushd.exchange, + &config.pushd.get_message_routing_key(), + ), + ) + .await + } + + pub async fn ack_message( + &self, + user_id: String, + channel_id: String, + message_id: String, + ) -> Result<(), AMQPError> { + let config = revolt_config::config().await; + + let payload = AckPayload { + user_id: user_id.clone(), + channel_id: channel_id.clone(), + message_id, + }; + let payload = to_string(&payload).unwrap(); + + info!( + "Sending ack payload on channel {}: {}", + config.pushd.ack_queue, payload + ); + + let mut headers = FieldTable::new(); + headers.insert( + "x-deduplication-header".try_into().unwrap(), + format!("{}-{}", &user_id, &channel_id).into(), + ); + + self.channel + .basic_publish( + BasicProperties::default() + .with_content_type("application/json") + .with_persistence(true) + //.with_headers(headers) + .finish(), + payload.into(), + BasicPublishArguments::new(&config.pushd.exchange, &config.pushd.ack_queue), + ) + .await + } +} diff --git a/crates/core/database/src/amqp/mod.rs b/crates/core/database/src/amqp/mod.rs new file mode 100644 index 000000000..9ed09d345 --- /dev/null +++ b/crates/core/database/src/amqp/mod.rs @@ -0,0 +1,2 @@ +#[allow(clippy::module_inception)] +pub mod amqp; diff --git a/crates/core/database/src/drivers/mod.rs b/crates/core/database/src/drivers/mod.rs index e209cd48c..605a56126 100644 --- a/crates/core/database/src/drivers/mod.rs +++ b/crates/core/database/src/drivers/mod.rs @@ -1,6 +1,14 @@ mod mongodb; mod reference; +use authifier::config::Captcha; +use authifier::config::EmailVerificationConfig; +use authifier::config::ResolveIp; +use authifier::config::SMTPSettings; +use authifier::config::Shield; +use authifier::config::Template; +use authifier::config::Templates; +use authifier::Authifier; use rand::Rng; use revolt_config::config; @@ -87,3 +95,87 @@ impl DatabaseInfo { }) } } + +impl Database { + /// Create an Authifier reference + pub async fn to_authifier(self) -> Authifier { + let config = config().await; + + let mut auth_config = authifier::Config { + email_verification: if !config.api.smtp.host.is_empty() { + EmailVerificationConfig::Enabled { + smtp: SMTPSettings { + from: config.api.smtp.from_address, + host: config.api.smtp.host, + username: config.api.smtp.username, + password: config.api.smtp.password, + reply_to: Some( + config + .api + .smtp + .reply_to + .unwrap_or("support@revolt.chat".into()), + ), + port: config.api.smtp.port, + use_tls: config.api.smtp.use_tls, + }, + expiry: Default::default(), + templates: Templates { + verify: Template { + title: "Verify your Revolt account.".into(), + text: include_str!("../../templates/verify.txt").into(), + url: format!("{}/login/verify/", config.hosts.app), + html: Some(include_str!("../../templates/verify.html").into()), + }, + reset: Template { + title: "Reset your Revolt password.".into(), + text: include_str!("../../templates/reset.txt").into(), + url: format!("{}/login/reset/", config.hosts.app), + html: Some(include_str!("../../templates/reset.html").into()), + }, + deletion: Template { + title: "Confirm account deletion.".into(), + text: include_str!("../../templates/deletion.txt").into(), + url: format!("{}/delete/", config.hosts.app), + html: Some(include_str!("../../templates/deletion.html").into()), + }, + welcome: None, + }, + } + } else { + EmailVerificationConfig::Disabled + }, + ..Default::default() + }; + + auth_config.invite_only = config.api.registration.invite_only; + + if !config.api.security.captcha.hcaptcha_key.is_empty() { + auth_config.captcha = Captcha::HCaptcha { + secret: config.api.security.captcha.hcaptcha_key, + }; + } + + if !config.api.security.authifier_shield_key.is_empty() { + auth_config.shield = Shield::Enabled { + api_key: config.api.security.authifier_shield_key, + strict: false, + }; + } + + if config.api.security.trust_cloudflare { + auth_config.resolve_ip = ResolveIp::Cloudflare; + } + + Authifier { + database: match self { + Database::Reference(_) => Default::default(), + Database::MongoDb(MongoDb(client, _)) => authifier::Database::MongoDb( + authifier::database::MongoDb(client.database("revolt")), + ), + }, + config: auth_config, + event_channel: Some(crate::tasks::authifier_relay::sender()), + } + } +} diff --git a/crates/core/database/src/drivers/reference.rs b/crates/core/database/src/drivers/reference.rs index 657aef0aa..6d2436663 100644 --- a/crates/core/database/src/drivers/reference.rs +++ b/crates/core/database/src/drivers/reference.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, sync::Arc}; use futures::lock::Mutex; use crate::{ - Bot, Channel, ChannelCompositeKey, ChannelUnread, Emoji, File, Invite, Member, + Bot, Channel, ChannelCompositeKey, ChannelUnread, Emoji, File, FileHash, Invite, Member, MemberCompositeKey, Message, RatelimitEvent, Report, Server, ServerBan, Snapshot, User, UserSettings, Webhook, }; @@ -18,6 +18,7 @@ database_derived!( pub channel_unreads: Arc>>, pub channel_webhooks: Arc>>, pub emojis: Arc>>, + pub file_hashes: Arc>>, pub files: Arc>>, pub messages: Arc>>, pub ratelimit_events: Arc>>, diff --git a/crates/core/database/src/events/client.rs b/crates/core/database/src/events/client.rs index f9222bfe0..9262d9996 100644 --- a/crates/core/database/src/events/client.rs +++ b/crates/core/database/src/events/client.rs @@ -1,25 +1,13 @@ use authifier::AuthifierEvent; +use revolt_result::Error; use serde::{Deserialize, Serialize}; use revolt_models::v0::{ - AppendMessage, Channel, ChannelVoiceState, Emoji, FieldsChannel, FieldsMember, FieldsRole, FieldsServer, FieldsUser, FieldsWebhook, Member, MemberCompositeKey, Message, PartialChannel, PartialMember, PartialMessage, PartialRole, PartialServer, PartialUser, PartialUserVoiceState, PartialWebhook, Report, Server, User, UserSettings, UserVoiceState, Webhook + AppendMessage, Channel, ChannelUnread, ChannelVoiceState, Emoji, FieldsChannel, FieldsMember, FieldsMessage, FieldsRole, FieldsServer, FieldsUser, FieldsWebhook, Member, MemberCompositeKey, Message, PartialChannel, PartialMember, PartialMessage, PartialRole, PartialServer, PartialUser, PartialUserVoiceState, PartialWebhook, RemovalIntention, Report, Server, User, UserSettings, UserVoiceState, Webhook }; -use revolt_result::Error; use crate::Database; -/// WebSocket Client Errors -#[derive(Serialize, Deserialize, Debug, Clone)] -#[serde(tag = "error")] -pub enum WebSocketError { - LabelMe, - InternalError { at: String }, - InvalidSession, - OnboardingNotFinished, - AlreadyAuthenticated, - MalformedData { msg: String }, -} - /// Ping Packet #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(untagged)] @@ -28,12 +16,18 @@ pub enum Ping { Number(usize), } -/// Untagged Error -#[derive(Serialize)] -#[serde(untagged)] -pub enum ErrorEvent { - Error(WebSocketError), - APIError(Error), +/// Fields provided in Ready payload +#[derive(PartialEq)] +pub enum ReadyPayloadFields { + Users, + Servers, + Channels, + Members, + Emoji, + VoiceStates, + + UserSettings(Vec), + ChannelUnreads, } /// Protocol Events @@ -42,17 +36,32 @@ pub enum ErrorEvent { pub enum EventV1 { /// Multiple events Bulk { v: Vec }, + /// Error event + Error { data: Error }, /// Successfully authenticated Authenticated, + /// Logged out + Logout, /// Basic data to cache Ready { - users: Vec, - servers: Vec, - channels: Vec, - members: Vec, - emojis: Vec, - voice_states: Vec + #[serde(skip_serializing_if = "Option::is_none")] + users: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + servers: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + channels: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + members: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + emojis: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + voice_states: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + user_settings: Option, + #[serde(skip_serializing_if = "Option::is_none")] + channel_unreads: Option>, }, /// Ping response @@ -65,6 +74,8 @@ pub enum EventV1 { id: String, channel: String, data: PartialMessage, + #[serde(default)] + clear: Vec, }, /// Append information to existing message @@ -115,6 +126,7 @@ pub enum EventV1 { ServerUpdate { id: String, data: PartialServer, + #[serde(default)] clear: Vec, }, @@ -125,6 +137,7 @@ pub enum EventV1 { ServerMemberUpdate { id: MemberCompositeKey, data: PartialMember, + #[serde(default)] clear: Vec, }, @@ -132,13 +145,18 @@ pub enum EventV1 { ServerMemberJoin { id: String, user: String }, /// User left server - ServerMemberLeave { id: String, user: String }, + ServerMemberLeave { + id: String, + user: String, + reason: RemovalIntention, + }, /// Server role created or updated ServerRoleUpdate { id: String, role_id: String, data: PartialRole, + #[serde(default)] clear: Vec, }, @@ -149,6 +167,7 @@ pub enum EventV1 { UserUpdate { id: String, data: PartialUser, + #[serde(default)] clear: Vec, event_id: Option, }, @@ -183,6 +202,7 @@ pub enum EventV1 { ChannelUpdate { id: String, data: PartialChannel, + #[serde(default)] clear: Vec, }, @@ -260,7 +280,7 @@ impl EventV1 { // TODO: this should be captured by member list in the future and not immediately fanned out to users if let Ok(members) = db.fetch_all_memberships(&id).await { for member in members { - self.clone().p(member.id.server).await; + self.clone().server(member.id.server).await; } } } @@ -270,6 +290,11 @@ impl EventV1 { self.p(format!("{id}!")).await; } + /// Publish server member event + pub async fn server(self, id: String) { + self.p(format!("{id}u")).await; + } + /// Publish internal global event pub async fn global(self) { self.p("global".to_string()).await; diff --git a/crates/core/database/src/events/mod.rs b/crates/core/database/src/events/mod.rs index c07f47e0f..608984357 100644 --- a/crates/core/database/src/events/mod.rs +++ b/crates/core/database/src/events/mod.rs @@ -1,2 +1,3 @@ pub mod client; +pub mod rabbit; pub mod server; diff --git a/crates/core/database/src/events/rabbit.rs b/crates/core/database/src/events/rabbit.rs new file mode 100644 index 000000000..613047521 --- /dev/null +++ b/crates/core/database/src/events/rabbit.rs @@ -0,0 +1,59 @@ +use std::collections::HashMap; + +use revolt_models::v0::PushNotification; +use serde::{Deserialize, Serialize}; + +use crate::User; + +#[derive(Serialize, Deserialize)] +pub struct MessageSentPayload { + pub notification: PushNotification, + pub users: Vec, +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct FRAcceptedPayload { + pub accepted_user: User, + pub user: String, +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct FRReceivedPayload { + pub from_user: User, + pub user: String, +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct GenericPayload { + pub title: String, + pub body: String, + pub icon: Option, + pub user: User, +} + +#[derive(Serialize, Deserialize)] +#[serde(tag = "type", content = "data")] +#[allow(clippy::large_enum_variant)] +pub enum PayloadKind { + MessageNotification(PushNotification), + FRAccepted(FRAcceptedPayload), + FRReceived(FRReceivedPayload), + BadgeUpdate(usize), + Generic(GenericPayload), +} + +#[derive(Serialize, Deserialize)] +pub struct PayloadToService { + pub notification: PayloadKind, + pub user_id: String, + pub session_id: String, + pub token: String, + pub extras: HashMap, +} + +#[derive(Serialize, Deserialize)] +pub struct AckPayload { + pub user_id: String, + pub channel_id: String, + pub message_id: String, +} diff --git a/crates/core/database/src/events/server.rs b/crates/core/database/src/events/server.rs index 883b92c30..cdaf6418b 100644 --- a/crates/core/database/src/events/server.rs +++ b/crates/core/database/src/events/server.rs @@ -8,5 +8,6 @@ pub enum ClientMessage { Authenticate { token: String }, BeginTyping { channel: String }, EndTyping { channel: String }, + Subscribe { server_id: String }, Ping { data: Ping, responded: Option<()> }, } diff --git a/crates/core/database/src/lib.rs b/crates/core/database/src/lib.rs index 90365598e..9e26336d1 100644 --- a/crates/core/database/src/lib.rs +++ b/crates/core/database/src/lib.rs @@ -16,6 +16,8 @@ extern crate revolt_optional_struct; #[macro_use] extern crate revolt_result; +pub use iso8601_timestamp; + #[cfg(feature = "mongodb")] pub use mongodb; @@ -23,6 +25,26 @@ pub use mongodb; #[macro_use] extern crate bson; +#[macro_export] +#[cfg(debug_assertions)] +macro_rules! query { + ( $self: ident, $type: ident, $collection: expr, $($rest:expr),+ ) => { + Ok($self.$type($collection, $($rest),+).await.unwrap()) + }; +} + +#[macro_export] +#[cfg(not(debug_assertions))] +macro_rules! query { + ( $self: ident, $type: ident, $collection: expr, $($rest:expr),+ ) => { + $self.$type($collection, $($rest),+).await + .map_err(|err| { + revolt_config::capture_internal_error!(err); + create_database_error!(stringify!($type), $collection) + }) + }; +} + macro_rules! database_derived { ( $( $item:item )+ ) => { $( @@ -83,7 +105,15 @@ pub use models::*; pub mod events; pub mod tasks; +mod amqp; +pub use amqp::amqp::AMQP; + /// Utility function to check if a boolean value is false pub fn if_false(t: &bool) -> bool { !t } + +/// Utility function to check if an option doesnt contain true +pub fn if_option_false(t: &Option) -> bool { + t != &Some(true) +} diff --git a/crates/core/database/src/models/admin_migrations/ops/mongodb/init.rs b/crates/core/database/src/models/admin_migrations/ops/mongodb/init.rs index 2bfd7ff12..ab25819b5 100644 --- a/crates/core/database/src/models/admin_migrations/ops/mongodb/init.rs +++ b/crates/core/database/src/models/admin_migrations/ops/mongodb/init.rs @@ -56,6 +56,10 @@ pub async fn create_database(db: &MongoDb) { .await .expect("Failed to create attachments collection."); + db.create_collection("attachment_hashes", None) + .await + .expect("Failed to create attachment_hashes collection."); + db.create_collection("user_settings", None) .await .expect("Failed to create user_settings collection."); @@ -146,7 +150,14 @@ pub async fn create_database(db: &MongoDb) { "author": 1_i32 }, "name": "author" - } + }, + { + "key": { + "channel": 1_i32, + "pinned": 1_i32 + }, + "name": "channel_pinned_compound" + }, ] }, None, @@ -202,6 +213,46 @@ pub async fn create_database(db: &MongoDb) { .await .expect("Failed to create server_members index."); + db.run_command( + doc! { + "createIndexes": "attachments", + "indexes": [ + { + "key": { + "hash": 1_i32 + }, + "name": "hash" + }, + { + "key": { + "used_for.id": 1_i32 + }, + "name": "used_for_id" + } + ] + }, + None, + ) + .await + .expect("Failed to create attachments index."); + + db.run_command( + doc! { + "createIndexes": "attachment_hashes", + "indexes": [ + { + "key": { + "processed_hash": 1_i32 + }, + "name": "processed_hash" + } + ] + }, + None, + ) + .await + .expect("Failed to create attachment_hashes index."); + db.collection("migrations") .insert_one( doc! { diff --git a/crates/core/database/src/models/admin_migrations/ops/mongodb/scripts.rs b/crates/core/database/src/models/admin_migrations/ops/mongodb/scripts.rs index bf36fed29..c83d8d8c5 100644 --- a/crates/core/database/src/models/admin_migrations/ops/mongodb/scripts.rs +++ b/crates/core/database/src/models/admin_migrations/ops/mongodb/scripts.rs @@ -5,11 +5,13 @@ use crate::{ bson::{doc, from_bson, from_document, to_document, Bson, DateTime, Document}, options::FindOptions, }, - MongoDb, DISCRIMINATOR_SEARCH_SPACE, + AbstractChannels, AbstractServers, Channel, Invite, MongoDb, DISCRIMINATOR_SEARCH_SPACE, }; +use bson::oid::ObjectId; use futures::StreamExt; use rand::seq::SliceRandom; use revolt_permissions::DEFAULT_WEBHOOK_PERMISSIONS; +use revolt_result::{Error, ErrorType}; use serde::{Deserialize, Serialize}; use unicode_segmentation::UnicodeSegmentation; @@ -19,7 +21,7 @@ struct MigrationInfo { revision: i32, } -pub const LATEST_REVISION: i32 = 27; +pub const LATEST_REVISION: i32 = 31; pub async fn migrate_database(db: &MongoDb) { let migrations = db.col::("migrations"); @@ -984,25 +986,266 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 { } if revision <= 26 { - info!("Running migration [revision 26 / 17-04-2024]: Add `can_publish` and `can_receive` to members"); + // Need to migrate fields on attachments, change `user_id`, `object_id`, etc to `parent`. + info!("Running migration [revision 26 / 15-05-2024]: fix invites being incorrectly serialized with wrong enum tagging."); + + auto_derived!( + pub enum OldInvite { + Server { + #[serde(rename = "_id")] + code: String, + server: String, + creator: String, + channel: String, + }, + Group { + #[serde(rename = "_id")] + code: String, + creator: String, + channel: String, + }, + } + ); + + #[derive(serde::Serialize, serde::Deserialize)] + struct Outer { + _id: ObjectId, + #[serde(flatten)] + invite: OldInvite, + } + + let invites = db + .db() + .collection::("channel_invites") + .find( + doc! { + "type": { "$exists": false } + }, + None, + ) + .await + .expect("failed to find invites") + .filter_map(|s| async { s.ok() }) + .collect::>() + .await + .into_iter() + .map(|invite| match invite.invite { + OldInvite::Server { + code, + server, + creator, + channel, + } => Invite::Server { + code, + server, + creator, + channel, + }, + OldInvite::Group { + code, + creator, + channel, + } => Invite::Group { + code, + creator, + channel, + }, + }) + .collect::>(); + + if !invites.is_empty() { + db.db() + .collection("channel_invites") + .insert_many(invites, None) + .await + .expect("failed to insert corrected invite"); + + db.db() + .collection::("channel_invites") + .delete_many( + doc! { + "type": { "$exists": false } + }, + None, + ) + .await + .expect("failed to find invites"); + } + } + + if revision <= 27 { + info!("Running migration [revision 27 / 21-07-2024]: create message pinned index."); + + db.db() + .run_command( + doc! { + "createIndexes": "messages", + "indexes": [ + { + "key": { + "channel": 1_i32, + "pinned": 1_i32 + }, + "name": "channel_pinned_compound" + } + ] + }, + None, + ) + .await + .expect("Failed to create message index."); + } + + if revision <= 28 { + info!("Running migration [revision 28 / 10-09-2024]: Add support for new Autumn."); + + db.db() + .create_collection("attachment_hashes", None) + .await + .ok(); + + db.db() + .run_command( + doc! { + "createIndexes": "attachments", + "indexes": [ + { + "key": { + "hash": 1_i32 + }, + "name": "hash" + } + ] + }, + None, + ) + .await + .expect("Failed to create attachments index."); + + db.db() + .run_command( + doc! { + "createIndexes": "attachment_hashes", + "indexes": [ + { + "key": { + "processed_hash": 1_i32 + }, + "name": "processed_hash" + } + ] + }, + None, + ) + .await + .expect("Failed to create attachment_hashes index."); + } + + // Revision 29 omitted due to bug. + + if revision <= 30 { + info!("Running migration [revision 30 / 29-09-2024]: Add index for used_for.id to attachments."); + + db.db() + .run_command( + doc! { + "createIndexes": "attachments", + "indexes": [ + { + "key": { + "used_for.id": 1_i32 + }, + "name": "used_for_id" + } + ] + }, + None, + ) + .await + .expect("Failed to create attachments index."); + } + + if revision <= 31 { + info!("Running migration [revision 31 / 31-10-2024]: Add creator_id to webhooks and delete those whose channels don't exist."); + + #[derive(serde::Serialize, serde::Deserialize)] + struct WebhookShell { + _id: String, + channel_id: String, + } + + let webhooks = db + .db() + .collection::("channel_webhooks") + .find(doc! {}, None) + .await + .expect("webhooks") + .filter_map(|s| async { s.ok() }) + .collect::>() + .await; + + for webhook in webhooks { + match db.fetch_channel(&webhook.channel_id).await { + Ok(channel) => { + let creator_id = match channel { + Channel::Group { owner, .. } => owner, + Channel::TextChannel { server, .. } + | Channel::VoiceChannel { server, .. } => { + let server = db.fetch_server(&server).await.expect("server"); + server.owner + } + _ => unreachable!("not server or group channel!"), + }; + + db.db() + .collection::("channel_webhooks") + .update_one( + doc! { + "_id": webhook._id, + }, + doc! { + "$set" : { + "creator_id": creator_id + } + }, + None, + ) + .await + .expect("update webhook"); + } + Err(Error { + error_type: ErrorType::NotFound, + .. + }) => { + db.db() + .collection::("channel_webhooks") + .delete_one(doc! { "_id": webhook._id }, None) + .await + .expect("failed to delete invalid webhook"); + } + Err(err) => panic!("{err:?}"), + } + } + } + + if revision <= 32 { + info!("Running migration [revision 32 / 26-01-2025]: Add `is_publishing` and `is_receiving` to members"); db.col::("server_members") .update_many( doc! {}, doc! { "$set": { - "can_publish": true, - "can_receive": true + "is_publishing": true, + "is_receiving": true } }, - None + None, ) .await .expect("Failed to update members"); } - // Need to migrate fields on attachments, change `user_id`, `object_id`, etc to `parent`. - // Reminder to update LATEST_REVISION when adding new migrations. LATEST_REVISION.max(revision) } diff --git a/crates/core/database/src/models/bots/model.rs b/crates/core/database/src/models/bots/model.rs index cf36bedc8..0a7960491 100644 --- a/crates/core/database/src/models/bots/model.rs +++ b/crates/core/database/src/models/bots/model.rs @@ -2,7 +2,7 @@ use revolt_config::config; use revolt_result::Result; use ulid::Ulid; -use crate::{BotInformation, Database, PartialUser, User}; +use crate::{events::client::EventV1, BotInformation, Database, PartialUser, User}; auto_derived_partial!( /// Bot @@ -72,7 +72,12 @@ impl Default for Bot { #[allow(clippy::disallowed_methods)] impl Bot { /// Create a new bot - pub async fn create(db: &Database, username: String, owner: &User, data: D) -> Result + pub async fn create( + db: &Database, + username: String, + owner: &User, + data: D, + ) -> Result<(Bot, User)> where D: Into>, { @@ -80,14 +85,13 @@ impl Bot { return Err(create_error!(IsBot)); } - let config = config().await; - if db.get_number_of_bots_by_user(&owner.id).await? >= config.features.limits.default.bots { + if db.get_number_of_bots_by_user(&owner.id).await? >= owner.limits().await.bots { return Err(create_error!(ReachedMaximumBots)); } let id = Ulid::new().to_string(); - User::create( + let user = User::create( db, username, Some(id.to_string()), @@ -112,7 +116,7 @@ impl Bot { } db.insert_bot(&bot).await?; - Ok(bot) + Ok((bot, user)) } /// Remove a field from this object @@ -142,6 +146,10 @@ impl Bot { db.update_bot(&self.id, &partial, remove).await?; + if partial.token.is_some() { + EventV1::Logout.private(self.id.clone()).await; + } + self.apply_options(partial); Ok(()) } @@ -164,7 +172,7 @@ mod tests { .await .unwrap(); - let bot = Bot::create( + let (bot, _) = Bot::create( &db, "Bot Name".to_string(), &owner, diff --git a/crates/core/database/src/models/channel_invites/model.rs b/crates/core/database/src/models/channel_invites/model.rs index aaaff55ea..e3af921ab 100644 --- a/crates/core/database/src/models/channel_invites/model.rs +++ b/crates/core/database/src/models/channel_invites/model.rs @@ -10,6 +10,7 @@ static ALPHABET: [char; 54] = [ auto_derived!( /// Invite + #[serde(tag = "type")] pub enum Invite { /// Invite to a specific server channel Server { diff --git a/crates/core/database/src/models/channel_unreads/ops.rs b/crates/core/database/src/models/channel_unreads/ops.rs index 534dfe01a..6a6e98af3 100644 --- a/crates/core/database/src/models/channel_unreads/ops.rs +++ b/crates/core/database/src/models/channel_unreads/ops.rs @@ -7,13 +7,13 @@ mod reference; #[async_trait] pub trait AbstractChannelUnreads: Sync + Send { - /// Acknowledge a message. + /// Acknowledge a message, and returns updated channel unread. async fn acknowledge_message( &self, channel_id: &str, user_id: &str, message_id: &str, - ) -> Result<()>; + ) -> Result>; /// Acknowledge many channels. async fn acknowledge_channels(&self, user_id: &str, channel_ids: &[String]) -> Result<()>; @@ -26,6 +26,12 @@ pub trait AbstractChannelUnreads: Sync + Send { message_ids: &[String], ) -> Result<()>; + /// Fetch all unreads with mentions for a user. + async fn fetch_unread_mentions(&self, user_id: &str) -> Result>; + /// Fetch all channel unreads for a user. async fn fetch_unreads(&self, user_id: &str) -> Result>; + + /// Fetch unread for a specific user in a channel. + async fn fetch_unread(&self, user_id: &str, channel_id: &str) -> Result>; } diff --git a/crates/core/database/src/models/channel_unreads/ops/mongodb.rs b/crates/core/database/src/models/channel_unreads/ops/mongodb.rs index df498bf01..76cead112 100644 --- a/crates/core/database/src/models/channel_unreads/ops/mongodb.rs +++ b/crates/core/database/src/models/channel_unreads/ops/mongodb.rs @@ -1,4 +1,6 @@ use bson::Document; +use mongodb::options::FindOneAndUpdateOptions; +use mongodb::options::ReturnDocument; use mongodb::options::UpdateOptions; use revolt_result::Result; use ulid::Ulid; @@ -12,31 +14,35 @@ static COL: &str = "channel_unreads"; #[async_trait] impl AbstractChannelUnreads for MongoDb { - /// Acknowledge a message. + /// Acknowledge a message, and returns updated channel unread. async fn acknowledge_message( &self, channel_id: &str, user_id: &str, message_id: &str, - ) -> Result<()> { - self.col::(COL) - .update_one( + ) -> Result> { + self.col::(COL) + .find_one_and_update( doc! { "_id.channel": channel_id, "_id.user": user_id, }, doc! { - "$unset": { - "mentions": 1_i32 + "$pull": { + "mentions": { + "$lte": message_id + } }, "$set": { "last_id": message_id } }, - UpdateOptions::builder().upsert(true).build(), + FindOneAndUpdateOptions::builder() + .upsert(true) + .return_document(ReturnDocument::After) + .build(), ) .await - .map(|_| ()) .map_err(|_| create_database_error!("update_one", COL)) } @@ -116,4 +122,29 @@ impl AbstractChannelUnreads for MongoDb { } ) } + + async fn fetch_unread_mentions(&self, user_id: &str) -> Result> { + query! { + self, + find, + COL, + doc! { + "_id.user": user_id, + "mentions": {"$ne": null} + } + } + } + + /// Fetch unread for a specific user in a channel. + async fn fetch_unread(&self, user_id: &str, channel_id: &str) -> Result> { + query!( + self, + find_one, + COL, + doc! { + "_id.user": user_id, + "_id.channel": channel_id + } + ) + } } diff --git a/crates/core/database/src/models/channel_unreads/ops/reference.rs b/crates/core/database/src/models/channel_unreads/ops/reference.rs index b7ba5b957..914ac95db 100644 --- a/crates/core/database/src/models/channel_unreads/ops/reference.rs +++ b/crates/core/database/src/models/channel_unreads/ops/reference.rs @@ -13,7 +13,7 @@ impl AbstractChannelUnreads for ReferenceDb { channel_id: &str, user_id: &str, message_id: &str, - ) -> Result<()> { + ) -> Result> { let mut unreads = self.channel_unreads.lock().await; let key = ChannelCompositeKey { channel: channel_id.to_string(), @@ -27,14 +27,14 @@ impl AbstractChannelUnreads for ReferenceDb { unreads.insert( key.clone(), ChannelUnread { - id: key, + id: key.clone(), last_id: Some(message_id.to_string()), mentions: None, }, ); } - Ok(()) + Ok(unreads.get(&key).cloned()) } /// Acknowledge many channels. @@ -78,6 +78,15 @@ impl AbstractChannelUnreads for ReferenceDb { Ok(()) } + async fn fetch_unread_mentions(&self, user_id: &str) -> Result> { + let unreads = self.channel_unreads.lock().await; + Ok(unreads + .values() + .filter(|unread| unread.id.user == user_id && unread.mentions.is_some()) + .cloned() + .collect()) + } + /// Fetch all channel unreads for a user. async fn fetch_unreads(&self, user_id: &str) -> Result> { let unreads = self.channel_unreads.lock().await; @@ -87,4 +96,16 @@ impl AbstractChannelUnreads for ReferenceDb { .cloned() .collect()) } + + /// Fetch unread for a specific user in a channel. + async fn fetch_unread(&self, user_id: &str, channel_id: &str) -> Result> { + let unreads = self.channel_unreads.lock().await; + + Ok(unreads + .get(&ChannelCompositeKey { + channel: channel_id.to_string(), + user: user_id.to_string(), + }) + .cloned()) + } } diff --git a/crates/core/database/src/models/channel_webhooks/model.rs b/crates/core/database/src/models/channel_webhooks/model.rs index 568326d6f..b163cbfeb 100644 --- a/crates/core/database/src/models/channel_webhooks/model.rs +++ b/crates/core/database/src/models/channel_webhooks/model.rs @@ -17,6 +17,9 @@ auto_derived_partial!( #[serde(skip_serializing_if = "Option::is_none")] pub avatar: Option, + /// User that created this webhook + pub creator_id: String, + /// The channel this webhook belongs to pub channel_id: String, @@ -43,6 +46,7 @@ impl Default for Webhook { id: Default::default(), name: Default::default(), avatar: None, + creator_id: Default::default(), channel_id: Default::default(), permissions: Default::default(), token: Default::default(), @@ -70,7 +74,7 @@ impl Webhook { if self.token.as_deref() == Some(token) { Ok(()) } else { - Err(create_error!(InvalidCredentials)) + Err(create_error!(NotAuthenticated)) } } diff --git a/crates/core/database/src/models/channels/model.rs b/crates/core/database/src/models/channels/model.rs index c96151fbb..94dd272a2 100644 --- a/crates/core/database/src/models/channels/model.rs +++ b/crates/core/database/src/models/channels/model.rs @@ -9,7 +9,7 @@ use ulid::Ulid; use crate::{ events::client::EventV1, tasks::ack::AckEvent, Database, File, IntoDocumentPath, PartialServer, - Server, SystemMessage, User, + Server, SystemMessage, User, AMQP, }; auto_derived!( @@ -205,9 +205,9 @@ impl Channel { update_server: bool, ) -> Result { let config = config().await; - if server.channels.len() > config.features.limits.default.server_channels { + if server.channels.len() > config.features.limits.global.server_channels { return Err(create_error!(TooManyChannels { - max: config.features.limits.default.server_channels, + max: config.features.limits.global.server_channels, })); }; @@ -268,9 +268,9 @@ impl Channel { data.users.insert(owner_id.to_string()); let config = config().await; - if data.users.len() > config.features.limits.default.group_size { + if data.users.len() > config.features.limits.global.group_size { return Err(create_error!(GroupTooLarge { - max: config.features.limits.default.group_size, + max: config.features.limits.global.group_size, })); } @@ -342,6 +342,7 @@ impl Channel { pub async fn add_user_to_group( &mut self, db: &Database, + amqp: &AMQP, user: &User, by_id: &str, ) -> Result<()> { @@ -351,9 +352,9 @@ impl Channel { } let config = config().await; - if recipients.len() >= config.features.limits.default.group_size { + if recipients.len() >= config.features.limits.global.group_size { return Err(create_error!(GroupTooLarge { - max: config.features.limits.default.group_size + max: config.features.limits.global.group_size })); } @@ -378,10 +379,13 @@ impl Channel { .into_message(id.to_string()) .send( db, + Some(amqp), MessageAuthor::System { username: &user.username, avatar: user.avatar.as_ref().map(|file| file.id.as_ref()), }, + None, + None, self, false, ) @@ -420,13 +424,13 @@ impl Channel { } /// Clone this channel's id - pub fn id(&self) -> String { + pub fn id(&self) -> &str { match self { Channel::DirectMessage { id, .. } | Channel::Group { id, .. } | Channel::SavedMessages { id, .. } | Channel::TextChannel { id, .. } - | Channel::VoiceChannel { id, .. } => id.clone(), + | Channel::VoiceChannel { id, .. } => id, } } @@ -659,7 +663,7 @@ impl Channel { .private(user.to_string()) .await; - crate::tasks::ack::queue( + crate::tasks::ack::queue_ack( self.id().to_string(), user.to_string(), AckEvent::AckMessage { @@ -675,6 +679,7 @@ impl Channel { pub async fn remove_user_from_group( &self, db: &Database, + amqp: &AMQP, user: &User, by_id: Option<&str>, silent: bool, @@ -706,10 +711,13 @@ impl Channel { .into_message(id.to_string()) .send( db, + Some(amqp), MessageAuthor::System { username: name, avatar: None, }, + None, + None, self, false, ) @@ -720,6 +728,8 @@ impl Channel { } } + db.remove_user_from_group(id, &user.id).await?; + EventV1::ChannelGroupLeave { id: id.to_string(), user: user.id.to_string(), @@ -741,10 +751,13 @@ impl Channel { .into_message(id.to_string()) .send( db, + Some(amqp), MessageAuthor::System { username: &user.username, avatar: user.avatar.as_ref().map(|file| file.id.as_ref()), }, + None, + None, self, false, ) diff --git a/crates/core/database/src/models/channels/ops/mongodb.rs b/crates/core/database/src/models/channels/ops/mongodb.rs index 9344d67b5..51c6b66e5 100644 --- a/crates/core/database/src/models/channels/ops/mongodb.rs +++ b/crates/core/database/src/models/channels/ops/mongodb.rs @@ -261,12 +261,12 @@ impl AbstractChannels for MongoDb { // Delete associated attachments self.delete_many_attachments(doc! { - "object_id": &id + "used_for.id": &id }) .await?; // Delete the channel itself - query!(self, delete_one_by_id, COL, &channel.id()).map(|_| ()) + query!(self, delete_one_by_id, COL, channel.id()).map(|_| ()) } } diff --git a/crates/core/database/src/models/channels/ops/reference.rs b/crates/core/database/src/models/channels/ops/reference.rs index a2c849a40..7d5c9a59f 100644 --- a/crates/core/database/src/models/channels/ops/reference.rs +++ b/crates/core/database/src/models/channels/ops/reference.rs @@ -11,7 +11,7 @@ impl AbstractChannels for ReferenceDb { /// Insert a new channel in the database async fn insert_channel(&self, channel: &Channel) -> Result<()> { let mut channels = self.channels.lock().await; - if let Entry::Vacant(entry) = channels.entry(channel.id()) { + if let Entry::Vacant(entry) = channels.entry(channel.id().to_string()) { entry.insert(channel.clone()); Ok(()) } else { @@ -148,7 +148,7 @@ impl AbstractChannels for ReferenceDb { // Delete a channel async fn delete_channel(&self, channel: &Channel) -> Result<()> { let mut channels = self.channels.lock().await; - if channels.remove(&channel.id()).is_some() { + if channels.remove(channel.id()).is_some() { Ok(()) } else { Err(create_error!(NotFound)) diff --git a/crates/core/database/src/models/file_hashes/mod.rs b/crates/core/database/src/models/file_hashes/mod.rs new file mode 100644 index 000000000..4d801b73e --- /dev/null +++ b/crates/core/database/src/models/file_hashes/mod.rs @@ -0,0 +1,5 @@ +mod model; +mod ops; + +pub use model::*; +pub use ops::*; diff --git a/crates/core/database/src/models/file_hashes/model.rs b/crates/core/database/src/models/file_hashes/model.rs new file mode 100644 index 000000000..46a249f0f --- /dev/null +++ b/crates/core/database/src/models/file_hashes/model.rs @@ -0,0 +1,92 @@ +use iso8601_timestamp::Timestamp; + +use crate::File; + +auto_derived_partial!( + /// File hash + pub struct FileHash { + /// Sha256 hash of the file + #[serde(rename = "_id")] + pub id: String, + /// Sha256 hash of file after it has been processed + pub processed_hash: String, + + /// When this file was created in system + pub created_at: Timestamp, + + /// The bucket this file is stored in + pub bucket_id: String, + /// The path at which this file exists in + pub path: String, + /// Cryptographic nonce used to encrypt this file + pub iv: String, + + /// Parsed metadata of this file + pub metadata: Metadata, + /// Raw content type of this file + pub content_type: String, + /// Size of this file (in bytes) + pub size: isize, + }, + "PartialFile" +); + +auto_derived!( + /// Metadata associated with a file + #[serde(tag = "type")] + #[derive(Default)] + pub enum Metadata { + /// File is just a generic uncategorised file + #[default] + File, + /// File contains textual data and should be displayed as such + Text, + /// File is an image with specific dimensions + Image { + width: isize, + height: isize, + // animated: bool // TODO: https://docs.rs/image/latest/image/trait.AnimationDecoder.html for APNG support + }, + /// File is a video with specific dimensions + Video { width: isize, height: isize }, + /// File is audio + Audio, + } +); + +impl FileHash { + /// Create a file from a file hash + pub fn into_file( + &self, + id: String, + tag: String, + filename: String, + uploader_id: String, + ) -> File { + File { + id, + tag, + filename, + hash: Some(self.id.clone()), + + uploaded_at: Some(Timestamp::now_utc()), + uploader_id: Some(uploader_id), + + used_for: None, + + deleted: None, + reported: None, + + // TODO: remove this data + metadata: self.metadata.clone(), + content_type: self.content_type.clone(), + size: self.size, + + // TODO: superseded by "used_for" + message_id: None, + object_id: None, + server_id: None, + user_id: None, + } + } +} diff --git a/crates/core/database/src/models/file_hashes/ops.rs b/crates/core/database/src/models/file_hashes/ops.rs new file mode 100644 index 000000000..e7f626584 --- /dev/null +++ b/crates/core/database/src/models/file_hashes/ops.rs @@ -0,0 +1,18 @@ +use revolt_result::Result; + +use crate::FileHash; + +mod mongodb; +mod reference; + +#[async_trait] +pub trait AbstractAttachmentHashes: Sync + Send { + /// Insert a new attachment hash into the database. + async fn insert_attachment_hash(&self, hash: &FileHash) -> Result<()>; + + /// Fetch an attachment hash entry by sha256 hash. + async fn fetch_attachment_hash(&self, hash: &str) -> Result; + + /// Update an attachment hash nonce value. + async fn set_attachment_hash_nonce(&self, hash: &str, nonce: &str) -> Result<()>; +} diff --git a/crates/core/database/src/models/file_hashes/ops/mongodb.rs b/crates/core/database/src/models/file_hashes/ops/mongodb.rs new file mode 100644 index 000000000..c55a06104 --- /dev/null +++ b/crates/core/database/src/models/file_hashes/ops/mongodb.rs @@ -0,0 +1,51 @@ +use revolt_result::Result; + +use crate::FileHash; +use crate::MongoDb; + +use super::AbstractAttachmentHashes; + +static COL: &str = "attachment_hashes"; + +#[async_trait] +impl AbstractAttachmentHashes for MongoDb { + /// Insert a new attachment hash into the database. + async fn insert_attachment_hash(&self, hash: &FileHash) -> Result<()> { + query!(self, insert_one, COL, &hash).map(|_| ()) + } + + /// Fetch an attachment hash entry by sha256 hash. + async fn fetch_attachment_hash(&self, hash: &str) -> Result { + query!( + self, + find_one, + COL, + doc! { + "$or": [ + {"_id": hash}, + {"processed_hash": hash} + ] + } + )? + .ok_or_else(|| create_error!(NotFound)) + } + + /// Update an attachment hash nonce value. + async fn set_attachment_hash_nonce(&self, hash: &str, nonce: &str) -> Result<()> { + self.col::(COL) + .update_one( + doc! { + "_id": hash + }, + doc! { + "$set": { + "iv": nonce + } + }, + None, + ) + .await + .map(|_| ()) + .map_err(|_| create_database_error!("update_one", COL)) + } +} diff --git a/crates/core/database/src/models/file_hashes/ops/reference.rs b/crates/core/database/src/models/file_hashes/ops/reference.rs new file mode 100644 index 000000000..86bbeee8b --- /dev/null +++ b/crates/core/database/src/models/file_hashes/ops/reference.rs @@ -0,0 +1,41 @@ +use revolt_result::Result; + +use crate::FileHash; +use crate::ReferenceDb; + +use super::AbstractAttachmentHashes; + +#[async_trait] +impl AbstractAttachmentHashes for ReferenceDb { + /// Insert a new attachment hash into the database. + async fn insert_attachment_hash(&self, hash: &FileHash) -> Result<()> { + let mut hashes = self.file_hashes.lock().await; + if hashes.contains_key(&hash.id) { + Err(create_database_error!("insert", "attachment")) + } else { + hashes.insert(hash.id.to_string(), hash.clone()); + Ok(()) + } + } + + /// Fetch an attachment hash entry by sha256 hash. + async fn fetch_attachment_hash(&self, hash_value: &str) -> Result { + let hashes = self.file_hashes.lock().await; + hashes + .values() + .cloned() + .find(|hash| hash.id == hash_value || hash.processed_hash == hash_value) + .ok_or(create_error!(NotFound)) + } + + /// Update an attachment hash nonce value. + async fn set_attachment_hash_nonce(&self, hash: &str, nonce: &str) -> Result<()> { + let mut hashes = self.file_hashes.lock().await; + if let Some(file) = hashes.get_mut(hash) { + file.iv = nonce.to_owned(); + Ok(()) + } else { + Err(create_error!(NotFound)) + } + } +} diff --git a/crates/core/database/src/models/files/model.rs b/crates/core/database/src/models/files/model.rs index 85edcaeaf..5ea78f9b2 100644 --- a/crates/core/database/src/models/files/model.rs +++ b/crates/core/database/src/models/files/model.rs @@ -1,5 +1,6 @@ -use crate::Database; +use crate::{Database, FileHash, Metadata}; +use iso8601_timestamp::Timestamp; use revolt_result::Result; auto_derived_partial!( @@ -12,12 +13,18 @@ auto_derived_partial!( pub tag: String, /// Original filename pub filename: String, - /// Parsed metadata of this file - pub metadata: Metadata, - /// Raw content type of this file - pub content_type: String, - /// Size of this file (in bytes) - pub size: isize, + /// Hash of this file + pub hash: Option, // these are Option<>s to not break file uploads on legacy Autumn + + /// When this file was uploaded + pub uploaded_at: Option, // these are Option<>s to not break file uploads on legacy Autumn + /// ID of user who uploaded this file + #[serde(skip_serializing_if = "Option::is_none")] + pub uploader_id: Option, // these are Option<>s to not break file uploads on legacy Autumn + + /// What the file was used for + #[serde(skip_serializing_if = "Option::is_none")] + pub used_for: Option, /// Whether this file was deleted #[serde(skip_serializing_if = "Option::is_none")] @@ -26,6 +33,14 @@ auto_derived_partial!( #[serde(skip_serializing_if = "Option::is_none")] pub reported: Option, + // !!! DEPRECATED: + /// Parsed metadata of this file + pub metadata: Metadata, + /// Raw content type of this file + pub content_type: String, + /// Size of this file (in bytes) + pub size: isize, + // TODO: migrate this mess to having: // - author_id // - parent: Parent { Message(id), User(id), etc } @@ -44,64 +59,184 @@ auto_derived_partial!( ); auto_derived!( - /// Metadata associated with a file - #[serde(tag = "type")] - #[derive(Default)] - pub enum Metadata { - /// File is just a generic uncategorised file - #[default] - File, - /// File contains textual data and should be displayed as such - Text, - /// File is an image with specific dimensions - Image { width: isize, height: isize }, - /// File is a video with specific dimensions - Video { width: isize, height: isize }, - /// File is audio - Audio, + /// Type of object file was used for + pub enum FileUsedForType { + Message, + ServerBanner, + Emoji, + UserAvatar, + WebhookAvatar, + UserProfileBackground, + LegacyGroupIcon, + ChannelIcon, + ServerIcon, + } + + /// Information about what the file was used for + pub struct FileUsedFor { + /// Type of the object + #[serde(rename = "type")] + pub object_type: FileUsedForType, + /// ID of the object + pub id: String, } ); impl File { + /// Get the hash entry for this file + pub async fn as_hash(&self, db: &Database) -> Result { + db.fetch_attachment_hash(self.hash.as_ref().unwrap()).await + } + /// Use a file for a message attachment - pub async fn use_attachment(db: &Database, id: &str, parent: &str) -> Result { - db.find_and_use_attachment(id, "attachments", "message", parent) - .await + pub async fn use_attachment( + db: &Database, + id: &str, + parent: &str, + uploader_id: &str, + ) -> Result { + db.find_and_use_attachment( + id, + "attachments", + FileUsedFor { + id: parent.to_owned(), + object_type: FileUsedForType::Message, + }, + uploader_id.to_owned(), + ) + .await } /// Use a file for a user profile background - pub async fn use_background(db: &Database, id: &str, parent: &str) -> Result { - db.find_and_use_attachment(id, "backgrounds", "user", parent) - .await + pub async fn use_background( + db: &Database, + id: &str, + parent: &str, + uploader_id: &str, + ) -> Result { + db.find_and_use_attachment( + id, + "backgrounds", + FileUsedFor { + id: parent.to_owned(), + object_type: FileUsedForType::UserProfileBackground, + }, + uploader_id.to_owned(), + ) + .await } /// Use a file for a user avatar - pub async fn use_avatar(db: &Database, id: &str, parent: &str) -> Result { - db.find_and_use_attachment(id, "avatars", "user", parent) - .await + pub async fn use_user_avatar( + db: &Database, + id: &str, + parent: &str, + uploader_id: &str, + ) -> Result { + db.find_and_use_attachment( + id, + "avatars", + FileUsedFor { + id: parent.to_owned(), + object_type: FileUsedForType::UserAvatar, + }, + uploader_id.to_owned(), + ) + .await } - /// Use a file for an icon - pub async fn use_icon(db: &Database, id: &str, parent: &str) -> Result { - db.find_and_use_attachment(id, "icons", "object", parent) - .await + /// Use a file for a webhook avatar + pub async fn use_webhook_avatar( + db: &Database, + id: &str, + parent: &str, + uploader_id: &str, + ) -> Result { + db.find_and_use_attachment( + id, + "avatars", + FileUsedFor { + id: parent.to_owned(), + object_type: FileUsedForType::WebhookAvatar, + }, + uploader_id.to_owned(), + ) + .await } /// Use a file for a server icon - pub async fn use_server_icon(db: &Database, id: &str, parent: &str) -> Result { - db.find_and_use_attachment(id, "icons", "object", parent) - .await + pub async fn use_server_icon( + db: &Database, + id: &str, + parent: &str, + uploader_id: &str, + ) -> Result { + db.find_and_use_attachment( + id, + "icons", + FileUsedFor { + id: parent.to_owned(), + object_type: FileUsedForType::ServerIcon, + }, + uploader_id.to_owned(), + ) + .await + } + + /// Use a file for a channel icon + pub async fn use_channel_icon( + db: &Database, + id: &str, + parent: &str, + uploader_id: &str, + ) -> Result { + db.find_and_use_attachment( + id, + "icons", + FileUsedFor { + id: parent.to_owned(), + object_type: FileUsedForType::ChannelIcon, + }, + uploader_id.to_owned(), + ) + .await } /// Use a file for a server banner - pub async fn use_banner(db: &Database, id: &str, parent: &str) -> Result { - db.find_and_use_attachment(id, "banners", "server", parent) - .await + pub async fn use_server_banner( + db: &Database, + id: &str, + parent: &str, + uploader_id: &str, + ) -> Result { + db.find_and_use_attachment( + id, + "banners", + FileUsedFor { + id: parent.to_owned(), + object_type: FileUsedForType::ServerBanner, + }, + uploader_id.to_owned(), + ) + .await } /// Use a file for an emoji - pub async fn use_emoji(db: &Database, id: &str, parent: &str) -> Result { - db.find_and_use_attachment(id, "emojis", "object", parent) - .await + pub async fn use_emoji( + db: &Database, + id: &str, + parent: &str, + uploader_id: &str, + ) -> Result { + db.find_and_use_attachment( + id, + "emojis", + FileUsedFor { + id: parent.to_owned(), + object_type: FileUsedForType::Emoji, + }, + uploader_id.to_owned(), + ) + .await } } diff --git a/crates/core/database/src/models/files/ops.rs b/crates/core/database/src/models/files/ops.rs index 55d139fce..58d599d35 100644 --- a/crates/core/database/src/models/files/ops.rs +++ b/crates/core/database/src/models/files/ops.rs @@ -2,6 +2,8 @@ use revolt_result::Result; use crate::File; +use super::FileUsedFor; + mod mongodb; mod reference; @@ -10,13 +12,16 @@ pub trait AbstractAttachments: Sync + Send { /// Insert attachment into database. async fn insert_attachment(&self, attachment: &File) -> Result<()>; + /// Fetch an attachment by its id. + async fn fetch_attachment(&self, tag: &str, file_id: &str) -> Result; + /// Find an attachment by its details and mark it as used by a given parent. async fn find_and_use_attachment( &self, id: &str, tag: &str, - parent_type: &str, - parent_id: &str, + used_for: FileUsedFor, + uploader_id: String, ) -> Result; /// Mark an attachment as having been reported. diff --git a/crates/core/database/src/models/files/ops/mongodb.rs b/crates/core/database/src/models/files/ops/mongodb.rs index f7c5960a6..c2295cf3d 100644 --- a/crates/core/database/src/models/files/ops/mongodb.rs +++ b/crates/core/database/src/models/files/ops/mongodb.rs @@ -1,7 +1,10 @@ +use bson::to_document; use bson::Document; +use revolt_config::report_internal_error; use revolt_result::Result; use crate::File; +use crate::FileUsedFor; use crate::MongoDb; use super::AbstractAttachments; @@ -15,15 +18,28 @@ impl AbstractAttachments for MongoDb { query!(self, insert_one, COL, &attachment).map(|_| ()) } + /// Fetch an attachment by its id. + async fn fetch_attachment(&self, tag: &str, file_id: &str) -> Result { + query!( + self, + find_one, + COL, + doc! { + "_id": file_id, + "tag": tag + } + )? + .ok_or_else(|| create_error!(NotFound)) + } + /// Find an attachment by its details and mark it as used by a given parent. async fn find_and_use_attachment( &self, id: &str, tag: &str, - parent_type: &str, - parent_id: &str, + used_for: FileUsedFor, + uploader_id: String, ) -> Result { - let key = format!("{parent_type}_id"); let file = query!( self, find_one, @@ -31,7 +47,7 @@ impl AbstractAttachments for MongoDb { doc! { "_id": id, "tag": tag, - &key: { + "used_for": { "$exists": false } } @@ -45,7 +61,8 @@ impl AbstractAttachments for MongoDb { }, doc! { "$set": { - key: parent_id + "used_for": report_internal_error!(to_document(&used_for))?, + "uploader_id": uploader_id } }, None, diff --git a/crates/core/database/src/models/files/ops/reference.rs b/crates/core/database/src/models/files/ops/reference.rs index b9b4f7352..e18794272 100644 --- a/crates/core/database/src/models/files/ops/reference.rs +++ b/crates/core/database/src/models/files/ops/reference.rs @@ -1,6 +1,7 @@ use revolt_result::Result; use crate::File; +use crate::FileUsedFor; use crate::ReferenceDb; use super::AbstractAttachments; @@ -18,24 +19,33 @@ impl AbstractAttachments for ReferenceDb { } } + /// Fetch an attachment by its id. + async fn fetch_attachment(&self, tag: &str, file_id: &str) -> Result { + let files = self.files.lock().await; + if let Some(file) = files.get(file_id) { + if file.tag == tag { + Ok(file.clone()) + } else { + Err(create_error!(NotFound)) + } + } else { + Err(create_error!(NotFound)) + } + } + /// Find an attachment by its details and mark it as used by a given parent. async fn find_and_use_attachment( &self, id: &str, tag: &str, - parent_type: &str, - parent_id: &str, + used_for: FileUsedFor, + uploader_id: String, ) -> Result { let mut files = self.files.lock().await; if let Some(file) = files.get_mut(id) { if file.tag == tag { - match parent_type { - "message" => file.message_id = Some(parent_id.to_owned()), - "user" => file.user_id = Some(parent_id.to_owned()), - "object" => file.object_id = Some(parent_id.to_owned()), - "server" => file.server_id = Some(parent_id.to_owned()), - _ => unreachable!(), - } + file.uploader_id = Some(uploader_id); + file.used_for = Some(used_for); Ok(file.clone()) } else { diff --git a/crates/core/database/src/models/messages/model.rs b/crates/core/database/src/models/messages/model.rs index 373cf2224..725d46d1d 100644 --- a/crates/core/database/src/models/messages/model.rs +++ b/crates/core/database/src/models/messages/model.rs @@ -1,11 +1,11 @@ -use std::collections::HashSet; +use std::{collections::HashSet, hash::RandomState}; use indexmap::{IndexMap, IndexSet}; use iso8601_timestamp::Timestamp; -use revolt_config::config; +use revolt_config::{config, FeaturesLimits}; use revolt_models::v0::{ - self, BulkMessageResponse, DataMessageSend, Embed, MessageAuthor, MessageSort, MessageWebhook, - PushNotification, ReplyIntent, SendableEmbed, Text, RE_MENTION, + self, BulkMessageResponse, DataMessageSend, Embed, MessageAuthor, MessageFlags, MessageSort, + MessageWebhook, PushNotification, ReplyIntent, SendableEmbed, Text, RE_MENTION, }; use revolt_permissions::{ChannelPermission, PermissionValue}; use revolt_result::Result; @@ -15,8 +15,8 @@ use validator::Validate; use crate::{ events::client::EventV1, tasks::{self, ack::AckEvent}, - util::idempotency::IdempotencyKey, - Channel, Database, Emoji, File, User, + util::{bulk_permissions::BulkDatabasePermissionQuery, idempotency::IdempotencyKey}, + Channel, Database, Emoji, File, User, AMQP, }; auto_derived_partial!( @@ -65,6 +65,13 @@ auto_derived_partial!( /// Name and / or avatar overrides for this message #[serde(skip_serializing_if = "Option::is_none")] pub masquerade: Option, + /// Whether or not the message in pinned + #[serde(skip_serializing_if = "crate::if_option_false")] + pub pinned: Option, + + /// Bitfield of message flags + #[serde(skip_serializing_if = "Option::is_none")] + pub flags: Option, }, "PartialMessage" ); @@ -95,6 +102,10 @@ auto_derived!( ChannelIconChanged { by: String }, #[serde(rename = "channel_ownership_changed")] ChannelOwnershipChanged { from: String, to: String }, + #[serde(rename = "message_pinned")] + MessagePinned { id: String, by: String }, + #[serde(rename = "message_unpinned")] + MessageUnpinned { id: String, by: String }, } /// Name and / or avatar override information @@ -164,6 +175,8 @@ auto_derived!( pub author: Option, /// Search query pub query: Option, + /// Search for pinned + pub pinned: Option, } /// Message Query @@ -179,6 +192,11 @@ auto_derived!( #[serde(flatten)] pub time_period: MessageTimePeriod, } + + /// Optional fields on message + pub enum FieldsMessage { + Pinned, + } ); #[allow(clippy::derivable_impls)] @@ -200,6 +218,8 @@ impl Default for Message { reactions: Default::default(), interactions: Default::default(), masquerade: None, + flags: None, + pinned: None, } } } @@ -207,11 +227,16 @@ impl Default for Message { #[allow(clippy::disallowed_methods)] impl Message { /// Create message from API data + #[allow(clippy::too_many_arguments)] pub async fn create_from_api( db: &Database, + amqp: Option<&AMQP>, channel: Channel, data: DataMessageSend, author: MessageAuthor<'_>, + user: Option, + member: Option, + limits: FeaturesLimits, mut idempotency: IdempotencyKey, generate_embeds: bool, allow_mentions: bool, @@ -221,7 +246,7 @@ impl Message { Message::validate_sum( &data.content, data.embeds.as_deref().unwrap_or_default(), - config.features.limits.default.message_length, + limits.message_length, )?; idempotency @@ -237,6 +262,13 @@ impl Message { return Err(create_error!(EmptyMessage)); } + // Ensure flags are either not set or have permissible values + if let Some(flags) = &data.flags { + if flags != &0 && flags != &1 { + return Err(create_error!(InvalidProperty)); + } + } + // Ensure restrict_reactions is not specified without reactions list if let Some(interactions) = &data.interactions { if interactions.restrict_reactions { @@ -262,7 +294,7 @@ impl Message { let message_id = Ulid::new().to_string(); let mut message = Message { id: message_id.clone(), - channel: channel.id(), + channel: channel.id().to_string(), masquerade: data.masquerade.map(|masquerade| masquerade.into()), interactions: data .interactions @@ -270,6 +302,7 @@ impl Message { .unwrap_or_default(), author: author_id, webhook: webhook.map(|w| w.into()), + flags: data.flags.map(|v| v as i32), ..Default::default() }; @@ -288,9 +321,9 @@ impl Message { // Verify replies are valid. let mut replies = HashSet::new(); if let Some(entries) = data.replies { - if entries.len() > config.features.limits.default.message_replies { + if entries.len() > config.features.limits.global.message_replies { return Err(create_error!(TooManyReplies { - max: config.features.limits.default.message_replies, + max: config.features.limits.global.message_replies, })); } @@ -305,6 +338,50 @@ impl Message { } } + // Validate the mentions go to users in the channel/server + if !mentions.is_empty() { + match channel { + Channel::DirectMessage { ref recipients, .. } + | Channel::Group { ref recipients, .. } => { + let recipients_hash: HashSet<&String, RandomState> = + HashSet::from_iter(recipients); + mentions.retain(|m| recipients_hash.contains(m)); + } + Channel::TextChannel { ref server, .. } + | Channel::VoiceChannel { ref server, .. } => { + let mentions_vec = Vec::from_iter(mentions.iter().cloned()); + + let valid_members = db.fetch_members(server.as_str(), &mentions_vec[..]).await; + if let Ok(valid_members) = valid_members { + let valid_mentions: HashSet<&String, RandomState> = + HashSet::from_iter(valid_members.iter().map(|m| &m.id.user)); + + mentions.retain(|m| valid_mentions.contains(m)); // quick pass, validate mentions are in the server + + if !mentions.is_empty() { + // if there are still mentions, drill down to a channel-level + let member_channel_view_perms = + BulkDatabasePermissionQuery::from_server_id(db, server) + .await + .channel(&channel) + .members(&valid_members) + .members_can_see_channel() + .await; + + mentions + .retain(|m| *member_channel_view_perms.get(m).unwrap_or(&false)); + } + } else { + revolt_config::capture_error(&valid_members.unwrap_err()); + return Err(create_error!(InternalError)); + } + } + Channel::SavedMessages { .. } => { + mentions.clear(); + } + } + } + if !mentions.is_empty() { message.mentions.replace(mentions.into_iter().collect()); } @@ -320,28 +397,26 @@ impl Message { if data .attachments .as_ref() - .is_some_and(|v| v.len() > config.features.limits.default.message_attachments) + .is_some_and(|v| v.len() > limits.message_attachments) { return Err(create_error!(TooManyAttachments { - max: config.features.limits.default.message_attachments, + max: limits.message_attachments, })); } if data .embeds .as_ref() - .is_some_and(|v| v.len() > config.features.limits.default.message_embeds) + .is_some_and(|v| v.len() > config.features.limits.global.message_embeds) { return Err(create_error!(TooManyEmbeds { - max: config.features.limits.default.message_embeds, + max: config.features.limits.global.message_embeds, })); } for attachment_id in data.attachments.as_deref().unwrap_or_default() { - attachments.push( - db.find_and_use_attachment(attachment_id, "attachments", "message", &message_id) - .await?, - ); + attachments + .push(File::use_attachment(db, attachment_id, &message_id, author.id()).await?); } if !attachments.is_empty() { @@ -360,7 +435,9 @@ impl Message { message.nonce = Some(idempotency.into_key()); // Send the message - message.send(db, author, &channel, generate_embeds).await?; + message + .send(db, amqp, author, user, member, &channel, generate_embeds) + .await?; Ok(message) } @@ -369,13 +446,18 @@ impl Message { pub async fn send_without_notifications( &mut self, db: &Database, + user: Option, + member: Option, is_dm: bool, generate_embeds: bool, + // This determines if this function should queue the mentions task or if somewhere else will. + // If this is true, you MUST call tasks::ack::queue yourself. + mentions_elsewhere: bool, ) -> Result<()> { db.insert_message(self).await?; // Fan out events - EventV1::Message(self.clone().into()) + EventV1::Message(self.clone().into_model(user, member)) .p(self.channel.to_string()) .await; @@ -383,13 +465,12 @@ impl Message { tasks::last_message_id::queue(self.channel.to_string(), self.id.to_string(), is_dm).await; // Add mentions for affected users - if let Some(mentions) = &self.mentions { - for user in mentions { - tasks::ack::queue( + if !mentions_elsewhere { + if let Some(mentions) = &self.mentions { + tasks::ack::queue_message( self.channel.to_string(), - user.to_string(), - AckEvent::AddMention { - ids: vec![self.id.to_string()], + AckEvent::ProcessMessage { + messages: vec![(None, self.clone(), mentions.clone(), true)], }, ) .await; @@ -412,33 +493,56 @@ impl Message { } /// Send a message + #[allow(clippy::too_many_arguments)] pub async fn send( &mut self, db: &Database, + amqp: Option<&AMQP>, // this is optional mostly for tests. author: MessageAuthor<'_>, + user: Option, + member: Option, channel: &Channel, generate_embeds: bool, ) -> Result<()> { self.send_without_notifications( db, + user.clone(), + member.clone(), matches!(channel, Channel::DirectMessage { .. }), generate_embeds, + true, ) .await?; - // Push out Web Push notifications - crate::tasks::web_push::queue( - { - match channel { - Channel::DirectMessage { recipients, .. } - | Channel::Group { recipients, .. } => recipients.clone(), - Channel::TextChannel { .. } => self.mentions.clone().unwrap_or_default(), - _ => vec![], - } - }, - PushNotification::from(self.clone().into(), Some(author), &channel.id()).await, - ) - .await; + if !self.has_suppressed_notifications() { + // send Push notifications + tasks::ack::queue_message( + self.channel.to_string(), + AckEvent::ProcessMessage { + messages: vec![( + Some( + PushNotification::from( + self.clone().into_model(user, member), + Some(author), + channel.to_owned().into(), + ) + .await, + ), + self.clone(), + match channel { + Channel::DirectMessage { recipients, .. } + | Channel::Group { recipients, .. } => recipients.clone(), + Channel::TextChannel { .. } => { + self.mentions.clone().unwrap_or_default() + } + _ => vec![], + }, + self.has_suppressed_notifications(), + )], + }, + ) + .await; + } Ok(()) } @@ -452,10 +556,7 @@ impl Message { })?; let media = if let Some(id) = embed.media { - Some( - db.find_and_use_attachment(&id, "attachments", "message", &self.id) - .await?, - ) + Some(File::use_attachment(db, &id, &self.id, &self.author).await?) } else { None }; @@ -470,15 +571,37 @@ impl Message { })) } + /// Whether this message has suppressed notifications + pub fn has_suppressed_notifications(&self) -> bool { + if let Some(flags) = self.flags { + flags & MessageFlags::SuppressNotifications as i32 + == MessageFlags::SuppressNotifications as i32 + } else { + false + } + } + /// Update message data - pub async fn update(&mut self, db: &Database, partial: PartialMessage) -> Result<()> { + pub async fn update( + &mut self, + db: &Database, + partial: PartialMessage, + remove: Vec, + ) -> Result<()> { self.apply_options(partial.clone()); - db.update_message(&self.id, &partial).await?; + + for field in &remove { + self.remove_field(field); + } + + db.update_message(&self.id, &partial, remove.clone()) + .await?; EventV1::MessageUpdate { id: self.id.clone(), channel: self.channel.clone(), data: partial.into(), + clear: remove.into_iter().map(|field| field.into()).collect(), } .p(self.channel.clone()) .await; @@ -498,13 +621,47 @@ impl Message { .fetch_messages(query) .await? .into_iter() - .map(Into::into) + .map(|msg| msg.into_model(None, None)) .collect(); if let Some(true) = include_users { let user_ids = messages .iter() - .map(|m| m.author.clone()) + .flat_map(|m| { + let mut users = vec![m.author.clone()]; + if let Some(system) = &m.system { + match system { + v0::SystemMessage::ChannelDescriptionChanged { by } => { + users.push(by.clone()) + } + v0::SystemMessage::ChannelIconChanged { by } => users.push(by.clone()), + v0::SystemMessage::ChannelOwnershipChanged { from, to, .. } => { + users.push(from.clone()); + users.push(to.clone()) + } + v0::SystemMessage::ChannelRenamed { by, .. } => users.push(by.clone()), + v0::SystemMessage::UserAdded { by, id, .. } + | v0::SystemMessage::UserRemove { by, id, .. } => { + users.push(by.clone()); + users.push(id.clone()); + } + v0::SystemMessage::UserBanned { id, .. } + | v0::SystemMessage::UserKicked { id, .. } + | v0::SystemMessage::UserJoined { id, .. } + | v0::SystemMessage::UserLeft { id, .. } => { + users.push(id.clone()); + } + v0::SystemMessage::Text { .. } => {} + v0::SystemMessage::MessagePinned { by, .. } => { + users.push(by.clone()); + } + v0::SystemMessage::MessageUnpinned { by, .. } => { + users.push(by.clone()); + } + } + } + users + }) .collect::>() .into_iter() .collect::>(); @@ -558,7 +715,7 @@ impl Message { ) -> Result<()> { let media: Option = if let Some(id) = embed.media { Some( - db.find_and_use_attachment(&id, "attachments", "message", &self.id) + File::use_attachment(db, &id, &self.id, &self.author) .await? .into(), ) @@ -588,7 +745,7 @@ impl Message { pub async fn add_reaction(&self, db: &Database, user: &User, emoji: &str) -> Result<()> { // Check how many reactions are already on the message let config = config().await; - if self.reactions.len() >= config.features.limits.default.message_reactions + if self.reactions.len() >= config.features.limits.global.message_reactions && !self.reactions.contains_key(emoji) { return Err(create_error!(InvalidOperation)); @@ -730,6 +887,12 @@ impl Message { // Write to database db.clear_reaction(&self.id, emoji).await } + + pub fn remove_field(&mut self, field: &FieldsMessage) { + match field { + FieldsMessage::Pinned => self.pinned = None, + } + } } impl SystemMessage { @@ -753,7 +916,7 @@ impl Interactions { if let Some(reactions) = &self.reactions { permissions.throw_if_lacking_channel_permission(ChannelPermission::React)?; - if reactions.len() > config.features.limits.default.message_reactions { + if reactions.len() > config.features.limits.global.message_reactions { return Err(create_error!(InvalidOperation)); } diff --git a/crates/core/database/src/models/messages/ops.rs b/crates/core/database/src/models/messages/ops.rs index c89caf9b4..b10022b4b 100644 --- a/crates/core/database/src/models/messages/ops.rs +++ b/crates/core/database/src/models/messages/ops.rs @@ -1,6 +1,6 @@ use revolt_result::Result; -use crate::{AppendMessage, Message, MessageQuery, PartialMessage}; +use crate::{AppendMessage, FieldsMessage, Message, MessageQuery, PartialMessage}; mod mongodb; mod reference; @@ -20,7 +20,7 @@ pub trait AbstractMessages: Sync + Send { async fn fetch_messages_by_id(&self, ids: &[String]) -> Result>; /// Update a given message with new information - async fn update_message(&self, id: &str, message: &PartialMessage) -> Result<()>; + async fn update_message(&self, id: &str, message: &PartialMessage, remove: Vec) -> Result<()>; /// Append information to a given message async fn append_message(&self, id: &str, append: &AppendMessage) -> Result<()>; diff --git a/crates/core/database/src/models/messages/ops/mongodb.rs b/crates/core/database/src/models/messages/ops/mongodb.rs index 530fd2f62..18eeb3796 100644 --- a/crates/core/database/src/models/messages/ops/mongodb.rs +++ b/crates/core/database/src/models/messages/ops/mongodb.rs @@ -5,7 +5,8 @@ use revolt_models::v0::MessageSort; use revolt_result::Result; use crate::{ - AppendMessage, DocumentId, Message, MessageQuery, MessageTimePeriod, MongoDb, PartialMessage, + AppendMessage, DocumentId, FieldsMessage, IntoDocumentPath, Message, MessageQuery, + MessageTimePeriod, MongoDb, PartialMessage, }; use super::AbstractMessages; @@ -50,6 +51,10 @@ impl AbstractMessages for MongoDb { false }; + if let Some(pinned) = query.filter.pinned { + filter.insert("pinned", pinned); + }; + // 2. Find query limit let limit = query.limit.unwrap_or(50); @@ -166,7 +171,7 @@ impl AbstractMessages for MongoDb { self.find_with_options( COL, doc! { - "ids": { + "_id": { "$in": ids } }, @@ -177,8 +182,22 @@ impl AbstractMessages for MongoDb { } /// Update a given message with new information - async fn update_message(&self, id: &str, message: &PartialMessage) -> Result<()> { - query!(self, update_one_by_id, COL, id, message, vec![], None).map(|_| ()) + async fn update_message( + &self, + id: &str, + message: &PartialMessage, + remove: Vec, + ) -> Result<()> { + query!( + self, + update_one_by_id, + COL, + id, + message, + remove.iter().map(|x| x as &dyn IntoDocumentPath).collect(), + None + ) + .map(|_| ()) } /// Append information to a given message @@ -296,6 +315,14 @@ impl AbstractMessages for MongoDb { } } +impl IntoDocumentPath for FieldsMessage { + fn as_path(&self) -> Option<&'static str> { + Some(match self { + FieldsMessage::Pinned => "pinned", + }) + } +} + impl MongoDb { pub async fn delete_bulk_messages(&self, projection: Document) -> Result<()> { let mut for_attachments = projection.clone(); diff --git a/crates/core/database/src/models/messages/ops/reference.rs b/crates/core/database/src/models/messages/ops/reference.rs index 80e024ee4..ae5b43f03 100644 --- a/crates/core/database/src/models/messages/ops/reference.rs +++ b/crates/core/database/src/models/messages/ops/reference.rs @@ -2,7 +2,7 @@ use futures::future::try_join_all; use indexmap::IndexSet; use revolt_result::Result; -use crate::{AppendMessage, Message, MessageQuery, PartialMessage, ReferenceDb}; +use crate::{AppendMessage, FieldsMessage, Message, MessageQuery, PartialMessage, ReferenceDb}; use super::AbstractMessages; @@ -56,6 +56,12 @@ impl AbstractMessages for ReferenceDb { } } + if let Some(pinned) = query.filter.pinned { + if message.pinned.unwrap_or_default() == pinned { + return false + } + } + true }) .cloned() @@ -183,10 +189,15 @@ impl AbstractMessages for ReferenceDb { } /// Update a given message with new information - async fn update_message(&self, id: &str, message: &PartialMessage) -> Result<()> { + async fn update_message(&self, id: &str, message: &PartialMessage, remove: Vec) -> Result<()> { let mut messages = self.messages.lock().await; if let Some(message_data) = messages.get_mut(id) { message_data.apply_options(message.to_owned()); + + for field in remove { + #[allow(clippy::disallowed_methods)] + message_data.remove_field(&field); + } Ok(()) } else { Err(create_error!(NotFound)) diff --git a/crates/core/database/src/models/mod.rs b/crates/core/database/src/models/mod.rs index 1f540fa15..671c7abbb 100644 --- a/crates/core/database/src/models/mod.rs +++ b/crates/core/database/src/models/mod.rs @@ -5,6 +5,7 @@ mod channel_unreads; mod channel_webhooks; mod channels; mod emojis; +mod file_hashes; mod files; mod messages; mod ratelimit_events; @@ -23,6 +24,7 @@ pub use channel_unreads::*; pub use channel_webhooks::*; pub use channels::*; pub use emojis::*; +pub use file_hashes::*; pub use files::*; pub use messages::*; pub use ratelimit_events::*; @@ -46,6 +48,7 @@ pub trait AbstractDatabase: + channel_unreads::AbstractChannelUnreads + channel_webhooks::AbstractWebhooks + emojis::AbstractEmojis + + file_hashes::AbstractAttachmentHashes + files::AbstractAttachments + messages::AbstractMessages + ratelimit_events::AbstractRatelimitEvents diff --git a/crates/core/database/src/models/server_members/model.rs b/crates/core/database/src/models/server_members/model.rs index fb9c4417c..0728c30a8 100644 --- a/crates/core/database/src/models/server_members/model.rs +++ b/crates/core/database/src/models/server_members/model.rs @@ -92,7 +92,7 @@ impl Member { server: &Server, user: &User, channels: Option>, - ) -> Result> { + ) -> Result<(Member, Vec)> { if db.fetch_ban(&server.id, &user.id).await.is_ok() { return Err(create_error!(Banned)); } @@ -161,12 +161,12 @@ impl Member { id: user.id.clone(), } .into_message(id.to_string()) - .send_without_notifications(db, false, false) + .send_without_notifications(db, None, None, false, false, false) .await .ok(); } - Ok(channels) + Ok((member, channels)) } /// Update member data @@ -202,7 +202,7 @@ impl Member { FieldsMember::Roles => self.roles.clear(), FieldsMember::Timeout => self.timeout = None, FieldsMember::CanReceive => self.can_receive = None, - FieldsMember::CanPublish => self.can_publish = None + FieldsMember::CanPublish => self.can_publish = None, } } @@ -242,6 +242,7 @@ impl Member { EventV1::ServerMemberLeave { id: self.id.server.to_string(), user: self.id.user.to_string(), + reason: intention.clone().into(), } .p(self.id.server.to_string()) .await; @@ -263,7 +264,7 @@ impl Member { } .into_message(id.to_string()) // TODO: support notifications here in the future? - .send_without_notifications(db, false, false) + .send_without_notifications(db, None, None, false, false, false) .await .ok(); } diff --git a/crates/core/database/src/models/server_members/ops/mongodb.rs b/crates/core/database/src/models/server_members/ops/mongodb.rs index 1c5116906..a9de60a0a 100644 --- a/crates/core/database/src/models/server_members/ops/mongodb.rs +++ b/crates/core/database/src/models/server_members/ops/mongodb.rs @@ -173,8 +173,8 @@ impl IntoDocumentPath for FieldsMember { FieldsMember::Nickname => "nickname", FieldsMember::Roles => "roles", FieldsMember::Timeout => "timeout", - FieldsMember::CanPublish => "can_publish", - FieldsMember::CanReceive => "can_receive" + FieldsMember::CanPublish => "is_publishing", + FieldsMember::CanReceive => "is_receiving", }) } } diff --git a/crates/core/database/src/models/server_members/ops/reference.rs b/crates/core/database/src/models/server_members/ops/reference.rs index 88c9c7b25..f038477ec 100644 --- a/crates/core/database/src/models/server_members/ops/reference.rs +++ b/crates/core/database/src/models/server_members/ops/reference.rs @@ -53,17 +53,17 @@ impl AbstractServerMembers for ReferenceDb { /// Fetch multiple members by their ids async fn fetch_members<'a>(&self, server_id: &str, ids: &'a [String]) -> Result> { let server_members = self.server_members.lock().await; - ids.iter() - .map(|id| { + Ok(ids + .iter() + .filter_map(|id| { server_members .get(&MemberCompositeKey { server: server_id.to_string(), user: id.to_string(), }) .cloned() - .ok_or_else(|| create_error!(NotFound)) }) - .collect() + .collect()) } /// Fetch member count of a server diff --git a/crates/core/database/src/models/servers/model.rs b/crates/core/database/src/models/servers/model.rs index 75a022cd5..67c4daedb 100644 --- a/crates/core/database/src/models/servers/model.rs +++ b/crates/core/database/src/models/servers/model.rs @@ -175,7 +175,7 @@ impl Server { vec![] }; - server.channels = channels.iter().map(|c| c.id()).collect(); + server.channels = channels.iter().map(|c| c.id().to_string()).collect(); db.insert_server(&server).await?; Ok((server, channels)) } diff --git a/crates/core/database/src/models/servers/ops/mongodb.rs b/crates/core/database/src/models/servers/ops/mongodb.rs index 24bb65dae..e6bee03f2 100644 --- a/crates/core/database/src/models/servers/ops/mongodb.rs +++ b/crates/core/database/src/models/servers/ops/mongodb.rs @@ -214,14 +214,21 @@ impl MongoDb { // Delete all emoji. self.col::("emojis") - .delete_many( + .update_many( doc! { "parent.id": &server_id }, + doc! { + "$set": { + "parent": { + "type": "Detached" + } + } + }, None, ) .await - .map_err(|_| create_database_error!("delete_many", "emojis"))?; + .map_err(|_| create_database_error!("update_many", "emojis"))?; // Delete all channels. self.col::("channels") @@ -253,7 +260,7 @@ impl MongoDb { // Update many attachments with parent id. self.delete_many_attachments(doc! { - "object_id": &server_id + "used_for.id": &server_id }) .await?; diff --git a/crates/core/database/src/models/users/axum.rs b/crates/core/database/src/models/users/axum.rs new file mode 100644 index 000000000..da83c5b13 --- /dev/null +++ b/crates/core/database/src/models/users/axum.rs @@ -0,0 +1,24 @@ +use axum::{extract::FromRequestParts, http::request::Parts}; + +use revolt_result::{create_error, Error, Result}; + +use crate::{Database, User}; + +#[async_trait::async_trait] +impl FromRequestParts for User { + type Rejection = Error; + + async fn from_request_parts(parts: &mut Parts, db: &Database) -> Result { + if let Some(Ok(bot_token)) = parts.headers.get("x-bot-token").map(|v| v.to_str()) { + let bot = db.fetch_bot_by_token(bot_token).await?; + db.fetch_user(&bot.id).await + } else if let Some(Ok(session_token)) = + parts.headers.get("x-session-token").map(|v| v.to_str()) + { + let session = db.fetch_session_by_token(session_token).await?; + db.fetch_user(&session.user_id).await + } else { + Err(create_error!(NotAuthenticated)) + } + } +} diff --git a/crates/core/database/src/models/users/mod.rs b/crates/core/database/src/models/users/mod.rs index 84935c6f8..748a87c0d 100644 --- a/crates/core/database/src/models/users/mod.rs +++ b/crates/core/database/src/models/users/mod.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "axum-impl")] +mod axum; mod model; mod ops; #[cfg(feature = "rocket-impl")] @@ -5,6 +7,8 @@ mod rocket; #[cfg(feature = "rocket-impl")] mod schema; +#[cfg(feature = "axum-impl")] +pub use self::axum::*; #[cfg(feature = "rocket-impl")] pub use self::rocket::*; #[cfg(feature = "rocket-impl")] diff --git a/crates/core/database/src/models/users/model.rs b/crates/core/database/src/models/users/model.rs index 7d52ed38a..d85801e75 100644 --- a/crates/core/database/src/models/users/model.rs +++ b/crates/core/database/src/models/users/model.rs @@ -1,14 +1,17 @@ -use std::{collections::HashSet, time::Duration}; +use std::{collections::HashSet, str::FromStr, time::Duration}; -use crate::{events::client::EventV1, Database, File, RatelimitEvent}; +use crate::{events::client::EventV1, Database, File, RatelimitEvent, AMQP}; +use authifier::config::{EmailVerificationConfig, Template}; +use iso8601_timestamp::Timestamp; use once_cell::sync::Lazy; use rand::seq::SliceRandom; use redis_kiss::{get_connection, AsyncCommands}; -use revolt_config::config; -use revolt_models::v0; +use revolt_config::{config, FeaturesLimits}; +use revolt_models::v0::{self, UserFlags}; use revolt_presence::filter_online; use revolt_result::{create_error, Result}; +use serde_json::json; use ulid::Ulid; auto_derived_partial!( @@ -50,6 +53,10 @@ auto_derived_partial!( /// Bot information #[serde(skip_serializing_if = "Option::is_none")] pub bot: Option, + + /// Time until user is unsuspended + #[serde(skip_serializing_if = "Option::is_none")] + pub suspended_until: Option, }, "PartialUser" ); @@ -62,6 +69,11 @@ auto_derived!( StatusPresence, ProfileContent, ProfileBackground, + DisplayName, + + // internal fields + Suspension, + None, } /// User's relationship with another user (or themselves) @@ -165,6 +177,7 @@ impl Default for User { flags: Default::default(), privileged: Default::default(), bot: Default::default(), + suspended_until: Default::default(), } } } @@ -198,6 +211,22 @@ impl User { Ok(user) } + /// Get limits for this user + pub async fn limits(&self) -> FeaturesLimits { + let config = config().await; + if ulid::Ulid::from_str(&self.id) + .expect("`ulid`") + .datetime() + .elapsed() + .expect("time went backwards") + <= Duration::from_secs(3600u64 * config.features.limits.global.new_user_hours as u64) + { + config.features.limits.new_user + } else { + config.features.limits.default + } + } + /// Get the relationship with another user pub fn relationship_with(&self, user_b: &str) -> RelationshipStatus { if self.id == user_b { @@ -236,12 +265,11 @@ impl User { /// Check if this user can acquire another server pub async fn can_acquire_server(&self, db: &Database) -> Result<()> { - let config = config().await; - if db.fetch_server_count(&self.id).await? <= config.features.limits.default.servers { + if db.fetch_server_count(&self.id).await? <= self.limits().await.servers { Ok(()) } else { Err(create_error!(TooManyServers { - max: config.features.limits.default.servers + max: self.limits().await.servers })) } } @@ -277,23 +305,27 @@ impl User { Ok(username) } - /// Find a user from a given token and hint + /// Find a user and session ID from a given token and hint #[async_recursion] - pub async fn from_token(db: &Database, token: &str, hint: UserHint) -> Result { + pub async fn from_token(db: &Database, token: &str, hint: UserHint) -> Result<(User, String)> { match hint { - UserHint::Bot => { + UserHint::Bot => Ok(( db.fetch_user( &db.fetch_bot_by_token(token) .await .map_err(|_| create_error!(InvalidSession))? .id, ) - .await + .await?, + String::new(), + )), + UserHint::User => { + let session = db.fetch_session_by_token(token).await?; + Ok((db.fetch_user(&session.user_id).await?, session.id)) } - UserHint::User => db.fetch_user_by_token(token).await, UserHint::Any => { - if let Ok(user) = User::from_token(db, token, UserHint::User).await { - Ok(user) + if let Ok(result) = User::from_token(db, token, UserHint::User).await { + Ok(result) } else { User::from_token(db, token, UserHint::Bot).await } @@ -466,7 +498,12 @@ impl User { } /// Add another user as a friend - pub async fn add_friend(&mut self, db: &Database, target: &mut User) -> Result<()> { + pub async fn add_friend( + &mut self, + db: &Database, + amqp: &AMQP, + target: &mut User, + ) -> Result<()> { match self.relationship_with(&target.id) { RelationshipStatus::User => Err(create_error!(NoEffect)), RelationshipStatus::Friend => Err(create_error!(AlreadyFriends)), @@ -474,6 +511,9 @@ impl User { RelationshipStatus::Blocked => Err(create_error!(Blocked)), RelationshipStatus::BlockedOther => Err(create_error!(BlockedByOther)), RelationshipStatus::Incoming => { + // Accept incoming friend request + _ = amqp.friend_request_accepted(self, target).await; + self.apply_relationship( db, target, @@ -483,6 +523,28 @@ impl User { .await } RelationshipStatus::None => { + // Get this user's current count of outgoing friend requests + let count = self + .relations + .as_ref() + .map(|relations| { + relations + .iter() + .filter(|r| matches!(r.status, RelationshipStatus::Outgoing)) + .count() + }) + .unwrap_or_default(); + + // If we're over the limit, don't allow creating more requests + if count >= self.limits().await.outgoing_friend_requests { + return Err(create_error!(TooManyPendingFriendRequests { + max: self.limits().await.outgoing_friend_requests + })); + } + + _ = amqp.friend_request_received(target, self).await; + + // Send the friend request self.apply_relationship( db, target, @@ -631,9 +693,107 @@ impl User { x.background = None; } } + FieldsUser::DisplayName => self.display_name = None, + FieldsUser::Suspension => self.suspended_until = None, + FieldsUser::None => {} } } + /// Suspend the user + /// + /// - If a duration is specified, the user will be automatically unsuspended after the given time. + /// - If a reason is specified, an email will be sent. + pub async fn suspend( + &mut self, + db: &Database, + duration_days: Option, + reason: Option>, + ) -> Result<()> { + let authifier = db.clone().to_authifier().await; + let mut account = authifier + .database + .find_account(&self.id) + .await + .map_err(|_| create_error!(InternalError))?; + + account + .disable(&authifier) + .await + .map_err(|_| create_error!(InternalError))?; + + account + .delete_all_sessions(&authifier, None) + .await + .map_err(|_| create_error!(InternalError))?; + + self.update( + db, + PartialUser { + flags: Some(UserFlags::SuspendedUntil as i32), + suspended_until: duration_days.and_then(|dur| { + Timestamp::now_utc().checked_add(iso8601_timestamp::Duration::days(dur as i64)) + }), + ..Default::default() + }, + vec![], + ) + .await?; + + if let Some(reason) = reason { + if let EmailVerificationConfig::Enabled { smtp, .. } = + authifier.config.email_verification + { + smtp.send_email( + account.email.clone(), + // maybe move this to common area? + &Template { + title: "Account Suspension".to_string(), + html: Some(include_str!("../../../templates/suspension.html").to_owned()), + text: include_str!("../../../templates/suspension.txt").to_owned(), + url: Default::default(), + }, + json!({ + "email": account.email, + "list": reason.join(", "), + "duration": duration_days, + "duration_display": if duration_days.is_some() { + "block" + } else { + "none" + } + }), + ) + .map_err(|_| create_error!(InternalError))?; + } + } + + Ok(()) + } + + /// Unsuspend the user + pub async fn unsuspend(&mut self, db: &Database) -> Result<()> { + self.update( + db, + PartialUser { + flags: Some(0), + suspended_until: None, + ..Default::default() + }, + vec![], + ) + .await?; + + unimplemented!() + } + + /// Permanently ban the user + /// + /// - If a reason is specified, an email will be sent. + pub async fn ban(&mut self, _db: &Database, _reason: Option) -> Result<()> { + // Send ban email (if reason provided) + unimplemented!() + } + /// Mark as deleted pub async fn mark_deleted(&mut self, db: &Database) -> Result<()> { self.update( @@ -649,6 +809,7 @@ impl User { FieldsUser::StatusPresence, FieldsUser::ProfileContent, FieldsUser::ProfileBackground, + FieldsUser::Suspension, ], ) .await diff --git a/crates/core/database/src/models/users/ops.rs b/crates/core/database/src/models/users/ops.rs index 58247d26d..fe6bb2ff9 100644 --- a/crates/core/database/src/models/users/ops.rs +++ b/crates/core/database/src/models/users/ops.rs @@ -1,3 +1,4 @@ +use authifier::models::Session; use revolt_result::Result; use crate::{FieldsUser, PartialUser, RelationshipStatus, User}; @@ -16,8 +17,8 @@ pub trait AbstractUsers: Sync + Send { /// Fetch a user from the database by their username async fn fetch_user_by_username(&self, username: &str, discriminator: &str) -> Result; - /// Fetch a user from the database by their session token - async fn fetch_user_by_token(&self, token: &str) -> Result; + /// Fetch a session from the database by token + async fn fetch_session_by_token(&self, token: &str) -> Result; /// Fetch multiple users by their ids async fn fetch_users<'a>(&self, ids: &'a [String]) -> Result>; @@ -57,4 +58,7 @@ pub trait AbstractUsers: Sync + Send { /// Delete a user by their id async fn delete_user(&self, id: &str) -> Result<()>; + + /// Remove push subscription for a session by session id (TODO: remove) + async fn remove_push_subscription_by_session_id(&self, session_id: &str) -> Result<()>; } diff --git a/crates/core/database/src/models/users/ops/mongodb.rs b/crates/core/database/src/models/users/ops/mongodb.rs index 44729959a..ae61b0297 100644 --- a/crates/core/database/src/models/users/ops/mongodb.rs +++ b/crates/core/database/src/models/users/ops/mongodb.rs @@ -46,10 +46,9 @@ impl AbstractUsers for MongoDb { .ok_or_else(|| create_error!(NotFound)) } - /// Fetch a user from the database by their session token - async fn fetch_user_by_token(&self, token: &str) -> Result { - let session = self - .col::("sessions") + /// Fetch a session from the database by token + async fn fetch_session_by_token(&self, token: &str) -> Result { + self.col::("sessions") .find_one( doc! { "token": token @@ -58,9 +57,7 @@ impl AbstractUsers for MongoDb { ) .await .map_err(|_| create_database_error!("find_one", "sessions"))? - .ok_or_else(|| create_error!(InvalidSession))?; - - self.fetch_user(&session.user_id).await + .ok_or_else(|| create_error!(InvalidSession)) } /// Fetch multiple users by their ids @@ -319,6 +316,25 @@ impl AbstractUsers for MongoDb { async fn delete_user(&self, id: &str) -> Result<()> { query!(self, delete_one_by_id, COL, id).map(|_| ()) } + + /// Remove push subscription for a session by session id (TODO: remove) + async fn remove_push_subscription_by_session_id(&self, session_id: &str) -> Result<()> { + self.col::("sessions") + .update_one( + doc! { + "_id": session_id + }, + doc! { + "$unset": { + "subscription": 1 + } + }, + None, + ) + .await + .map(|_| ()) + .map_err(|_| create_database_error!("update_one", COL)) + } } impl IntoDocumentPath for FieldsUser { @@ -329,6 +345,9 @@ impl IntoDocumentPath for FieldsUser { FieldsUser::ProfileContent => "profile.content", FieldsUser::StatusPresence => "status.presence", FieldsUser::StatusText => "status.text", + FieldsUser::DisplayName => "display_name", + FieldsUser::Suspension => "suspended_until", + FieldsUser::None => "none", }) } } diff --git a/crates/core/database/src/models/users/ops/reference.rs b/crates/core/database/src/models/users/ops/reference.rs index 4c2d4e7d0..503490804 100644 --- a/crates/core/database/src/models/users/ops/reference.rs +++ b/crates/core/database/src/models/users/ops/reference.rs @@ -1,3 +1,4 @@ +use authifier::models::Session; use revolt_result::Result; use crate::{FieldsUser, PartialUser, RelationshipStatus, User}; @@ -40,8 +41,8 @@ impl AbstractUsers for ReferenceDb { .ok_or_else(|| create_error!(NotFound)) } - /// Fetch a user from the database by their session token - async fn fetch_user_by_token(&self, _token: &str) -> Result { + /// Fetch a session from the database by token + async fn fetch_session_by_token(&self, _token: &str) -> Result { todo!() } @@ -162,4 +163,9 @@ impl AbstractUsers for ReferenceDb { Err(create_error!(NotFound)) } } + + /// Remove push subscription for a session by session id (TODO: remove) + async fn remove_push_subscription_by_session_id(&self, _session_id: &str) -> Result<()> { + todo!() + } } diff --git a/crates/core/database/src/models/users/rocket.rs b/crates/core/database/src/models/users/rocket.rs index ab8866947..57b634b15 100644 --- a/crates/core/database/src/models/users/rocket.rs +++ b/crates/core/database/src/models/users/rocket.rs @@ -38,7 +38,7 @@ impl<'r> FromRequest<'r> for User { if let Some(user) = user { Outcome::Success(user.clone()) } else { - Outcome::Failure((Status::Unauthorized, authifier::Error::InvalidSession)) + Outcome::Error((Status::Unauthorized, authifier::Error::InvalidSession)) } } } diff --git a/crates/core/database/src/tasks/ack.rs b/crates/core/database/src/tasks/ack.rs index c055fb4a7..89971f661 100644 --- a/crates/core/database/src/tasks/ack.rs +++ b/crates/core/database/src/tasks/ack.rs @@ -1,19 +1,27 @@ // Queue Type: Debounced -use crate::Database; +use crate::{Database, Message, AMQP}; use deadqueue::limited::Queue; use once_cell::sync::Lazy; -use std::{collections::HashMap, time::Duration}; +use revolt_models::v0::PushNotification; +use rocket::form::validate::Contains; +use std::{ + collections::{HashMap, HashSet}, + time::Duration, +}; +use validator::HasLen; + +use revolt_result::Result; use super::DelayedTask; /// Enumeration of possible events #[derive(Debug, Eq, PartialEq)] pub enum AckEvent { - /// Add mentions for a user in a channel - AddMention { - /// Message IDs - ids: Vec, + /// Add mentions for a channel + ProcessMessage { + /// push notification, message, recipients, push silenced + messages: Vec<(Option, Message, Vec, bool)>, }, /// Acknowledge message in a channel for a user @@ -28,7 +36,7 @@ struct Data { /// Channel to ack channel: String, /// User to ack for - user: String, + user: Option, /// Event event: AckEvent, } @@ -41,21 +49,130 @@ struct Task { static Q: Lazy> = Lazy::new(|| Queue::new(10_000)); /// Queue a new task for a worker -pub async fn queue(channel: String, user: String, event: AckEvent) { +pub async fn queue_ack(channel: String, user: String, event: AckEvent) { Q.try_push(Data { channel, - user, + user: Some(user), event, }) .ok(); - info!("Queue is using {} slots from {}.", Q.len(), Q.capacity()); + info!( + "Queue is using {} slots from {}. Queued type: ACK", + Q.len(), + Q.capacity() + ); +} + +pub async fn queue_message(channel: String, event: AckEvent) { + Q.try_push(Data { + channel, + user: None, + event, + }) + .ok(); + + info!( + "Queue is using {} slots from {}. Queued type: MENTION", + Q.len(), + Q.capacity() + ); +} + +pub async fn handle_ack_event( + event: &AckEvent, + db: &Database, + amqp: &AMQP, + user: &Option, + channel: &str, +) -> Result<()> { + match &event { + #[allow(clippy::disallowed_methods)] // event is sent by higher level function + AckEvent::AckMessage { id } => { + let user = user.as_ref().unwrap(); + let user: &str = user.as_str(); + + let unread = db.fetch_unread(user, channel).await?; + let updated = db.acknowledge_message(channel, user, id).await?; + + if let (Some(before), Some(after)) = (unread, updated) { + let before_mentions = before.mentions.unwrap_or_default().len(); + let after_mentions = after.mentions.unwrap_or_default().len(); + + let mentions_acked = before_mentions - after_mentions; + + if mentions_acked > 0 { + if let Err(err) = amqp + .ack_message(user.to_string(), channel.to_string(), id.to_owned()) + .await + { + revolt_config::capture_error(&err); + } + }; + } + } + AckEvent::ProcessMessage { messages } => { + let mut users: HashSet<&String> = HashSet::new(); + debug!( + "Processing {} messages from channel {}", + messages.len(), + messages[0].1.channel + ); + + // find all the users we'll be notifying + messages + .iter() + .for_each(|(_, _, recipents, _)| users.extend(recipents.iter())); + + debug!("Found {} users to notify.", users.len()); + + for user in users { + let message_ids: Vec = messages + .iter() + .filter(|(_, _, recipients, _)| recipients.contains(user)) + .map(|(_, message, _, _)| message.id.clone()) + .collect(); + + if !message_ids.is_empty() { + db.add_mention_to_unread(channel, user, &message_ids) + .await?; + } + debug!("Added {} mentions for user {}", message_ids.len(), &user); + } + + for (push, _, recipients, silenced) in messages { + if *silenced || recipients.is_empty() || push.is_none() { + debug!( + "Rejecting push: silenced: {}, recipient count: {}, push exists: {:?}", + *silenced, + recipients.length(), + push + ); + continue; + } + + debug!( + "Sending push event to AMQP; message {} for {} users", + push.as_ref().unwrap().message.id, + recipients.len() + ); + if let Err(err) = amqp + .message_sent(recipients.clone(), push.clone().unwrap()) + .await + { + revolt_config::capture_error(&err); + } + } + } + }; + + Ok(()) } /// Start a new worker -pub async fn worker(db: Database) { - let mut tasks = HashMap::<(String, String), DelayedTask>::new(); - let mut keys = vec![]; +pub async fn worker(db: Database, amqp: AMQP) { + let mut tasks = HashMap::<(Option, String, u8), DelayedTask>::new(); + let mut keys: Vec<(Option, String, u8)> = vec![]; loop { // Find due tasks. @@ -69,18 +186,13 @@ pub async fn worker(db: Database) { for key in &keys { if let Some(task) = tasks.remove(key) { let Task { event } = task.data; - let (user, channel) = key; + let (user, channel, _) = key; - if let Err(err) = match &event { - #[allow(clippy::disallowed_methods)] // event is sent by higher level function - AckEvent::AckMessage { id } => db.acknowledge_message(channel, user, id).await, - AckEvent::AddMention { ids } => { - db.add_mention_to_unread(channel, user, ids).await - } - } { - error!("{err:?} for {event:?}. ({user}, {channel})"); + if let Err(err) = handle_ack_event(&event, &db, &amqp, user, channel).await { + revolt_config::capture_error(&err); + error!("{err:?} for {event:?}. ({user:?}, {channel})"); } else { - info!("User {user} ack in {channel} with {event:?}"); + info!("User {user:?} ack in {channel} with {event:?}"); } } } @@ -95,20 +207,41 @@ pub async fn worker(db: Database) { mut event, }) = Q.try_pop() { - let key = (user, channel); + let key: (Option, String, u8) = ( + user, + channel, + match &event { + AckEvent::AckMessage { .. } => 0, + AckEvent::ProcessMessage { .. } => 1, + }, + ); if let Some(task) = tasks.get_mut(&key) { - task.delay(); - match &mut event { - AckEvent::AddMention { ids } => { - if let AckEvent::AddMention { ids: existing } = &mut task.data.event { - existing.append(ids); + AckEvent::ProcessMessage { messages: new_data } => { + if let AckEvent::ProcessMessage { messages: existing } = + &mut task.data.event + { + // add the new message to the list of messages to be processed. + existing.append(new_data); + + // put a cap on the amount of messages that can be queued, for particularly active channels + if (existing.length() as u16) + < revolt_config::config() + .await + .features + .advanced + .process_message_delay_limit + { + task.delay(); + } } else { - task.data.event = event; + panic!("Somehow got an ack message in the add mention arm"); } } AckEvent::AckMessage { .. } => { + // replace the last acked message with the new acked message task.data.event = event; + task.delay(); } } } else { diff --git a/crates/core/database/src/tasks/authifier_relay.rs b/crates/core/database/src/tasks/authifier_relay.rs new file mode 100644 index 000000000..9dac7020e --- /dev/null +++ b/crates/core/database/src/tasks/authifier_relay.rs @@ -0,0 +1,29 @@ +use async_std::channel::{unbounded, Receiver, Sender}; +use authifier::AuthifierEvent; +use once_cell::sync::Lazy; + +use crate::events::client::EventV1; + +static Q: Lazy<(Sender, Receiver)> = Lazy::new(|| unbounded()); + +/// Get sender +pub fn sender() -> Sender { + Q.0.clone() +} + +/// Start a new worker +pub async fn worker() { + loop { + let event = Q.1.recv().await.unwrap(); + match &event { + AuthifierEvent::CreateSession { .. } | AuthifierEvent::CreateAccount { .. } => { + EventV1::Auth(event).global().await + } + AuthifierEvent::DeleteSession { user_id, .. } + | AuthifierEvent::DeleteAllSessions { user_id, .. } => { + let id = user_id.to_string(); + EventV1::Auth(event).private(id).await + } + } + } +} diff --git a/crates/core/database/src/tasks/mod.rs b/crates/core/database/src/tasks/mod.rs index 12a5d122a..81bfd4e1f 100644 --- a/crates/core/database/src/tasks/mod.rs +++ b/crates/core/database/src/tasks/mod.rs @@ -1,6 +1,6 @@ //! Semi-important background task management -use crate::Database; +use crate::{Database, AMQP}; use async_std::task; use std::time::Instant; @@ -8,17 +8,18 @@ use std::time::Instant; const WORKER_COUNT: usize = 5; pub mod ack; +pub mod authifier_relay; pub mod last_message_id; pub mod process_embeds; -pub mod web_push; /// Spawn background workers -pub async fn start_workers(db: Database, authifier_db: authifier::Database) { +pub fn start_workers(db: Database, amqp: AMQP) { + task::spawn(authifier_relay::worker()); + for _ in 0..WORKER_COUNT { - task::spawn(ack::worker(db.clone())); + task::spawn(ack::worker(db.clone(), amqp.clone())); task::spawn(last_message_id::worker(db.clone())); task::spawn(process_embeds::worker(db.clone())); - task::spawn(web_push::worker(authifier_db.clone())); } } diff --git a/crates/core/database/src/tasks/process_embeds.rs b/crates/core/database/src/tasks/process_embeds.rs index 459b8b3ad..d9d61d75a 100644 --- a/crates/core/database/src/tasks/process_embeds.rs +++ b/crates/core/database/src/tasks/process_embeds.rs @@ -56,7 +56,7 @@ pub async fn worker(db: Database) { let embeds = generate( task.content, &config.hosts.january, - config.features.limits.default.message_embeds, + config.features.limits.global.message_embeds, semaphore, ) .await; diff --git a/crates/core/database/src/tasks/web_push.rs b/crates/core/database/src/tasks/web_push.rs deleted file mode 100644 index 8a055afe6..000000000 --- a/crates/core/database/src/tasks/web_push.rs +++ /dev/null @@ -1,162 +0,0 @@ -use std::collections::HashSet; - -use authifier::Database; -use base64::{ - engine::{self}, - Engine as _, -}; -use deadqueue::limited::Queue; -use once_cell::sync::Lazy; -use revolt_config::config; -use revolt_models::v0::PushNotification; -use revolt_presence::filter_online; -use serde_json::json; -use web_push::{ - ContentEncoding, IsahcWebPushClient, SubscriptionInfo, SubscriptionKeys, VapidSignatureBuilder, - WebPushClient, WebPushMessageBuilder, -}; - -/// Task information -#[derive(Debug)] -struct PushTask { - /// User IDs of the targets that are to receive this notification - recipients: Vec, - /// Push Notification - payload: PushNotification, -} - -static Q: Lazy> = Lazy::new(|| Queue::new(10_000)); - -/// Queue a new task for a worker -pub async fn queue(recipients: Vec, payload: PushNotification) { - if recipients.is_empty() { - return; - } - - let online_ids = filter_online(&recipients).await; - let recipients = (&recipients.into_iter().collect::>() - &online_ids) - .into_iter() - .collect::>(); - - Q.try_push(PushTask { - recipients, - payload, - }) - .ok(); - - info!("Queue is using {} slots from {}.", Q.len(), Q.capacity()); -} - -/// Start a new worker -pub async fn worker(db: Database) { - let config = config().await; - - let web_push_client = IsahcWebPushClient::new().unwrap(); - let fcm_client = if config.api.fcm.api_key.is_empty() { - None - } else { - Some(fcm::Client::new()) - }; - - let web_push_private_key = engine::general_purpose::URL_SAFE_NO_PAD - .decode(config.api.vapid.private_key) - .expect("valid `VAPID_PRIVATE_KEY`"); - - loop { - let task = Q.pop().await; - - if let Ok(sessions) = db.find_sessions_with_subscription(&task.recipients).await { - for session in sessions { - if let Some(sub) = session.subscription { - if sub.endpoint == "fcm" { - // Use Firebase Cloud Messaging - if let Some(client) = &fcm_client { - let PushNotification { - author, - icon, - image: _, - body, - tag, - timestamp: _, - url: _, - } = &task.payload; - - let mut notification = fcm::NotificationBuilder::new(); - notification.title(author); - notification.icon(icon); - notification.body(body); - notification.tag(tag); - // TODO: expand support for fields - let notification = notification.finalize(); - - let mut message_builder = - fcm::MessageBuilder::new(&config.api.fcm.api_key, &sub.auth); - message_builder.notification(notification); - - if let Err(err) = client.send(message_builder.finalize()).await { - error!("Failed to send FCM notification! {:?}", err); - } else { - info!("Sent FCM notification to {:?}.", session.id); - } - } else { - info!("No FCM token was specified!"); - } - } else { - // Use Web Push Standard - let subscription = SubscriptionInfo { - endpoint: sub.endpoint, - keys: SubscriptionKeys { - auth: sub.auth, - p256dh: sub.p256dh, - }, - }; - - match VapidSignatureBuilder::from_pem( - std::io::Cursor::new(&web_push_private_key), - &subscription, - ) { - Ok(sig_builder) => match sig_builder.build() { - Ok(signature) => { - let mut builder = WebPushMessageBuilder::new(&subscription); - builder.set_vapid_signature(signature); - - let payload = json!(task.payload).to_string(); - builder - .set_payload(ContentEncoding::AesGcm, payload.as_bytes()); - - match builder.build() { - Ok(msg) => match web_push_client.send(msg).await { - Ok(_) => { - info!( - "Sent Web Push notification to {:?}.", - session.id - ) - } - Err(err) => { - error!("Hit error sending Web Push! {:?}", err) - } - }, - Err(err) => { - error!( - "Failed to build message for {}! {:?}", - session.user_id, err - ) - } - } - } - Err(err) => error!( - "Failed to build signature for {}! {:?}", - session.user_id, err - ), - }, - Err(err) => error!( - "Failed to create signature builder for {}! {:?}", - session.user_id, err - ), - } - } - } - } - } - } -} diff --git a/crates/core/database/src/util/bridge/v0.rs b/crates/core/database/src/util/bridge/v0.rs index de7505bef..c04af1554 100644 --- a/crates/core/database/src/util/bridge/v0.rs +++ b/crates/core/database/src/util/bridge/v0.rs @@ -1,7 +1,7 @@ use revolt_models::v0::*; use revolt_permissions::{calculate_user_permissions, UserPermission}; -use crate::{util::permissions::DatabasePermissionQuery, Database}; +use crate::{util::permissions::DatabasePermissionQuery, Database, FileUsedFor}; impl crate::Bot { pub fn into_public_bot(self, user: crate::User) -> PublicBot { @@ -108,6 +108,7 @@ impl From for Webhook { id: value.id, name: value.name, avatar: value.avatar.map(|file| file.into()), + creator_id: value.creator_id, channel_id: value.channel_id, token: value.token, permissions: value.permissions, @@ -121,6 +122,7 @@ impl From for PartialWebhook { id: value.id, name: value.name, avatar: value.avatar.map(|file| file.into()), + creator_id: value.creator_id, channel_id: value.channel_id, token: value.token, permissions: value.permissions, @@ -423,6 +425,10 @@ impl From for crate::File { user_id: value.user_id, server_id: value.server_id, object_id: value.object_id, + hash: None, + uploaded_at: None, + uploader_id: None, + used_for: None, } } } @@ -463,26 +469,30 @@ impl From for crate::Metadata { } } -impl From for Message { - fn from(value: crate::Message) -> Self { +impl crate::Message { + pub fn into_model(self, user: Option, member: Option) -> Message { Message { - id: value.id, - nonce: value.nonce, - channel: value.channel, - author: value.author, - webhook: value.webhook, - content: value.content, - system: value.system.map(|system| system.into()), - attachments: value + id: self.id, + nonce: self.nonce, + channel: self.channel, + author: self.author, + user, + member, + webhook: self.webhook, + content: self.content, + system: self.system.map(Into::into), + attachments: self .attachments .map(|v| v.into_iter().map(|f| f.into()).collect()), - edited: value.edited, - embeds: value.embeds, - mentions: value.mentions, - replies: value.replies, - reactions: value.reactions, - interactions: value.interactions.into(), - masquerade: value.masquerade.map(|masq| masq.into()), + edited: self.edited, + embeds: self.embeds, + mentions: self.mentions, + replies: self.replies, + reactions: self.reactions, + interactions: self.interactions.into(), + masquerade: self.masquerade.map(Into::into), + flags: self.flags.map(|flags| flags as u32).unwrap_or_default(), + pinned: self.pinned, } } } @@ -494,9 +504,11 @@ impl From for PartialMessage { nonce: value.nonce, channel: value.channel, author: value.author, + user: None, + member: None, webhook: value.webhook, content: value.content, - system: value.system.map(|system| system.into()), + system: value.system.map(Into::into), attachments: value .attachments .map(|v| v.into_iter().map(|f| f.into()).collect()), @@ -505,8 +517,10 @@ impl From for PartialMessage { mentions: value.mentions, replies: value.replies, reactions: value.reactions, - interactions: value.interactions.map(|interactions| interactions.into()), - masquerade: value.masquerade.map(|masq| masq.into()), + interactions: value.interactions.map(Into::into), + masquerade: value.masquerade.map(Into::into), + flags: value.flags.map(|flags| flags as u32), + pinned: value.pinned, } } } @@ -529,6 +543,8 @@ impl From for SystemMessage { crate::SystemMessage::UserKicked { id } => Self::UserKicked { id }, crate::SystemMessage::UserLeft { id } => Self::UserLeft { id }, crate::SystemMessage::UserRemove { id, by } => Self::UserRemove { id, by }, + crate::SystemMessage::MessagePinned { id, by } => Self::MessagePinned { id, by }, + crate::SystemMessage::MessageUnpinned { id, by } => Self::MessageUnpinned { id, by }, } } } @@ -1011,26 +1027,31 @@ impl crate::User { vec![] }, badges: self.badges.unwrap_or_default() as u32, + online: can_see_profile + && revolt_presence::is_online(&self.id).await + && !matches!( + self.status, + Some(crate::UserStatus { + presence: Some(crate::Presence::Invisible), + .. + }) + ), status: if can_see_profile { self.status.map(|status| status.into()) } else { None }, - profile: if can_see_profile { - self.profile.map(|profile| profile.into()) - } else { - None - }, flags: self.flags.unwrap_or_default() as u32, privileged: self.privileged, bot: self.bot.map(|bot| bot.into()), relationship, - online: can_see_profile && revolt_presence::is_online(&self.id).await, id: self.id, } } /// Convert user object into user model assuming mutual connection + /// + /// Relations will never be included, i.e. when we process ourselves pub fn into_known<'a, P>(self, perspective: P, is_online: bool) -> User where P: Into>, @@ -1066,40 +1087,57 @@ impl crate::User { discriminator: self.discriminator, display_name: self.display_name, avatar: self.avatar.map(|file| file.into()), - relations: if let Some(crate::User { id, .. }) = perspective { - if id == &self.id { - self.relations - .unwrap_or_default() - .into_iter() - .map(|relation| relation.into()) - .collect() - } else { - vec![] - } - } else { - vec![] - }, + relations: vec![], badges: self.badges.unwrap_or_default() as u32, + online: can_see_profile + && is_online + && !matches!( + self.status, + Some(crate::UserStatus { + presence: Some(crate::Presence::Invisible), + .. + }) + ), status: if can_see_profile { self.status.map(|status| status.into()) } else { None }, - profile: if can_see_profile { - self.profile.map(|profile| profile.into()) - } else { - None - }, flags: self.flags.unwrap_or_default() as u32, privileged: self.privileged, bot: self.bot.map(|bot| bot.into()), relationship, - online: can_see_profile && is_online, id: self.id, } } - pub async fn into_self(self) -> User { + /// Convert user object into user model without presence information + pub fn into_known_static<'a>(self, is_online: bool) -> User { + User { + username: self.username, + discriminator: self.discriminator, + display_name: self.display_name, + avatar: self.avatar.map(|file| file.into()), + relations: vec![], + badges: self.badges.unwrap_or_default() as u32, + online: is_online + && !matches!( + self.status, + Some(crate::UserStatus { + presence: Some(crate::Presence::Invisible), + .. + }) + ), + status: self.status.map(|status| status.into()), + flags: self.flags.unwrap_or_default() as u32, + privileged: self.privileged, + bot: self.bot.map(|bot| bot.into()), + relationship: RelationshipStatus::None, // events client will populate this from cache + id: self.id, + } + } + + pub async fn into_self(self, force_online: bool) -> User { User { username: self.username, discriminator: self.discriminator, @@ -1115,13 +1153,19 @@ impl crate::User { }) .unwrap_or_default(), badges: self.badges.unwrap_or_default() as u32, + online: (force_online || revolt_presence::is_online(&self.id).await) + && !matches!( + self.status, + Some(crate::UserStatus { + presence: Some(crate::Presence::Invisible), + .. + }) + ), status: self.status.map(|status| status.into()), - profile: self.profile.map(|profile| profile.into()), flags: self.flags.unwrap_or_default() as u32, privileged: self.privileged, bot: self.bot.map(|bot| bot.into()), relationship: RelationshipStatus::User, - online: revolt_presence::is_online(&self.id).await, id: self.id, } } @@ -1145,10 +1189,11 @@ impl From for crate::User { relations: None, badges: Some(value.badges as i32), status: value.status.map(Into::into), - profile: value.profile.map(Into::into), + profile: None, flags: Some(value.flags as i32), privileged: value.privileged, bot: value.bot.map(Into::into), + suspended_until: None, } } } @@ -1168,7 +1213,6 @@ impl From for PartialUser { }), badges: value.badges.map(|badges| badges as u32), status: value.status.map(|status| status.into()), - profile: value.profile.map(|profile| profile.into()), flags: value.flags.map(|flags| flags as u32), privileged: value.privileged, bot: value.bot.map(|bot| bot.into()), @@ -1187,6 +1231,9 @@ impl From for crate::FieldsUser { FieldsUser::ProfileContent => crate::FieldsUser::ProfileContent, FieldsUser::StatusPresence => crate::FieldsUser::StatusPresence, FieldsUser::StatusText => crate::FieldsUser::StatusText, + FieldsUser::DisplayName => crate::FieldsUser::DisplayName, + + FieldsUser::Internal => crate::FieldsUser::None, } } } @@ -1199,6 +1246,10 @@ impl From for FieldsUser { crate::FieldsUser::ProfileContent => FieldsUser::ProfileContent, crate::FieldsUser::StatusPresence => FieldsUser::StatusPresence, crate::FieldsUser::StatusText => FieldsUser::StatusText, + crate::FieldsUser::DisplayName => FieldsUser::DisplayName, + + crate::FieldsUser::Suspension => FieldsUser::Internal, + crate::FieldsUser::None => FieldsUser::Internal, } } } @@ -1301,3 +1352,18 @@ impl From for crate::BotInformation { } } } + +impl From for FieldsMessage { + fn from(value: crate::FieldsMessage) -> Self { + match value { + crate::FieldsMessage::Pinned => FieldsMessage::Pinned, + } + } +} +impl From for crate::FieldsMessage { + fn from(value: FieldsMessage) -> Self { + match value { + FieldsMessage::Pinned => crate::FieldsMessage::Pinned, + } + } +} diff --git a/crates/core/database/src/util/bulk_permissions.rs b/crates/core/database/src/util/bulk_permissions.rs new file mode 100644 index 000000000..400359772 --- /dev/null +++ b/crates/core/database/src/util/bulk_permissions.rs @@ -0,0 +1,337 @@ +use std::{collections::HashMap, hash::RandomState}; + +use revolt_permissions::{ + ChannelPermission, ChannelType, Override, OverrideField, PermissionValue, ALLOW_IN_TIMEOUT, + DEFAULT_PERMISSION_DIRECT_MESSAGE, +}; + +use crate::{Channel, Database, Member, Server, User}; + +#[derive(Clone)] +pub struct BulkDatabasePermissionQuery<'a> { + #[allow(dead_code)] + database: &'a Database, + + server: Server, + channel: Option, + users: Option>, + members: Option>, + + // In case the users or members are fetched as part of the permissions checking operation + pub(crate) cached_users: Option>, + pub(crate) cached_members: Option>, + + cached_member_perms: Option>, +} + +impl<'z, 'x> BulkDatabasePermissionQuery<'x> { + pub async fn members_can_see_channel(&'z mut self) -> HashMap + where + 'z: 'x, + { + let member_perms = if self.cached_member_perms.is_some() { + // This isn't done as an if let to prevent borrow checker errors with the mut self call when the perms aren't cached. + let perms = self.cached_member_perms.as_ref().unwrap(); + perms + .iter() + .map(|(m, p)| { + ( + m.clone(), + p.has_channel_permission(ChannelPermission::ViewChannel), + ) + }) + .collect() + } else { + calculate_members_permissions(self) + .await + .iter() + .map(|(m, p)| { + ( + m.clone(), + p.has_channel_permission(ChannelPermission::ViewChannel), + ) + }) + .collect() + }; + member_perms + } +} + +impl<'z> BulkDatabasePermissionQuery<'z> { + pub fn new(database: &Database, server: Server) -> BulkDatabasePermissionQuery<'_> { + BulkDatabasePermissionQuery { + database, + server, + channel: None, + users: None, + members: None, + cached_members: None, + cached_users: None, + cached_member_perms: None, + } + } + + pub async fn from_server_id<'a>( + db: &'a Database, + server: &str, + ) -> BulkDatabasePermissionQuery<'a> { + BulkDatabasePermissionQuery { + database: db, + server: db.fetch_server(server).await.unwrap(), + channel: None, + users: None, + members: None, + cached_members: None, + cached_users: None, + cached_member_perms: None, + } + } + + pub fn channel(self, channel: &'z Channel) -> BulkDatabasePermissionQuery { + BulkDatabasePermissionQuery { + channel: Some(channel.clone()), + ..self + } + } + + pub fn members(self, members: &'z [Member]) -> BulkDatabasePermissionQuery { + BulkDatabasePermissionQuery { + members: Some(members.to_owned()), + ..self + } + } + + pub fn users(self, users: &'z [User]) -> BulkDatabasePermissionQuery { + BulkDatabasePermissionQuery { + users: Some(users.to_owned()), + ..self + } + } + + /// Get the default channel permissions + /// Group channel defaults should be mapped to an allow-only override + #[allow(dead_code)] + async fn get_default_channel_permissions(&mut self) -> Override { + if let Some(channel) = &self.channel { + match channel { + Channel::Group { permissions, .. } => Override { + allow: permissions.unwrap_or(*DEFAULT_PERMISSION_DIRECT_MESSAGE as i64) as u64, + deny: 0, + }, + Channel::TextChannel { + default_permissions, + .. + } + | Channel::VoiceChannel { + default_permissions, + .. + } => default_permissions.unwrap_or_default().into(), + _ => Default::default(), + } + } else { + Default::default() + } + } + + #[allow(dead_code)] + fn get_channel_type(&mut self) -> ChannelType { + if let Some(channel) = &self.channel { + match channel { + Channel::DirectMessage { .. } => ChannelType::DirectMessage, + Channel::Group { .. } => ChannelType::Group, + Channel::SavedMessages { .. } => ChannelType::SavedMessages, + Channel::TextChannel { .. } | Channel::VoiceChannel { .. } => { + ChannelType::ServerChannel + } + } + } else { + ChannelType::Unknown + } + } + + /// Get the ordered role overrides (from lowest to highest) for this member in this channel + #[allow(dead_code)] + async fn get_channel_role_overrides(&mut self) -> &HashMap { + if let Some(channel) = &self.channel { + match channel { + Channel::TextChannel { + role_permissions, .. + } + | Channel::VoiceChannel { + role_permissions, .. + } => role_permissions, + _ => panic!("Not supported for non-server channels"), + } + } else { + panic!("No channel added to query") + } + } +} + +/// Calculate members permissions in a server channel. +async fn calculate_members_permissions<'a>( + query: &'a mut BulkDatabasePermissionQuery<'a>, +) -> HashMap { + let mut resp = HashMap::new(); + + let (_, channel_role_permissions, channel_default_permissions) = match query + .channel + .as_ref() + .expect("A channel must be assigned to calculate channel permissions") + .clone() + { + Channel::TextChannel { + id, + role_permissions, + default_permissions, + .. + } + | Channel::VoiceChannel { + id, + role_permissions, + default_permissions, + .. + } => (id, role_permissions, default_permissions), + _ => panic!("Calculation of member permissions must be done on a server channel"), + }; + + if query.users.is_none() { + let ids: Vec = query + .members + .as_ref() + .expect("No users or members added to the query") + .iter() + .map(|m| m.id.user.clone()) + .collect(); + + query.cached_users = Some( + query + .database + .fetch_users(&ids[..]) + .await + .expect("Failed to get data from the db"), + ); + + query.users = Some(query.cached_users.as_ref().unwrap().to_vec()) + } + + let users = query.users.as_ref().unwrap(); + + if query.members.is_none() { + let ids: Vec = query + .users + .as_ref() + .expect("No users or members added to the query") + .iter() + .map(|m| m.id.clone()) + .collect(); + + query.cached_members = Some( + query + .database + .fetch_members(&query.server.id, &ids[..]) + .await + .expect("Failed to get data from the db"), + ); + query.members = Some(query.cached_members.as_ref().unwrap().to_vec()) + } + + let members: HashMap<&String, &Member, RandomState> = HashMap::from_iter( + query + .members + .as_ref() + .unwrap() + .iter() + .map(|m| (&m.id.user, m)), + ); + + for user in users { + let member = members.get(&user.id); + + // User isn't a part of the server + if member.is_none() { + resp.insert(user.id.clone(), 0_u64.into()); + continue; + } + + let member = *member.unwrap(); + + if user.privileged { + resp.insert( + user.id.clone(), + PermissionValue::from(ChannelPermission::GrantAllSafe), + ); + continue; + } + + if user.id == query.server.owner { + resp.insert( + user.id.clone(), + PermissionValue::from(ChannelPermission::GrantAllSafe), + ); + continue; + } + + // Get the user's server permissions + let mut permission = calculate_server_permissions(&query.server, user, member); + + if let Some(defaults) = channel_default_permissions { + permission.apply(defaults.into()); + } + + // Get the applicable role overrides + let mut roles = channel_role_permissions + .iter() + .filter(|(id, _)| member.roles.contains(id)) + .filter_map(|(id, permission)| { + query.server.roles.get(id).map(|role| { + let v: Override = (*permission).into(); + (role.rank, v) + }) + }) + .collect::>(); + + roles.sort_by(|a, b| b.0.cmp(&a.0)); + let overrides = roles.into_iter().map(|(_, v)| v); + + for role_override in overrides { + permission.apply(role_override) + } + + resp.insert(user.id.clone(), permission); + } + + resp +} + +/// Calculates a member's server permissions +fn calculate_server_permissions(server: &Server, user: &User, member: &Member) -> PermissionValue { + if user.privileged || server.owner == user.id { + return ChannelPermission::GrantAllSafe.into(); + } + + let mut permissions: PermissionValue = server.default_permissions.into(); + + let mut roles = server + .roles + .iter() + .filter(|(id, _)| member.roles.contains(id)) + .map(|(_, role)| { + let v: Override = role.permissions.into(); + (role.rank, v) + }) + .collect::>(); + + roles.sort_by(|a, b| b.0.cmp(&a.0)); + let role_overrides: Vec = roles.into_iter().map(|(_, v)| v).collect(); + + for role in role_overrides { + permissions.apply(role); + } + + if member.in_timeout() { + permissions.restrict(*ALLOW_IN_TIMEOUT); + } + + permissions +} diff --git a/crates/core/database/src/util/idempotency.rs b/crates/core/database/src/util/idempotency.rs index 94448c04c..df68000d9 100644 --- a/crates/core/database/src/util/idempotency.rs +++ b/crates/core/database/src/util/idempotency.rs @@ -18,6 +18,10 @@ static TOKEN_CACHE: Lazy>> = Lazy::new(|| Mutex::new(lru::LruCache::new(NonZeroUsize::new(1000).unwrap()))); impl IdempotencyKey { + pub fn unchecked_from_string(key: String) -> Self { + Self { key } + } + // Backwards compatibility. // Issue #109 pub async fn consume_nonce(&mut self, v: Option) -> Result<()> { @@ -98,7 +102,7 @@ impl<'r> FromRequest<'r> for IdempotencyKey { .map(|k| k.to_string()) { if key.len() > 64 { - return Outcome::Failure(( + return Outcome::Error(( Status::BadRequest, create_error!(FailedValidation { error: "idempotency key too long".to_string(), @@ -109,7 +113,7 @@ impl<'r> FromRequest<'r> for IdempotencyKey { let idempotency = IdempotencyKey { key }; let mut cache = TOKEN_CACHE.lock().await; if cache.get(&idempotency.key).is_some() { - return Outcome::Failure((Status::Conflict, create_error!(DuplicateNonce))); + return Outcome::Error((Status::Conflict, create_error!(DuplicateNonce))); } cache.put(idempotency.key.clone(), ()); diff --git a/crates/core/database/src/util/mod.rs b/crates/core/database/src/util/mod.rs index 26cf436e6..1baf7d8ba 100644 --- a/crates/core/database/src/util/mod.rs +++ b/crates/core/database/src/util/mod.rs @@ -1,4 +1,5 @@ pub mod bridge; +pub mod bulk_permissions; pub mod idempotency; pub mod permissions; pub mod reference; diff --git a/crates/core/database/src/util/permissions.rs b/crates/core/database/src/util/permissions.rs index 08fe3a9eb..436b236f6 100644 --- a/crates/core/database/src/util/permissions.rs +++ b/crates/core/database/src/util/permissions.rs @@ -53,6 +53,12 @@ impl PermissionQuery for DatabasePermissionQuery<'_> { if let Some(other_user) = &self.user { if self.perspective.id == other_user.id { return RelationshipStatus::User; + } else if let Some(bot) = &other_user.bot { + // For the purposes of permissions checks, + // assume owner is the same as bot + if self.perspective.id == bot.owner { + return RelationshipStatus::User; + } } if let Some(relations) = &self.perspective.relations { @@ -181,7 +187,7 @@ impl PermissionQuery for DatabasePermissionQuery<'_> { async fn do_we_have_publish_overwrites(&mut self) -> bool { if let Some(member) = &self.member { - member.can_publish.unwrap_or(true) + member.can_publish.unwrap_or(false) } else { false } @@ -189,7 +195,7 @@ impl PermissionQuery for DatabasePermissionQuery<'_> { async fn do_we_have_receive_overwrites(&mut self) -> bool { if let Some(member) = &self.member { - member.can_receive.unwrap_or(true) + member.can_receive.unwrap_or(false) } else { false } diff --git a/crates/core/database/src/util/reference.rs b/crates/core/database/src/util/reference.rs index 4c2206545..9282d39d7 100644 --- a/crates/core/database/src/util/reference.rs +++ b/crates/core/database/src/util/reference.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use revolt_result::Result; #[cfg(feature = "rocket-impl")] use rocket::request::FromParam; @@ -44,9 +46,27 @@ impl Reference { db.fetch_channel(&self.id).await } - /// Fetch invite from Ref + /// Fetch invite from Ref or create invite to server if discoverable pub async fn as_invite(&self, db: &Database) -> Result { - db.fetch_invite(&self.id).await + if ulid::Ulid::from_str(&self.id).is_ok() { + let server = self.as_server(db).await?; + if !server.discoverable { + return Err(create_error!(NotFound)); + } + + Ok(Invite::Server { + code: self.id.to_string(), + server: server.id, + creator: server.owner, + channel: server + .channels + .into_iter() + .next() + .ok_or(create_error!(NotFound))?, + }) + } else { + db.fetch_invite(&self.id).await + } } /// Fetch message from Ref diff --git a/crates/core/database/templates/deletion.html b/crates/core/database/templates/deletion.html new file mode 100644 index 000000000..ef19ee40f --- /dev/null +++ b/crates/core/database/templates/deletion.html @@ -0,0 +1,554 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/delta/src/templates/deletion.original.html b/crates/core/database/templates/deletion.original.html similarity index 97% rename from crates/delta/src/templates/deletion.original.html rename to crates/core/database/templates/deletion.original.html index 1028ce49d..fa9f83e4c 100644 --- a/crates/delta/src/templates/deletion.original.html +++ b/crates/core/database/templates/deletion.original.html @@ -23,7 +23,7 @@

Account Deletion

This email is intended for {{email}}
Sent from Revolt
- Made in the EU + Made in Europe
diff --git a/crates/delta/src/templates/deletion.txt b/crates/core/database/templates/deletion.txt similarity index 93% rename from crates/delta/src/templates/deletion.txt rename to crates/core/database/templates/deletion.txt index 88112b8ec..71a5a936c 100644 --- a/crates/delta/src/templates/deletion.txt +++ b/crates/core/database/templates/deletion.txt @@ -4,4 +4,4 @@ Please navigate to: {{url}} This email is intended for {{email}} Sent by Revolt -Made in the EU +Made in Europe diff --git a/crates/core/database/templates/reset.html b/crates/core/database/templates/reset.html new file mode 100644 index 000000000..2ca5d90c4 --- /dev/null +++ b/crates/core/database/templates/reset.html @@ -0,0 +1,552 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/delta/src/templates/reset.original.html b/crates/core/database/templates/reset.original.html similarity index 97% rename from crates/delta/src/templates/reset.original.html rename to crates/core/database/templates/reset.original.html index f40c7254d..86041f120 100644 --- a/crates/delta/src/templates/reset.original.html +++ b/crates/core/database/templates/reset.original.html @@ -19,7 +19,7 @@

Password Reset

This email is intended for {{email}}
Sent from Revolt
- Made in the EU + Made in Europe
diff --git a/crates/delta/src/templates/reset.txt b/crates/core/database/templates/reset.txt similarity index 92% rename from crates/delta/src/templates/reset.txt rename to crates/core/database/templates/reset.txt index fdc2751fa..d72421d4c 100644 --- a/crates/delta/src/templates/reset.txt +++ b/crates/core/database/templates/reset.txt @@ -4,4 +4,4 @@ Please navigate to: {{url}} This email is intended for {{email}} Sent by Revolt -Made in the EU +Made in Europe diff --git a/crates/core/database/templates/suspension.html b/crates/core/database/templates/suspension.html new file mode 100644 index 000000000..fafd2b40b --- /dev/null +++ b/crates/core/database/templates/suspension.html @@ -0,0 +1,625 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/core/database/templates/suspension.original.html b/crates/core/database/templates/suspension.original.html new file mode 100644 index 000000000..624801e93 --- /dev/null +++ b/crates/core/database/templates/suspension.original.html @@ -0,0 +1,38 @@ + + + + + + +
+ Revolt Logo +
+

Account Suspended

+

Your account has been suspended, for one or more reasons:

+
    + {{list}} +
+

+ You will be able to use your account again in {{duration}} days. +

+

+ Further violations may result in a permanent ban depending on + severity, please abide by the + Acceptable Usage Policy. +

+

Ban evasion is prohibited and will be dealt with accordingly.

+
+
+ This email is intended for {{email}}
+ Sent from Revolt
+ Made in Europe +
+
+ + diff --git a/crates/core/database/templates/suspension.txt b/crates/core/database/templates/suspension.txt new file mode 100644 index 000000000..ace1d25cc --- /dev/null +++ b/crates/core/database/templates/suspension.txt @@ -0,0 +1,12 @@ +Your account has been suspended, for one or more reasons: +{{list}} + +You will be able to use your account again in {{duration}} days. + +Further violations may result in a permanent ban depending on severity, please abide by the Acceptable Usage Policy (https://revolt.chat/aup). + +Ban evasion is prohibited and will be dealt with accordingly. + +This email is intended for {{email}} +Sent by Revolt +Made in Europe diff --git a/crates/core/database/templates/verify.html b/crates/core/database/templates/verify.html new file mode 100644 index 000000000..a2b196c4e --- /dev/null +++ b/crates/core/database/templates/verify.html @@ -0,0 +1,552 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/delta/src/templates/verify.original.html b/crates/core/database/templates/verify.original.html similarity index 97% rename from crates/delta/src/templates/verify.original.html rename to crates/core/database/templates/verify.original.html index e3ce0d5cf..1d16a46c0 100644 --- a/crates/delta/src/templates/verify.original.html +++ b/crates/core/database/templates/verify.original.html @@ -20,7 +20,7 @@

Almost there!

This email is intended for {{email}}
Sent from Revolt
- Made in the EU + Made in Europe
diff --git a/crates/delta/src/templates/verify.txt b/crates/core/database/templates/verify.txt similarity index 91% rename from crates/delta/src/templates/verify.txt rename to crates/core/database/templates/verify.txt index 1fdaaa63a..a3f104c82 100644 --- a/crates/delta/src/templates/verify.txt +++ b/crates/core/database/templates/verify.txt @@ -5,4 +5,4 @@ Please navigate to: {{url}} This email is intended for {{email}} Sent by Revolt -Made in the EU +Made in Europe diff --git a/crates/core/files/Cargo.toml b/crates/core/files/Cargo.toml new file mode 100644 index 000000000..59ce115ab --- /dev/null +++ b/crates/core/files/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "revolt-files" +version = "0.8.1" +edition = "2021" +license = "AGPL-3.0-or-later" +authors = ["Paul Makles "] +description = "Revolt Backend: S3 and encryption subroutines" + +[dependencies] +tracing = "0.1" + +ffprobe = "0.4.0" +imagesize = "0.13.0" +tempfile = "3.12.0" + +base64 = "0.22.1" +aes-gcm = "0.10.3" +typenum = "1.17.0" + +aws-config = "1.5.5" +aws-sdk-s3 = { version = "1.46.0", features = ["behavior-version-latest"] } + +revolt-config = { version = "0.8.1", path = "../config", features = [ + "report-macros", +] } +revolt-result = { version = "0.8.1", path = "../result" } + +# image processing +jxl-oxide = "0.8.1" +image = { version = "0.25.2" } + +# svg rendering +usvg = "0.44.0" +resvg = "0.44.0" +tiny-skia = "0.11.4" + +# encoding +webp = "0.3.0" diff --git a/crates/core/files/src/lib.rs b/crates/core/files/src/lib.rs new file mode 100644 index 000000000..c09853dd9 --- /dev/null +++ b/crates/core/files/src/lib.rs @@ -0,0 +1,273 @@ +use std::io::{BufRead, Read, Seek, Write}; + +use aes_gcm::{ + aead::{AeadCore, AeadMutInPlace, OsRng}, + Aes256Gcm, Key, KeyInit, Nonce, +}; +use image::{DynamicImage, ImageBuffer}; +use revolt_config::{config, report_internal_error, FilesS3}; +use revolt_result::{create_error, Result}; + +use aws_sdk_s3::{ + config::{Credentials, Region}, + Client, Config, +}; + +use base64::prelude::*; +use tempfile::NamedTempFile; +use tiny_skia::Pixmap; + +/// Size of the authentication tag in the buffer +pub const AUTHENTICATION_TAG_SIZE_BYTES: usize = 16; + +/// Create an S3 client +pub fn create_client(s3_config: FilesS3) -> Client { + let provider_name = "my-creds"; + let creds = Credentials::new( + s3_config.access_key_id, + s3_config.secret_access_key, + None, + None, + provider_name, + ); + + let config = Config::builder() + .region(Region::new(s3_config.region)) + .endpoint_url(s3_config.endpoint) + .force_path_style(s3_config.path_style_buckets) + .credentials_provider(creds) + .build(); + + Client::from_conf(config) +} + +/// Create an AES-256-GCM cipher +pub fn create_cipher(key: &str) -> Aes256Gcm { + let key = &BASE64_STANDARD.decode(key).expect("valid base64 string")[..]; + let key: &Key = key.into(); + Aes256Gcm::new(key) +} + +/// Fetch a file from S3 (and decrypt it) +pub async fn fetch_from_s3(bucket_id: &str, path: &str, nonce: &str) -> Result> { + let config = config().await; + let client = create_client(config.files.s3); + + // Send a request for the file + let mut obj = + report_internal_error!(client.get_object().bucket(bucket_id).key(path).send().await)?; + + // Read the file from remote + let mut buf = vec![]; + while let Some(bytes) = obj.body.next().await { + let data = report_internal_error!(bytes)?; + report_internal_error!(buf.write_all(&data))?; + // is there a more efficient way to do this? + // we just want the Vec + } + + // File is not encrypted + if nonce.is_empty() { + return Ok(buf); + } + + // Recover nonce as bytes + let nonce = &BASE64_STANDARD.decode(nonce).unwrap()[..]; + let nonce: &Nonce = nonce.into(); + + // Decrypt the file + create_cipher(&config.files.encryption_key) + .decrypt_in_place(nonce, b"", &mut buf) + .map_err(|_| create_error!(InternalError))?; + + Ok(buf) +} + +/// Encrypt and upload a file to S3 (returning its nonce/IV) +pub async fn upload_to_s3(bucket_id: &str, path: &str, buf: &[u8]) -> Result { + let config = config().await; + let client = create_client(config.files.s3); + + // Generate a nonce + let nonce = Aes256Gcm::generate_nonce(&mut OsRng); + + // Extend the buffer for in-place encryption + let mut buf = [buf, &[0; AUTHENTICATION_TAG_SIZE_BYTES]].concat(); + + // Encrypt the file in place + create_cipher(&config.files.encryption_key) + .encrypt_in_place(&nonce, b"", &mut buf) + .map_err(|_| create_error!(InternalError))?; + + // Upload the file to remote + report_internal_error!( + client + .put_object() + .bucket(bucket_id) + .key(path) + .body(buf.into()) + .send() + .await + )?; + + Ok(BASE64_STANDARD.encode(nonce)) +} + +/// Determine size of image at temp file +pub fn image_size(f: &NamedTempFile) -> Option<(usize, usize)> { + if let Ok(size) = imagesize::size(f.path()) + .inspect_err(|err| tracing::error!("Failed to generate image size! {err:?}")) + { + Some((size.width, size.height)) + } else { + None + } +} + +/// Determine size of image with buffer +pub fn image_size_vec(v: &[u8], mime: &str) -> Option<(usize, usize)> { + match mime { + "image/svg+xml" => { + let tree = + report_internal_error!(usvg::Tree::from_data(v, &Default::default())).ok()?; + + let size = tree.size(); + Some((size.width() as usize, size.height() as usize)) + } + _ => { + if let Ok(size) = imagesize::blob_size(v) + .inspect_err(|err| tracing::error!("Failed to generate image size! {err:?}")) + { + Some((size.width, size.height)) + } else { + None + } + } + } +} + +/// Determine size of video at temp file +pub fn video_size(f: &NamedTempFile) -> Option<(i64, i64)> { + if let Ok(data) = ffprobe::ffprobe(f.path()) + .inspect_err(|err| tracing::error!("Failed to ffprobe file! {err:?}")) + { + // Use first valid stream + for stream in data.streams { + if let (Some(w), Some(h)) = (stream.width, stream.height) { + return Some((w, h)); + } + } + + None + } else { + None + } +} + +/// Decode image from reader +pub fn decode_image(reader: &mut R, mime: &str) -> Result { + match mime { + // Read image using jxl-oxide crate + "image/jxl" => { + let jxl_image = report_internal_error!(jxl_oxide::JxlImage::builder().read(reader))?; + if let Ok(frame) = jxl_image.render_frame(0) { + match frame.color_channels().len() { + 3 => Ok(DynamicImage::ImageRgb8( + DynamicImage::ImageRgb32F( + ImageBuffer::from_vec( + jxl_image.width(), + jxl_image.height(), + frame.image().buf().to_vec(), + ) + .ok_or_else(|| create_error!(ImageProcessingFailed))?, + ) + .to_rgb8(), + )), + 4 => Ok(DynamicImage::ImageRgba8( + DynamicImage::ImageRgba32F( + ImageBuffer::from_vec( + jxl_image.width(), + jxl_image.height(), + frame.image().buf().to_vec(), + ) + .ok_or_else(|| create_error!(ImageProcessingFailed))?, + ) + .to_rgba8(), + )), + _ => Err(create_error!(ImageProcessingFailed)), + } + } else { + Err(create_error!(ImageProcessingFailed)) + } + } + // Read image using resvg + "image/svg+xml" => { + // usvg doesn't support Read trait so copy to buffer + let mut buf = Vec::new(); + report_internal_error!(reader.read_to_end(&mut buf))?; + + let tree = report_internal_error!(usvg::Tree::from_data(&buf, &Default::default()))?; + let size = tree.size(); + let mut pixmap = Pixmap::new(size.width() as u32, size.height() as u32) + .ok_or_else(|| create_error!(ImageProcessingFailed))?; + + let mut pixmap_mut = pixmap.as_mut(); + resvg::render(&tree, Default::default(), &mut pixmap_mut); + + Ok(DynamicImage::ImageRgba8( + ImageBuffer::from_vec( + size.width() as u32, + size.height() as u32, + pixmap.data().to_vec(), + ) + .ok_or_else(|| create_error!(ImageProcessingFailed))?, + )) + } + // Check if we can read using image-rs crate + _ => report_internal_error!(report_internal_error!( + image::ImageReader::new(reader).with_guessed_format() + )? + .decode()), + } +} + +/// Check whether given reader has a valid image +pub fn is_valid_image(reader: &mut R, mime: &str) -> bool { + match mime { + // Check if we can read using jxl-oxide crate + "image/jxl" => jxl_oxide::JxlImage::builder() + .read(reader) + .inspect_err(|err| tracing::error!("Failed to read JXL! {err:?}")) + .is_ok(), + // Check if we can read using image-rs crate + _ => !matches!( + image::ImageReader::new(reader) + .with_guessed_format() + .inspect_err(|err| tracing::error!("Failed to read image! {err:?}")) + .map(|f| f.decode()), + Err(_) | Ok(Err(_)) + ), + } +} + +/// Create thumbnail from given image +pub async fn create_thumbnail(image: DynamicImage, tag: &str) -> Vec { + // Load configuration + let config = config().await; + let [w, h] = config.files.preview.get(tag).unwrap(); + + // Create thumbnail + //.resize(width as u32, height as u32, image::imageops::FilterType::Gaussian) + // resize is about 2.5x slower, + // thumbnail doesn't have terrible quality + // so we use thumbnail + let image = image.thumbnail(image.width().min(*w as u32), image.height().min(*h as u32)); + + // Encode it into WEBP + let encoder = webp::Encoder::from_image(&image).expect("Could not create encoder."); + if config.files.webp_quality != 100.0 { + encoder.encode(config.files.webp_quality).to_vec() + } else { + encoder.encode_lossless().to_vec() + } +} diff --git a/crates/core/models/Cargo.toml b/crates/core/models/Cargo.toml index 5b41b771b..b840d47f7 100644 --- a/crates/core/models/Cargo.toml +++ b/crates/core/models/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "revolt-models" -version = "0.7.1" +version = "0.8.1" edition = "2021" -license = "AGPL-3.0-or-later" +license = "MIT" authors = ["Paul Makles "] description = "Revolt Backend: API Models" @@ -11,16 +11,17 @@ description = "Revolt Backend: API Models" [features] serde = ["dep:serde", "revolt-permissions/serde", "indexmap/serde"] schemas = ["dep:schemars", "revolt-permissions/schemas"] +utoipa = ["dep:utoipa"] validator = ["dep:validator"] rocket = ["dep:rocket"] -partials = ["dep:revolt_optional_struct", "serde", "schemas"] +partials = ["dep:revolt_optional_struct", "serde", "schemas", "utoipa"] default = ["serde", "partials", "rocket"] [dependencies] # Core -revolt-config = { version = "0.7.1", path = "../config" } -revolt-permissions = { version = "0.7.1", path = "../permissions" } +revolt-config = { version = "0.8.1", path = "../config" } +revolt-permissions = { version = "0.8.1", path = "../permissions" } # Utility regex = "1" @@ -37,6 +38,7 @@ iso8601-timestamp = { version = "0.2.11", features = ["schema", "bson"] } # Spec Generation schemars = { version = "0.8.8", optional = true, features = ["indexmap1"] } +utoipa = { version = "4.2.3", optional = true } # Validation validator = { version = "0.16.0", optional = true, features = ["derive"] } diff --git a/crates/core/models/LICENSE b/crates/core/models/LICENSE new file mode 100644 index 000000000..7c2815b9f --- /dev/null +++ b/crates/core/models/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2024 Pawel Makles + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/crates/core/models/src/lib.rs b/crates/core/models/src/lib.rs index 4e73efedc..7f3d3d23b 100644 --- a/crates/core/models/src/lib.rs +++ b/crates/core/models/src/lib.rs @@ -6,6 +6,10 @@ extern crate serde; #[macro_use] extern crate schemars; +#[cfg(feature = "utoipa")] +#[macro_use] +extern crate utoipa; + #[cfg(feature = "partials")] #[macro_use] extern crate revolt_optional_struct; @@ -18,6 +22,7 @@ macro_rules! auto_derived { $( #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "schemas", derive(JsonSchema))] + #[cfg_attr(feature = "utoipa", derive(ToSchema))] #[derive(Debug, Clone, Eq, PartialEq)] $item )+ @@ -66,3 +71,8 @@ pub fn if_false(t: &bool) -> bool { pub fn if_zero_u32(t: &u32) -> bool { t == &0 } + +/// Utility function to check if an option doesnt contain true +pub fn if_option_false(t: &Option) -> bool { + t != &Some(true) +} diff --git a/crates/core/models/src/v0/bots.rs b/crates/core/models/src/v0/bots.rs index 57995ae9d..0e58bd150 100644 --- a/crates/core/models/src/v0/bots.rs +++ b/crates/core/models/src/v0/bots.rs @@ -162,4 +162,11 @@ auto_derived!( /// User objects pub users: Vec, } + + /// Bot with user response + pub struct BotWithUserResponse { + #[serde(flatten)] + pub bot: Bot, + pub user: User, + } ); diff --git a/crates/core/models/src/v0/channel_invites.rs b/crates/core/models/src/v0/channel_invites.rs index f0e889d51..475bf6fc3 100644 --- a/crates/core/models/src/v0/channel_invites.rs +++ b/crates/core/models/src/v0/channel_invites.rs @@ -2,6 +2,7 @@ use super::{Channel, File, Server, User}; auto_derived!( /// Invite + #[serde(tag = "type")] pub enum Invite { /// Invite to a specific server channel Server { diff --git a/crates/core/models/src/v0/channel_webhooks.rs b/crates/core/models/src/v0/channel_webhooks.rs index 77bbd961f..710152b52 100644 --- a/crates/core/models/src/v0/channel_webhooks.rs +++ b/crates/core/models/src/v0/channel_webhooks.rs @@ -16,6 +16,9 @@ auto_derived_partial!( #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub avatar: Option, + /// User that created this webhook + pub creator_id: String, + /// The channel this webhook belongs to pub channel_id: String, diff --git a/crates/core/models/src/v0/channels.rs b/crates/core/models/src/v0/channels.rs index fd88f093a..7947f1fa8 100644 --- a/crates/core/models/src/v0/channels.rs +++ b/crates/core/models/src/v0/channels.rs @@ -331,4 +331,18 @@ impl Channel { | Channel::VoiceChannel { id, .. } => id, } } + + /// This returns a Result because the recipient name can't be determined here without a db call, + /// which can't be done since this is models, which can't reference the database crate. + /// + /// If it returns Err, you need to fetch the name from the db. + pub fn name(&self) -> Result<&str, ()> { + match self { + Channel::DirectMessage { .. } => Err(()), + Channel::SavedMessages { .. } => Ok("Saved Messages"), + Channel::TextChannel { name, .. } + | Channel::Group { name, .. } + | Channel::VoiceChannel { name, .. } => Ok(name), + } + } } diff --git a/crates/core/models/src/v0/embeds.rs b/crates/core/models/src/v0/embeds.rs index faeeeb3da..4944afaec 100644 --- a/crates/core/models/src/v0/embeds.rs +++ b/crates/core/models/src/v0/embeds.rs @@ -14,9 +14,9 @@ auto_derived!( /// URL to the original image pub url: String, /// Width of the image - pub width: isize, + pub width: usize, /// Height of the image - pub height: isize, + pub height: usize, /// Positioning and size pub size: ImageSize, } @@ -26,9 +26,9 @@ auto_derived!( /// URL to the original video pub url: String, /// Width of the video - pub width: isize, + pub width: usize, /// Height of the video - pub height: isize, + pub height: usize, } /// Type of remote Twitch content @@ -84,6 +84,12 @@ auto_derived!( content_type: BandcampType, id: String, }, + AppleMusic { + album_id: String, + + #[serde(skip_serializing_if = "Option::is_none")] + track_id: Option, + }, /// Streamable Video Streamable { id: String }, } @@ -92,38 +98,36 @@ auto_derived!( pub struct WebsiteMetadata { /// Direct URL to web page #[serde(skip_serializing_if = "Option::is_none")] - url: Option, + pub url: Option, /// Original direct URL #[serde(skip_serializing_if = "Option::is_none")] - original_url: Option, + pub original_url: Option, /// Remote content #[serde(skip_serializing_if = "Option::is_none")] - special: Option, + pub special: Option, /// Title of website #[serde(skip_serializing_if = "Option::is_none")] - title: Option, + pub title: Option, /// Description of website #[serde(skip_serializing_if = "Option::is_none")] - description: Option, + pub description: Option, /// Embedded image #[serde(skip_serializing_if = "Option::is_none")] - image: Option, + pub image: Option, /// Embedded video #[serde(skip_serializing_if = "Option::is_none")] - video: Option