Compare commits

..
4 Commits
189 changed files with 2475 additions and 1986 deletions

No files matched your search

+3 -4
View File
@@ -7,20 +7,19 @@ on:
pull_request: pull_request:
paths: paths:
- "Dockerfile" - "Dockerfile"
workflow_dispatch:
permissions: permissions:
contents: read contents: read
packages: write packages: write
concurrency: concurrency:
group: ${{ github.head_ref || github.ref }} group: ${{ github.head_ref }}
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:
base: base:
name: Test base image build name: Test base image build
runs-on: arc-runner-set runs-on: ubuntu-latest
if: github.event_name == 'pull_request' if: github.event_name == 'pull_request'
steps: steps:
# Configure build environment # Configure build environment
@@ -41,7 +40,7 @@ jobs:
cache-to: type=gha,scope=buildx-base-multi-arch,mode=max cache-to: type=gha,scope=buildx-base-multi-arch,mode=max
publish: publish:
runs-on: arc-runner-set runs-on: self-hosted
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
name: Publish Docker images name: Publish Docker images
steps: steps:
-60
View File
@@ -1,60 +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
publish-please:
name: Publish Please
needs: release-please
if: needs.release-please.outputs.release_created == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- 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 }}
- name: Send release notification webhook
run: |
RELEASE_URL="https://github.com/revoltchat/backend/releases/tag/${{ needs.release-please.outputs.tag_name }}"
curl -X POST "${{ secrets.STOAT_WEBHOOK_UPDATES_URL }}" \
-H "Content-Type: application/json" \
-d "{\"content\": \"$RELEASE_URL\"}"
- name: Publish crates
uses: katyo/publish-crates@v2
with:
registry-token: ${{ secrets.CRATES_IO_PUBLISH_TOKEN }}
+19 -18
View File
@@ -6,7 +6,7 @@ on:
pull_request: pull_request:
concurrency: concurrency:
group: ${{ github.head_ref || github.ref }} group: ${{ github.head_ref }}
cancel-in-progress: true cancel-in-progress: true
env: env:
@@ -15,28 +15,29 @@ env:
jobs: jobs:
check: check:
name: Rust project name: Rust project
runs-on: arc-runner-set runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - uses: actions/checkout@v2
- name: Free up disk space
# Using our own runners for now: run: |
# - name: Free up disk space sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc
# run: |
# sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc
- name: Install latest stable - name: Install latest stable
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # v1 uses: actions-rs/toolchain@v1
with: with:
toolchain: stable toolchain: stable
override: true
components: rustfmt, clippy components: rustfmt, clippy
- name: Install cargo-nextest - name: Install cargo-nextest
uses: taiki-e/install-action@a58ae9526b2c3acee9ad8e1926b950b7863305d4 # 2.65.16 uses: baptiste0928/cargo-install@v1
with: with:
tool: cargo-nextest@0.9.119 crate: cargo-nextest
args: --locked
- name: Run cargo build - name: Run cargo build
run: cargo build uses: actions-rs/cargo@v1
with:
command: build
- name: Run services in background - name: Run services in background
run: | run: |
@@ -64,13 +65,13 @@ jobs:
- name: Wait for API to go up - name: Wait for API to go up
if: github.event_name != 'pull_request' && github.ref_name == 'main' 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: with:
url: "http://localhost:14702/" url: "http://localhost:14702/"
- name: Checkout API repository - name: Checkout API repository
if: github.event_name != 'pull_request' && github.ref_name == 'main' if: github.event_name != 'pull_request' && github.ref_name == 'main'
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 uses: actions/checkout@v3
with: with:
repository: stoatchat/javascript-client-api repository: stoatchat/javascript-client-api
path: api path: api
@@ -82,10 +83,10 @@ jobs:
- name: Commit changes - name: Commit changes
if: github.event_name != 'pull_request' && github.ref_name == 'main' 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: with:
cwd: "api" cwd: "api"
add: "*.json" add: "*.json"
author_name: Stoat CI author_name: Revolt CI
author_email: stoat-ci@users.noreply.github.com author_email: revolt-ci@users.noreply.github.com
message: "chore: generate OpenAPI specification" message: "chore: generate OpenAPI specification"
-3
View File
@@ -1,3 +0,0 @@
{
".": "0.9.2"
}
-58
View File
@@ -1,58 +0,0 @@
# Changelog
## [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))
Generated
+329 -211
View File
File diff suppressed because it is too large. Load diff
+3 -2
View File
@@ -11,8 +11,9 @@ members = [
[patch.crates-io] [patch.crates-io]
redis23 = { package = "redis", version = "0.23.3", git = "https://github.com/revoltchat/redis-rs", rev = "523b2937367e17bd0073722bf6e23d06042cb4e4" } 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" } 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" } 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 # I'm 99% sure this is overloading the GitHub worker
# hence builds have been failing since, let's just # hence builds have been failing since, let's just
+3
View File
@@ -37,6 +37,9 @@ The services and libraries that power the Revolt service.<br/>
Rust 1.86.0 or higher. 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 ## 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 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/).
+2 -3
View File
@@ -1,9 +1,8 @@
[package] [package]
name = "revolt-bonfire" name = "revolt-bonfire"
version = "0.9.2" version = "0.8.9"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
edition = "2021" edition = "2021"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # 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-models = { path = "../core/models" }
revolt-config = { path = "../core/config" } revolt-config = { path = "../core/config" }
revolt-database = { path = "../core/database", features = ["voice"] } 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"] } revolt-presence = { path = "../core/presence", features = ["redis-is-patched"] }
# redis # redis
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-coalesced" name = "revolt-coalesced"
version = "0.9.2" version = "0.8.9"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
authors = ["Paul Makles <me@insrt.uk>", "Zomatree <me@zomatree.live>"] authors = ["Paul Makles <me@insrt.uk>", "Zomatree <me@zomatree.live>"]
+1 -1
View File
@@ -15,7 +15,7 @@ use crate::{CoalescionServiceConfig, Error};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
/// # Coalescion service /// Coalescion service
/// ///
/// See module description for example usage. /// See module description for example usage.
pub struct CoalescionService<Id: Hash + Clone + Eq> { pub struct CoalescionService<Id: Hash + Clone + Eq> {
+2 -2
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-config" name = "revolt-config"
version = "0.9.2" version = "0.8.9"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"] 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 } sentry-anyhow = { version = "0.38.1", optional = true }
# Core # Core
revolt-result = { version = "0.9.2", path = "../result", optional = true } revolt-result = { version = "0.8.9", path = "../result", optional = true }
+17 -17
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-database" name = "revolt-database"
version = "0.9.2" version = "0.8.9"
edition = "2021" edition = "2021"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
authors = ["Paul Makles <me@insrt.uk>"] authors = ["Paul Makles <me@insrt.uk>"]
@@ -15,7 +15,7 @@ mongodb = ["dep:mongodb", "bson", "authifier/database-mongodb"]
# ... Other # ... Other
tasks = ["isahc", "linkify", "url-escape"] tasks = ["isahc", "linkify", "url-escape"]
async-std-runtime = ["async-std", "authifier/async-std-runtime"] 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"] axum-impl = ["axum"]
redis-is-patched = ["revolt-presence/redis-is-patched"] redis-is-patched = ["revolt-presence/redis-is-patched"]
voice = ["livekit-api", "livekit-protocol", "livekit-runtime"] voice = ["livekit-api", "livekit-protocol", "livekit-runtime"]
@@ -25,19 +25,19 @@ default = ["mongodb", "async-std-runtime", "tasks"]
[dependencies] [dependencies]
# Core # Core
revolt-config = { version = "0.9.2", path = "../config", features = [ revolt-config = { version = "0.8.9", path = "../config", features = [
"report-macros", "report-macros",
] } ] }
revolt-result = { version = "0.9.2", path = "../result" } revolt-result = { version = "0.8.9", path = "../result" }
revolt-models = { version = "0.9.2", path = "../models", features = [ revolt-models = { version = "0.8.9", path = "../models", features = [
"validator", "validator",
] } ] }
revolt-presence = { version = "0.9.2", path = "../presence" } revolt-presence = { version = "0.8.9", path = "../presence" }
revolt-permissions = { version = "0.9.2", path = "../permissions", features = [ revolt-permissions = { version = "0.8.9", path = "../permissions", features = [
"serde", "serde",
"bson", "bson",
] } ] }
revolt-parser = { version = "0.9.2", path = "../parser" } revolt-parser = { version = "0.8.9", path = "../parser" }
# Utility # Utility
log = "0.4" log = "0.4"
@@ -47,7 +47,7 @@ ulid = "1.0.0"
nanoid = "0.4.0" nanoid = "0.4.0"
base64 = "0.21.3" base64 = "0.21.3"
once_cell = "1.17" once_cell = "1.17"
indexmap = "1.9.1" indexmap = "2.12.0"
decancer = "1.6.2" decancer = "1.6.2"
deadqueue = "0.2.4" deadqueue = "0.2.4"
linkify = { optional = true, version = "0.8.1" } linkify = { optional = true, version = "0.8.1" }
@@ -59,7 +59,7 @@ isahc = { optional = true, version = "1.7", features = ["json"] }
serde_json = "1" serde_json = "1"
revolt_optional_struct = "0.2.0" revolt_optional_struct = "0.2.0"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
iso8601-timestamp = { version = "0.2.10", features = ["serde", "bson"] } iso8601-timestamp = { version = "0.4.0", features = ["serde", "bson"] }
# Events # Events
redis-kiss = { version = "0.1.4" } 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 } async-std = { version = "1.8.0", features = ["attributes"], optional = true }
# Axum Impl # Axum Impl
axum = { version = "0.7.5", optional = true } axum = { version = "0.8.6", optional = true }
# Rocket Impl # Rocket Impl
schemars = { version = "0.8.8", optional = true }
rocket = { version = "0.5.1", default-features = false, features = [ rocket = { version = "0.5.1", default-features = false, features = [
"json", "json",
], optional = true } ], 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
authifier = { version = "1.0.15" } authifier = { version = "1.0.15" }
@@ -99,6 +99,6 @@ authifier = { version = "1.0.15" }
amqprs = { version = "1.7.0" } amqprs = { version = "1.7.0" }
# Voice # Voice
livekit-api = { version = "0.4.4", optional = true} livekit-api = { version = "=0.4.4", optional = true}
livekit-protocol = { version = "0.4.0", optional = true } livekit-protocol = { version = "=0.4.0", optional = true }
livekit-runtime = { version = "0.3.1", features = ["tokio"], optional = true } livekit-runtime = { version = "=0.3.1", features = ["tokio"], optional = true }
@@ -47,7 +47,6 @@
], ],
"roles": { "roles": {
"__ID:5__": { "__ID:5__": {
"_id": "__ID:5__",
"name": "Moderator", "name": "Moderator",
"permissions": { "permissions": {
"a": 545270208, "a": 545270208,
@@ -56,7 +55,6 @@
"rank": 1 "rank": 1
}, },
"__ID:6__": { "__ID:6__": {
"_id": "__ID:6__",
"name": "Owner", "name": "Owner",
"permissions": { "permissions": {
"a": 0, "a": 0,
+7 -3
View File
@@ -25,6 +25,10 @@ pub use mongodb;
#[macro_use] #[macro_use]
extern crate bson; extern crate bson;
#[cfg(feature = "utoipa")]
#[macro_use]
extern crate utoipa;
#[cfg(not(feature = "async-std-runtime"))] #[cfg(not(feature = "async-std-runtime"))]
compile_error!("async-std-runtime feature must be enabled."); compile_error!("async-std-runtime feature must be enabled.");
@@ -60,7 +64,7 @@ macro_rules! database_derived {
macro_rules! auto_derived { macro_rules! auto_derived {
( $( $item:item )+ ) => { ( $( $item:item )+ ) => {
$( $(
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
$item $item
)+ )+
}; };
@@ -68,8 +72,8 @@ macro_rules! auto_derived {
macro_rules! auto_derived_partial { macro_rules! auto_derived_partial {
( $item:item, $name:expr ) => { ( $item:item, $name:expr ) => {
#[derive(OptionalStruct, Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] #[derive(OptionalStruct, Serialize, Deserialize, Debug, Clone, PartialEq)]
#[optional_derive(Serialize, Deserialize, Debug, Clone, Default, Eq, PartialEq)] #[optional_derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
#[optional_name = $name] #[optional_name = $name]
#[opt_skip_serializing_none] #[opt_skip_serializing_none]
#[opt_some_priority] #[opt_some_priority]
@@ -25,7 +25,7 @@ struct MigrationInfo {
revision: i32, 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) { pub async fn migrate_database(db: &MongoDb) {
let migrations = db.col::<Document>("migrations"); let migrations = db.col::<Document>("migrations");
@@ -1264,48 +1264,6 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
.expect("Failed to update default_permissions"); .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. // Reminder to update LATEST_REVISION when adding new migrations.
LATEST_REVISION.max(revision) LATEST_REVISION.max(revision)
} }
@@ -14,7 +14,7 @@ auto_derived!(
} }
/// Composite primary key consisting of channel and user id /// Composite primary key consisting of channel and user id
#[derive(Hash)] #[derive(Hash, Eq)]
pub struct ChannelCompositeKey { pub struct ChannelCompositeKey {
/// Channel Id /// Channel Id
pub channel: String, pub channel: String,
@@ -247,7 +247,7 @@ impl AbstractMessages for ReferenceDb {
let mut messages = self.messages.lock().await; let mut messages = self.messages.lock().await;
if let Some(message) = messages.get_mut(id) { if let Some(message) = messages.get_mut(id) {
if let Some(users) = message.reactions.get_mut(emoji) { if let Some(users) = message.reactions.get_mut(emoji) {
users.remove(&user.to_string()); users.swap_remove(&user.to_string());
} }
Ok(()) Ok(())
@@ -260,7 +260,7 @@ impl AbstractMessages for ReferenceDb {
async fn clear_reaction(&self, id: &str, emoji: &str) -> Result<()> { async fn clear_reaction(&self, id: &str, emoji: &str) -> Result<()> {
let mut messages = self.messages.lock().await; let mut messages = self.messages.lock().await;
if let Some(message) = messages.get_mut(id) { if let Some(message) = messages.get_mut(id) {
message.reactions.remove(emoji); message.reactions.swap_remove(emoji);
Ok(()) Ok(())
} else { } else {
Err(create_error!(NotFound)) Err(create_error!(NotFound))
@@ -55,7 +55,7 @@ auto_derived_partial!(
auto_derived!( auto_derived!(
/// Composite primary key consisting of server and user id /// Composite primary key consisting of server and user id
#[derive(Hash, Default)] #[derive(Hash, Default, Eq)]
pub struct MemberCompositeKey { pub struct MemberCompositeKey {
/// Server Id /// Server Id
pub server: String, pub server: String,
@@ -68,9 +68,6 @@ auto_derived_partial!(
auto_derived_partial!( auto_derived_partial!(
/// Role /// Role
pub struct Role { pub struct Role {
/// Unique Id
#[serde(rename = "_id")]
pub id: String,
/// Role name /// Role name
pub name: String, pub name: String,
/// Permissions available to this role /// Permissions available to this role
@@ -249,6 +246,7 @@ impl Server {
role.update( role.update(
db, db,
&self.id, &self.id,
role_id,
PartialRole { PartialRole {
permissions: Some(permissions), permissions: Some(permissions),
..Default::default() ..Default::default()
@@ -299,7 +297,6 @@ impl Role {
/// Into optional struct /// Into optional struct
pub fn into_optional(self) -> PartialRole { pub fn into_optional(self) -> PartialRole {
PartialRole { PartialRole {
id: Some(self.id),
name: Some(self.name), name: Some(self.name),
permissions: Some(self.permissions), permissions: Some(self.permissions),
colour: self.colour, colour: self.colour,
@@ -309,29 +306,20 @@ impl Role {
} }
/// Create a role /// Create a role
pub async fn create(db: &Database, server: &Server, name: String) -> Result<Self> { pub async fn create(&self, db: &Database, server_id: &str) -> Result<String> {
let role = Role { let role_id = Ulid::new().to_string();
id: Ulid::new().to_string(), db.insert_role(server_id, &role_id, self).await?;
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?;
EventV1::ServerRoleUpdate { EventV1::ServerRoleUpdate {
id: server.id.clone(), id: server_id.to_string(),
role_id: role.id.clone(), role_id: role_id.to_string(),
data: role.clone().into_optional().into(), data: self.clone().into_optional().into(),
clear: vec![], clear: vec![],
} }
.p(server.id.clone()) .p(server_id.to_string())
.await; .await;
Ok(role) Ok(role_id)
} }
/// Update server data /// Update server data
@@ -339,6 +327,7 @@ impl Role {
&mut self, &mut self,
db: &Database, db: &Database,
server_id: &str, server_id: &str,
role_id: &str,
partial: PartialRole, partial: PartialRole,
remove: Vec<FieldsRole>, remove: Vec<FieldsRole>,
) -> Result<()> { ) -> Result<()> {
@@ -348,14 +337,14 @@ impl Role {
self.apply_options(partial.clone()); 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?; .await?;
EventV1::ServerRoleUpdate { EventV1::ServerRoleUpdate {
id: server_id.to_string(), id: server_id.to_string(),
role_id: self.id.clone(), role_id: role_id.to_string(),
data: partial.into(), data: partial.into(),
clear: remove.into_iter().map(Into::into).collect(), clear: vec![],
} }
.p(server_id.to_string()) .p(server_id.to_string())
.await; .await;
@@ -371,15 +360,15 @@ impl Role {
} }
/// Delete a 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 { EventV1::ServerRoleDelete {
id: server_id.to_string(), id: server_id.to_string(),
role_id: self.id.clone(), role_id: role_id.to_string(),
} }
.p(server_id.to_string()) .p(server_id.to_string())
.await; .await;
db.delete_role(server_id, &self.id).await db.delete_role(server_id, role_id).await
} }
} }
@@ -29,7 +29,7 @@ pub trait AbstractServers: Sync + Send {
async fn delete_server(&self, id: &str) -> Result<()>; async fn delete_server(&self, id: &str) -> Result<()>;
/// Insert a new role into server object /// 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 /// Update an existing role on a server
async fn update_role( async fn update_role(
@@ -69,7 +69,7 @@ impl AbstractServers for MongoDb {
} }
/// Insert a new role into server object /// 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) self.col::<Document>(COL)
.update_one( .update_one(
doc! { doc! {
@@ -77,7 +77,7 @@ impl AbstractServers for MongoDb {
}, },
doc! { doc! {
"$set": { "$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"))? .map_err(|_| create_database_error!("to_document", "role"))?
} }
}, },
@@ -72,10 +72,10 @@ impl AbstractServers for ReferenceDb {
} }
/// Insert a new role into server object /// 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; let mut servers = self.servers.lock().await;
if let Some(server) = servers.get_mut(server_id) { 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(()) Ok(())
} else { } else {
Err(create_error!(NotFound)) Err(create_error!(NotFound))
@@ -4,7 +4,6 @@ use revolt_result::{create_error, Error, Result};
use crate::{Database, User}; use crate::{Database, User};
#[async_trait::async_trait]
impl<S> FromRequestParts<S> for User impl<S> FromRequestParts<S> for User
where where
Database: FromRef<S>, Database: FromRef<S>,
@@ -4,8 +4,6 @@ mod model;
mod ops; mod ops;
#[cfg(feature = "rocket-impl")] #[cfg(feature = "rocket-impl")]
mod rocket; mod rocket;
#[cfg(feature = "rocket-impl")]
mod schema;
pub use model::*; pub use model::*;
pub use ops::*; pub use ops::*;
@@ -15,7 +15,7 @@ use serde_json::json;
use ulid::Ulid; use ulid::Ulid;
auto_derived_partial!( auto_derived_partial!(
/// # User /// User
pub struct User { pub struct User {
/// Unique Id /// Unique Id
#[serde(rename = "_id")] #[serde(rename = "_id")]
@@ -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,
))
}
}
+1 -1
View File
@@ -17,7 +17,7 @@ use super::DelayedTask;
use crate::Channel::TextChannel; use crate::Channel::TextChannel;
/// Enumeration of possible events /// Enumeration of possible events
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, PartialEq)]
pub enum AckEvent { pub enum AckEvent {
/// Add mentions for a channel /// Add mentions for a channel
ProcessMessage { ProcessMessage {
@@ -896,7 +896,6 @@ impl From<SystemMessageChannels> for crate::SystemMessageChannels {
impl From<crate::Role> for Role { impl From<crate::Role> for Role {
fn from(value: crate::Role) -> Self { fn from(value: crate::Role) -> Self {
Role { Role {
id: value.id,
name: value.name, name: value.name,
permissions: value.permissions, permissions: value.permissions,
colour: value.colour, colour: value.colour,
@@ -909,7 +908,6 @@ impl From<crate::Role> for Role {
impl From<Role> for crate::Role { impl From<Role> for crate::Role {
fn from(value: Role) -> crate::Role { fn from(value: Role) -> crate::Role {
crate::Role { crate::Role {
id: value.id,
name: value.name, name: value.name,
permissions: value.permissions, permissions: value.permissions,
colour: value.colour, colour: value.colour,
@@ -922,7 +920,6 @@ impl From<Role> for crate::Role {
impl From<crate::PartialRole> for PartialRole { impl From<crate::PartialRole> for PartialRole {
fn from(value: crate::PartialRole) -> Self { fn from(value: crate::PartialRole) -> Self {
PartialRole { PartialRole {
id: value.id,
name: value.name, name: value.name,
permissions: value.permissions, permissions: value.permissions,
colour: value.colour, colour: value.colour,
@@ -935,7 +932,6 @@ impl From<crate::PartialRole> for PartialRole {
impl From<PartialRole> for crate::PartialRole { impl From<PartialRole> for crate::PartialRole {
fn from(value: PartialRole) -> crate::PartialRole { fn from(value: PartialRole) -> crate::PartialRole {
crate::PartialRole { crate::PartialRole {
id: value.id,
name: value.name, name: value.name,
permissions: value.permissions, permissions: value.permissions,
colour: value.colour, colour: value.colour,
+47 -49
View File
@@ -10,16 +10,16 @@ use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct IdempotencyKey { #[cfg_attr(feature = "utoipa", derive(IntoParams))]
key: String, #[cfg_attr(feature = "utoipa", into_params(names("Idempotency-Key"), parameter_in=Header))]
} pub struct IdempotencyKey(String);
static TOKEN_CACHE: Lazy<Mutex<lru::LruCache<String, ()>>> = static TOKEN_CACHE: Lazy<Mutex<lru::LruCache<String, ()>>> =
Lazy::new(|| Mutex::new(lru::LruCache::new(NonZeroUsize::new(1000).unwrap()))); Lazy::new(|| Mutex::new(lru::LruCache::new(NonZeroUsize::new(1000).unwrap())));
impl IdempotencyKey { impl IdempotencyKey {
pub fn unchecked_from_string(key: String) -> Self { pub fn unchecked_from_string(key: String) -> Self {
Self { key } Self(key)
} }
// Backwards compatibility. // Backwards compatibility.
@@ -32,56 +32,56 @@ impl IdempotencyKey {
} }
cache.put(v.clone(), ()); cache.put(v.clone(), ());
self.key = v; self.0 = v;
} }
Ok(()) Ok(())
} }
pub fn into_key(self) -> String { pub fn into_key(self) -> String {
self.key self.0
} }
} }
#[cfg(feature = "rocket-impl")] // #[cfg(feature = "rocket-impl")]
use revolt_rocket_okapi::{ // use revolt_rocket_okapi::{
gen::OpenApiGenerator, // gen::OpenApiGenerator,
request::{OpenApiFromRequest, RequestHeaderInput}, // request::{OpenApiFromRequest, RequestHeaderInput},
revolt_okapi::openapi3::{Parameter, ParameterValue}, // revolt_okapi::openapi3::{Parameter, ParameterValue},
}; // };
#[cfg(feature = "rocket-impl")] // #[cfg(feature = "rocket-impl")]
use schemars::schema::{InstanceType, SchemaObject, SingleOrVec}; // use schemars::schema::{InstanceType, SchemaObject, SingleOrVec};
#[cfg(feature = "rocket-impl")] // #[cfg(feature = "rocket-impl")]
impl OpenApiFromRequest<'_> for IdempotencyKey { // impl OpenApiFromRequest<'_> for IdempotencyKey {
fn from_request_input( // fn from_request_input(
_gen: &mut OpenApiGenerator, // _gen: &mut OpenApiGenerator,
_name: String, // _name: String,
_required: bool, // _required: bool,
) -> revolt_rocket_okapi::Result<RequestHeaderInput> { // ) -> revolt_rocket_okapi::Result<RequestHeaderInput> {
Ok(RequestHeaderInput::Parameter(Parameter { // Ok(RequestHeaderInput::Parameter(Parameter {
name: "Idempotency-Key".to_string(), // name: "Idempotency-Key".to_string(),
description: Some("Unique key to prevent duplicate requests".to_string()), // description: Some("Unique key to prevent duplicate requests".to_string()),
allow_empty_value: false, // allow_empty_value: false,
required: false, // required: false,
deprecated: false, // deprecated: false,
extensions: schemars::Map::new(), // extensions: schemars::Map::new(),
location: "header".to_string(), // location: "header".to_string(),
value: ParameterValue::Schema { // value: ParameterValue::Schema {
allow_reserved: false, // allow_reserved: false,
example: None, // example: None,
examples: None, // examples: None,
explode: None, // explode: None,
style: None, // style: None,
schema: SchemaObject { // schema: SchemaObject {
instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))), // instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))),
..Default::default() // ..Default::default()
}, // },
}, // },
})) // }))
} // }
} // }
#[cfg(feature = "rocket-impl")] #[cfg(feature = "rocket-impl")]
use rocket::{ 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; 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))); return Outcome::Error((Status::Conflict, create_error!(DuplicateNonce)));
} }
cache.put(idempotency.key.clone(), ()); cache.put(idempotency.0.clone(), ());
return Outcome::Success(idempotency); return Outcome::Success(idempotency);
} }
Outcome::Success(IdempotencyKey { Outcome::Success(IdempotencyKey(ulid::Ulid::new().to_string()))
key: ulid::Ulid::new().to_string(),
})
} }
} }
+2
View File
@@ -5,5 +5,7 @@ pub mod idempotency;
pub mod permissions; pub mod permissions;
pub mod reference; pub mod reference;
pub mod test_fixtures; pub mod test_fixtures;
#[cfg(feature = "utoipa")]
pub mod utoipa;
pub use funcs::*; pub use funcs::*;
@@ -3,11 +3,6 @@ use std::str::FromStr;
use revolt_result::Result; use revolt_result::Result;
#[cfg(feature = "rocket-impl")] #[cfg(feature = "rocket-impl")]
use rocket::request::FromParam; use rocket::request::FromParam;
#[cfg(feature = "rocket-impl")]
use schemars::{
schema::{InstanceType, Schema, SchemaObject, SingleOrVec},
JsonSchema,
};
use crate::{ use crate::{
Bot, Channel, Database, Emoji, Invite, Member, Message, Server, ServerBan, User, Webhook, 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)) 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()
})
}
}
+49
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(),
)
}
}
+3 -3
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-files" name = "revolt-files"
version = "0.9.2" version = "0.8.9"
edition = "2021" edition = "2021"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
authors = ["Paul Makles <me@insrt.uk>"] authors = ["Paul Makles <me@insrt.uk>"]
@@ -20,10 +20,10 @@ typenum = "1.17.0"
aws-config = "1.5.5" aws-config = "1.5.5"
aws-sdk-s3 = { version = "1.46.0", features = ["behavior-version-latest"] } aws-sdk-s3 = { version = "1.46.0", features = ["behavior-version-latest"] }
revolt-config = { version = "0.9.2", path = "../config", features = [ revolt-config = { version = "0.8.9", path = "../config", features = [
"report-macros", "report-macros",
] } ] }
revolt-result = { version = "0.9.2", path = "../result" } revolt-result = { version = "0.8.9", path = "../result" }
# image processing # image processing
jxl-oxide = "0.8.1" jxl-oxide = "0.8.1"
+9 -10
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-models" name = "revolt-models"
version = "0.9.2" version = "0.8.9"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"] authors = ["Paul Makles <me@insrt.uk>"]
@@ -10,22 +10,22 @@ description = "Revolt Backend: API Models"
[features] [features]
serde = ["dep:serde", "revolt-permissions/serde", "indexmap/serde"] serde = ["dep:serde", "revolt-permissions/serde", "indexmap/serde"]
schemas = ["dep:schemars", "revolt-permissions/schemas"]
utoipa = ["dep:utoipa"]
validator = ["dep:validator"] validator = ["dep:validator"]
rocket = ["dep:rocket"] rocket = ["dep:rocket"]
partials = ["dep:revolt_optional_struct", "serde", "schemas", "utoipa"] partials = ["dep:revolt_optional_struct", "serde", "utoipa"]
default = ["serde", "partials", "rocket"] default = ["serde", "partials", "rocket"]
[dependencies] [dependencies]
# Core # Core
revolt-config = { version = "0.9.2", path = "../config" } revolt-config = { version = "0.8.9", path = "../config" }
revolt-permissions = { version = "0.9.2", path = "../permissions" } revolt-permissions = { version = "0.8.9", path = "../permissions", features = [
"utoipa",
] }
# Utility # Utility
regex = "1.11" regex = "1.11"
indexmap = "1.9.3" indexmap = "2.12.0"
once_cell = "1.17.1" once_cell = "1.17.1"
num_enum = "0.6.1" num_enum = "0.6.1"
@@ -35,11 +35,10 @@ rocket = { optional = true, version = "0.5.0-rc.2", default-features = false }
# Serialisation # Serialisation
revolt_optional_struct = { version = "0.2.0", optional = true } revolt_optional_struct = { version = "0.2.0", optional = true }
serde = { version = "1", features = ["derive"], 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 # Spec Generation
schemars = { version = "0.8.8", optional = true, features = ["indexmap1"] } utoipa = { version = "5.4.0", features = ["indexmap"], optional = true }
utoipa = { version = "4.2.3", optional = true }
# Validation # Validation
validator = { version = "0.16.0", optional = true, features = ["derive"] } validator = { version = "0.16.0", optional = true, features = ["derive"] }
+4 -20
View File
@@ -2,10 +2,6 @@
#[macro_use] #[macro_use]
extern crate serde; extern crate serde;
#[cfg(feature = "schemas")]
#[macro_use]
extern crate schemars;
#[cfg(feature = "utoipa")] #[cfg(feature = "utoipa")]
#[macro_use] #[macro_use]
extern crate utoipa; extern crate utoipa;
@@ -21,9 +17,8 @@ macro_rules! auto_derived {
( $( $item:item )+ ) => { ( $( $item:item )+ ) => {
$( $(
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemas", derive(JsonSchema))]
#[cfg_attr(feature = "utoipa", derive(ToSchema))] #[cfg_attr(feature = "utoipa", derive(ToSchema))]
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, PartialEq)]
$item $item
)+ )+
}; };
@@ -32,19 +27,8 @@ macro_rules! auto_derived {
#[cfg(feature = "partials")] #[cfg(feature = "partials")]
macro_rules! auto_derived_partial { macro_rules! auto_derived_partial {
( $item:item, $name:expr ) => { ( $item:item, $name:expr ) => {
#[derive( #[derive(OptionalStruct, Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)]
OptionalStruct, Debug, Clone, Eq, PartialEq, Serialize, Deserialize, JsonSchema, #[optional_derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema, Default)]
)]
#[optional_derive(
Debug,
Clone,
Eq,
PartialEq,
Serialize,
Deserialize,
JsonSchema,
Default
)]
#[optional_name = $name] #[optional_name = $name]
#[opt_skip_serializing_none] #[opt_skip_serializing_none]
#[opt_some_priority] #[opt_some_priority]
@@ -55,7 +39,7 @@ macro_rules! auto_derived_partial {
#[cfg(not(feature = "partials"))] #[cfg(not(feature = "partials"))]
macro_rules! auto_derived_partial { macro_rules! auto_derived_partial {
( $item:item, $name:expr ) => { ( $item:item, $name:expr ) => {
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, JsonSchema)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)]
$item $item
}; };
} }
@@ -86,10 +86,10 @@ auto_derived!(
/// Information for the webhook /// Information for the webhook
#[cfg_attr(feature = "validator", derive(Validate))] #[cfg_attr(feature = "validator", derive(Validate))]
pub struct CreateWebhookBody { pub struct CreateWebhookBody {
#[validate(length(min = 1, max = 32))] #[cfg_attr(feature = "validator", validate(length(min = 1, max = 32)))]
pub name: String, pub name: String,
#[validate(length(min = 1, max = 128))] #[cfg_attr(feature = "validator", validate(length(min = 1, max = 128)))]
pub avatar: Option<String>, pub avatar: Option<String>,
} }
); );
+1
View File
@@ -271,6 +271,7 @@ auto_derived!(
/// Options when deleting a channel /// Options when deleting a channel
#[cfg_attr(feature = "rocket", derive(FromForm))] #[cfg_attr(feature = "rocket", derive(FromForm))]
#[cfg_attr(feature = "utoipa", derive(IntoParams))]
pub struct OptionsChannelDelete { pub struct OptionsChannelDelete {
/// Whether to not send a leave message /// Whether to not send a leave message
pub leave_silently: Option<bool>, pub leave_silently: Option<bool>,
+78
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,
}
);
+1 -1
View File
@@ -46,7 +46,7 @@ auto_derived!(
#[cfg_attr(feature = "validator", derive(Validate))] #[cfg_attr(feature = "validator", derive(Validate))]
pub struct DataCreateEmoji { pub struct DataCreateEmoji {
/// Server name /// 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, pub name: String,
/// Parent information /// Parent information
pub parent: EmojiParent, pub parent: EmojiParent,
+6 -4
View File
@@ -141,17 +141,17 @@ auto_derived!(
pub struct Masquerade { pub struct Masquerade {
/// Replace the display name shown on this message /// Replace the display name shown on this message
#[serde(skip_serializing_if = "Option::is_none")] #[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>, pub name: Option<String>,
/// Replace the avatar shown on this message (URL to image file) /// Replace the avatar shown on this message (URL to image file)
#[serde(skip_serializing_if = "Option::is_none")] #[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>, pub avatar: Option<String>,
/// Replace the display role colour shown on this message /// Replace the display role colour shown on this message
/// ///
/// Must have `ManageRole` permission to use /// Must have `ManageRole` permission to use
#[serde(skip_serializing_if = "Option::is_none")] #[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>, pub colour: Option<String>,
} }
@@ -281,6 +281,7 @@ auto_derived!(
/// Options for querying messages /// Options for querying messages
#[cfg_attr(feature = "validator", derive(Validate))] #[cfg_attr(feature = "validator", derive(Validate))]
#[cfg_attr(feature = "rocket", derive(FromForm))] #[cfg_attr(feature = "rocket", derive(FromForm))]
#[cfg_attr(feature = "utoipa", derive(IntoParams))]
pub struct OptionsQueryMessages { pub struct OptionsQueryMessages {
/// Maximum number of messages to fetch /// Maximum number of messages to fetch
/// ///
@@ -353,12 +354,13 @@ auto_derived!(
)] )]
pub struct OptionsBulkDelete { pub struct OptionsBulkDelete {
/// Message IDs /// Message IDs
#[validate(length(min = 1, max = 100))] #[cfg_attr(feature = "validator", validate(length(min = 1, max = 100)))]
pub ids: Vec<String>, pub ids: Vec<String>,
} }
/// Options for removing reaction /// Options for removing reaction
#[cfg_attr(feature = "rocket", derive(FromForm))] #[cfg_attr(feature = "rocket", derive(FromForm))]
#[cfg_attr(feature = "utoipa", derive(IntoParams))]
pub struct OptionsUnreact { pub struct OptionsUnreact {
/// Remove a specific user's reaction /// Remove a specific user's reaction
pub user_id: Option<String>, pub user_id: Option<String>,
+2
View File
@@ -3,6 +3,7 @@ mod channel_invites;
mod channel_unreads; mod channel_unreads;
mod channel_webhooks; mod channel_webhooks;
mod channels; mod channels;
mod config;
mod embeds; mod embeds;
mod emojis; mod emojis;
mod files; mod files;
@@ -20,6 +21,7 @@ pub use channel_invites::*;
pub use channel_unreads::*; pub use channel_unreads::*;
pub use channel_webhooks::*; pub use channel_webhooks::*;
pub use channels::*; pub use channels::*;
pub use config::*;
pub use embeds::*; pub use embeds::*;
pub use emojis::*; pub use emojis::*;
pub use files::*; pub use files::*;
@@ -116,6 +116,7 @@ auto_derived!(
/// Options for fetching all members /// Options for fetching all members
#[cfg_attr(feature = "rocket", derive(FromForm))] #[cfg_attr(feature = "rocket", derive(FromForm))]
#[cfg_attr(feature = "utoipa", derive(IntoParams))]
pub struct OptionsFetchAllMembers { pub struct OptionsFetchAllMembers {
/// Whether to exclude offline users /// Whether to exclude offline users
pub exclude_offline: Option<bool>, pub exclude_offline: Option<bool>,
+2 -4
View File
@@ -85,9 +85,6 @@ auto_derived_partial!(
auto_derived_partial!( auto_derived_partial!(
/// Role /// Role
pub struct Role { pub struct Role {
/// Unique Id
#[cfg_attr(feature = "serde", serde(rename = "_id"))]
pub id: String,
/// Role name /// Role name
pub name: String, pub name: String,
/// Permissions available to this role /// Permissions available to this role
@@ -184,7 +181,6 @@ auto_derived!(
} }
/// Response after creating new role /// Response after creating new role
// TODO: remove this in favor of just Role
pub struct NewRoleResponse { pub struct NewRoleResponse {
/// Id of the role /// Id of the role
pub id: String, pub id: String,
@@ -202,6 +198,7 @@ auto_derived!(
/// Options when fetching server /// Options when fetching server
#[cfg_attr(feature = "rocket", derive(FromForm))] #[cfg_attr(feature = "rocket", derive(FromForm))]
#[cfg_attr(feature = "utoipa", derive(IntoParams))]
pub struct OptionsFetchServer { pub struct OptionsFetchServer {
/// Whether to include channels /// Whether to include channels
pub include_channels: Option<bool>, pub include_channels: Option<bool>,
@@ -288,6 +285,7 @@ auto_derived!(
/// Options when leaving a server /// Options when leaving a server
#[cfg_attr(feature = "rocket", derive(FromForm))] #[cfg_attr(feature = "rocket", derive(FromForm))]
#[cfg_attr(feature = "utoipa", derive(IntoParams))]
pub struct OptionsServerDelete { pub struct OptionsServerDelete {
/// Whether to not send a leave message /// Whether to not send a leave message
pub leave_silently: Option<bool>, pub leave_silently: Option<bool>,
@@ -17,6 +17,7 @@ auto_derived!(
/// Additional options for inserting settings /// Additional options for inserting settings
#[cfg_attr(feature = "rocket", derive(FromForm))] #[cfg_attr(feature = "rocket", derive(FromForm))]
#[cfg_attr(feature = "utoipa", derive(IntoParams))]
pub struct OptionsSetSettings { pub struct OptionsSetSettings {
/// Timestamp of settings change. /// Timestamp of settings change.
/// ///
+2 -2
View File
@@ -142,7 +142,7 @@ auto_derived!(
#[cfg_attr(feature = "validator", derive(Validate))] #[cfg_attr(feature = "validator", derive(Validate))]
pub struct UserStatus { pub struct UserStatus {
/// Custom status text /// 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"))] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub text: Option<String>, pub text: Option<String>,
/// Current presence option /// Current presence option
@@ -155,7 +155,7 @@ auto_derived!(
#[cfg_attr(feature = "validator", derive(Validate))] #[cfg_attr(feature = "validator", derive(Validate))]
pub struct UserProfile { pub struct UserProfile {
/// Text content on user's profile /// 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"))] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub content: Option<String>, pub content: Option<String>,
/// Background visible on user's profile /// Background visible on user's profile
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-parser" name = "revolt-parser"
version = "0.9.2" version = "0.8.9"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
authors = ["Zomatree <me@zomatree.live>", "Paul Makles <me@insrt.uk>"] authors = ["Zomatree <me@zomatree.live>", "Paul Makles <me@insrt.uk>"]
+3 -5
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-permissions" name = "revolt-permissions"
version = "0.9.2" version = "0.8.9"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"] authors = ["Paul Makles <me@insrt.uk>"]
@@ -11,17 +11,15 @@ description = "Revolt Backend: Permission Logic"
[features] [features]
bson = ["dep:bson"] bson = ["dep:bson"]
serde = ["dep:serde"] serde = ["dep:serde"]
schemas = ["dep:schemars"]
try-from-primitive = ["dep:num_enum"] try-from-primitive = ["dep:num_enum"]
[dev-dependencies] [dev-dependencies]
# Async # Async
async-std = { version = "1.8.0", features = ["attributes"] } async-std = { version = "1.8.0", features = ["attributes"] }
[dependencies] [dependencies]
# Core # Core
revolt-result = { version = "0.9.2", path = "../result" } revolt-result = { version = "0.8.9", path = "../result" }
# Utility # Utility
auto_ops = "0.3.0" auto_ops = "0.3.0"
@@ -36,4 +34,4 @@ serde = { version = "1", features = ["derive"], optional = true }
bson = { version = "2.1.0", optional = true } bson = { version = "2.1.0", optional = true }
# Spec Generation # Spec Generation
schemars = { version = "0.8.8", optional = true } utoipa = { version = "5.4.0", optional = true }
+7 -7
View File
@@ -1,10 +1,10 @@
#[cfg(feature = "schemas")] #[cfg(feature = "utoipa")]
use schemars::JsonSchema; use utoipa::ToSchema;
/// Representation of a single permission override /// Representation of a single permission override
#[derive(Debug, Clone, Eq, PartialEq, Default)] #[derive(Debug, Clone, Eq, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "schemas", derive(JsonSchema))] #[cfg_attr(feature = "utoipa", derive(ToSchema))]
pub struct Override { pub struct Override {
/// Allow bit flags /// Allow bit flags
pub allow: u64, pub allow: u64,
@@ -15,7 +15,7 @@ pub struct Override {
/// Data permissions Field - contains both allow and deny /// Data permissions Field - contains both allow and deny
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[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 struct DataPermissionsField {
pub permissions: Override, pub permissions: Override,
} }
@@ -23,7 +23,7 @@ pub struct DataPermissionsField {
/// Data permissions Value - contains allow /// Data permissions Value - contains allow
#[derive(Debug, Clone, Copy, Eq, PartialEq)] #[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[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 struct DataPermissionsValue {
pub permissions: u64, pub permissions: u64,
} }
@@ -31,7 +31,7 @@ pub struct DataPermissionsValue {
/// Data permissions Poly - can contain either Value or Field /// Data permissions Poly - can contain either Value or Field
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[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))] #[cfg_attr(feature = "serde", serde(untagged))]
pub enum DataPermissionPoly { pub enum DataPermissionPoly {
Value { Value {
@@ -48,7 +48,7 @@ pub enum DataPermissionPoly {
/// as it appears on models and in the database /// as it appears on models and in the database
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)] #[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "schemas", derive(JsonSchema))] #[cfg_attr(feature = "utoipa", derive(ToSchema))]
pub struct OverrideField { pub struct OverrideField {
/// Allow bit flags /// Allow bit flags
pub a: i64, pub a: i64,
+2 -2
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-presence" name = "revolt-presence"
version = "0.9.2" version = "0.8.9"
edition = "2021" edition = "2021"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
authors = ["Paul Makles <me@insrt.uk>"] authors = ["Paul Makles <me@insrt.uk>"]
@@ -16,7 +16,7 @@ redis-is-patched = []
async-std = { version = "1.8.0", features = ["attributes"] } async-std = { version = "1.8.0", features = ["attributes"] }
# Config for loading Redis URI # Config for loading Redis URI
revolt-config = { version = "0.9.2", path = "../config" } revolt-config = { version = "0.8.9", path = "../config" }
[dependencies] [dependencies]
# Utility # Utility
+9 -7
View File
@@ -1,23 +1,25 @@
[package] [package]
name = "revolt-ratelimits" name = "revolt-ratelimits"
version = "0.9.2" version = "0.8.9"
edition = "2024" edition = "2024"
[features] [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"] axum = ["dep:axum", "revolt-database/axum-impl"]
default = ["rocket", "axum"] default = ["rocket", "axum"]
[dependencies] [dependencies]
revolt-database = { version = "0.9.2", path = "../database"} revolt-database = { version = "0.8.9", path = "../database" }
revolt-result = { version = "0.9.2", path = "../result" } revolt-result = { version = "0.8.9", path = "../result" }
revolt-config = { version = "0.9.2", path = "../config" } revolt-config = { version = "0.8.9", path = "../config" }
rocket = { version = "0.5.1", optional = true } 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"] } serde = { version = "1", features = ["derive"] }
authifier = { version = "1.0.15" } authifier = { version = "1.0.15" }
-3
View File
@@ -1,6 +1,5 @@
use std::net::SocketAddr; use std::net::SocketAddr;
use async_trait::async_trait;
use axum::{ use axum::{
Json, RequestPartsExt, Router, Json, RequestPartsExt, Router,
body::Body, body::Body,
@@ -44,7 +43,6 @@ async fn to_real_ip(parts: &Parts) -> String {
} }
} }
#[async_trait]
impl<S: Send + Sync> FromRequestParts<S> for Ratelimiter impl<S: Send + Sync> FromRequestParts<S> for Ratelimiter
where where
Database: FromRef<S>, Database: FromRef<S>,
@@ -82,7 +80,6 @@ where
} }
} }
#[async_trait]
impl<S: Send + Sync> FromRequestParts<S> for RatelimitInformation impl<S: Send + Sync> FromRequestParts<S> for RatelimitInformation
where where
Database: FromRef<S>, Database: FromRef<S>,
-13
View File
@@ -8,9 +8,6 @@ use rocket::serde::json::Json;
use rocket::{Data, Request, Response, State}; use rocket::{Data, Request, Response, State};
use revolt_config::config; use revolt_config::config;
use revolt_rocket_okapi::r#gen::OpenApiGenerator;
use revolt_rocket_okapi::request::{OpenApiFromRequest, RequestHeaderInput};
use authifier::models::Session; use authifier::models::Session;
use crate::ratelimiter::RequestKind; 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 /// Attach ratelimiter to the Rocket application
pub struct RatelimitFairing; pub struct RatelimitFairing;
+3 -8
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-result" name = "revolt-result"
version = "0.9.2" version = "0.8.9"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"] authors = ["Paul Makles <me@insrt.uk>"]
@@ -10,11 +10,9 @@ description = "Revolt Backend: Result and Error types"
[features] [features]
serde = ["dep:serde"] serde = ["dep:serde"]
schemas = ["dep:schemars"]
utoipa = ["dep:utoipa"] utoipa = ["dep:utoipa"]
rocket = ["dep:rocket", "dep:serde_json"] rocket = ["dep:rocket", "dep:serde_json"]
axum = ["dep:axum", "dep:serde_json"] axum = ["dep:axum", "dep:serde_json"]
okapi = ["dep:revolt_rocket_okapi", "dep:revolt_okapi", "schemas"]
sentry = ["dep:sentry"] sentry = ["dep:sentry"]
default = ["serde", "sentry"] default = ["serde", "sentry"]
@@ -25,17 +23,14 @@ serde_json = { version = "1", optional = true }
serde = { version = "1", features = ["derive"], optional = true } serde = { version = "1", features = ["derive"], optional = true }
# Spec Generation # Spec Generation
schemars = { version = "0.8.8", optional = true } utoipa = { version = "5.4.0", optional = true }
utoipa = { version = "4.2.3", optional = true }
# Rocket # Rocket
rocket = { optional = true, version = "0.5.0-rc.2", default-features = false } 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 # utilities
log = "0.4" log = "0.4"
# Axum # 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 }
+5 -13
View File
@@ -5,13 +5,10 @@ use std::fmt::Display;
#[macro_use] #[macro_use]
extern crate serde; extern crate serde;
#[cfg(feature = "schemas")]
#[macro_use]
extern crate schemars;
#[cfg(feature = "utoipa")] #[cfg(feature = "utoipa")]
#[macro_use] mod utoipa_impl;
extern crate utoipa; #[cfg(feature = "utoipa")]
pub use crate::utoipa_impl::*;
#[cfg(feature = "rocket")] #[cfg(feature = "rocket")]
pub mod rocket; pub mod rocket;
@@ -19,16 +16,12 @@ pub mod rocket;
#[cfg(feature = "axum")] #[cfg(feature = "axum")]
pub mod axum; pub mod axum;
#[cfg(feature = "okapi")]
pub mod okapi;
/// Result type with custom Error /// Result type with custom Error
pub type Result<T, E = Error> = std::result::Result<T, E>; pub type Result<T, E = Error> = std::result::Result<T, E>;
/// Error information /// Error information
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemas", derive(JsonSchema))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "utoipa", derive(ToSchema))]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Error { pub struct Error {
/// Type of error and additional information /// Type of error and additional information
@@ -50,8 +43,7 @@ impl std::error::Error for Error {}
/// Possible error types /// Possible error types
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "type"))] #[cfg_attr(feature = "serde", serde(tag = "type"))]
#[cfg_attr(feature = "schemas", derive(JsonSchema))] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "utoipa", derive(ToSchema))]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ErrorType { pub enum ErrorType {
/// This error was not labeled :( /// This error was not labeled :(
-49
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()
})
}
}
+52
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()));
};
}
}
}
+5 -6
View File
@@ -1,11 +1,10 @@
[package] [package]
name = "revolt-crond" name = "revolt-crond"
version = "0.9.2" version = "0.8.9"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
authors = ["Paul Makles <me@insrt.uk>"] authors = ["Paul Makles <me@insrt.uk>"]
edition = "2021" edition = "2021"
description = "Revolt Daemon Service: Timed data clean up tasks" 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 # 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" } tokio = { version = "1" }
# Core # Core
revolt-database = { version = "0.9.2", path = "../../core/database" } revolt-database = { version = "0.8.9", path = "../../core/database" }
revolt-result = { version = "0.9.2", path = "../../core/result" } revolt-result = { version = "0.8.9", path = "../../core/result" }
revolt-config = { version = "0.9.2", path = "../../core/config" } revolt-config = { version = "0.8.9", path = "../../core/config" }
revolt-files = { version = "0.9.2", path = "../../core/files" } revolt-files = { version = "0.8.9", path = "../../core/files" }
+7 -8
View File
@@ -1,21 +1,20 @@
[package] [package]
name = "revolt-pushd" name = "revolt-pushd"
version = "0.9.2" version = "0.8.9"
edition = "2021" edition = "2021"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
publish = false
[dependencies] [dependencies]
revolt-result = { version = "0.9.2", path = "../../core/result" } revolt-result = { version = "0.8.9", path = "../../core/result" }
revolt-config = { version = "0.9.2", path = "../../core/config", features = [ revolt-config = { version = "0.8.9", path = "../../core/config", features = [
"report-macros", "report-macros",
"anyhow" "anyhow"
] } ] }
revolt-database = { version = "0.9.2", path = "../../core/database" } revolt-database = { version = "0.8.9", path = "../../core/database" }
revolt-models = { version = "0.9.2", path = "../../core/models", features = [ revolt-models = { version = "0.8.9", path = "../../core/models", features = [
"validator", "validator",
] } ] }
revolt-presence = { version = "0.9.2", path = "../../core/presence", features = [ revolt-presence = { version = "0.8.9", path = "../../core/presence", features = [
"redis-is-patched", "redis-is-patched",
] } ] }
@@ -39,5 +38,5 @@ pretty_env_logger = "0.4.0"
serde_json = "1" serde_json = "1"
revolt_optional_struct = "0.2.0" revolt_optional_struct = "0.2.0"
serde = { version = "1", features = ["derive"] } 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" base64 = "0.22.1"
+4 -5
View File
@@ -1,9 +1,8 @@
[package] [package]
name = "revolt-voice-ingress" name = "revolt-voice-ingress"
version = "0.9.2" version = "0.7.1"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
edition = "2021" edition = "2021"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # 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" } revolt-permissions = { path = "../../core/permissions" }
# Voice # Voice
livekit-api = "0.4.4" livekit-api = "=0.4.4"
livekit-protocol = "0.4.0" livekit-protocol = "=0.4.0"
livekit-runtime = { version = "0.3.1", features = ["tokio"] } livekit-runtime = { version = "=0.3.1", features = ["tokio"] }
# RabbitMQ # RabbitMQ
amqprs = { version = "1.7.0" } amqprs = { version = "1.7.0" }
+11 -11
View File
@@ -1,10 +1,9 @@
[package] [package]
name = "revolt-delta" name = "revolt-delta"
version = "0.9.2" version = "0.8.9"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
authors = ["Paul Makles <paulmakles@gmail.com>"] authors = ["Paul Makles <paulmakles@gmail.com>"]
edition = "2018" edition = "2018"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # 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_json = "1.0.57"
serde = { version = "1.0.115", features = ["derive"] } serde = { version = "1.0.115", features = ["derive"] }
validator = { version = "0.16", features = ["derive"] } validator = { version = "0.16", features = ["derive"] }
iso8601-timestamp = { version = "0.2.11", features = [] } iso8601-timestamp = { version = "0.4.0", features = [] }
# async # async
futures = "0.3.8" 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 = { version = "0.5.1", default-features = false, features = ["json"] }
rocket_cors = { git = "https://github.com/lawliet89/rocket_cors", rev = "072d90359b23e9b291df6b672c07c93de9c46011" } 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_authifier = { version = "1.0.15" }
rocket_prometheus = "0.10.0-rc.3" rocket_prometheus = "0.10.0-rc.3"
# spec generation # spec generation
schemars = "0.8.8" utoipa = { version = "5.4.0", features = ["rocket_extras"] }
revolt_rocket_okapi = { version = "0.10.0", features = ["swagger"] } utoipa-scalar = { version = "0.3.0", features = ["rocket"] }
# rabbit # rabbit
amqprs = { version = "1.7.0" } amqprs = { version = "1.7.0" }
@@ -73,21 +72,22 @@ revolt-config = { path = "../core/config" }
revolt-database = { path = "../core/database", features = [ revolt-database = { path = "../core/database", features = [
"rocket-impl", "rocket-impl",
"redis-is-patched", "redis-is-patched",
"utoipa",
"voice", "voice",
] } ] }
revolt-models = { path = "../core/models", features = [ revolt-models = { path = "../core/models", features = [
"schemas", "utoipa",
"validator", "validator",
"rocket", "rocket",
] } ] }
revolt-presence = { path = "../core/presence" } revolt-presence = { path = "../core/presence" }
revolt-result = { path = "../core/result", features = ["rocket", "okapi"] } revolt-result = { path = "../core/result", features = ["rocket", "utoipa"] }
revolt-permissions = { path = "../core/permissions", features = ["schemas"] } revolt-permissions = { path = "../core/permissions", features = ["utoipa"] }
revolt-ratelimits = { path = "../core/ratelimits", features = ["rocket"] } revolt-ratelimits = { path = "../core/ratelimits", features = ["rocket"] }
# voice # voice
livekit-api = "0.4.4" livekit-api = "=0.4.4"
livekit-protocol = "0.4.0" livekit-protocol = "=0.4.0"
[build-dependencies] [build-dependencies]
vergen = "7.5.0" vergen = "7.5.0"
@@ -47,7 +47,6 @@
], ],
"roles": { "roles": {
"__ID:5__": { "__ID:5__": {
"_id": "__ID:5__",
"name": "Moderator", "name": "Moderator",
"permissions": { "permissions": {
"a": 545270216, "a": 545270216,
@@ -56,7 +55,6 @@
"rank": 1 "rank": 1
}, },
"__ID:6__": { "__ID:6__": {
"_id": "__ID:6__",
"name": "Owner", "name": "Owner",
"permissions": { "permissions": {
"a": 0, "a": 0,
@@ -65,7 +63,6 @@
"rank": 0 "rank": 0
}, },
"__ID:7__": { "__ID:7__": {
"_id": "__ID:7__",
"name": "Lower Rank 1", "name": "Lower Rank 1",
"permissions": { "permissions": {
"a": 0, "a": 0,
@@ -74,7 +71,6 @@
"rank": 2 "rank": 2
}, },
"__ID:8__": { "__ID:8__": {
"_id": "__ID:8__",
"name": "Lower Rank 2", "name": "Lower Rank 2",
"permissions": { "permissions": {
"a": 0, "a": 0,
+4 -28
View File
@@ -1,7 +1,7 @@
#[macro_use] #[macro_use]
extern crate rocket; extern crate rocket;
#[macro_use] #[macro_use]
extern crate revolt_rocket_okapi; extern crate utoipa;
#[macro_use] #[macro_use]
extern crate serde_json; extern crate serde_json;
@@ -15,6 +15,8 @@ use revolt_ratelimits::rocket as ratelimiter;
use rocket::{Build, Rocket}; use rocket::{Build, Rocket};
use rocket_cors::{AllowedOrigins, CorsOptions}; use rocket_cors::{AllowedOrigins, CorsOptions};
use rocket_prometheus::PrometheusMetrics; use rocket_prometheus::PrometheusMetrics;
use utoipa::OpenApi;
use utoipa_scalar::{Scalar, Servable};
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
use std::str::FromStr; use std::str::FromStr;
@@ -75,31 +77,6 @@ pub async fn web() -> Rocket<Build> {
.to_cors() .to_cors()
.expect("Failed to create 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 // Voice handler
let voice_client = VoiceClient::new(config.api.livekit.nodes.clone()); let voice_client = VoiceClient::new(config.api.livekit.nodes.clone());
// Configure Rabbit // Configure Rabbit
@@ -143,8 +120,7 @@ pub async fn web() -> Rocket<Build> {
.mount("/metrics", prometheus) .mount("/metrics", prometheus)
.mount("/", rocket_cors::catch_all_options_routes()) .mount("/", rocket_cors::catch_all_options_routes())
.mount("/", ratelimiter::routes()) .mount("/", ratelimiter::routes())
.mount("/swagger/", swagger) .mount("/", Scalar::with_url("/scalar", routes::ApiDoc::openapi()))
.mount("/0.8/swagger/", swagger_0_8)
.manage(authifier) .manage(authifier)
.manage(db) .manage(db)
.manage(amqp) .manage(amqp)
+8 -2
View File
@@ -5,10 +5,16 @@ use rocket::serde::json::Json;
use rocket::State; use rocket::State;
use validator::Validate; use validator::Validate;
/// # Create Bot /// Create Bot
/// ///
/// Create a new Revolt 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>")] #[post("/create", data = "<info>")]
pub async fn create_bot( pub async fn create_bot(
db: &State<Database>, db: &State<Database>,
+8 -2
View File
@@ -3,10 +3,16 @@ use revolt_result::{create_error, Result};
use rocket::State; use rocket::State;
use rocket_empty::EmptyResponse; use rocket_empty::EmptyResponse;
/// # Delete Bot /// Delete Bot
/// ///
/// Delete a bot by its id. /// Delete a bot by its id.
#[openapi(tag = "Bots")] #[utoipa::path(
tag = "Bots",
security(("Session-Token" = [])),
responses(
(status = 204),
),
)]
#[delete("/<target>")] #[delete("/<target>")]
pub async fn delete_bot( pub async fn delete_bot(
db: &State<Database>, db: &State<Database>,
+9 -10
View File
@@ -6,10 +6,16 @@ use rocket::State;
use rocket::serde::json::Json; use rocket::serde::json::Json;
use validator::Validate; use validator::Validate;
/// # Edit Bot /// Edit Bot
/// ///
/// Edit bot details by its id. /// 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>")] #[patch("/<target>", data = "<data>")]
pub async fn edit_bot( pub async fn edit_bot(
db: &State<Database>, db: &State<Database>,
@@ -60,14 +66,7 @@ pub async fn edit_bot(
..Default::default() ..Default::default()
}; };
bot.update( bot.update(db, partial, remove.into_iter().map(|v| v.into()).collect())
db,
partial,
remove
.into_iter()
.map(|v| v.into())
.collect(),
)
.await?; .await?;
Ok(Json(v0::BotWithUserResponse { Ok(Json(v0::BotWithUserResponse {
+8 -2
View File
@@ -3,10 +3,16 @@ use revolt_models::v0::FetchBotResponse;
use revolt_result::{create_error, Result}; use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State}; use rocket::{serde::json::Json, State};
/// # Fetch Bot /// Fetch Bot
/// ///
/// Fetch details of a bot you own by its id. /// 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>")] #[get("/<bot>")]
pub async fn fetch_bot( pub async fn fetch_bot(
db: &State<Database>, db: &State<Database>,
+8 -2
View File
@@ -5,10 +5,16 @@ use revolt_result::Result;
use rocket::serde::json::Json; use rocket::serde::json::Json;
use rocket::State; use rocket::State;
/// # Fetch Owned Bots /// Fetch Owned Bots
/// ///
/// Fetch all of the bots that you have control over. /// 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")] #[get("/@me")]
pub async fn fetch_owned_bots(db: &State<Database>, user: User) -> Result<Json<OwnedBotsResponse>> { 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?; let mut bots = db.fetch_bots_by_user(&user.id).await?;
+8 -2
View File
@@ -5,10 +5,16 @@ use revolt_result::{create_error, Result};
use rocket::serde::json::Json; use rocket::serde::json::Json;
use rocket::State; use rocket::State;
/// # Fetch Public Bot /// Fetch Public Bot
/// ///
/// Fetch details of a public (or owned) bot by its id. /// 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")] #[get("/<target>/invite")]
pub async fn fetch_public_bot( pub async fn fetch_public_bot(
db: &State<Database>, db: &State<Database>,
+8 -2
View File
@@ -11,10 +11,16 @@ use rocket::State;
use rocket::serde::json::Json; use rocket::serde::json::Json;
use rocket_empty::EmptyResponse; use rocket_empty::EmptyResponse;
/// # Invite Bot /// Invite Bot
/// ///
/// Invite a bot to a server or group by its id.` /// 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>")] #[post("/<target>/invite", data = "<dest>")]
pub async fn invite_bot( pub async fn invite_bot(
db: &State<Database>, db: &State<Database>,
+16 -3
View File
@@ -1,4 +1,3 @@
use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi;
use rocket::Route; use rocket::Route;
mod create; mod create;
@@ -9,8 +8,22 @@ mod fetch_owned;
mod fetch_public; mod fetch_public;
mod invite; mod invite;
pub fn routes() -> (Vec<Route>, OpenApi) { #[derive(OpenApi)]
openapi_get_routes_spec![ #[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, create::create_bot,
invite::invite_bot, invite::invite_bot,
fetch_public::fetch_public_bot, fetch_public::fetch_public_bot,
@@ -7,10 +7,20 @@ use revolt_result::{create_error, Result};
use rocket::State; use rocket::State;
use rocket_empty::EmptyResponse; 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. /// 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>")] #[put("/<target>/ack/<message>")]
pub async fn ack( pub async fn ack(
db: &State<Database>, db: &State<Database>,
@@ -9,10 +9,20 @@ use revolt_result::{create_error, Result};
use rocket::State; use rocket::State;
use rocket_empty::EmptyResponse; use rocket_empty::EmptyResponse;
/// # Close Channel /// Close Channel
/// ///
/// Deletes a server channel, leaves a group or closes a group. /// 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..>")] #[delete("/<target>?<options..>")]
pub async fn delete( pub async fn delete(
db: &State<Database>, db: &State<Database>,
@@ -9,10 +9,19 @@ use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State}; use rocket::{serde::json::Json, State};
use validator::Validate; use validator::Validate;
/// # Edit Channel /// Edit Channel
/// ///
/// Edit a channel object by its id. /// 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>")] #[patch("/<target>", data = "<data>")]
pub async fn edit( pub async fn edit(
db: &State<Database>, db: &State<Database>,
@@ -8,10 +8,19 @@ use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::Result; use revolt_result::Result;
use rocket::{serde::json::Json, State}; use rocket::{serde::json::Json, State};
/// # Fetch Channel /// Fetch Channel
/// ///
/// Fetch channel by its id. /// 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>")] #[get("/<target>")]
pub async fn fetch( pub async fn fetch(
db: &State<Database>, db: &State<Database>,
@@ -8,10 +8,20 @@ use revolt_result::{create_error, Result};
use rocket::State; use rocket::State;
use rocket_empty::EmptyResponse; use rocket_empty::EmptyResponse;
/// # Add Member to Group /// Add Member to Group
/// ///
/// Adds another user to the 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>")] #[put("/<group_id>/recipients/<member_id>")]
pub async fn add_member( pub async fn add_member(
db: &State<Database>, db: &State<Database>,
@@ -6,10 +6,16 @@ use rocket::serde::json::Json;
use rocket::State; use rocket::State;
use validator::Validate; use validator::Validate;
/// # Create Group /// Create Group
/// ///
/// Create a new group channel. /// 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>")] #[post("/create", data = "<data>")]
pub async fn create_group( pub async fn create_group(
db: &State<Database>, db: &State<Database>,
@@ -9,10 +9,20 @@ use revolt_result::{create_error, Result};
use rocket::State; use rocket::State;
use rocket_empty::EmptyResponse; use rocket_empty::EmptyResponse;
/// # Remove Member from Group /// Remove Member from Group
/// ///
/// Removes a user from the 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>")] #[delete("/<target>/recipients/<member>")]
pub async fn remove_member( pub async fn remove_member(
db: &State<Database>, db: &State<Database>,
@@ -8,12 +8,21 @@ use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::{create_error, Result}; use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State}; use rocket::{serde::json::Json, State};
/// # Create Invite /// Create Invite
/// ///
/// Creates an invite to this channel. /// Creates an invite to this channel.
/// ///
/// Channel must be a `TextChannel`. /// 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")] #[post("/<target>/invites")]
pub async fn create_invite( pub async fn create_invite(
db: &State<Database>, db: &State<Database>,
@@ -7,12 +7,21 @@ use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::{create_error, Result}; use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State}; use rocket::{serde::json::Json, State};
/// # Fetch Group Members /// Fetch Group Members
/// ///
/// Retrieves all users who are part of this group. /// 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. /// 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")] #[get("/<target>/members")]
pub async fn fetch_members( pub async fn fetch_members(
db: &State<Database>, db: &State<Database>,
@@ -10,14 +10,23 @@ use rocket::{serde::json::Json, State};
use rocket_empty::EmptyResponse; use rocket_empty::EmptyResponse;
use validator::Validate; use validator::Validate;
/// # Bulk Delete Messages /// Bulk Delete Messages
/// ///
/// Delete multiple messages you've sent or one you have permission to delete. /// 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. /// 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. /// 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)] #[delete("/<target>/messages/bulk", data = "<options>", rank = 1)]
pub async fn bulk_delete_messages( pub async fn bulk_delete_messages(
db: &State<Database>, db: &State<Database>,
@@ -7,12 +7,22 @@ use revolt_result::Result;
use rocket::State; use rocket::State;
use rocket_empty::EmptyResponse; 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. /// Remove your own, someone else's or all of a given reaction.
/// ///
/// Requires `ManageMessages` permission. /// 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")] #[delete("/<target>/messages/<msg>/reactions")]
pub async fn clear_reactions( pub async fn clear_reactions(
db: &State<Database>, db: &State<Database>,
@@ -37,7 +47,7 @@ pub async fn clear_reactions(
reactions: Some(Default::default()), reactions: Some(Default::default()),
..Default::default() ..Default::default()
}, },
vec![] vec![],
) )
.await .await
.map(|_| EmptyResponse) .map(|_| EmptyResponse)
@@ -7,10 +7,20 @@ use revolt_result::Result;
use rocket::State; use rocket::State;
use rocket_empty::EmptyResponse; use rocket_empty::EmptyResponse;
/// # Delete Message /// Delete Message
/// ///
/// Delete a message you've sent or one you have permission to delete. /// 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)] #[delete("/<target>/messages/<msg>", rank = 2)]
pub async fn delete( pub async fn delete(
db: &State<Database>, db: &State<Database>,
@@ -10,10 +10,20 @@ use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State}; use rocket::{serde::json::Json, State};
use validator::Validate; use validator::Validate;
/// # Edit Message /// Edit Message
/// ///
/// Edits a message that you've previously sent. /// 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>")] #[patch("/<target>/messages/<msg>", data = "<edit>")]
pub async fn edit( pub async fn edit(
db: &State<Database>, db: &State<Database>,
@@ -7,10 +7,20 @@ use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::{create_error, Result}; use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State}; use rocket::{serde::json::Json, State};
/// # Fetch Message /// Fetch Message
/// ///
/// Retrieves a message by its id. /// 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>")] #[get("/<target>/messages/<msg>")]
pub async fn fetch( pub async fn fetch(
db: &State<Database>, db: &State<Database>,
@@ -8,10 +8,20 @@ use revolt_result::{create_error, Result};
use rocket::State; use rocket::State;
use rocket_empty::EmptyResponse; use rocket_empty::EmptyResponse;
/// # Pins a message /// Pins a message
/// ///
/// Pins a message by its id. /// 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")] #[post("/<target>/messages/<msg>/pin")]
pub async fn message_pin( pub async fn message_pin(
db: &State<Database>, db: &State<Database>,
@@ -8,10 +8,20 @@ use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State}; use rocket::{serde::json::Json, State};
use validator::Validate; use validator::Validate;
/// # Fetch Messages /// Fetch Messages
/// ///
/// Fetch multiple 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..>")] #[get("/<target>/messages?<options..>")]
pub async fn query( pub async fn query(
db: &State<Database>, db: &State<Database>,
@@ -7,10 +7,21 @@ use revolt_result::Result;
use rocket::State; use rocket::State;
use rocket_empty::EmptyResponse; use rocket_empty::EmptyResponse;
/// # Add Reaction to Message /// Add Reaction to Message
/// ///
/// React to a given 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>")] #[put("/<target>/messages/<msg>/reactions/<emoji>")]
pub async fn react_message( pub async fn react_message(
db: &State<Database>, db: &State<Database>,
@@ -8,10 +8,19 @@ use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State}; use rocket::{serde::json::Json, State};
use validator::Validate; use validator::Validate;
/// # Search for Messages /// Search for Messages
/// ///
/// This route searches for messages within the given parameters. /// 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>")] #[post("/<target>/search", data = "<options>")]
pub async fn search( pub async fn search(
db: &State<Database>, db: &State<Database>,
@@ -12,10 +12,20 @@ use rocket::serde::json::Json;
use rocket::State; use rocket::State;
use validator::Validate; use validator::Validate;
/// # Send Message /// Send Message
/// ///
/// Sends a message to the given channel. /// 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>")] #[post("/<target>/messages", data = "<data>")]
pub async fn message_send( pub async fn message_send(
db: &State<Database>, db: &State<Database>,
@@ -151,20 +161,29 @@ mod test {
name: "Hidden Channel".to_string(), name: "Hidden Channel".to_string(),
description: None, description: None,
nsfw: Some(false), nsfw: Some(false),
voice: None, voice: None
}, },
true, true,
) )
.await .await
.expect("Failed to make new channel"); .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 .await
.expect("Failed to create the role"); .expect("Failed to create the role");
let mut overrides = HashMap::new(); let mut overrides = HashMap::new();
overrides.insert( overrides.insert(
role.id.clone(), role_id.clone(),
OverrideField { OverrideField {
a: (ChannelPermission::ViewChannel) as i64, a: (ChannelPermission::ViewChannel) as i64,
d: 0, d: 0,
@@ -272,7 +291,7 @@ mod test {
"Mention failed to be scrubbed when the user cannot see the channel" "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 { let partial = PartialMember {
id: None, id: None,
joined_at: None, joined_at: None,
@@ -281,7 +300,7 @@ mod test {
timeout: None, timeout: None,
roles: Some(second_member_roles), roles: Some(second_member_roles),
can_publish: None, can_publish: None,
can_receive: None, can_receive: None
}; };
second_member second_member
.update(&harness.db, partial, vec![]) .update(&harness.db, partial, vec![])
@@ -487,7 +506,7 @@ mod test {
let (_, _, other_user) = harness.new_user().await; let (_, _, other_user) = harness.new_user().await;
let (server, _) = harness.new_server(&user).await; let (server, _) = harness.new_server(&user).await;
let channel = harness.new_channel(&server).await; let channel = harness.new_channel(&server).await;
let role = harness let (role_id, _role) = harness
.new_role( .new_role(
&server, &server,
1, 1,
@@ -509,7 +528,7 @@ mod test {
Some(&harness.amqp), Some(&harness.amqp),
channel.clone(), channel.clone(),
v0::DataMessageSend { v0::DataMessageSend {
content: Some(format!("Mentioning @everyone and role <%{}>", &role.id)), content: Some(format!("Mentioning @everyone and role <%{}>", &role_id)),
nonce: None, nonce: None,
attachments: None, attachments: None,
replies: None, replies: None,
@@ -554,7 +573,7 @@ mod test {
Some(&harness.amqp), Some(&harness.amqp),
channel.clone(), channel.clone(),
v0::DataMessageSend { v0::DataMessageSend {
content: Some(format!("Mentioning `@everyone` and role `<%{}>`", &role.id)), content: Some(format!("Mentioning `@everyone` and role `<%{}>`", &role_id)),
nonce: None, nonce: None,
attachments: None, attachments: None,
replies: None, replies: None,
@@ -596,7 +615,7 @@ mod test {
"Role mentions detected when inside codeblock" "Role mentions detected when inside codeblock"
); );
other_member.roles.push(role.id.clone()); other_member.roles.push(role_id.clone());
harness harness
.db .db
.update_member( .update_member(
@@ -606,10 +625,10 @@ mod test {
id: None, id: None,
joined_at: None, joined_at: None,
nickname: None, nickname: None,
roles: Some(vec![role.id.clone()]), roles: Some(vec![role_id.clone()]),
timeout: None, timeout: None,
can_publish: None, can_publish: None,
can_receive: None, can_receive: None
}, },
vec![], vec![],
) )
@@ -623,7 +642,7 @@ mod test {
Some(&harness.amqp), Some(&harness.amqp),
channel.clone(), channel.clone(),
v0::DataMessageSend { v0::DataMessageSend {
content: Some(format!("Mentioning @everyone and role <%{}>", &role.id)), content: Some(format!("Mentioning @everyone and role <%{}>", &role_id)),
nonce: None, nonce: None,
attachments: None, attachments: None,
replies: None, replies: None,
@@ -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_models::v0::MessageAuthor;
use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::{create_error, Result}; use revolt_result::{create_error, Result};
use rocket::State; use rocket::State;
use rocket_empty::EmptyResponse; use rocket_empty::EmptyResponse;
/// # Unpins a message /// Unpins a message
/// ///
/// Unpins a message by its id. /// 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")] #[delete("/<target>/messages/<msg>/pin")]
pub async fn message_unpin( pub async fn message_unpin(
db: &State<Database>, db: &State<Database>,
@@ -8,12 +8,24 @@ use revolt_result::Result;
use rocket::State; use rocket::State;
use rocket_empty::EmptyResponse; 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. /// Remove your own, someone else's or all of a given reaction.
/// ///
/// Requires `ManageMessages` if changing others' reactions. /// 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..>")] #[delete("/<target>/messages/<msg>/reactions/<emoji>?<options..>")]
pub async fn unreact_message( pub async fn unreact_message(
db: &State<Database>, db: &State<Database>,
+36 -3
View File
@@ -1,4 +1,3 @@
use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi;
use rocket::Route; use rocket::Route;
mod channel_ack; mod channel_ack;
@@ -29,8 +28,42 @@ mod voice_stop_ring;
mod webhook_create; mod webhook_create;
mod webhook_fetch_all; mod webhook_fetch_all;
pub fn routes() -> (Vec<Route>, OpenApi) { #[derive(OpenApi)]
openapi_get_routes_spec![ #[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_ack::ack,
channel_fetch::fetch, channel_fetch::fetch,
members_fetch::fetch_members, members_fetch::fetch_members,
@@ -6,12 +6,22 @@ use revolt_permissions::{calculate_channel_permissions, ChannelPermission, Overr
use revolt_result::{create_error, Result}; use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State}; use rocket::{serde::json::Json, State};
/// # Set Role Permission /// Set Role Permission
/// ///
/// Sets permissions for the specified role in this channel. /// Sets permissions for the specified role in this channel.
/// ///
/// Channel must be a `TextChannel`. /// 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)] #[put("/<target>/permissions/<role_id>", data = "<data>", rank = 2)]
pub async fn set_role_permissions( pub async fn set_role_permissions(
db: &State<Database>, db: &State<Database>,
@@ -6,12 +6,21 @@ use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::{create_error, Result}; use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State}; use rocket::{serde::json::Json, State};
/// # Set Default Permission /// Set Default Permission
/// ///
/// Sets permissions for the default role in this channel. /// Sets permissions for the default role in this channel.
/// ///
/// Channel must be a `Group` or `TextChannel`. /// 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)] #[put("/<target>/permissions/default", data = "<data>", rank = 1)]
pub async fn set_default_channel_permissions( pub async fn set_default_channel_permissions(
db: &State<Database>, db: &State<Database>,
+11 -2
View File
@@ -13,10 +13,19 @@ use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State}; use rocket::{serde::json::Json, State};
/// # Join Call /// Join Call
/// ///
/// Asks the voice server for a token to join the 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>")] #[post("/<target>/join_call", data = "<data>")]
pub async fn call( pub async fn call(
db: &State<Database>, db: &State<Database>,
@@ -8,12 +8,23 @@ use revolt_result::{create_error, Result, ToRevoltError};
use rocket::State; use rocket::State;
use rocket_empty::EmptyResponse; use rocket_empty::EmptyResponse;
/// # Stop Ring /// Stop Ring
///
/// Stops ringing a specific user in a dm call. /// Stops ringing a specific user in a dm call.
/// You must be in the call to use this endpoint, returns NotConnected otherwise. /// You must be in the call to use this endpoint, returns NotConnected otherwise.
/// Only valid in DM/Group channels, will return NoEffect in servers. /// Only valid in DM/Group channels, will return NoEffect in servers.
/// Returns NotFound if the user is not in the dm/group channel /// 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>")] #[put("/<target>/end_ring/<target_user>")]
pub async fn stop_ring( pub async fn stop_ring(
db: &State<Database>, db: &State<Database>,
@@ -11,10 +11,19 @@ use rocket::{serde::json::Json, State};
use ulid::Ulid; use ulid::Ulid;
use validator::Validate; use validator::Validate;
/// # Creates a webhook /// Creates a webhook
/// ///
/// Creates a webhook which 3rd party platforms can use to send messages /// Creates a webhook which 3rd party platforms can use to send messages
#[openapi(tag = "Webhooks")] #[utoipa::path(
tag = "Webhooks",
security(("Session-Token" = []), ("Bot-Token" = [])),
params(
("target" = Reference, Path),
),
responses(
(status = 200, body = v0::Webhook),
),
)]
#[post("/<target>/webhooks", data = "<data>")] #[post("/<target>/webhooks", data = "<data>")]
pub async fn create_webhook( pub async fn create_webhook(
db: &State<Database>, db: &State<Database>,
@@ -7,10 +7,19 @@ use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::Result; use revolt_result::Result;
use rocket::{serde::json::Json, State}; use rocket::{serde::json::Json, State};
/// # Gets all webhooks /// Gets all webhooks
/// ///
/// Gets all webhooks inside the channel /// Gets all webhooks inside the channel
#[openapi(tag = "Webhooks")] #[utoipa::path(
tag = "Webhooks",
security(("Session-Token" = []), ("Bot-Token" = [])),
params(
("target" = Reference, Path),
),
responses(
(status = 200, body = Vec<Webhook>),
),
)]
#[get("/<channel_id>/webhooks")] #[get("/<channel_id>/webhooks")]
pub async fn fetch_webhooks( pub async fn fetch_webhooks(
db: &State<Database>, db: &State<Database>,
@@ -1,5 +1,5 @@
use revolt_config::config; use revolt_config::config;
use revolt_database::{util::permissions::DatabasePermissionQuery, Database, Emoji, File, User}; use revolt_database::{Database, Emoji, File, User, util::{permissions::DatabasePermissionQuery, reference::Reference}};
use revolt_models::v0; use revolt_models::v0;
use revolt_permissions::{calculate_server_permissions, ChannelPermission}; use revolt_permissions::{calculate_server_permissions, ChannelPermission};
use revolt_result::{create_error, Result}; use revolt_result::{create_error, Result};
@@ -7,10 +7,19 @@ use validator::Validate;
use rocket::{serde::json::Json, State}; use rocket::{serde::json::Json, State};
/// # Create New Emoji /// Create New Emoji
/// ///
/// Create an emoji by its Autumn upload id. /// Create an emoji by its Autumn upload id.
#[openapi(tag = "Emojis")] #[utoipa::path(
tag = "Emojis",
security(("Session-Token" = []), ("Bot-Token" = [])),
params(
("id" = Reference, Path),
),
responses(
(status = 200, body = v0::Emoji),
),
)]
#[put("/emoji/<id>", data = "<data>")] #[put("/emoji/<id>", data = "<data>")]
pub async fn create_emoji( pub async fn create_emoji(
db: &State<Database>, db: &State<Database>,
Loaded 100 of 189 files, more files were not shown because too many files have changed in this diff. Show more