Compare commits

..

4 Commits

Author SHA1 Message Date
Zomatree
7780aa69c0 fix: manually specify params 2025-11-18 14:57:34 +00:00
Zomatree
d198796fde Merge remote-tracking branch 'origin/main' into chore/utoipa 2025-11-17 23:11:21 +00:00
Zomatree
43c94b68b0 chore: update routes to use utoipa 2025-11-17 22:13:07 +00:00
Zomatree
ac60b2c795 chore: update everything to work with utoipa 2025-11-13 23:06:41 +00:00
192 changed files with 2567 additions and 2157 deletions

View File

@@ -7,20 +7,19 @@ on:
pull_request:
paths:
- "Dockerfile"
workflow_dispatch:
permissions:
contents: read
packages: write
concurrency:
group: ${{ github.head_ref || github.ref }}
concurrency:
group: ${{ github.head_ref }}
cancel-in-progress: true
jobs:
base:
name: Test base image build
runs-on: arc-runner-set
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
# Configure build environment
@@ -41,7 +40,7 @@ jobs:
cache-to: type=gha,scope=buildx-base-multi-arch,mode=max
publish:
runs-on: arc-runner-set
runs-on: self-hosted
if: github.event_name != 'pull_request'
name: Publish Docker images
steps:

View File

@@ -1,21 +0,0 @@
name: Publish Crates
on:
workflow_dispatch:
release:
types: [published]
jobs:
publish:
name: Publish Crates
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false
- name: Publish crates
uses: katyo/publish-crates@02cc2f1ad653fb25c7d1ff9eb590a8a50d06186b # v2
with:
registry-token: ${{ secrets.CRATES_IO_PUBLISH_TOKEN }}

View File

@@ -1,62 +0,0 @@
name: Release Please
on:
push:
branches: [main] # updates/opens the release PR when commits land on main
workflow_dispatch:
permissions:
contents: write
pull-requests: write
id-token: write
concurrency:
group: release-please
cancel-in-progress: true
jobs:
release-please:
name: Release Please
runs-on: ubuntu-latest
outputs:
release_created: ${{ steps.rp.outputs.release_created }}
tag_name: ${{ steps.rp.outputs.tag_name }}
steps:
- id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.GH_STOAT_RELEASE_APP_ID }}
private-key: ${{ secrets.GH_STOAT_RELEASE_APP_PRIVATE_KEY }}
- id: rp
uses: googleapis/release-please-action@v4
with:
token: ${{ steps.app-token.outputs.token }}
config-file: release-please-config.json
- name: Install latest stable
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # v1
with:
toolchain: stable
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
token: ${{ steps.app-token.outputs.token }}
- name: Update Cargo.lock
if: ${{ steps.rp.outputs.prs_created == 'true' || steps.rp.outputs.prs_updated == 'true' }}
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
PR_NUMBER=$(echo '${{ steps.rp.outputs.prs }}' | jq -r '.[0].number')
gh pr checkout "$PR_NUMBER"
cargo update -w
if git diff --quiet Cargo.lock; then
echo "No changes to Cargo.lock"
else
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Cargo.lock
git commit -s -m "chore: update Cargo.lock"
git push
fi

View File

@@ -1,19 +0,0 @@
name: Release Webhook
on:
workflow_dispatch:
release:
types: [published]
jobs:
release-webhook:
name: Send Release Webhook
runs-on: ubuntu-latest
steps:
- name: Send release notification webhook
run: |
RELEASE_URL="https://github.com/${{ github.repository }}/releases/tag/${{ github.event.release.tag_name }}"
curl -X POST "${{ secrets.STOAT_WEBHOOK_UPDATES_URL }}" \
-H "Content-Type: application/json" \
-d "{\"content\": \"$RELEASE_URL\"}"

View File

@@ -5,8 +5,8 @@ on:
branches: [main]
pull_request:
concurrency:
group: ${{ github.head_ref || github.ref }}
concurrency:
group: ${{ github.head_ref }}
cancel-in-progress: true
env:
@@ -15,30 +15,29 @@ env:
jobs:
check:
name: Rust project
runs-on: arc-runner-set
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false
# Using our own runners for now:
# - name: Free up disk space
# run: |
# sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc
- uses: actions/checkout@v2
- name: Free up disk space
run: |
sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc
- name: Install latest stable
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # v1
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
components: rustfmt, clippy
- name: Install cargo-nextest
uses: taiki-e/install-action@a58ae9526b2c3acee9ad8e1926b950b7863305d4 # 2.65.16
uses: baptiste0928/cargo-install@v1
with:
tool: cargo-nextest@0.9.119
crate: cargo-nextest
args: --locked
- name: Run cargo build
run: cargo build
uses: actions-rs/cargo@v1
with:
command: build
- name: Run services in background
run: |
@@ -66,13 +65,13 @@ jobs:
- name: Wait for API to go up
if: github.event_name != 'pull_request' && github.ref_name == 'main'
uses: nev7n/wait_for_response@7fef3c1a6e8939d0b09062f14fec50d3c5d15fa1 # v1.0.1
uses: nev7n/wait_for_response@v1
with:
url: "http://localhost:14702/"
- name: Checkout API repository
if: github.event_name != 'pull_request' && github.ref_name == 'main'
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
uses: actions/checkout@v3
with:
repository: stoatchat/javascript-client-api
path: api
@@ -84,10 +83,10 @@ jobs:
- name: Commit changes
if: github.event_name != 'pull_request' && github.ref_name == 'main'
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4
uses: EndBug/add-and-commit@v4
with:
cwd: "api"
add: "*.json"
author_name: Stoat CI
author_email: stoat-ci@users.noreply.github.com
author_name: Revolt CI
author_email: revolt-ci@users.noreply.github.com
message: "chore: generate OpenAPI specification"

View File

@@ -1,3 +0,0 @@
{
".": "0.9.4"
}

View File

@@ -1,73 +0,0 @@
# Changelog
## [0.9.4](https://github.com/stoatchat/stoatchat/compare/v0.9.3...v0.9.4) (2026-01-10)
### Bug Fixes
* checkout repo. before bumping lock ([#490](https://github.com/stoatchat/stoatchat/issues/490)) ([b2da2a8](https://github.com/stoatchat/stoatchat/commit/b2da2a858787853be43136fd526a0bd72baf78ef))
* persist credentials for git repo ([#492](https://github.com/stoatchat/stoatchat/issues/492)) ([c674a9f](https://github.com/stoatchat/stoatchat/commit/c674a9fd4e0abbd51569870e4b38074d4a1de03c))
## [0.9.3](https://github.com/stoatchat/stoatchat/compare/v0.9.2...v0.9.3) (2026-01-10)
### Bug Fixes
* pipeline fixes ([#487](https://github.com/stoatchat/stoatchat/issues/487)) ([aeeafeb](https://github.com/stoatchat/stoatchat/commit/aeeafebefc36a43a656cf797c9251ca50292733c))
## [0.9.2](https://github.com/stoatchat/stoatchat/compare/v0.9.1...v0.9.2) (2026-01-10)
### Bug Fixes
* disable publish for services ([#485](https://github.com/stoatchat/stoatchat/issues/485)) ([d13609c](https://github.com/stoatchat/stoatchat/commit/d13609c37279d6a40445dcd99564e5c3dd03bac1))
## [0.9.1](https://github.com/stoatchat/stoatchat/compare/v0.9.0...v0.9.1) (2026-01-10)
### Bug Fixes
* **ci:** pipeline fixes (marked as fix to force release) ([#483](https://github.com/stoatchat/stoatchat/issues/483)) ([303e52b](https://github.com/stoatchat/stoatchat/commit/303e52b476585eea81c33837f1b01506ce387684))
## [0.9.0](https://github.com/stoatchat/stoatchat/compare/v0.8.8...v0.9.0) (2026-01-10)
### Features
* add id field to role ([#470](https://github.com/stoatchat/stoatchat/issues/470)) ([2afea56](https://github.com/stoatchat/stoatchat/commit/2afea56e56017f02de98e67316b4457568ad5b26))
* add ratelimits to gifbox ([1542047](https://github.com/stoatchat/stoatchat/commit/154204742d21cbeff6e2577b00f50b495ea44631))
* include groups and dms in fetch mutuals ([caa8607](https://github.com/stoatchat/stoatchat/commit/caa86074680d46223cebc20f41e9c91c41ec825d))
* include member payload in ServerMemberJoin event ([480f210](https://github.com/stoatchat/stoatchat/commit/480f210ce85271e13d1dac58a5dae08de108579d))
* initial work on tenor gif searching ([b0c977b](https://github.com/stoatchat/stoatchat/commit/b0c977b324b8144c1152589546eb8fec5954c3e7))
* make message lexer use unowned string ([1561481](https://github.com/stoatchat/stoatchat/commit/1561481eb4cdc0f385fbf0a81e4950408050e11f))
* ready payload field customisation ([db57706](https://github.com/stoatchat/stoatchat/commit/db577067948f13e830b5fb773034e9713a1abaff))
* require auth for search ([b5cd5e3](https://github.com/stoatchat/stoatchat/commit/b5cd5e30ef7d5e56e8964fb7c543965fa6bf5a4a))
* trending and categories routes ([5885e06](https://github.com/stoatchat/stoatchat/commit/5885e067a627b8fff1c8ce2bf9e852ff8cf3f07a))
* voice chats v2 ([#414](https://github.com/stoatchat/stoatchat/issues/414)) ([d567155](https://github.com/stoatchat/stoatchat/commit/d567155f124e4da74115b1a8f810062f7c6559d9))
### Bug Fixes
* add license to revolt-parser ([5335124](https://github.com/stoatchat/stoatchat/commit/53351243064cac8d499dd74284be73928fa78a43))
* allow for disabling default features ([65fbd36](https://github.com/stoatchat/stoatchat/commit/65fbd3662462aed1333b79e59155fa6377e83fcc))
* apple music to use original url instead of metadata url ([bfe4018](https://github.com/stoatchat/stoatchat/commit/bfe4018e436a4075bae780dd4d35a9b58315e12f))
* apply uname fix to january and autumn ([8f9015a](https://github.com/stoatchat/stoatchat/commit/8f9015a6ff181d208d9269ab8691bd417d39811a))
* **ci:** publish images under stoatchat and remove docker hub ([d65c1a1](https://github.com/stoatchat/stoatchat/commit/d65c1a1ab3bdc7e5684b03f280af77d881661a3d))
* correct miniz_oxide in lockfile ([#478](https://github.com/stoatchat/stoatchat/issues/478)) ([5d27a91](https://github.com/stoatchat/stoatchat/commit/5d27a91e901dd2ea3e860aeaed8468db6c5f3214))
* correct shebang for try-tag-and-release ([050ba16](https://github.com/stoatchat/stoatchat/commit/050ba16d4adad5d0fb247867aa3e94e3d42bd12d))
* correct string_cache in lockfile ([#479](https://github.com/stoatchat/stoatchat/issues/479)) ([0b178fc](https://github.com/stoatchat/stoatchat/commit/0b178fc791583064bf9ca94b1d39b42d021e1d79))
* don't remove timeouts when a member leaves a server ([#409](https://github.com/stoatchat/stoatchat/issues/409)) ([e635bc2](https://github.com/stoatchat/stoatchat/commit/e635bc23ec857d648d5705e1a3875d7bc3402b0d))
* don't update the same field while trying to remove it ([f4ee35f](https://github.com/stoatchat/stoatchat/commit/f4ee35fb093ca49f0a64ff4b17fd61587df28145)), closes [#392](https://github.com/stoatchat/stoatchat/issues/392)
* github webhook incorrect payload and formatting ([#468](https://github.com/stoatchat/stoatchat/issues/468)) ([dc9c82a](https://github.com/stoatchat/stoatchat/commit/dc9c82aa4e9667ea6639256c65ac8de37a24d1f7))
* implement Serialize to ClientMessage ([dea0f67](https://github.com/stoatchat/stoatchat/commit/dea0f675dde7a63c7a59b38d469f878b7a8a3af4))
* newly created roles should be ranked the lowest ([947eb15](https://github.com/stoatchat/stoatchat/commit/947eb15771ed6785b3dcd16c354c03ded5e4cbe0))
* permit empty `remove` array in edit requests ([6ad3da5](https://github.com/stoatchat/stoatchat/commit/6ad3da5f35f989a2e7d8e29718b98374248e76af))
* preserve order of replies in message ([#447](https://github.com/stoatchat/stoatchat/issues/447)) ([657a3f0](https://github.com/stoatchat/stoatchat/commit/657a3f08e5d652814bbf0647e089ed9ebb139bbf))
* prevent timing out members which have TimeoutMembers permission ([e36fc97](https://github.com/stoatchat/stoatchat/commit/e36fc9738bac0de4f3fcbccba521f1e3754f7ae7))
* relax settings name regex ([3a34159](https://github.com/stoatchat/stoatchat/commit/3a3415915f0d0fdce1499d47a2b7fa097f5946ea))
* remove authentication tag bytes from attachment download ([32e6600](https://github.com/stoatchat/stoatchat/commit/32e6600272b885c595c094f0bc69459250220dcb))
* rename openapi operation ids ([6048587](https://github.com/stoatchat/stoatchat/commit/6048587d348fbca0dc3a9b47690c56df8fece576)), closes [#406](https://github.com/stoatchat/stoatchat/issues/406)
* respond with 201 if no body in requests ([#465](https://github.com/stoatchat/stoatchat/issues/465)) ([24fedf8](https://github.com/stoatchat/stoatchat/commit/24fedf8c4d9cd3160bdec97aa451520f8beaa739))
* swap to using reqwest for query building ([38dd4d1](https://github.com/stoatchat/stoatchat/commit/38dd4d10797b3e6e397fc219e818f379bdff19f2))
* use `trust_cloudflare` config value instead of env var ([cc7a796](https://github.com/stoatchat/stoatchat/commit/cc7a7962a882e1627fcd0bc75858a017415e8cfc))
* use our own result types instead of tenors types ([a92152d](https://github.com/stoatchat/stoatchat/commit/a92152d86da136997817e797c7af8e38731cdde8))

570
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -11,8 +11,9 @@ members = [
[patch.crates-io]
redis23 = { package = "redis", version = "0.23.3", git = "https://github.com/revoltchat/redis-rs", rev = "523b2937367e17bd0073722bf6e23d06042cb4e4" }
#authifier = { package = "authifier", version = "1.0.10", path = "../authifier/crates/authifier" }
#rocket_authifier = { package = "rocket_authifier", version = "1.0.10", path = "../authifier/crates/rocket_authifier" }
authifier = { package = "authifier", version = "1.0.10", path = "../authifier/crates/authifier" }
rocket_authifier = { package = "rocket_authifier", version = "1.0.10", path = "../authifier/crates/rocket_authifier" }
iso8601-timestamp = { path = "../iso8601-timestamp" }
# I'm 99% sure this is overloading the GitHub worker
# hence builds have been failing since, let's just

View File

@@ -37,6 +37,9 @@ The services and libraries that power the Revolt service.<br/>
Rust 1.86.0 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). This is currently solved by build time options but we are looking for a proper fix.
## 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/).

View File

@@ -55,7 +55,7 @@ services:
# Mock SMTP server
maildev:
image: maildev/maildev
image: soulteary/maildev
ports:
- "14025:25"
- "14080:8080"

View File

@@ -1,9 +1,8 @@
[package]
name = "revolt-bonfire"
version = "0.9.4"
version = "0.8.9"
license = "AGPL-3.0-or-later"
edition = "2021"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -43,7 +42,7 @@ revolt-result = { path = "../core/result" }
revolt-models = { path = "../core/models" }
revolt-config = { path = "../core/config" }
revolt-database = { path = "../core/database", features = ["voice"] }
revolt-permissions = { path = "../core/permissions" }
revolt-permissions = { version = "0.8.9", path = "../core/permissions" }
revolt-presence = { path = "../core/presence", features = ["redis-is-patched"] }
# redis

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-coalesced"
version = "0.9.4"
version = "0.8.9"
edition = "2021"
license = "MIT"
authors = ["Paul Makles <me@insrt.uk>", "Zomatree <me@zomatree.live>"]
@@ -15,8 +15,8 @@ default = ["tokio"]
[dependencies]
tokio = { version = "1.47.0", features = ["sync"], optional = true }
indexmap = { version = "2.13.0", optional = true }
lru = { version = "0.16.3", optional = true }
indexmap = { version = "*", optional = true }
lru = { version = "*", optional = true }
[dev-dependencies]
tokio = { version = "1.47.0", features = ["rt", "rt-multi-thread", "macros", "time"] }

View File

@@ -15,7 +15,7 @@ use crate::{CoalescionServiceConfig, Error};
#[derive(Debug, Clone)]
#[allow(clippy::type_complexity)]
/// # Coalescion service
/// Coalescion service
///
/// See module description for example usage.
pub struct CoalescionService<Id: Hash + Clone + Eq> {

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-config"
version = "0.9.4"
version = "0.8.9"
edition = "2021"
license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"]
@@ -37,4 +37,4 @@ sentry = { version = "0.31.5", optional = true }
sentry-anyhow = { version = "0.38.1", optional = true }
# Core
revolt-result = { version = "0.9.4", path = "../result", optional = true }
revolt-result = { version = "0.8.9", path = "../result", optional = true }

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-database"
version = "0.9.4"
version = "0.8.9"
edition = "2021"
license = "AGPL-3.0-or-later"
authors = ["Paul Makles <me@insrt.uk>"]
@@ -15,7 +15,7 @@ mongodb = ["dep:mongodb", "bson", "authifier/database-mongodb"]
# ... Other
tasks = ["isahc", "linkify", "url-escape"]
async-std-runtime = ["async-std", "authifier/async-std-runtime"]
rocket-impl = ["rocket", "schemars", "revolt_okapi", "revolt_rocket_okapi", "authifier/rocket_impl"]
rocket-impl = ["rocket"]
axum-impl = ["axum"]
redis-is-patched = ["revolt-presence/redis-is-patched"]
voice = ["livekit-api", "livekit-protocol", "livekit-runtime"]
@@ -25,19 +25,19 @@ default = ["mongodb", "async-std-runtime", "tasks"]
[dependencies]
# Core
revolt-config = { version = "0.9.4", path = "../config", features = [
revolt-config = { version = "0.8.9", path = "../config", features = [
"report-macros",
] }
revolt-result = { version = "0.9.4", path = "../result" }
revolt-models = { version = "0.9.4", path = "../models", features = [
revolt-result = { version = "0.8.9", path = "../result" }
revolt-models = { version = "0.8.9", path = "../models", features = [
"validator",
] }
revolt-presence = { version = "0.9.4", path = "../presence" }
revolt-permissions = { version = "0.9.4", path = "../permissions", features = [
revolt-presence = { version = "0.8.9", path = "../presence" }
revolt-permissions = { version = "0.8.9", path = "../permissions", features = [
"serde",
"bson",
] }
revolt-parser = { version = "0.9.4", path = "../parser" }
revolt-parser = { version = "0.8.9", path = "../parser" }
# Utility
log = "0.4"
@@ -47,7 +47,7 @@ ulid = "1.0.0"
nanoid = "0.4.0"
base64 = "0.21.3"
once_cell = "1.17"
indexmap = "1.9.1"
indexmap = "2.12.0"
decancer = "1.6.2"
deadqueue = "0.2.4"
linkify = { optional = true, version = "0.8.1" }
@@ -59,7 +59,7 @@ isahc = { optional = true, version = "1.7", features = ["json"] }
serde_json = "1"
revolt_optional_struct = "0.2.0"
serde = { version = "1", features = ["derive"] }
iso8601-timestamp = { version = "0.2.10", features = ["serde", "bson"] }
iso8601-timestamp = { version = "0.4.0", features = ["serde", "bson"] }
# Events
redis-kiss = { version = "0.1.4" }
@@ -82,15 +82,15 @@ async-recursion = "1.0.4"
async-std = { version = "1.8.0", features = ["attributes"], optional = true }
# Axum Impl
axum = { version = "0.7.5", optional = true }
axum = { version = "0.8.6", optional = true }
# Rocket Impl
schemars = { version = "0.8.8", optional = true }
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.10.0", optional = true }
# Openapi Schema
utoipa = { version = "5.4.0", optional = true }
# Authifier
authifier = { version = "1.0.15" }
@@ -99,6 +99,6 @@ authifier = { version = "1.0.15" }
amqprs = { version = "1.7.0" }
# Voice
livekit-api = { version = "0.4.4", optional = true}
livekit-protocol = { version = "0.4.0", optional = true }
livekit-runtime = { version = "0.3.1", features = ["tokio"], optional = true }
livekit-api = { version = "=0.4.4", optional = true}
livekit-protocol = { version = "=0.4.0", optional = true }
livekit-runtime = { version = "=0.3.1", features = ["tokio"], optional = true }

View File

@@ -47,7 +47,6 @@
],
"roles": {
"__ID:5__": {
"_id": "__ID:5__",
"name": "Moderator",
"permissions": {
"a": 545270208,
@@ -56,7 +55,6 @@
"rank": 1
},
"__ID:6__": {
"_id": "__ID:6__",
"name": "Owner",
"permissions": {
"a": 0,

View File

@@ -25,6 +25,10 @@ pub use mongodb;
#[macro_use]
extern crate bson;
#[cfg(feature = "utoipa")]
#[macro_use]
extern crate utoipa;
#[cfg(not(feature = "async-std-runtime"))]
compile_error!("async-std-runtime feature must be enabled.");
@@ -60,7 +64,7 @@ macro_rules! database_derived {
macro_rules! auto_derived {
( $( $item:item )+ ) => {
$(
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
$item
)+
};
@@ -68,8 +72,8 @@ macro_rules! auto_derived {
macro_rules! auto_derived_partial {
( $item:item, $name:expr ) => {
#[derive(OptionalStruct, Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
#[optional_derive(Serialize, Deserialize, Debug, Clone, Default, Eq, PartialEq)]
#[derive(OptionalStruct, Serialize, Deserialize, Debug, Clone, PartialEq)]
#[optional_derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
#[optional_name = $name]
#[opt_skip_serializing_none]
#[opt_some_priority]

View File

@@ -25,7 +25,7 @@ struct MigrationInfo {
revision: i32,
}
pub const LATEST_REVISION: i32 = 50; // MUST BE +1 to last migration
pub const LATEST_REVISION: i32 = 49; // MUST BE +1 to last migration
pub async fn migrate_database(db: &MongoDb) {
let migrations = db.col::<Document>("migrations");
@@ -1246,7 +1246,7 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
.expect("Failed to update voice channels");
};
if revision <= 48 {
if revision <= 48 {
info!("Running migration [revision 48 / 22-10-2025]: Add Video + Listen to default permissions");
db.col::<Document>("servers")
@@ -1264,48 +1264,6 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
.expect("Failed to update default_permissions");
};
if revision <= 49 {
info!("Running migration [revision 49 / 12-12-2025]: Add _id key to roles");
#[derive(Serialize, Deserialize, Clone)]
struct Server {
#[serde(rename = "_id")]
pub id: String,
#[serde(default = "HashMap::<String, Document>::new")]
pub roles: HashMap<String, Document>,
}
let mut servers = db
.db()
.collection::<Server>("servers")
.find(doc! {
"roles": {
"$exists": true,
"$ne": {}
}
})
.await
.unwrap()
.map(|res| res.expect("Failed to decode Server { id, roles }"));
while let Some(server) = servers.next().await {
let mut doc = doc! {};
for id in server.roles.keys() {
doc.insert(
format!("roles.{id}._id"),
id,
);
}
db.db()
.collection::<Server>("servers")
.update_one(doc! { "_id": &server.id }, doc! { "$set": doc })
.await
.unwrap();
}
};
// Reminder to update LATEST_REVISION when adding new migrations.
LATEST_REVISION.max(revision)
}

View File

@@ -14,7 +14,7 @@ auto_derived!(
}
/// Composite primary key consisting of channel and user id
#[derive(Hash)]
#[derive(Hash, Eq)]
pub struct ChannelCompositeKey {
/// Channel Id
pub channel: String,

View File

@@ -247,7 +247,7 @@ impl AbstractMessages for ReferenceDb {
let mut messages = self.messages.lock().await;
if let Some(message) = messages.get_mut(id) {
if let Some(users) = message.reactions.get_mut(emoji) {
users.remove(&user.to_string());
users.swap_remove(&user.to_string());
}
Ok(())
@@ -260,7 +260,7 @@ impl AbstractMessages for ReferenceDb {
async fn clear_reaction(&self, id: &str, emoji: &str) -> Result<()> {
let mut messages = self.messages.lock().await;
if let Some(message) = messages.get_mut(id) {
message.reactions.remove(emoji);
message.reactions.swap_remove(emoji);
Ok(())
} else {
Err(create_error!(NotFound))

View File

@@ -55,7 +55,7 @@ auto_derived_partial!(
auto_derived!(
/// Composite primary key consisting of server and user id
#[derive(Hash, Default)]
#[derive(Hash, Default, Eq)]
pub struct MemberCompositeKey {
/// Server Id
pub server: String,

View File

@@ -68,9 +68,6 @@ auto_derived_partial!(
auto_derived_partial!(
/// Role
pub struct Role {
/// Unique Id
#[serde(rename = "_id")]
pub id: String,
/// Role name
pub name: String,
/// Permissions available to this role
@@ -249,6 +246,7 @@ impl Server {
role.update(
db,
&self.id,
role_id,
PartialRole {
permissions: Some(permissions),
..Default::default()
@@ -299,7 +297,6 @@ impl Role {
/// Into optional struct
pub fn into_optional(self) -> PartialRole {
PartialRole {
id: Some(self.id),
name: Some(self.name),
permissions: Some(self.permissions),
colour: self.colour,
@@ -309,29 +306,20 @@ impl Role {
}
/// Create a role
pub async fn create(db: &Database, server: &Server, name: String) -> Result<Self> {
let role = Role {
id: Ulid::new().to_string(),
name,
// Rank of the new role should be below the lowest role
rank: server.roles.len() as i64,
colour: None,
hoist: false,
permissions: Default::default(),
};
db.insert_role(&server.id, &role).await?;
pub async fn create(&self, db: &Database, server_id: &str) -> Result<String> {
let role_id = Ulid::new().to_string();
db.insert_role(server_id, &role_id, self).await?;
EventV1::ServerRoleUpdate {
id: server.id.clone(),
role_id: role.id.clone(),
data: role.clone().into_optional().into(),
id: server_id.to_string(),
role_id: role_id.to_string(),
data: self.clone().into_optional().into(),
clear: vec![],
}
.p(server.id.clone())
.p(server_id.to_string())
.await;
Ok(role)
Ok(role_id)
}
/// Update server data
@@ -339,6 +327,7 @@ impl Role {
&mut self,
db: &Database,
server_id: &str,
role_id: &str,
partial: PartialRole,
remove: Vec<FieldsRole>,
) -> Result<()> {
@@ -348,14 +337,14 @@ impl Role {
self.apply_options(partial.clone());
db.update_role(server_id, &self.id, &partial, remove.clone())
db.update_role(server_id, role_id, &partial, remove.clone())
.await?;
EventV1::ServerRoleUpdate {
id: server_id.to_string(),
role_id: self.id.clone(),
role_id: role_id.to_string(),
data: partial.into(),
clear: remove.into_iter().map(Into::into).collect(),
clear: vec![],
}
.p(server_id.to_string())
.await;
@@ -371,15 +360,15 @@ impl Role {
}
/// Delete a role
pub async fn delete(self, db: &Database, server_id: &str) -> Result<()> {
pub async fn delete(self, db: &Database, server_id: &str, role_id: &str) -> Result<()> {
EventV1::ServerRoleDelete {
id: server_id.to_string(),
role_id: self.id.clone(),
role_id: role_id.to_string(),
}
.p(server_id.to_string())
.await;
db.delete_role(server_id, &self.id).await
db.delete_role(server_id, role_id).await
}
}

View File

@@ -29,7 +29,7 @@ pub trait AbstractServers: Sync + Send {
async fn delete_server(&self, id: &str) -> Result<()>;
/// Insert a new role into server object
async fn insert_role(&self, server_id: &str, role: &Role) -> Result<()>;
async fn insert_role(&self, server_id: &str, role_id: &str, role: &Role) -> Result<()>;
/// Update an existing role on a server
async fn update_role(

View File

@@ -69,7 +69,7 @@ impl AbstractServers for MongoDb {
}
/// Insert a new role into server object
async fn insert_role(&self, server_id: &str, role: &Role) -> Result<()> {
async fn insert_role(&self, server_id: &str, role_id: &str, role: &Role) -> Result<()> {
self.col::<Document>(COL)
.update_one(
doc! {
@@ -77,7 +77,7 @@ impl AbstractServers for MongoDb {
},
doc! {
"$set": {
"roles.".to_owned() + &role.id: to_document(role)
"roles.".to_owned() + role_id: to_document(role)
.map_err(|_| create_database_error!("to_document", "role"))?
}
},

View File

@@ -72,10 +72,10 @@ impl AbstractServers for ReferenceDb {
}
/// Insert a new role into server object
async fn insert_role(&self, server_id: &str, role: &Role) -> Result<()> {
async fn insert_role(&self, server_id: &str, role_id: &str, role: &Role) -> Result<()> {
let mut servers = self.servers.lock().await;
if let Some(server) = servers.get_mut(server_id) {
server.roles.insert(role.id.clone(), role.clone());
server.roles.insert(role_id.to_string(), role.clone());
Ok(())
} else {
Err(create_error!(NotFound))

View File

@@ -4,7 +4,6 @@ use revolt_result::{create_error, Error, Result};
use crate::{Database, User};
#[async_trait::async_trait]
impl<S> FromRequestParts<S> for User
where
Database: FromRef<S>,

View File

@@ -4,8 +4,6 @@ mod model;
mod ops;
#[cfg(feature = "rocket-impl")]
mod rocket;
#[cfg(feature = "rocket-impl")]
mod schema;
pub use model::*;
pub use ops::*;

View File

@@ -15,7 +15,7 @@ use serde_json::json;
use ulid::Ulid;
auto_derived_partial!(
/// # User
/// User
pub struct User {
/// Unique Id
#[serde(rename = "_id")]

View File

@@ -1,31 +0,0 @@
use revolt_okapi::openapi3::{SecurityScheme, SecuritySchemeData};
use revolt_rocket_okapi::{
gen::OpenApiGenerator,
request::{OpenApiFromRequest, RequestHeaderInput},
};
use crate::User;
impl OpenApiFromRequest<'_> for User {
fn from_request_input(
_gen: &mut OpenApiGenerator,
_name: String,
_required: bool,
) -> revolt_rocket_okapi::Result<RequestHeaderInput> {
let mut requirements = schemars::Map::new();
requirements.insert("Session Token".to_owned(), vec![]);
Ok(RequestHeaderInput::Security(
"Session Token".to_owned(),
SecurityScheme {
data: SecuritySchemeData::ApiKey {
name: "x-session-token".to_owned(),
location: "header".to_owned(),
},
description: Some("Used to authenticate as a user.".to_owned()),
extensions: schemars::Map::new(),
},
requirements,
))
}
}

View File

@@ -17,7 +17,7 @@ use super::DelayedTask;
use crate::Channel::TextChannel;
/// Enumeration of possible events
#[derive(Debug, Eq, PartialEq)]
#[derive(Debug, PartialEq)]
pub enum AckEvent {
/// Add mentions for a channel
ProcessMessage {

View File

@@ -896,7 +896,6 @@ impl From<SystemMessageChannels> for crate::SystemMessageChannels {
impl From<crate::Role> for Role {
fn from(value: crate::Role) -> Self {
Role {
id: value.id,
name: value.name,
permissions: value.permissions,
colour: value.colour,
@@ -909,7 +908,6 @@ impl From<crate::Role> for Role {
impl From<Role> for crate::Role {
fn from(value: Role) -> crate::Role {
crate::Role {
id: value.id,
name: value.name,
permissions: value.permissions,
colour: value.colour,
@@ -922,7 +920,6 @@ impl From<Role> for crate::Role {
impl From<crate::PartialRole> for PartialRole {
fn from(value: crate::PartialRole) -> Self {
PartialRole {
id: value.id,
name: value.name,
permissions: value.permissions,
colour: value.colour,
@@ -935,7 +932,6 @@ impl From<crate::PartialRole> for PartialRole {
impl From<PartialRole> for crate::PartialRole {
fn from(value: PartialRole) -> crate::PartialRole {
crate::PartialRole {
id: value.id,
name: value.name,
permissions: value.permissions,
colour: value.colour,

View File

@@ -10,16 +10,16 @@ use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct IdempotencyKey {
key: String,
}
#[cfg_attr(feature = "utoipa", derive(IntoParams))]
#[cfg_attr(feature = "utoipa", into_params(names("Idempotency-Key"), parameter_in=Header))]
pub struct IdempotencyKey(String);
static TOKEN_CACHE: Lazy<Mutex<lru::LruCache<String, ()>>> =
Lazy::new(|| Mutex::new(lru::LruCache::new(NonZeroUsize::new(1000).unwrap())));
impl IdempotencyKey {
pub fn unchecked_from_string(key: String) -> Self {
Self { key }
Self(key)
}
// Backwards compatibility.
@@ -32,56 +32,56 @@ impl IdempotencyKey {
}
cache.put(v.clone(), ());
self.key = v;
self.0 = v;
}
Ok(())
}
pub fn into_key(self) -> String {
self.key
self.0
}
}
#[cfg(feature = "rocket-impl")]
use revolt_rocket_okapi::{
gen::OpenApiGenerator,
request::{OpenApiFromRequest, RequestHeaderInput},
revolt_okapi::openapi3::{Parameter, ParameterValue},
};
// #[cfg(feature = "rocket-impl")]
// use revolt_rocket_okapi::{
// gen::OpenApiGenerator,
// request::{OpenApiFromRequest, RequestHeaderInput},
// revolt_okapi::openapi3::{Parameter, ParameterValue},
// };
#[cfg(feature = "rocket-impl")]
use schemars::schema::{InstanceType, SchemaObject, SingleOrVec};
// #[cfg(feature = "rocket-impl")]
// use schemars::schema::{InstanceType, SchemaObject, SingleOrVec};
#[cfg(feature = "rocket-impl")]
impl OpenApiFromRequest<'_> for IdempotencyKey {
fn from_request_input(
_gen: &mut OpenApiGenerator,
_name: String,
_required: bool,
) -> revolt_rocket_okapi::Result<RequestHeaderInput> {
Ok(RequestHeaderInput::Parameter(Parameter {
name: "Idempotency-Key".to_string(),
description: Some("Unique key to prevent duplicate requests".to_string()),
allow_empty_value: false,
required: false,
deprecated: false,
extensions: schemars::Map::new(),
location: "header".to_string(),
value: ParameterValue::Schema {
allow_reserved: false,
example: None,
examples: None,
explode: None,
style: None,
schema: SchemaObject {
instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))),
..Default::default()
},
},
}))
}
}
// #[cfg(feature = "rocket-impl")]
// impl OpenApiFromRequest<'_> for IdempotencyKey {
// fn from_request_input(
// _gen: &mut OpenApiGenerator,
// _name: String,
// _required: bool,
// ) -> revolt_rocket_okapi::Result<RequestHeaderInput> {
// Ok(RequestHeaderInput::Parameter(Parameter {
// name: "Idempotency-Key".to_string(),
// description: Some("Unique key to prevent duplicate requests".to_string()),
// allow_empty_value: false,
// required: false,
// deprecated: false,
// extensions: schemars::Map::new(),
// location: "header".to_string(),
// value: ParameterValue::Schema {
// allow_reserved: false,
// example: None,
// examples: None,
// explode: None,
// style: None,
// schema: SchemaObject {
// instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))),
// ..Default::default()
// },
// },
// }))
// }
// }
#[cfg(feature = "rocket-impl")]
use rocket::{
@@ -110,18 +110,16 @@ impl<'r> FromRequest<'r> for IdempotencyKey {
));
}
let idempotency = IdempotencyKey { key };
let idempotency = IdempotencyKey(key);
let mut cache = TOKEN_CACHE.lock().await;
if cache.get(&idempotency.key).is_some() {
if cache.get(&idempotency.0).is_some() {
return Outcome::Error((Status::Conflict, create_error!(DuplicateNonce)));
}
cache.put(idempotency.key.clone(), ());
cache.put(idempotency.0.clone(), ());
return Outcome::Success(idempotency);
}
Outcome::Success(IdempotencyKey {
key: ulid::Ulid::new().to_string(),
})
Outcome::Success(IdempotencyKey(ulid::Ulid::new().to_string()))
}
}

View File

@@ -5,5 +5,7 @@ pub mod idempotency;
pub mod permissions;
pub mod reference;
pub mod test_fixtures;
#[cfg(feature = "utoipa")]
pub mod utoipa;
pub use funcs::*;

View File

@@ -3,11 +3,6 @@ use std::str::FromStr;
use revolt_result::Result;
#[cfg(feature = "rocket-impl")]
use rocket::request::FromParam;
#[cfg(feature = "rocket-impl")]
use schemars::{
schema::{InstanceType, Schema, SchemaObject, SingleOrVec},
JsonSchema,
};
use crate::{
Bot, Channel, Database, Emoji, Invite, Member, Message, Server, ServerBan, User, Webhook,
@@ -112,17 +107,3 @@ impl<'r> FromParam<'r> for Reference<'r> {
Ok(Reference::from_unchecked(param))
}
}
#[cfg(feature = "rocket-impl")]
impl<'a> JsonSchema for Reference<'a> {
fn schema_name() -> String {
"Id".to_string()
}
fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> Schema {
Schema::Object(SchemaObject {
instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))),
..Default::default()
})
}
}

View File

@@ -0,0 +1,49 @@
use utoipa::{
openapi::{
schema::SchemaType,
security::{ApiKey, ApiKeyValue, SecurityScheme},
ObjectBuilder, OpenApi, RefOr, Schema, Type,
},
Modify, PartialSchema, ToSchema,
};
use crate::util::reference::Reference;
pub struct TokenSecurity;
impl Modify for TokenSecurity {
fn modify(&self, openapi: &mut OpenApi) {
let components = openapi.components.get_or_insert_default();
components.add_security_scheme(
"Session-Token",
SecurityScheme::ApiKey(ApiKey::Header(ApiKeyValue::new(
"X-Session-Token".to_string(),
))),
);
components.add_security_scheme(
"Bot-Token",
SecurityScheme::ApiKey(ApiKey::Header(ApiKeyValue::new("X-Bot-Ticket".to_string()))),
);
}
}
impl ToSchema for Reference<'_> {
fn name() -> std::borrow::Cow<'static, str> {
std::borrow::Cow::Borrowed("Reference")
}
}
impl PartialSchema for Reference<'_> {
fn schema() -> RefOr<Schema> {
RefOr::T(
ObjectBuilder::new()
.description(Some("An id referencing a stoat model."))
.schema_type(SchemaType::Type(Type::String))
.examples(["01FD58YK5W7QRV5H3D64KTQYX3"])
.build()
.into(),
)
}
}

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-files"
version = "0.9.4"
version = "0.8.9"
edition = "2021"
license = "AGPL-3.0-or-later"
authors = ["Paul Makles <me@insrt.uk>"]
@@ -20,10 +20,10 @@ typenum = "1.17.0"
aws-config = "1.5.5"
aws-sdk-s3 = { version = "1.46.0", features = ["behavior-version-latest"] }
revolt-config = { version = "0.9.4", path = "../config", features = [
revolt-config = { version = "0.8.9", path = "../config", features = [
"report-macros",
] }
revolt-result = { version = "0.9.4", path = "../result" }
revolt-result = { version = "0.8.9", path = "../result" }
# image processing
jxl-oxide = "0.8.1"

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-models"
version = "0.9.4"
version = "0.8.9"
edition = "2021"
license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"]
@@ -10,22 +10,22 @@ 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", "utoipa"]
partials = ["dep:revolt_optional_struct", "serde", "utoipa"]
default = ["serde", "partials", "rocket"]
[dependencies]
# Core
revolt-config = { version = "0.9.4", path = "../config" }
revolt-permissions = { version = "0.9.4", path = "../permissions" }
revolt-config = { version = "0.8.9", path = "../config" }
revolt-permissions = { version = "0.8.9", path = "../permissions", features = [
"utoipa",
] }
# Utility
regex = "1.11"
indexmap = "1.9.3"
indexmap = "2.12.0"
once_cell = "1.17.1"
num_enum = "0.6.1"
@@ -35,11 +35,10 @@ rocket = { optional = true, version = "0.5.0-rc.2", default-features = false }
# Serialisation
revolt_optional_struct = { version = "0.2.0", optional = true }
serde = { version = "1", features = ["derive"], optional = true }
iso8601-timestamp = { version = "0.2.11", features = ["schema", "bson"] }
iso8601-timestamp = { version = "0.4.0", features = ["utoipa", "bson"] }
# Spec Generation
schemars = { version = "0.8.8", optional = true, features = ["indexmap1"] }
utoipa = { version = "4.2.3", optional = true }
utoipa = { version = "5.4.0", features = ["indexmap"], optional = true }
# Validation
validator = { version = "0.16.0", optional = true, features = ["derive"] }

View File

@@ -2,10 +2,6 @@
#[macro_use]
extern crate serde;
#[cfg(feature = "schemas")]
#[macro_use]
extern crate schemars;
#[cfg(feature = "utoipa")]
#[macro_use]
extern crate utoipa;
@@ -21,9 +17,8 @@ macro_rules! auto_derived {
( $( $item:item )+ ) => {
$(
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemas", derive(JsonSchema))]
#[cfg_attr(feature = "utoipa", derive(ToSchema))]
#[derive(Debug, Clone, Eq, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
$item
)+
};
@@ -32,19 +27,8 @@ macro_rules! auto_derived {
#[cfg(feature = "partials")]
macro_rules! auto_derived_partial {
( $item:item, $name:expr ) => {
#[derive(
OptionalStruct, Debug, Clone, Eq, PartialEq, Serialize, Deserialize, JsonSchema,
)]
#[optional_derive(
Debug,
Clone,
Eq,
PartialEq,
Serialize,
Deserialize,
JsonSchema,
Default
)]
#[derive(OptionalStruct, Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)]
#[optional_derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema, Default)]
#[optional_name = $name]
#[opt_skip_serializing_none]
#[opt_some_priority]
@@ -55,7 +39,7 @@ macro_rules! auto_derived_partial {
#[cfg(not(feature = "partials"))]
macro_rules! auto_derived_partial {
( $item:item, $name:expr ) => {
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)]
$item
};
}

View File

@@ -86,10 +86,10 @@ auto_derived!(
/// Information for the webhook
#[cfg_attr(feature = "validator", derive(Validate))]
pub struct CreateWebhookBody {
#[validate(length(min = 1, max = 32))]
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 32)))]
pub name: String,
#[validate(length(min = 1, max = 128))]
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 128)))]
pub avatar: Option<String>,
}
);

View File

@@ -271,6 +271,7 @@ auto_derived!(
/// Options when deleting a channel
#[cfg_attr(feature = "rocket", derive(FromForm))]
#[cfg_attr(feature = "utoipa", derive(IntoParams))]
pub struct OptionsChannelDelete {
/// Whether to not send a leave message
pub leave_silently: Option<bool>,

View File

@@ -0,0 +1,78 @@
auto_derived!(
/// hCaptcha Configuration
pub struct CaptchaFeature {
/// Whether captcha is enabled
pub enabled: bool,
/// Client key used for solving captcha
pub key: String,
}
/// Generic Service Configuration
pub struct Feature {
/// Whether the service is enabled
pub enabled: bool,
/// URL pointing to the service
pub url: String,
}
/// # Information about a livekit node
pub struct VoiceNode {
pub name: String,
pub lat: f64,
pub lon: f64,
pub public_url: String,
}
/// # Voice Server Configuration
pub struct VoiceFeature {
/// Whether voice is enabled
pub enabled: bool,
/// All livekit nodes
pub nodes: Vec<VoiceNode>,
}
/// Feature Configuration
pub struct RevoltFeatures {
/// hCaptcha configuration
pub captcha: CaptchaFeature,
/// Whether email verification is enabled
pub email: bool,
/// Whether this server is invite only
pub invite_only: bool,
/// File server service configuration
pub autumn: Feature,
/// Proxy service configuration
pub january: Feature,
/// Voice server configuration
pub livekit: VoiceFeature,
}
/// Build Information
pub struct BuildInformation {
/// Commit Hash
pub commit_sha: String,
/// Commit Timestamp
pub commit_timestamp: String,
/// Git Semver
pub semver: String,
/// Git Origin URL
pub origin_url: String,
/// Build Timestamp
pub timestamp: String,
}
/// Server Configuration
pub struct RevoltConfig {
/// Revolt API Version
pub revolt: String,
/// Features enabled on this Revolt node
pub features: RevoltFeatures,
/// WebSocket URL
pub ws: String,
/// URL pointing to the client serving this node
pub app: String,
/// Web Push VAPID public key
pub vapid: String,
/// Build information
pub build: BuildInformation,
}
);

View File

@@ -46,7 +46,7 @@ auto_derived!(
#[cfg_attr(feature = "validator", derive(Validate))]
pub struct DataCreateEmoji {
/// Server name
#[validate(length(min = 1, max = 32), regex = "RE_EMOJI")]
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 32), regex = "RE_EMOJI"))]
pub name: String,
/// Parent information
pub parent: EmojiParent,

View File

@@ -141,17 +141,17 @@ auto_derived!(
pub struct Masquerade {
/// Replace the display name shown on this message
#[serde(skip_serializing_if = "Option::is_none")]
#[validate(length(min = 1, max = 32))]
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 32)))]
pub name: Option<String>,
/// Replace the avatar shown on this message (URL to image file)
#[serde(skip_serializing_if = "Option::is_none")]
#[validate(length(min = 1, max = 256))]
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 256)))]
pub avatar: Option<String>,
/// Replace the display role colour shown on this message
///
/// Must have `ManageRole` permission to use
#[serde(skip_serializing_if = "Option::is_none")]
#[validate(length(min = 1, max = 128), regex = "RE_COLOUR")]
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 128), regex = "RE_COLOUR"))]
pub colour: Option<String>,
}
@@ -281,6 +281,7 @@ auto_derived!(
/// Options for querying messages
#[cfg_attr(feature = "validator", derive(Validate))]
#[cfg_attr(feature = "rocket", derive(FromForm))]
#[cfg_attr(feature = "utoipa", derive(IntoParams))]
pub struct OptionsQueryMessages {
/// Maximum number of messages to fetch
///
@@ -353,12 +354,13 @@ auto_derived!(
)]
pub struct OptionsBulkDelete {
/// Message IDs
#[validate(length(min = 1, max = 100))]
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 100)))]
pub ids: Vec<String>,
}
/// Options for removing reaction
#[cfg_attr(feature = "rocket", derive(FromForm))]
#[cfg_attr(feature = "utoipa", derive(IntoParams))]
pub struct OptionsUnreact {
/// Remove a specific user's reaction
pub user_id: Option<String>,

View File

@@ -3,6 +3,7 @@ mod channel_invites;
mod channel_unreads;
mod channel_webhooks;
mod channels;
mod config;
mod embeds;
mod emojis;
mod files;
@@ -20,6 +21,7 @@ pub use channel_invites::*;
pub use channel_unreads::*;
pub use channel_webhooks::*;
pub use channels::*;
pub use config::*;
pub use embeds::*;
pub use emojis::*;
pub use files::*;

View File

@@ -116,6 +116,7 @@ auto_derived!(
/// Options for fetching all members
#[cfg_attr(feature = "rocket", derive(FromForm))]
#[cfg_attr(feature = "utoipa", derive(IntoParams))]
pub struct OptionsFetchAllMembers {
/// Whether to exclude offline users
pub exclude_offline: Option<bool>,

View File

@@ -85,9 +85,6 @@ auto_derived_partial!(
auto_derived_partial!(
/// Role
pub struct Role {
/// Unique Id
#[cfg_attr(feature = "serde", serde(rename = "_id"))]
pub id: String,
/// Role name
pub name: String,
/// Permissions available to this role
@@ -184,7 +181,6 @@ auto_derived!(
}
/// Response after creating new role
// TODO: remove this in favor of just Role
pub struct NewRoleResponse {
/// Id of the role
pub id: String,
@@ -202,6 +198,7 @@ auto_derived!(
/// Options when fetching server
#[cfg_attr(feature = "rocket", derive(FromForm))]
#[cfg_attr(feature = "utoipa", derive(IntoParams))]
pub struct OptionsFetchServer {
/// Whether to include channels
pub include_channels: Option<bool>,
@@ -288,6 +285,7 @@ auto_derived!(
/// Options when leaving a server
#[cfg_attr(feature = "rocket", derive(FromForm))]
#[cfg_attr(feature = "utoipa", derive(IntoParams))]
pub struct OptionsServerDelete {
/// Whether to not send a leave message
pub leave_silently: Option<bool>,

View File

@@ -17,6 +17,7 @@ auto_derived!(
/// Additional options for inserting settings
#[cfg_attr(feature = "rocket", derive(FromForm))]
#[cfg_attr(feature = "utoipa", derive(IntoParams))]
pub struct OptionsSetSettings {
/// Timestamp of settings change.
///

View File

@@ -142,7 +142,7 @@ auto_derived!(
#[cfg_attr(feature = "validator", derive(Validate))]
pub struct UserStatus {
/// Custom status text
#[validate(length(min = 0, max = 128))]
#[cfg_attr(feature = "validator", validate(length(min = 0, max = 128)))]
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub text: Option<String>,
/// Current presence option
@@ -155,7 +155,7 @@ auto_derived!(
#[cfg_attr(feature = "validator", derive(Validate))]
pub struct UserProfile {
/// Text content on user's profile
#[validate(length(min = 0, max = 2000))]
#[cfg_attr(feature = "validator", validate(length(min = 0, max = 2000)))]
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub content: Option<String>,
/// Background visible on user's profile

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-parser"
version = "0.9.4"
version = "0.8.9"
edition = "2021"
license = "MIT"
authors = ["Zomatree <me@zomatree.live>", "Paul Makles <me@insrt.uk>"]

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-permissions"
version = "0.9.4"
version = "0.8.9"
edition = "2021"
license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"]
@@ -11,17 +11,15 @@ description = "Revolt Backend: Permission Logic"
[features]
bson = ["dep:bson"]
serde = ["dep:serde"]
schemas = ["dep:schemars"]
try-from-primitive = ["dep:num_enum"]
[dev-dependencies]
# Async
async-std = { version = "1.8.0", features = ["attributes"] }
[dependencies]
# Core
revolt-result = { version = "0.9.4", path = "../result" }
revolt-result = { version = "0.8.9", path = "../result" }
# Utility
auto_ops = "0.3.0"
@@ -36,4 +34,4 @@ serde = { version = "1", features = ["derive"], optional = true }
bson = { version = "2.1.0", optional = true }
# Spec Generation
schemars = { version = "0.8.8", optional = true }
utoipa = { version = "5.4.0", optional = true }

View File

@@ -1,10 +1,10 @@
#[cfg(feature = "schemas")]
use schemars::JsonSchema;
#[cfg(feature = "utoipa")]
use utoipa::ToSchema;
/// Representation of a single permission override
#[derive(Debug, Clone, Eq, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "schemas", derive(JsonSchema))]
#[cfg_attr(feature = "utoipa", derive(ToSchema))]
pub struct Override {
/// Allow bit flags
pub allow: u64,
@@ -15,7 +15,7 @@ pub struct Override {
/// Data permissions Field - contains both allow and deny
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "schemas", derive(JsonSchema))]
#[cfg_attr(feature = "utoipa", derive(ToSchema))]
pub struct DataPermissionsField {
pub permissions: Override,
}
@@ -23,7 +23,7 @@ pub struct DataPermissionsField {
/// Data permissions Value - contains allow
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "schemas", derive(JsonSchema))]
#[cfg_attr(feature = "utoipa", derive(ToSchema))]
pub struct DataPermissionsValue {
pub permissions: u64,
}
@@ -31,7 +31,7 @@ pub struct DataPermissionsValue {
/// Data permissions Poly - can contain either Value or Field
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "schemas", derive(JsonSchema))]
#[cfg_attr(feature = "utoipa", derive(ToSchema))]
#[cfg_attr(feature = "serde", serde(untagged))]
pub enum DataPermissionPoly {
Value {
@@ -48,7 +48,7 @@ pub enum DataPermissionPoly {
/// as it appears on models and in the database
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "schemas", derive(JsonSchema))]
#[cfg_attr(feature = "utoipa", derive(ToSchema))]
pub struct OverrideField {
/// Allow bit flags
pub a: i64,

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-presence"
version = "0.9.4"
version = "0.8.9"
edition = "2021"
license = "AGPL-3.0-or-later"
authors = ["Paul Makles <me@insrt.uk>"]
@@ -16,7 +16,7 @@ redis-is-patched = []
async-std = { version = "1.8.0", features = ["attributes"] }
# Config for loading Redis URI
revolt-config = { version = "0.9.4", path = "../config" }
revolt-config = { version = "0.8.9", path = "../config" }
[dependencies]
# Utility

View File

@@ -1,23 +1,25 @@
[package]
name = "revolt-ratelimits"
version = "0.9.4"
version = "0.8.9"
edition = "2024"
[features]
rocket = ["dep:rocket", "dep:revolt_rocket_okapi", "revolt-database/rocket-impl"]
rocket = [
"dep:rocket",
"revolt-database/rocket-impl",
]
axum = ["dep:axum", "revolt-database/axum-impl"]
default = ["rocket", "axum"]
[dependencies]
revolt-database = { version = "0.9.4", path = "../database"}
revolt-result = { version = "0.9.4", path = "../result" }
revolt-config = { version = "0.9.4", path = "../config" }
revolt-database = { version = "0.8.9", path = "../database" }
revolt-result = { version = "0.8.9", path = "../result" }
revolt-config = { version = "0.8.9", path = "../config" }
rocket = { version = "0.5.1", optional = true }
revolt_rocket_okapi = { version = "0.10.0", optional = true }
axum = { version = "0.7.5", optional = true, features = ["macros"] }
axum = { version = "0.8.6", optional = true, features = ["macros"] }
serde = { version = "1", features = ["derive"] }
authifier = { version = "1.0.15" }

View File

@@ -1,6 +1,5 @@
use std::net::SocketAddr;
use async_trait::async_trait;
use axum::{
Json, RequestPartsExt, Router,
body::Body,
@@ -44,7 +43,6 @@ async fn to_real_ip(parts: &Parts) -> String {
}
}
#[async_trait]
impl<S: Send + Sync> FromRequestParts<S> for Ratelimiter
where
Database: FromRef<S>,
@@ -82,7 +80,6 @@ where
}
}
#[async_trait]
impl<S: Send + Sync> FromRequestParts<S> for RatelimitInformation
where
Database: FromRef<S>,

View File

@@ -8,9 +8,6 @@ use rocket::serde::json::Json;
use rocket::{Data, Request, Response, State};
use revolt_config::config;
use revolt_rocket_okapi::r#gen::OpenApiGenerator;
use revolt_rocket_okapi::request::{OpenApiFromRequest, RequestHeaderInput};
use authifier::models::Session;
use crate::ratelimiter::RequestKind;
@@ -78,16 +75,6 @@ impl<'r> FromRequest<'r> for Ratelimiter {
}
}
impl OpenApiFromRequest<'_> for Ratelimiter {
fn from_request_input(
_gen: &mut OpenApiGenerator,
_name: String,
_required: bool,
) -> revolt_rocket_okapi::Result<RequestHeaderInput> {
Ok(RequestHeaderInput::None)
}
}
/// Attach ratelimiter to the Rocket application
pub struct RatelimitFairing;

View File

@@ -1,6 +1,6 @@
[package]
name = "revolt-result"
version = "0.9.4"
version = "0.8.9"
edition = "2021"
license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"]
@@ -10,11 +10,9 @@ description = "Revolt Backend: Result and Error types"
[features]
serde = ["dep:serde"]
schemas = ["dep:schemars"]
utoipa = ["dep:utoipa"]
rocket = ["dep:rocket", "dep:serde_json"]
axum = ["dep:axum", "dep:serde_json"]
okapi = ["dep:revolt_rocket_okapi", "dep:revolt_okapi", "schemas"]
sentry = ["dep:sentry"]
default = ["serde", "sentry"]
@@ -25,17 +23,14 @@ serde_json = { version = "1", optional = true }
serde = { version = "1", features = ["derive"], optional = true }
# Spec Generation
schemars = { version = "0.8.8", optional = true }
utoipa = { version = "4.2.3", optional = true }
utoipa = { version = "5.4.0", optional = true }
# Rocket
rocket = { optional = true, version = "0.5.0-rc.2", default-features = false }
revolt_rocket_okapi = { version = "0.10.0", optional = true }
revolt_okapi = { version = "0.9.1", optional = true }
# utilities
log = "0.4"
# Axum
axum = { version = "0.7.5", optional = true }
axum = { version = "0.8.6", optional = true }
sentry = { version = "0.31.5", optional = true }
sentry = { version = "0.31.5", optional = true }

View File

@@ -5,13 +5,10 @@ use std::fmt::Display;
#[macro_use]
extern crate serde;
#[cfg(feature = "schemas")]
#[macro_use]
extern crate schemars;
#[cfg(feature = "utoipa")]
#[macro_use]
extern crate utoipa;
mod utoipa_impl;
#[cfg(feature = "utoipa")]
pub use crate::utoipa_impl::*;
#[cfg(feature = "rocket")]
pub mod rocket;
@@ -19,16 +16,12 @@ pub mod rocket;
#[cfg(feature = "axum")]
pub mod axum;
#[cfg(feature = "okapi")]
pub mod okapi;
/// Result type with custom Error
pub type Result<T, E = Error> = std::result::Result<T, E>;
/// Error information
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemas", derive(JsonSchema))]
#[cfg_attr(feature = "utoipa", derive(ToSchema))]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Clone)]
pub struct Error {
/// Type of error and additional information
@@ -50,8 +43,7 @@ impl std::error::Error for Error {}
/// Possible error types
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "type"))]
#[cfg_attr(feature = "schemas", derive(JsonSchema))]
#[cfg_attr(feature = "utoipa", derive(ToSchema))]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[derive(Debug, Clone)]
pub enum ErrorType {
/// This error was not labeled :(

View File

@@ -1,49 +0,0 @@
use revolt_okapi::openapi3::SchemaObject;
use revolt_rocket_okapi::revolt_okapi::openapi3;
use schemars::schema::Schema;
use crate::Error;
impl revolt_rocket_okapi::response::OpenApiResponderInner for Error {
fn responses(
gen: &mut revolt_rocket_okapi::gen::OpenApiGenerator,
) -> std::result::Result<openapi3::Responses, revolt_rocket_okapi::OpenApiError> {
let mut content = revolt_okapi::Map::new();
let settings = schemars::gen::SchemaSettings::default().with(|s| {
s.option_nullable = true;
s.option_add_null_type = false;
s.definitions_path = "#/components/schemas/".to_string();
});
let mut schema_generator = settings.into_generator();
let schema = schema_generator.root_schema_for::<Error>();
let definitions = gen.schema_generator().definitions_mut();
for (key, value) in schema.definitions {
definitions.insert(key, value);
}
definitions.insert("Error".to_string(), Schema::Object(schema.schema));
content.insert(
"application/json".to_string(),
openapi3::MediaType {
schema: Some(SchemaObject {
reference: Some("#/components/schemas/Error".to_string()),
..Default::default()
}),
..Default::default()
},
);
Ok(openapi3::Responses {
default: Some(openapi3::RefOr::Object(openapi3::Response {
content,
description: "An error occurred.".to_string(),
..Default::default()
})),
..Default::default()
})
}
}

View File

@@ -0,0 +1,52 @@
use utoipa::{Modify, openapi::{Content, OpenApi, Ref, RefOr, ResponseBuilder}};
pub struct ErrorAddon;
impl Modify for ErrorAddon {
fn modify(&self, utoipa: &mut OpenApi) {
let response = ResponseBuilder::new()
.description("An error occurred")
.content(
"application/json",
Content::new(Some(RefOr::Ref(Ref::from_schema_name("Error")))),
)
.build();
for path in utoipa.paths.paths.values_mut() {
if let Some(route) = path.get.as_mut() {
route
.responses
.responses
.insert("default".to_string(), RefOr::T(response.clone()));
};
if let Some(route) = path.delete.as_mut() {
route
.responses
.responses
.insert("default".to_string(), RefOr::T(response.clone()));
};
if let Some(route) = path.patch.as_mut() {
route
.responses
.responses
.insert("default".to_string(), RefOr::T(response.clone()));
};
if let Some(route) = path.post.as_mut() {
route
.responses
.responses
.insert("default".to_string(), RefOr::T(response.clone()));
};
if let Some(route) = path.put.as_mut() {
route
.responses
.responses
.insert("default".to_string(), RefOr::T(response.clone()));
};
}
}
}

View File

@@ -1,11 +1,10 @@
[package]
name = "revolt-crond"
version = "0.9.4"
version = "0.8.9"
license = "AGPL-3.0-or-later"
authors = ["Paul Makles <me@insrt.uk>"]
edition = "2021"
description = "Revolt Daemon Service: Timed data clean up tasks"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -17,7 +16,7 @@ log = "0.4"
tokio = { version = "1" }
# Core
revolt-database = { version = "0.9.4", path = "../../core/database" }
revolt-result = { version = "0.9.4", path = "../../core/result" }
revolt-config = { version = "0.9.4", path = "../../core/config" }
revolt-files = { version = "0.9.4", path = "../../core/files" }
revolt-database = { version = "0.8.9", path = "../../core/database" }
revolt-result = { version = "0.8.9", path = "../../core/result" }
revolt-config = { version = "0.8.9", path = "../../core/config" }
revolt-files = { version = "0.8.9", path = "../../core/files" }

View File

@@ -1,21 +1,20 @@
[package]
name = "revolt-pushd"
version = "0.9.4"
version = "0.8.9"
edition = "2021"
license = "AGPL-3.0-or-later"
publish = false
[dependencies]
revolt-result = { version = "0.9.4", path = "../../core/result" }
revolt-config = { version = "0.9.4", path = "../../core/config", features = [
revolt-result = { version = "0.8.9", path = "../../core/result" }
revolt-config = { version = "0.8.9", path = "../../core/config", features = [
"report-macros",
"anyhow"
] }
revolt-database = { version = "0.9.4", path = "../../core/database" }
revolt-models = { version = "0.9.4", path = "../../core/models", features = [
revolt-database = { version = "0.8.9", path = "../../core/database" }
revolt-models = { version = "0.8.9", path = "../../core/models", features = [
"validator",
] }
revolt-presence = { version = "0.9.4", path = "../../core/presence", features = [
revolt-presence = { version = "0.8.9", path = "../../core/presence", features = [
"redis-is-patched",
] }
@@ -39,5 +38,5 @@ pretty_env_logger = "0.4.0"
serde_json = "1"
revolt_optional_struct = "0.2.0"
serde = { version = "1", features = ["derive"] }
iso8601-timestamp = { version = "0.2.10", features = ["serde", "bson"] }
iso8601-timestamp = { version = "0.4.0", features = ["serde", "bson"] }
base64 = "0.22.1"

View File

@@ -1,9 +1,8 @@
[package]
name = "revolt-voice-ingress"
version = "0.9.4"
version = "0.7.1"
license = "AGPL-3.0-or-later"
edition = "2021"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -41,9 +40,9 @@ revolt-database = { path = "../../core/database", features = ["voice"] }
revolt-permissions = { path = "../../core/permissions" }
# Voice
livekit-api = "0.4.4"
livekit-protocol = "0.4.0"
livekit-runtime = { version = "0.3.1", features = ["tokio"] }
livekit-api = "=0.4.4"
livekit-protocol = "=0.4.0"
livekit-runtime = { version = "=0.3.1", features = ["tokio"] }
# RabbitMQ
amqprs = { version = "1.7.0" }

View File

@@ -1,10 +1,9 @@
[package]
name = "revolt-delta"
version = "0.9.4"
version = "0.8.9"
license = "AGPL-3.0-or-later"
authors = ["Paul Makles <paulmakles@gmail.com>"]
edition = "2018"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -36,7 +35,7 @@ nanoid = "0.4.0"
serde_json = "1.0.57"
serde = { version = "1.0.115", features = ["derive"] }
validator = { version = "0.16", features = ["derive"] }
iso8601-timestamp = { version = "0.2.11", features = [] }
iso8601-timestamp = { version = "0.4.0", features = [] }
# async
futures = "0.3.8"
@@ -56,13 +55,13 @@ lettre = "0.10.0-alpha.4"
rocket = { version = "0.5.1", default-features = false, features = ["json"] }
rocket_cors = { git = "https://github.com/lawliet89/rocket_cors", rev = "072d90359b23e9b291df6b672c07c93de9c46011" }
rocket_empty = { version = "0.1.1", features = ["schema"] }
rocket_empty = { version = "0.1.1" }
rocket_authifier = { version = "1.0.15" }
rocket_prometheus = "0.10.0-rc.3"
# spec generation
schemars = "0.8.8"
revolt_rocket_okapi = { version = "0.10.0", features = ["swagger"] }
utoipa = { version = "5.4.0", features = ["rocket_extras"] }
utoipa-scalar = { version = "0.3.0", features = ["rocket"] }
# rabbit
amqprs = { version = "1.7.0" }
@@ -73,21 +72,22 @@ revolt-config = { path = "../core/config" }
revolt-database = { path = "../core/database", features = [
"rocket-impl",
"redis-is-patched",
"utoipa",
"voice",
] }
revolt-models = { path = "../core/models", features = [
"schemas",
"utoipa",
"validator",
"rocket",
] }
revolt-presence = { path = "../core/presence" }
revolt-result = { path = "../core/result", features = ["rocket", "okapi"] }
revolt-permissions = { path = "../core/permissions", features = ["schemas"] }
revolt-result = { path = "../core/result", features = ["rocket", "utoipa"] }
revolt-permissions = { path = "../core/permissions", features = ["utoipa"] }
revolt-ratelimits = { path = "../core/ratelimits", features = ["rocket"] }
# voice
livekit-api = "0.4.4"
livekit-protocol = "0.4.0"
livekit-api = "=0.4.4"
livekit-protocol = "=0.4.0"
[build-dependencies]
vergen = "7.5.0"

View File

@@ -47,7 +47,6 @@
],
"roles": {
"__ID:5__": {
"_id": "__ID:5__",
"name": "Moderator",
"permissions": {
"a": 545270216,
@@ -56,7 +55,6 @@
"rank": 1
},
"__ID:6__": {
"_id": "__ID:6__",
"name": "Owner",
"permissions": {
"a": 0,
@@ -65,7 +63,6 @@
"rank": 0
},
"__ID:7__": {
"_id": "__ID:7__",
"name": "Lower Rank 1",
"permissions": {
"a": 0,
@@ -74,7 +71,6 @@
"rank": 2
},
"__ID:8__": {
"_id": "__ID:8__",
"name": "Lower Rank 2",
"permissions": {
"a": 0,

View File

@@ -1,7 +1,7 @@
#[macro_use]
extern crate rocket;
#[macro_use]
extern crate revolt_rocket_okapi;
extern crate utoipa;
#[macro_use]
extern crate serde_json;
@@ -15,6 +15,8 @@ use revolt_ratelimits::rocket as ratelimiter;
use rocket::{Build, Rocket};
use rocket_cors::{AllowedOrigins, CorsOptions};
use rocket_prometheus::PrometheusMetrics;
use utoipa::OpenApi;
use utoipa_scalar::{Scalar, Servable};
use std::net::Ipv4Addr;
use std::str::FromStr;
@@ -75,31 +77,6 @@ pub async fn web() -> Rocket<Build> {
.to_cors()
.expect("Failed to create CORS.");
// Configure Swagger
let swagger = revolt_rocket_okapi::swagger_ui::make_swagger_ui(
&revolt_rocket_okapi::swagger_ui::SwaggerUIConfig {
url: "/openapi.json".to_owned(),
..Default::default()
},
)
.into();
let swagger_0_8 = revolt_rocket_okapi::swagger_ui::make_swagger_ui(
&revolt_rocket_okapi::swagger_ui::SwaggerUIConfig {
url: "/0.8/openapi.json".to_owned(),
..Default::default()
},
)
.into();
let swagger_0_8 = revolt_rocket_okapi::swagger_ui::make_swagger_ui(
&revolt_rocket_okapi::swagger_ui::SwaggerUIConfig {
url: "/0.8/openapi.json".to_owned(),
..Default::default()
},
)
.into();
// Voice handler
let voice_client = VoiceClient::new(config.api.livekit.nodes.clone());
// Configure Rabbit
@@ -143,8 +120,7 @@ pub async fn web() -> Rocket<Build> {
.mount("/metrics", prometheus)
.mount("/", rocket_cors::catch_all_options_routes())
.mount("/", ratelimiter::routes())
.mount("/swagger/", swagger)
.mount("/0.8/swagger/", swagger_0_8)
.mount("/", Scalar::with_url("/scalar", routes::ApiDoc::openapi()))
.manage(authifier)
.manage(db)
.manage(amqp)

View File

@@ -5,10 +5,16 @@ use rocket::serde::json::Json;
use rocket::State;
use validator::Validate;
/// # Create Bot
/// Create Bot
///
/// Create a new Revolt bot.
#[openapi(tag = "Bots")]
#[utoipa::path(
tag = "Bots",
security(("Session-Token" = [])),
responses(
(status = 200, body = v0::BotWithUserResponse),
),
)]
#[post("/create", data = "<info>")]
pub async fn create_bot(
db: &State<Database>,

View File

@@ -3,10 +3,16 @@ use revolt_result::{create_error, Result};
use rocket::State;
use rocket_empty::EmptyResponse;
/// # Delete Bot
/// Delete Bot
///
/// Delete a bot by its id.
#[openapi(tag = "Bots")]
#[utoipa::path(
tag = "Bots",
security(("Session-Token" = [])),
responses(
(status = 204),
),
)]
#[delete("/<target>")]
pub async fn delete_bot(
db: &State<Database>,

View File

@@ -6,10 +6,16 @@ use rocket::State;
use rocket::serde::json::Json;
use validator::Validate;
/// # Edit Bot
/// Edit Bot
///
/// Edit bot details by its id.
#[openapi(tag = "Bots")]
#[utoipa::path(
tag = "Bots",
security(("Session-Token" = [])),
responses(
(status = 200, body = v0::BotWithUserResponse),
),
)]
#[patch("/<target>", data = "<data>")]
pub async fn edit_bot(
db: &State<Database>,
@@ -60,15 +66,8 @@ pub async fn edit_bot(
..Default::default()
};
bot.update(
db,
partial,
remove
.into_iter()
.map(|v| v.into())
.collect(),
)
.await?;
bot.update(db, partial, remove.into_iter().map(|v| v.into()).collect())
.await?;
Ok(Json(v0::BotWithUserResponse {
bot: bot.into(),

View File

@@ -3,10 +3,16 @@ use revolt_models::v0::FetchBotResponse;
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
/// # Fetch Bot
/// Fetch Bot
///
/// Fetch details of a bot you own by its id.
#[openapi(tag = "Bots")]
#[utoipa::path(
tag = "Bots",
security(("Session-Token" = [])),
responses(
(status = 200, body = FetchBotResponse),
),
)]
#[get("/<bot>")]
pub async fn fetch_bot(
db: &State<Database>,

View File

@@ -5,10 +5,16 @@ use revolt_result::Result;
use rocket::serde::json::Json;
use rocket::State;
/// # Fetch Owned Bots
/// Fetch Owned Bots
///
/// Fetch all of the bots that you have control over.
#[openapi(tag = "Bots")]
#[utoipa::path(
tag = "Bots",
security(("Session-Token" = [])),
responses(
(status = 200, body = OwnedBotsResponse),
),
)]
#[get("/@me")]
pub async fn fetch_owned_bots(db: &State<Database>, user: User) -> Result<Json<OwnedBotsResponse>> {
let mut bots = db.fetch_bots_by_user(&user.id).await?;

View File

@@ -5,10 +5,16 @@ use revolt_result::{create_error, Result};
use rocket::serde::json::Json;
use rocket::State;
/// # Fetch Public Bot
/// Fetch Public Bot
///
/// Fetch details of a public (or owned) bot by its id.
#[openapi(tag = "Bots")]
#[utoipa::path(
tag = "Bots",
security(("Session-Token" = []), ()),
responses(
(status = 200, body = PublicBot),
),
)]
#[get("/<target>/invite")]
pub async fn fetch_public_bot(
db: &State<Database>,

View File

@@ -11,10 +11,16 @@ use rocket::State;
use rocket::serde::json::Json;
use rocket_empty::EmptyResponse;
/// # Invite Bot
/// Invite Bot
///
/// Invite a bot to a server or group by its id.`
#[openapi(tag = "Bots")]
#[utoipa::path(
tag = "Bots",
security(("Session-Token" = [])),
responses(
(status = 200, body = v0::BotWithUserResponse),
),
)]
#[post("/<target>/invite", data = "<dest>")]
pub async fn invite_bot(
db: &State<Database>,

View File

@@ -1,4 +1,3 @@
use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi;
use rocket::Route;
mod create;
@@ -9,8 +8,22 @@ mod fetch_owned;
mod fetch_public;
mod invite;
pub fn routes() -> (Vec<Route>, OpenApi) {
openapi_get_routes_spec![
#[derive(OpenApi)]
#[openapi(
paths(
create::create_bot,
invite::invite_bot,
fetch_public::fetch_public_bot,
fetch::fetch_bot,
fetch_owned::fetch_owned_bots,
edit::edit_bot,
delete::delete_bot,
)
)]
pub struct ApiDoc;
pub fn routes() -> Vec<Route> {
routes![
create::create_bot,
invite::invite_bot,
fetch_public::fetch_public_bot,

View File

@@ -7,10 +7,20 @@ use revolt_result::{create_error, Result};
use rocket::State;
use rocket_empty::EmptyResponse;
/// # Acknowledge Message
/// Acknowledge Message
///
/// Lets the server and all other clients know that we've seen this message id in this channel.
#[openapi(tag = "Messaging")]
#[utoipa::path(
tag = "Messaging",
security(("Session-Token" = [])),
params(
("target" = Reference, Path),
("message" = Reference, Path),
),
responses(
(status = 204)
)
)]
#[put("/<target>/ack/<message>")]
pub async fn ack(
db: &State<Database>,

View File

@@ -9,10 +9,20 @@ use revolt_result::{create_error, Result};
use rocket::State;
use rocket_empty::EmptyResponse;
/// # Close Channel
/// Close Channel
///
/// Deletes a server channel, leaves a group or closes a group.
#[openapi(tag = "Channel Information")]
#[utoipa::path(
tag = "Channel Information",
security(("Session-Token" = []), ("Bot-Token" = [])),
params(
("target" = Reference, Path),
v0::OptionsChannelDelete,
),
responses(
(status = 204),
),
)]
#[delete("/<target>?<options..>")]
pub async fn delete(
db: &State<Database>,

View File

@@ -9,10 +9,19 @@ use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use validator::Validate;
/// # Edit Channel
/// Edit Channel
///
/// Edit a channel object by its id.
#[openapi(tag = "Channel Information")]
#[utoipa::path(
tag = "Channel Information",
security(("Session-Token" = []), ("Bot-Token" = [])),
params(
("target" = Reference, Path),
),
responses(
(status = 200, body = v0::Channel),
),
)]
#[patch("/<target>", data = "<data>")]
pub async fn edit(
db: &State<Database>,

View File

@@ -8,10 +8,19 @@ use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::Result;
use rocket::{serde::json::Json, State};
/// # Fetch Channel
/// Fetch Channel
///
/// Fetch channel by its id.
#[openapi(tag = "Channel Information")]
#[utoipa::path(
tag = "Channel Information",
security(("Session-Token" = []), ("Bot-Token" = [])),
params(
("target" = Reference, Path),
),
responses(
(status = 200, body = v0::Channel),
),
)]
#[get("/<target>")]
pub async fn fetch(
db: &State<Database>,

View File

@@ -8,10 +8,20 @@ use revolt_result::{create_error, Result};
use rocket::State;
use rocket_empty::EmptyResponse;
/// # Add Member to Group
/// Add Member to Group
///
/// Adds another user to the group.
#[openapi(tag = "Groups")]
#[utoipa::path(
tag = "Groups",
security(("Session-Token" = [])),
params(
("group_id" = Reference, Path),
("member_id" = Reference, Path),
),
responses(
(status = 204),
),
)]
#[put("/<group_id>/recipients/<member_id>")]
pub async fn add_member(
db: &State<Database>,

View File

@@ -6,10 +6,16 @@ use rocket::serde::json::Json;
use rocket::State;
use validator::Validate;
/// # Create Group
/// Create Group
///
/// Create a new group channel.
#[openapi(tag = "Groups")]
#[utoipa::path(
tag = "Groups",
security(("Session-Token" = [])),
responses(
(status = 200, body = v0::Channel),
),
)]
#[post("/create", data = "<data>")]
pub async fn create_group(
db: &State<Database>,

View File

@@ -9,10 +9,20 @@ use revolt_result::{create_error, Result};
use rocket::State;
use rocket_empty::EmptyResponse;
/// # Remove Member from Group
/// Remove Member from Group
///
/// Removes a user from the group.
#[openapi(tag = "Groups")]
#[utoipa::path(
tag = "Groups",
security(("Session-Token" = [])),
params(
("target" = Reference, Path),
("member" = Reference, Path),
),
responses(
(status = 204),
),
)]
#[delete("/<target>/recipients/<member>")]
pub async fn remove_member(
db: &State<Database>,

View File

@@ -8,12 +8,21 @@ use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
/// # Create Invite
/// Create Invite
///
/// Creates an invite to this channel.
///
/// Channel must be a `TextChannel`.
#[openapi(tag = "Channel Invites")]
#[utoipa::path(
tag = "Channel Invites",
security(("Session-Token" = [])),
params(
("target" = Reference, Path),
),
responses(
(status = 200, body = v0::Invite),
),
)]
#[post("/<target>/invites")]
pub async fn create_invite(
db: &State<Database>,

View File

@@ -7,12 +7,21 @@ use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
/// # Fetch Group Members
/// Fetch Group Members
///
/// Retrieves all users who are part of this group.
///
/// This may not return full user information if users are not friends but have mutual connections.
#[openapi(tag = "Groups")]
#[utoipa::path(
tag = "Groups",
security(("Session-Token" = []), ("Bot-Token" = [])),
params(
("target" = Reference, Path),
),
responses(
(status = 200, body = Vec<v0::User>),
),
)]
#[get("/<target>/members")]
pub async fn fetch_members(
db: &State<Database>,

View File

@@ -10,14 +10,23 @@ use rocket::{serde::json::Json, State};
use rocket_empty::EmptyResponse;
use validator::Validate;
/// # Bulk Delete Messages
/// Bulk Delete Messages
///
/// Delete multiple messages you've sent or one you have permission to delete.
///
/// This will always require `ManageMessages` permission regardless of whether you own the message or not.
///
/// Messages must have been sent within the past 1 week.
#[openapi(tag = "Messaging")]
#[utoipa::path(
tag = "Messaging",
security(("Session-Token" = []), ("Bot-Token" = [])),
params(
("target" = Reference, Path),
),
responses(
(status = 204),
),
)]
#[delete("/<target>/messages/bulk", data = "<options>", rank = 1)]
pub async fn bulk_delete_messages(
db: &State<Database>,

View File

@@ -7,12 +7,22 @@ use revolt_result::Result;
use rocket::State;
use rocket_empty::EmptyResponse;
/// # Remove All Reactions from Message
/// Remove All Reactions from Message
///
/// Remove your own, someone else's or all of a given reaction.
///
/// Requires `ManageMessages` permission.
#[openapi(tag = "Interactions")]
#[utoipa::path(
tag = "Interactions",
security(("Session-Token" = []), ("Bot-Token" = [])),
params(
("target" = Reference, Path),
("msg" = Reference, Path),
),
responses(
(status = 204),
),
)]
#[delete("/<target>/messages/<msg>/reactions")]
pub async fn clear_reactions(
db: &State<Database>,
@@ -37,7 +47,7 @@ pub async fn clear_reactions(
reactions: Some(Default::default()),
..Default::default()
},
vec![]
vec![],
)
.await
.map(|_| EmptyResponse)

View File

@@ -7,10 +7,20 @@ use revolt_result::Result;
use rocket::State;
use rocket_empty::EmptyResponse;
/// # Delete Message
/// Delete Message
///
/// Delete a message you've sent or one you have permission to delete.
#[openapi(tag = "Messaging")]
#[utoipa::path(
tag = "Messaging",
security(("Session-Token" = []), ("Bot-Token" = [])),
params(
("target" = Reference, Path),
("msg" = Reference, Path),
),
responses(
(status = 204),
),
)]
#[delete("/<target>/messages/<msg>", rank = 2)]
pub async fn delete(
db: &State<Database>,

View File

@@ -10,10 +10,20 @@ use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use validator::Validate;
/// # Edit Message
/// Edit Message
///
/// Edits a message that you've previously sent.
#[openapi(tag = "Messaging")]
#[utoipa::path(
tag = "Messaging",
security(("Session-Token" = []), ("Bot-Token" = [])),
params(
("target" = Reference, Path),
("msg" = Reference, Path),
),
responses(
(status = 200, body = v0::Message),
),
)]
#[patch("/<target>/messages/<msg>", data = "<edit>")]
pub async fn edit(
db: &State<Database>,

View File

@@ -7,10 +7,20 @@ use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
/// # Fetch Message
/// Fetch Message
///
/// Retrieves a message by its id.
#[openapi(tag = "Messaging")]
#[utoipa::path(
tag = "Messaging",
security(("Session-Token" = []), ("Bot-Token" = [])),
params(
("target" = Reference, Path),
("msg" = Reference, Path),
),
responses(
(status = 200, body = v0::Message),
),
)]
#[get("/<target>/messages/<msg>")]
pub async fn fetch(
db: &State<Database>,

View File

@@ -8,10 +8,20 @@ use revolt_result::{create_error, Result};
use rocket::State;
use rocket_empty::EmptyResponse;
/// # Pins a message
/// Pins a message
///
/// Pins a message by its id.
#[openapi(tag = "Messaging")]
#[utoipa::path(
tag = "Messaging",
security(("Session-Token" = []), ("Bot-Token" = [])),
params(
("target" = Reference, Path),
("msg" = Reference, Path),
),
responses(
(status = 204),
),
)]
#[post("/<target>/messages/<msg>/pin")]
pub async fn message_pin(
db: &State<Database>,

View File

@@ -8,10 +8,20 @@ use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use validator::Validate;
/// # Fetch Messages
/// Fetch Messages
///
/// Fetch multiple messages.
#[openapi(tag = "Messaging")]
#[utoipa::path(
tag = "Messaging",
security(("Session-Token" = []), ("Bot-Token" = [])),
params(
("target" = Reference, Path),
v0::OptionsQueryMessages,
),
responses(
(status = 200, body = v0::BulkMessageResponse),
),
)]
#[get("/<target>/messages?<options..>")]
pub async fn query(
db: &State<Database>,

View File

@@ -7,10 +7,21 @@ use revolt_result::Result;
use rocket::State;
use rocket_empty::EmptyResponse;
/// # Add Reaction to Message
/// Add Reaction to Message
///
/// React to a given message.
#[openapi(tag = "Interactions")]
#[utoipa::path(
tag = "Interactions",
security(("Session-Token" = []), ("Bot-Token" = [])),
params(
("target" = Reference, Path),
("msg" = Reference, Path),
("emoji" = Reference, Path),
),
responses(
(status = 204),
),
)]
#[put("/<target>/messages/<msg>/reactions/<emoji>")]
pub async fn react_message(
db: &State<Database>,

View File

@@ -8,10 +8,19 @@ use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use validator::Validate;
/// # Search for Messages
/// Search for Messages
///
/// This route searches for messages within the given parameters.
#[openapi(tag = "Messaging")]
#[utoipa::path(
tag = "Messaging",
security(("Session-Token" = [])),
params(
("target" = Reference, Path),
),
responses(
(status = 200, body = v0::BulkMessageResponse),
),
)]
#[post("/<target>/search", data = "<options>")]
pub async fn search(
db: &State<Database>,

View File

@@ -12,10 +12,20 @@ use rocket::serde::json::Json;
use rocket::State;
use validator::Validate;
/// # Send Message
/// Send Message
///
/// Sends a message to the given channel.
#[openapi(tag = "Messaging")]
#[utoipa::path(
tag = "Messaging",
security(("Session-Token" = []), ("Bot-Token" = [])),
params(IdempotencyKey),
params(
("target" = Reference, Path),
),
responses(
(status = 200, body = v0::Message),
),
)]
#[post("/<target>/messages", data = "<data>")]
pub async fn message_send(
db: &State<Database>,
@@ -151,20 +161,29 @@ mod test {
name: "Hidden Channel".to_string(),
description: None,
nsfw: Some(false),
voice: None,
voice: None
},
true,
)
.await
.expect("Failed to make new channel");
let role = Role::create(&harness.db, &server, "Show Hidden Channel".to_string())
let role = Role {
name: "Show Hidden Channel".to_string(),
permissions: OverrideField { a: 0, d: 0 },
colour: None,
hoist: false,
rank: 5,
};
let role_id = role
.create(&harness.db, &server.id)
.await
.expect("Failed to create the role");
let mut overrides = HashMap::new();
overrides.insert(
role.id.clone(),
role_id.clone(),
OverrideField {
a: (ChannelPermission::ViewChannel) as i64,
d: 0,
@@ -272,7 +291,7 @@ mod test {
"Mention failed to be scrubbed when the user cannot see the channel"
);
let second_member_roles = vec![role.id.clone()];
let second_member_roles = vec![role_id.clone()];
let partial = PartialMember {
id: None,
joined_at: None,
@@ -281,7 +300,7 @@ mod test {
timeout: None,
roles: Some(second_member_roles),
can_publish: None,
can_receive: None,
can_receive: None
};
second_member
.update(&harness.db, partial, vec![])
@@ -487,7 +506,7 @@ mod test {
let (_, _, other_user) = harness.new_user().await;
let (server, _) = harness.new_server(&user).await;
let channel = harness.new_channel(&server).await;
let role = harness
let (role_id, _role) = harness
.new_role(
&server,
1,
@@ -509,7 +528,7 @@ mod test {
Some(&harness.amqp),
channel.clone(),
v0::DataMessageSend {
content: Some(format!("Mentioning @everyone and role <%{}>", &role.id)),
content: Some(format!("Mentioning @everyone and role <%{}>", &role_id)),
nonce: None,
attachments: None,
replies: None,
@@ -554,7 +573,7 @@ mod test {
Some(&harness.amqp),
channel.clone(),
v0::DataMessageSend {
content: Some(format!("Mentioning `@everyone` and role `<%{}>`", &role.id)),
content: Some(format!("Mentioning `@everyone` and role `<%{}>`", &role_id)),
nonce: None,
attachments: None,
replies: None,
@@ -596,7 +615,7 @@ mod test {
"Role mentions detected when inside codeblock"
);
other_member.roles.push(role.id.clone());
other_member.roles.push(role_id.clone());
harness
.db
.update_member(
@@ -606,10 +625,10 @@ mod test {
id: None,
joined_at: None,
nickname: None,
roles: Some(vec![role.id.clone()]),
roles: Some(vec![role_id.clone()]),
timeout: None,
can_publish: None,
can_receive: None,
can_receive: None
},
vec![],
)
@@ -623,7 +642,7 @@ mod test {
Some(&harness.amqp),
channel.clone(),
v0::DataMessageSend {
content: Some(format!("Mentioning @everyone and role <%{}>", &role.id)),
content: Some(format!("Mentioning @everyone and role <%{}>", &role_id)),
nonce: None,
attachments: None,
replies: None,

View File

@@ -1,14 +1,27 @@
use revolt_database::{util::{permissions::DatabasePermissionQuery, reference::Reference}, Channel, Database, FieldsMessage, PartialMessage, SystemMessage, User, AMQP};
use revolt_database::{
util::{permissions::DatabasePermissionQuery, reference::Reference},
Channel, Database, FieldsMessage, PartialMessage, SystemMessage, User, AMQP,
};
use revolt_models::v0::MessageAuthor;
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::{create_error, Result};
use rocket::State;
use rocket_empty::EmptyResponse;
/// # Unpins a message
/// Unpins a message
///
/// Unpins a message by its id.
#[openapi(tag = "Messaging")]
#[utoipa::path(
tag = "Messaging",
security(("Session-Token" = []), ("Bot-Token" = [])),
params(
("target" = Reference, Path),
("msg" = Reference, Path),
),
responses(
(status = 204),
),
)]
#[delete("/<target>/messages/<msg>/pin")]
pub async fn message_unpin(
db: &State<Database>,

View File

@@ -8,12 +8,24 @@ use revolt_result::Result;
use rocket::State;
use rocket_empty::EmptyResponse;
/// # Remove Reaction(s) to Message
/// Remove Reaction(s) to Message
///
/// Remove your own, someone else's or all of a given reaction.
///
/// Requires `ManageMessages` if changing others' reactions.
#[openapi(tag = "Interactions")]
#[utoipa::path(
tag = "Interactions",
security(("Session-Token" = []), ("Bot-Token" = [])),
params(
("target" = Reference, Path),
("msg" = Reference, Path),
("emoji" = Reference, Path),
v0::OptionsUnreact,
),
responses(
(status = 204),
),
)]
#[delete("/<target>/messages/<msg>/reactions/<emoji>?<options..>")]
pub async fn unreact_message(
db: &State<Database>,

View File

@@ -1,4 +1,3 @@
use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi;
use rocket::Route;
mod channel_ack;
@@ -29,8 +28,42 @@ mod voice_stop_ring;
mod webhook_create;
mod webhook_fetch_all;
pub fn routes() -> (Vec<Route>, OpenApi) {
openapi_get_routes_spec![
#[derive(OpenApi)]
#[openapi(
paths(
channel_ack::ack,
channel_fetch::fetch,
members_fetch::fetch_members,
channel_delete::delete,
channel_edit::edit,
invite_create::create_invite,
message_send::message_send,
message_query::query,
message_search::search,
message_pin::message_pin,
message_fetch::fetch,
message_edit::edit,
message_bulk_delete::bulk_delete_messages,
message_delete::delete,
message_unpin::message_unpin,
group_create::create_group,
group_add_member::add_member,
group_remove_member::remove_member,
voice_join::call,
voice_stop_ring::stop_ring,
permissions_set::set_role_permissions,
permissions_set_default::set_default_channel_permissions,
message_react::react_message,
message_unreact::unreact_message,
message_clear_reactions::clear_reactions,
webhook_create::create_webhook,
webhook_fetch_all::fetch_webhooks,
)
)]
pub struct ApiDoc;
pub fn routes() -> Vec<Route> {
routes![
channel_ack::ack,
channel_fetch::fetch,
members_fetch::fetch_members,

View File

@@ -6,12 +6,22 @@ use revolt_permissions::{calculate_channel_permissions, ChannelPermission, Overr
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
/// # Set Role Permission
/// Set Role Permission
///
/// Sets permissions for the specified role in this channel.
///
/// Channel must be a `TextChannel`.
#[openapi(tag = "Channel Permissions")]
#[utoipa::path(
tag = "Channel Permissions",
security(("Session-Token" = []), ("Bot-Token" = [])),
params(
("target" = Reference, Path),
("role_id" = Reference, Path),
),
responses(
(status = 200, body = v0::Channel),
),
)]
#[put("/<target>/permissions/<role_id>", data = "<data>", rank = 2)]
pub async fn set_role_permissions(
db: &State<Database>,

View File

@@ -6,12 +6,21 @@ use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
/// # Set Default Permission
/// Set Default Permission
///
/// Sets permissions for the default role in this channel.
///
/// Channel must be a `Group` or `TextChannel`.
#[openapi(tag = "Channel Permissions")]
#[utoipa::path(
tag = "Channel Permissions",
security(("Session-Token" = []), ("Bot-Token" = [])),
params(
("target" = Reference, Path),
),
responses(
(status = 200, body = v0::Channel),
),
)]
#[put("/<target>/permissions/default", data = "<data>", rank = 1)]
pub async fn set_default_channel_permissions(
db: &State<Database>,

View File

@@ -13,10 +13,19 @@ use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
/// # Join Call
/// Join Call
///
/// Asks the voice server for a token to join the call.
#[openapi(tag = "Voice")]
#[utoipa::path(
tag = "Voice",
security(("Session-Token" = []), ("Bot-Token" = [])),
params(
("target" = Reference, Path),
),
responses(
(status = 200, body = v0::DataJoinCall),
),
)]
#[post("/<target>/join_call", data = "<data>")]
pub async fn call(
db: &State<Database>,

View File

@@ -8,12 +8,23 @@ use revolt_result::{create_error, Result, ToRevoltError};
use rocket::State;
use rocket_empty::EmptyResponse;
/// # Stop Ring
/// Stop Ring
///
/// Stops ringing a specific user in a dm call.
/// You must be in the call to use this endpoint, returns NotConnected otherwise.
/// Only valid in DM/Group channels, will return NoEffect in servers.
/// Returns NotFound if the user is not in the dm/group channel
#[openapi(tag = "Voice")]
#[utoipa::path(
tag = "Voice",
security(("Session-Token" = []), ("Bot-Token" = [])),
params(
("target" = Reference, Path),
("target_user." = Reference, Path),
),
responses(
(status = 204),
),
)]
#[put("/<target>/end_ring/<target_user>")]
pub async fn stop_ring(
db: &State<Database>,

Some files were not shown because too many files have changed in this diff Show More