Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
962c7d62c7 | ||
|
|
d677716f93 | ||
|
|
8099310f89 | ||
|
|
5c40f66010 | ||
|
|
8e7dd21bce | ||
|
|
4868205df4 | ||
|
|
96fb0eecca | ||
|
|
2cb20618da | ||
|
|
92e948aabc | ||
|
|
cb4435d700 | ||
|
|
4c6e78e1a5 | ||
|
|
da9a91e05f | ||
|
|
506968634e |
No files matched your search
Generated
+9
-8
@@ -3408,10 +3408,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-bonfire"
|
name = "revolt-bonfire"
|
||||||
version = "0.7.2"
|
version = "0.7.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-tungstenite",
|
"async-tungstenite",
|
||||||
|
"authifier",
|
||||||
"bincode",
|
"bincode",
|
||||||
"fred",
|
"fred",
|
||||||
"futures",
|
"futures",
|
||||||
@@ -3435,7 +3436,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-config"
|
name = "revolt-config"
|
||||||
version = "0.7.2"
|
version = "0.7.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"cached",
|
"cached",
|
||||||
@@ -3451,7 +3452,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-database"
|
name = "revolt-database"
|
||||||
version = "0.7.2"
|
version = "0.7.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-lock",
|
"async-lock",
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
@@ -3497,7 +3498,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-delta"
|
name = "revolt-delta"
|
||||||
version = "0.7.2"
|
version = "0.7.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-channel",
|
"async-channel",
|
||||||
"async-std",
|
"async-std",
|
||||||
@@ -3543,7 +3544,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-models"
|
name = "revolt-models"
|
||||||
version = "0.7.2"
|
version = "0.7.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"iso8601-timestamp 0.2.11",
|
"iso8601-timestamp 0.2.11",
|
||||||
@@ -3560,7 +3561,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-permissions"
|
name = "revolt-permissions"
|
||||||
version = "0.7.2"
|
version = "0.7.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -3575,7 +3576,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-presence"
|
name = "revolt-presence"
|
||||||
version = "0.7.2"
|
version = "0.7.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"log",
|
"log",
|
||||||
@@ -3586,7 +3587,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-result"
|
name = "revolt-result"
|
||||||
version = "0.7.2"
|
version = "0.7.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"revolt_okapi",
|
"revolt_okapi",
|
||||||
"revolt_rocket_okapi",
|
"revolt_rocket_okapi",
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
With the exception of crates that specify their own LICENSE file,
|
||||||
|
the following license applies to the source code of this project.
|
||||||
|
|
||||||
|
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||||
Version 3, 19 November 2007
|
Version 3, 19 November 2007
|
||||||
|
|
||||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
|||||||
@@ -48,7 +48,29 @@ cp .env.example .env
|
|||||||
# (optionally) copy the default configuration file
|
# (optionally) copy the default configuration file
|
||||||
cp crates/core/config/Revolt.toml Revolt.toml
|
cp crates/core/config/Revolt.toml Revolt.toml
|
||||||
# configure as necessary...
|
# configure as necessary...
|
||||||
|
```
|
||||||
|
|
||||||
|
You may want to copy the following configuration:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Revolt.toml
|
||||||
|
[database]
|
||||||
|
mongodb = "mongodb://localhost"
|
||||||
|
redis = "redis://localhost"
|
||||||
|
|
||||||
|
[hosts]
|
||||||
|
app = "http://local.revolt.chat"
|
||||||
|
api = "http://local.revolt.chat:8000"
|
||||||
|
events = "ws://local.revolt.chat:9000"
|
||||||
|
autumn = "http://local.revolt.chat:3000"
|
||||||
|
january = "http://local.revolt.chat:7000"
|
||||||
|
voso_legacy = ""
|
||||||
|
voso_legacy_ws = ""
|
||||||
|
```
|
||||||
|
|
||||||
|
Then continue:
|
||||||
|
|
||||||
|
```bash
|
||||||
# start other necessary services
|
# start other necessary services
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
|
|
||||||
@@ -105,6 +127,23 @@ Tag and push a new release by running:
|
|||||||
just release
|
just release
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you have bumped the crate versions, proceed to [GitHub releases](https://github.com/revoltchat/backend/releases/new) to create a changelog.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
First, start the required services:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker compose -f docker-compose.db.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Now run tests for whichever database:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
TEST_DB=REFERENCE cargo nextest run
|
||||||
|
TEST_DB=MONGOBD cargo nextest run
|
||||||
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
The Revolt backend is generally licensed under the [GNU Affero General Public License v3.0](https://github.com/revoltchat/backend/blob/master/LICENSE).
|
The Revolt backend is generally licensed under the [GNU Affero General Public License v3.0](https://github.com/revoltchat/backend/blob/master/LICENSE).
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-bonfire"
|
name = "revolt-bonfire"
|
||||||
version = "0.7.2"
|
version = "0.7.4"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
@@ -34,11 +34,12 @@ async-std = { version = "1.8.0", features = [
|
|||||||
] }
|
] }
|
||||||
|
|
||||||
# core
|
# core
|
||||||
|
authifier = { version = "1.0.8" }
|
||||||
revolt-result = { path = "../core/result" }
|
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" }
|
revolt-database = { path = "../core/database" }
|
||||||
revolt-permissions = { version = "0.7.2", path = "../core/permissions" }
|
revolt-permissions = { version = "0.7.4", path = "../core/permissions" }
|
||||||
revolt-presence = { path = "../core/presence", features = ["redis-is-patched"] }
|
revolt-presence = { path = "../core/presence", features = ["redis-is-patched"] }
|
||||||
|
|
||||||
# redis
|
# redis
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ impl Cache {
|
|||||||
let server = self.servers.get(server);
|
let server = self.servers.get(server);
|
||||||
let mut query =
|
let mut query =
|
||||||
DatabasePermissionQuery::new(db, self.users.get(&self.user_id).unwrap())
|
DatabasePermissionQuery::new(db, self.users.get(&self.user_id).unwrap())
|
||||||
.channel(&channel);
|
.channel(channel);
|
||||||
// let mut perms = perms(self.users.get(&self.user_id).unwrap()).channel(channel);
|
// let mut perms = perms(self.users.get(&self.user_id).unwrap()).channel(channel);
|
||||||
|
|
||||||
if let Some(member) = member {
|
if let Some(member) = member {
|
||||||
query = query.member(&member);
|
query = query.member(member);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(server) = server {
|
if let Some(server) = server {
|
||||||
@@ -182,19 +182,19 @@ impl State {
|
|||||||
users.push(user.into_self().await);
|
users.push(user.into_self().await);
|
||||||
|
|
||||||
// Set subscription state internally.
|
// Set subscription state internally.
|
||||||
self.reset_state();
|
self.reset_state().await;
|
||||||
self.insert_subscription(self.private_topic.clone());
|
self.insert_subscription(self.private_topic.clone()).await;
|
||||||
|
|
||||||
for user in &users {
|
for user in &users {
|
||||||
self.insert_subscription(user.id.clone());
|
self.insert_subscription(user.id.clone()).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
for server in &servers {
|
for server in &servers {
|
||||||
self.insert_subscription(server.id.clone());
|
self.insert_subscription(server.id.clone()).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
for channel in &channels {
|
for channel in &channels {
|
||||||
self.insert_subscription(channel.id().to_string());
|
self.insert_subscription(channel.id().to_string()).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(EventV1::Ready {
|
Ok(EventV1::Ready {
|
||||||
@@ -236,11 +236,11 @@ impl State {
|
|||||||
let mut bulk_events = vec![];
|
let mut bulk_events = vec![];
|
||||||
|
|
||||||
for id in added_channels {
|
for id in added_channels {
|
||||||
self.insert_subscription(id);
|
self.insert_subscription(id).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
for id in removed_channels {
|
for id in removed_channels {
|
||||||
self.remove_subscription(&id);
|
self.remove_subscription(&id).await;
|
||||||
self.cache.channels.remove(&id);
|
self.cache.channels.remove(&id);
|
||||||
|
|
||||||
bulk_events.push(EventV1::ChannelDelete { id });
|
bulk_events.push(EventV1::ChannelDelete { id });
|
||||||
@@ -263,7 +263,7 @@ impl State {
|
|||||||
.channels
|
.channels
|
||||||
.insert(channel.id().to_string(), channel.clone());
|
.insert(channel.id().to_string(), channel.clone());
|
||||||
|
|
||||||
self.insert_subscription(channel.id().to_string());
|
self.insert_subscription(channel.id().to_string()).await;
|
||||||
bulk_events.push(EventV1::ChannelCreate(channel.into()));
|
bulk_events.push(EventV1::ChannelCreate(channel.into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -336,7 +336,7 @@ impl State {
|
|||||||
match event {
|
match event {
|
||||||
EventV1::ChannelCreate(channel) => {
|
EventV1::ChannelCreate(channel) => {
|
||||||
let id = channel.id().to_string();
|
let id = channel.id().to_string();
|
||||||
self.insert_subscription(id.clone());
|
self.insert_subscription(id.clone()).await;
|
||||||
self.cache.channels.insert(id, channel.clone().into());
|
self.cache.channels.insert(id, channel.clone().into());
|
||||||
}
|
}
|
||||||
EventV1::ChannelUpdate {
|
EventV1::ChannelUpdate {
|
||||||
@@ -376,17 +376,17 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
EventV1::ChannelDelete { id } => {
|
EventV1::ChannelDelete { id } => {
|
||||||
self.remove_subscription(id);
|
self.remove_subscription(id).await;
|
||||||
self.cache.channels.remove(id);
|
self.cache.channels.remove(id);
|
||||||
}
|
}
|
||||||
EventV1::ChannelGroupJoin { user, .. } => {
|
EventV1::ChannelGroupJoin { user, .. } => {
|
||||||
self.insert_subscription(user.clone());
|
self.insert_subscription(user.clone()).await;
|
||||||
}
|
}
|
||||||
EventV1::ChannelGroupLeave { id, user, .. } => {
|
EventV1::ChannelGroupLeave { id, user, .. } => {
|
||||||
if user == &self.cache.user_id {
|
if user == &self.cache.user_id {
|
||||||
self.remove_subscription(id);
|
self.remove_subscription(id).await;
|
||||||
} else if !self.cache.can_subscribe_to_user(user) {
|
} else if !self.cache.can_subscribe_to_user(user) {
|
||||||
self.remove_subscription(user);
|
self.remove_subscription(user).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,7 +396,7 @@ impl State {
|
|||||||
channels,
|
channels,
|
||||||
emojis: _,
|
emojis: _,
|
||||||
} => {
|
} => {
|
||||||
self.insert_subscription(id.clone());
|
self.insert_subscription(id.clone()).await;
|
||||||
self.cache.servers.insert(id.clone(), server.clone().into());
|
self.cache.servers.insert(id.clone(), server.clone().into());
|
||||||
let member = Member {
|
let member = Member {
|
||||||
id: MemberCompositeKey {
|
id: MemberCompositeKey {
|
||||||
@@ -435,11 +435,11 @@ impl State {
|
|||||||
}
|
}
|
||||||
EventV1::ServerMemberLeave { id, user } => {
|
EventV1::ServerMemberLeave { id, user } => {
|
||||||
if user == &self.cache.user_id {
|
if user == &self.cache.user_id {
|
||||||
self.remove_subscription(id);
|
self.remove_subscription(id).await;
|
||||||
|
|
||||||
if let Some(server) = self.cache.servers.remove(id) {
|
if let Some(server) = self.cache.servers.remove(id) {
|
||||||
for channel in &server.channels {
|
for channel in &server.channels {
|
||||||
self.remove_subscription(channel);
|
self.remove_subscription(channel).await;
|
||||||
self.cache.channels.remove(channel);
|
self.cache.channels.remove(channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -447,11 +447,11 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
EventV1::ServerDelete { id } => {
|
EventV1::ServerDelete { id } => {
|
||||||
self.remove_subscription(id);
|
self.remove_subscription(id).await;
|
||||||
|
|
||||||
if let Some(server) = self.cache.servers.remove(id) {
|
if let Some(server) = self.cache.servers.remove(id) {
|
||||||
for channel in &server.channels {
|
for channel in &server.channels {
|
||||||
self.remove_subscription(channel);
|
self.remove_subscription(channel).await;
|
||||||
self.cache.channels.remove(channel);
|
self.cache.channels.remove(channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -524,9 +524,9 @@ impl State {
|
|||||||
self.cache.users.insert(id.clone(), user.clone().into());
|
self.cache.users.insert(id.clone(), user.clone().into());
|
||||||
|
|
||||||
if self.cache.can_subscribe_to_user(id) {
|
if self.cache.can_subscribe_to_user(id) {
|
||||||
self.insert_subscription(id.clone());
|
self.insert_subscription(id.clone()).await;
|
||||||
} else {
|
} else {
|
||||||
self.remove_subscription(id);
|
self.remove_subscription(id).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -540,11 +540,11 @@ impl State {
|
|||||||
|
|
||||||
// Sub / unsub accordingly.
|
// Sub / unsub accordingly.
|
||||||
if let Some(id) = queue_add {
|
if let Some(id) = queue_add {
|
||||||
self.insert_subscription(id);
|
self.insert_subscription(id).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(id) = queue_remove {
|
if let Some(id) = queue_remove {
|
||||||
self.remove_subscription(&id);
|
self.remove_subscription(&id).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
use async_std::sync::RwLock;
|
||||||
use lru::LruCache;
|
use lru::LruCache;
|
||||||
use revolt_database::{Channel, Member, Server, User};
|
use revolt_database::{Channel, Member, Server, User};
|
||||||
|
|
||||||
@@ -58,14 +62,15 @@ impl Default for Cache {
|
|||||||
pub struct State {
|
pub struct State {
|
||||||
pub cache: Cache,
|
pub cache: Cache,
|
||||||
|
|
||||||
|
pub session_id: String,
|
||||||
pub private_topic: String,
|
pub private_topic: String,
|
||||||
subscribed: HashSet<String>,
|
pub state: SubscriptionStateChange,
|
||||||
state: SubscriptionStateChange,
|
pub subscribed: Arc<RwLock<HashSet<String>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
/// Create state from User
|
/// Create state from User
|
||||||
pub fn from(user: User) -> State {
|
pub fn from(user: User, session_id: String) -> State {
|
||||||
let mut subscribed = HashSet::new();
|
let mut subscribed = HashSet::new();
|
||||||
let private_topic = format!("{}!", user.id);
|
let private_topic = format!("{}!", user.id);
|
||||||
subscribed.insert(private_topic.clone());
|
subscribed.insert(private_topic.clone());
|
||||||
@@ -80,22 +85,24 @@ impl State {
|
|||||||
|
|
||||||
State {
|
State {
|
||||||
cache,
|
cache,
|
||||||
subscribed,
|
subscribed: Arc::new(RwLock::new(subscribed)),
|
||||||
|
session_id,
|
||||||
private_topic,
|
private_topic,
|
||||||
state: SubscriptionStateChange::Reset,
|
state: SubscriptionStateChange::Reset,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply currently queued state
|
/// Apply currently queued state
|
||||||
pub fn apply_state(&mut self) -> SubscriptionStateChange {
|
pub async fn apply_state(&mut self) -> SubscriptionStateChange {
|
||||||
let state = std::mem::replace(&mut self.state, SubscriptionStateChange::None);
|
let state = std::mem::replace(&mut self.state, SubscriptionStateChange::None);
|
||||||
|
let mut subscribed = self.subscribed.write().await;
|
||||||
if let SubscriptionStateChange::Change { add, remove } = &state {
|
if let SubscriptionStateChange::Change { add, remove } = &state {
|
||||||
for id in add {
|
for id in add {
|
||||||
self.subscribed.insert(id.clone());
|
subscribed.insert(id.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
for id in remove {
|
for id in remove {
|
||||||
self.subscribed.remove(id);
|
subscribed.remove(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,20 +114,16 @@ impl State {
|
|||||||
self.cache.users.get(&self.cache.user_id).unwrap().clone()
|
self.cache.users.get(&self.cache.user_id).unwrap().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate through all subscriptions
|
|
||||||
pub fn iter_subscriptions(&self) -> std::collections::hash_set::Iter<'_, std::string::String> {
|
|
||||||
self.subscribed.iter()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reset the current state
|
/// Reset the current state
|
||||||
pub fn reset_state(&mut self) {
|
pub async fn reset_state(&mut self) {
|
||||||
self.state = SubscriptionStateChange::Reset;
|
self.state = SubscriptionStateChange::Reset;
|
||||||
self.subscribed.clear();
|
self.subscribed.write().await.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a new subscription
|
/// Add a new subscription
|
||||||
pub fn insert_subscription(&mut self, subscription: String) {
|
pub async fn insert_subscription(&mut self, subscription: String) {
|
||||||
if self.subscribed.contains(&subscription) {
|
let mut subscribed = self.subscribed.write().await;
|
||||||
|
if subscribed.contains(&subscription) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,12 +140,13 @@ impl State {
|
|||||||
SubscriptionStateChange::Reset => {}
|
SubscriptionStateChange::Reset => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.subscribed.insert(subscription);
|
subscribed.insert(subscription);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove existing subscription
|
/// Remove existing subscription
|
||||||
pub fn remove_subscription(&mut self, subscription: &str) {
|
pub async fn remove_subscription(&mut self, subscription: &str) {
|
||||||
if !self.subscribed.contains(&subscription.to_string()) {
|
let mut subscribed = self.subscribed.write().await;
|
||||||
|
if !subscribed.contains(&subscription.to_string()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,6 +163,6 @@ impl State {
|
|||||||
SubscriptionStateChange::Reset => panic!("Should not remove during a reset!"),
|
SubscriptionStateChange::Reset => panic!("Should not remove during a reset!"),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.subscribed.remove(subscription);
|
subscribed.remove(subscription);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
use std::net::SocketAddr;
|
use std::{collections::HashSet, net::SocketAddr, sync::Arc};
|
||||||
|
|
||||||
use async_tungstenite::WebSocketStream;
|
use async_tungstenite::WebSocketStream;
|
||||||
|
use authifier::AuthifierEvent;
|
||||||
use fred::{
|
use fred::{
|
||||||
interfaces::{ClientLike, EventInterface, PubsubInterface},
|
interfaces::{ClientLike, EventInterface, PubsubInterface},
|
||||||
types::RedisConfig,
|
types::RedisConfig,
|
||||||
@@ -18,7 +19,11 @@ use revolt_database::{
|
|||||||
};
|
};
|
||||||
use revolt_presence::{create_session, delete_session};
|
use revolt_presence::{create_session, delete_session};
|
||||||
|
|
||||||
use async_std::{net::TcpStream, sync::Mutex};
|
use async_std::{
|
||||||
|
net::TcpStream,
|
||||||
|
sync::{Mutex, RwLock},
|
||||||
|
};
|
||||||
|
use revolt_result::create_error;
|
||||||
|
|
||||||
use crate::config::{ProtocolConfiguration, WebsocketHandshakeCallback};
|
use crate::config::{ProtocolConfiguration, WebsocketHandshakeCallback};
|
||||||
use crate::events::state::{State, SubscriptionStateChange};
|
use crate::events::state::{State, SubscriptionStateChange};
|
||||||
@@ -42,6 +47,7 @@ pub async fn client(db: &'static Database, stream: TcpStream, addr: SocketAddr)
|
|||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Verify we've received a valid config, otherwise we should just drop the connection.
|
// Verify we've received a valid config, otherwise we should just drop the connection.
|
||||||
let Ok(mut config) = receiver.await else {
|
let Ok(mut config) = receiver.await else {
|
||||||
return;
|
return;
|
||||||
@@ -69,19 +75,22 @@ pub async fn client(db: &'static Database, stream: TcpStream, addr: SocketAddr)
|
|||||||
|
|
||||||
// Try to authenticate the user.
|
// Try to authenticate the user.
|
||||||
let Some(token) = config.get_session_token().as_ref() else {
|
let Some(token) = config.get_session_token().as_ref() else {
|
||||||
|
write.send(config.encode(&create_error!(InvalidSession))).await.ok();
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let user = match User::from_token(db, token, UserHint::Any).await {
|
|
||||||
|
let (user, session_id) = match User::from_token(db, token, UserHint::Any).await {
|
||||||
Ok(user) => user,
|
Ok(user) => user,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
write.send(config.encode(&err)).await.ok();
|
write.send(config.encode(&err)).await.ok();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
info!("User {addr:?} authenticated as @{}", user.username);
|
info!("User {addr:?} authenticated as @{}", user.username);
|
||||||
|
|
||||||
// Create local state.
|
// Create local state.
|
||||||
let mut state = State::from(user);
|
let mut state = State::from(user, session_id);
|
||||||
let user_id = state.cache.user_id.clone();
|
let user_id = state.cache.user_id.clone();
|
||||||
|
|
||||||
// Notify socket we have authenticated.
|
// Notify socket we have authenticated.
|
||||||
@@ -97,6 +106,7 @@ pub async fn client(db: &'static Database, stream: TcpStream, addr: SocketAddr)
|
|||||||
let Ok(ready_payload) = state.generate_ready_payload(db).await else {
|
let Ok(ready_payload) = state.generate_ready_payload(db).await else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if write.send(config.encode(&ready_payload)).await.is_err() {
|
if write.send(config.encode(&ready_payload)).await.is_err() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -111,10 +121,12 @@ pub async fn client(db: &'static Database, stream: TcpStream, addr: SocketAddr)
|
|||||||
|
|
||||||
{
|
{
|
||||||
let write = Mutex::new(write);
|
let write = Mutex::new(write);
|
||||||
|
let subscribed = state.subscribed.clone();
|
||||||
|
|
||||||
// Create a PubSub connection to poll on.
|
// Create a PubSub connection to poll on.
|
||||||
let listener = listener(db, &mut state, addr, &config, &write).fuse();
|
let listener = listener(db, &mut state, addr, &config, &write).fuse();
|
||||||
// Read from WebSocket stream.
|
// Read from WebSocket stream.
|
||||||
let worker = worker(addr, user_id.clone(), &config, read, &write).fuse();
|
let worker = worker(addr, subscribed, user_id.clone(), &config, read, &write).fuse();
|
||||||
|
|
||||||
// Pin both tasks.
|
// Pin both tasks.
|
||||||
pin_mut!(listener, worker);
|
pin_mut!(listener, worker);
|
||||||
@@ -152,10 +164,12 @@ async fn listener(
|
|||||||
let mut message_rx = subscriber.message_rx();
|
let mut message_rx = subscriber.message_rx();
|
||||||
loop {
|
loop {
|
||||||
// Check for state changes for subscriptions.
|
// Check for state changes for subscriptions.
|
||||||
match state.apply_state() {
|
match state.apply_state().await {
|
||||||
SubscriptionStateChange::Reset => {
|
SubscriptionStateChange::Reset => {
|
||||||
subscriber.unsubscribe_all().await.unwrap();
|
subscriber.unsubscribe_all().await.unwrap();
|
||||||
for id in state.iter_subscriptions() {
|
|
||||||
|
let subscribed = state.subscribed.read().await;
|
||||||
|
for id in subscribed.iter() {
|
||||||
subscriber.subscribe(id).await.unwrap();
|
subscriber.subscribe(id).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,6 +201,7 @@ async fn listener(
|
|||||||
}) else {
|
}) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let event = match *REDIS_PAYLOAD_TYPE {
|
let event = match *REDIS_PAYLOAD_TYPE {
|
||||||
PayloadType::Json => message
|
PayloadType::Json => message
|
||||||
.value
|
.value
|
||||||
@@ -201,13 +216,34 @@ async fn listener(
|
|||||||
.as_bytes()
|
.as_bytes()
|
||||||
.and_then(|b| bincode::deserialize::<EventV1>(b).ok()),
|
.and_then(|b| bincode::deserialize::<EventV1>(b).ok()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(mut event) = event else {
|
let Some(mut event) = event else {
|
||||||
warn!("Failed to deserialise an event for {}!", message.channel);
|
warn!("Failed to deserialise an event for {}!", message.channel);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let should_send = state.handle_incoming_event_v1(db, &mut event).await;
|
|
||||||
if !should_send {
|
if let EventV1::Auth(auth) = &event {
|
||||||
continue;
|
if let AuthifierEvent::DeleteSession { session_id, .. } = auth {
|
||||||
|
if &state.session_id == session_id {
|
||||||
|
event = EventV1::Logout;
|
||||||
|
}
|
||||||
|
} else if let AuthifierEvent::DeleteAllSessions {
|
||||||
|
exclude_session_id, ..
|
||||||
|
} = auth
|
||||||
|
{
|
||||||
|
if let Some(excluded) = exclude_session_id {
|
||||||
|
if &state.session_id != excluded {
|
||||||
|
event = EventV1::Logout;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
event = EventV1::Logout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let should_send = state.handle_incoming_event_v1(db, &mut event).await;
|
||||||
|
if !should_send {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = write.lock().await.send(config.encode(&event)).await;
|
let result = write.lock().await.send(config.encode(&event)).await;
|
||||||
@@ -216,6 +252,11 @@ async fn listener(
|
|||||||
if !matches!(e, Error::AlreadyClosed | Error::ConnectionClosed) {
|
if !matches!(e, Error::AlreadyClosed | Error::ConnectionClosed) {
|
||||||
warn!("Error while sending an event to {addr:?}: {e:?}");
|
warn!("Error while sending an event to {addr:?}: {e:?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let EventV1::Logout = event {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -223,6 +264,7 @@ async fn listener(
|
|||||||
|
|
||||||
async fn worker(
|
async fn worker(
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
|
subscribed: Arc<RwLock<HashSet<String>>>,
|
||||||
user_id: String,
|
user_id: String,
|
||||||
config: &ProtocolConfiguration,
|
config: &ProtocolConfiguration,
|
||||||
mut read: WsReader,
|
mut read: WsReader,
|
||||||
@@ -245,8 +287,13 @@ async fn worker(
|
|||||||
let Ok(payload) = config.decode(&msg) else {
|
let Ok(payload) = config.decode(&msg) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
match payload {
|
match payload {
|
||||||
ClientMessage::BeginTyping { channel } => {
|
ClientMessage::BeginTyping { channel } => {
|
||||||
|
if !subscribed.read().await.contains(&channel) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
EventV1::ChannelStartTyping {
|
EventV1::ChannelStartTyping {
|
||||||
id: channel.clone(),
|
id: channel.clone(),
|
||||||
user: user_id.clone(),
|
user: user_id.clone(),
|
||||||
@@ -255,6 +302,10 @@ async fn worker(
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
ClientMessage::EndTyping { channel } => {
|
ClientMessage::EndTyping { channel } => {
|
||||||
|
if !subscribed.read().await.contains(&channel) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
EventV1::ChannelStopTyping {
|
EventV1::ChannelStopTyping {
|
||||||
id: channel.clone(),
|
id: channel.clone(),
|
||||||
user: user_id.clone(),
|
user: user_id.clone(),
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-config"
|
name = "revolt-config"
|
||||||
version = "0.7.2"
|
version = "0.7.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "MIT"
|
||||||
authors = ["Paul Makles <me@insrt.uk>"]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
description = "Revolt Backend: Configuration"
|
description = "Revolt Backend: Configuration"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Pawel Makles
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-database"
|
name = "revolt-database"
|
||||||
version = "0.7.2"
|
version = "0.7.4"
|
||||||
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>"]
|
||||||
@@ -23,13 +23,13 @@ default = ["mongodb", "async-std-runtime", "tasks"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Core
|
# Core
|
||||||
revolt-config = { version = "0.7.2", path = "../config" }
|
revolt-config = { version = "0.7.4", path = "../config" }
|
||||||
revolt-result = { version = "0.7.2", path = "../result" }
|
revolt-result = { version = "0.7.4", path = "../result" }
|
||||||
revolt-models = { version = "0.7.2", path = "../models", features = [
|
revolt-models = { version = "0.7.4", path = "../models", features = [
|
||||||
"validator",
|
"validator",
|
||||||
] }
|
] }
|
||||||
revolt-presence = { version = "0.7.2", path = "../presence" }
|
revolt-presence = { version = "0.7.4", path = "../presence" }
|
||||||
revolt-permissions = { version = "0.7.2", path = "../permissions", features = [
|
revolt-permissions = { version = "0.7.4", path = "../permissions", features = [
|
||||||
"serde",
|
"serde",
|
||||||
"bson",
|
"bson",
|
||||||
] }
|
] }
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ pub enum EventV1 {
|
|||||||
|
|
||||||
/// Successfully authenticated
|
/// Successfully authenticated
|
||||||
Authenticated,
|
Authenticated,
|
||||||
|
/// Logged out
|
||||||
|
Logout,
|
||||||
/// Basic data to cache
|
/// Basic data to cache
|
||||||
Ready {
|
Ready {
|
||||||
users: Vec<User>,
|
users: Vec<User>,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use revolt_config::config;
|
|||||||
use revolt_result::Result;
|
use revolt_result::Result;
|
||||||
use ulid::Ulid;
|
use ulid::Ulid;
|
||||||
|
|
||||||
use crate::{BotInformation, Database, PartialUser, User};
|
use crate::{events::client::EventV1, BotInformation, Database, PartialUser, User};
|
||||||
|
|
||||||
auto_derived_partial!(
|
auto_derived_partial!(
|
||||||
/// Bot
|
/// Bot
|
||||||
@@ -142,6 +142,10 @@ impl Bot {
|
|||||||
|
|
||||||
db.update_bot(&self.id, &partial, remove).await?;
|
db.update_bot(&self.id, &partial, remove).await?;
|
||||||
|
|
||||||
|
if partial.token.is_some() {
|
||||||
|
EventV1::Logout.private(self.id.clone()).await;
|
||||||
|
}
|
||||||
|
|
||||||
self.apply_options(partial);
|
self.apply_options(partial);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -276,23 +276,27 @@ impl User {
|
|||||||
Ok(username)
|
Ok(username)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find a user from a given token and hint
|
/// Find a user and session ID from a given token and hint
|
||||||
#[async_recursion]
|
#[async_recursion]
|
||||||
pub async fn from_token(db: &Database, token: &str, hint: UserHint) -> Result<User> {
|
pub async fn from_token(db: &Database, token: &str, hint: UserHint) -> Result<(User, String)> {
|
||||||
match hint {
|
match hint {
|
||||||
UserHint::Bot => {
|
UserHint::Bot => Ok((
|
||||||
db.fetch_user(
|
db.fetch_user(
|
||||||
&db.fetch_bot_by_token(token)
|
&db.fetch_bot_by_token(token)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| create_error!(InvalidSession))?
|
.map_err(|_| create_error!(InvalidSession))?
|
||||||
.id,
|
.id,
|
||||||
)
|
)
|
||||||
.await
|
.await?,
|
||||||
|
String::new(),
|
||||||
|
)),
|
||||||
|
UserHint::User => {
|
||||||
|
let session = db.fetch_session_by_token(token).await?;
|
||||||
|
Ok((db.fetch_user(&session.user_id).await?, session.id))
|
||||||
}
|
}
|
||||||
UserHint::User => db.fetch_user_by_token(token).await,
|
|
||||||
UserHint::Any => {
|
UserHint::Any => {
|
||||||
if let Ok(user) = User::from_token(db, token, UserHint::User).await {
|
if let Ok(result) = User::from_token(db, token, UserHint::User).await {
|
||||||
Ok(user)
|
Ok(result)
|
||||||
} else {
|
} else {
|
||||||
User::from_token(db, token, UserHint::Bot).await
|
User::from_token(db, token, UserHint::Bot).await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use authifier::models::Session;
|
||||||
use revolt_result::Result;
|
use revolt_result::Result;
|
||||||
|
|
||||||
use crate::{FieldsUser, PartialUser, RelationshipStatus, User};
|
use crate::{FieldsUser, PartialUser, RelationshipStatus, User};
|
||||||
@@ -16,8 +17,8 @@ pub trait AbstractUsers: Sync + Send {
|
|||||||
/// Fetch a user from the database by their username
|
/// Fetch a user from the database by their username
|
||||||
async fn fetch_user_by_username(&self, username: &str, discriminator: &str) -> Result<User>;
|
async fn fetch_user_by_username(&self, username: &str, discriminator: &str) -> Result<User>;
|
||||||
|
|
||||||
/// Fetch a user from the database by their session token
|
/// Fetch a session from the database by token
|
||||||
async fn fetch_user_by_token(&self, token: &str) -> Result<User>;
|
async fn fetch_session_by_token(&self, token: &str) -> Result<Session>;
|
||||||
|
|
||||||
/// Fetch multiple users by their ids
|
/// Fetch multiple users by their ids
|
||||||
async fn fetch_users<'a>(&self, ids: &'a [String]) -> Result<Vec<User>>;
|
async fn fetch_users<'a>(&self, ids: &'a [String]) -> Result<Vec<User>>;
|
||||||
|
|||||||
@@ -46,10 +46,9 @@ impl AbstractUsers for MongoDb {
|
|||||||
.ok_or_else(|| create_error!(NotFound))
|
.ok_or_else(|| create_error!(NotFound))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch a user from the database by their session token
|
/// Fetch a session from the database by token
|
||||||
async fn fetch_user_by_token(&self, token: &str) -> Result<User> {
|
async fn fetch_session_by_token(&self, token: &str) -> Result<Session> {
|
||||||
let session = self
|
self.col::<Session>("sessions")
|
||||||
.col::<Session>("sessions")
|
|
||||||
.find_one(
|
.find_one(
|
||||||
doc! {
|
doc! {
|
||||||
"token": token
|
"token": token
|
||||||
@@ -58,9 +57,7 @@ impl AbstractUsers for MongoDb {
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| create_database_error!("find_one", "sessions"))?
|
.map_err(|_| create_database_error!("find_one", "sessions"))?
|
||||||
.ok_or_else(|| create_error!(InvalidSession))?;
|
.ok_or_else(|| create_error!(InvalidSession))
|
||||||
|
|
||||||
self.fetch_user(&session.user_id).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch multiple users by their ids
|
/// Fetch multiple users by their ids
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use authifier::models::Session;
|
||||||
use revolt_result::Result;
|
use revolt_result::Result;
|
||||||
|
|
||||||
use crate::{FieldsUser, PartialUser, RelationshipStatus, User};
|
use crate::{FieldsUser, PartialUser, RelationshipStatus, User};
|
||||||
@@ -40,8 +41,8 @@ impl AbstractUsers for ReferenceDb {
|
|||||||
.ok_or_else(|| create_error!(NotFound))
|
.ok_or_else(|| create_error!(NotFound))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch a user from the database by their session token
|
/// Fetch a session from the database by token
|
||||||
async fn fetch_user_by_token(&self, _token: &str) -> Result<User> {
|
async fn fetch_session_by_token(&self, _token: &str) -> Result<Session> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -995,6 +995,15 @@ impl crate::User {
|
|||||||
vec![]
|
vec![]
|
||||||
},
|
},
|
||||||
badges: self.badges.unwrap_or_default() as u32,
|
badges: self.badges.unwrap_or_default() as u32,
|
||||||
|
online: can_see_profile
|
||||||
|
&& revolt_presence::is_online(&self.id).await
|
||||||
|
&& !matches!(
|
||||||
|
self.status,
|
||||||
|
Some(crate::UserStatus {
|
||||||
|
presence: Some(crate::Presence::Invisible),
|
||||||
|
..
|
||||||
|
})
|
||||||
|
),
|
||||||
status: if can_see_profile {
|
status: if can_see_profile {
|
||||||
self.status.map(|status| status.into())
|
self.status.map(|status| status.into())
|
||||||
} else {
|
} else {
|
||||||
@@ -1009,7 +1018,6 @@ impl crate::User {
|
|||||||
privileged: self.privileged,
|
privileged: self.privileged,
|
||||||
bot: self.bot.map(|bot| bot.into()),
|
bot: self.bot.map(|bot| bot.into()),
|
||||||
relationship,
|
relationship,
|
||||||
online: can_see_profile && revolt_presence::is_online(&self.id).await,
|
|
||||||
id: self.id,
|
id: self.id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1064,6 +1072,15 @@ impl crate::User {
|
|||||||
vec![]
|
vec![]
|
||||||
},
|
},
|
||||||
badges: self.badges.unwrap_or_default() as u32,
|
badges: self.badges.unwrap_or_default() as u32,
|
||||||
|
online: can_see_profile
|
||||||
|
&& is_online
|
||||||
|
&& !matches!(
|
||||||
|
self.status,
|
||||||
|
Some(crate::UserStatus {
|
||||||
|
presence: Some(crate::Presence::Invisible),
|
||||||
|
..
|
||||||
|
})
|
||||||
|
),
|
||||||
status: if can_see_profile {
|
status: if can_see_profile {
|
||||||
self.status.map(|status| status.into())
|
self.status.map(|status| status.into())
|
||||||
} else {
|
} else {
|
||||||
@@ -1078,7 +1095,6 @@ impl crate::User {
|
|||||||
privileged: self.privileged,
|
privileged: self.privileged,
|
||||||
bot: self.bot.map(|bot| bot.into()),
|
bot: self.bot.map(|bot| bot.into()),
|
||||||
relationship,
|
relationship,
|
||||||
online: can_see_profile && is_online,
|
|
||||||
id: self.id,
|
id: self.id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1099,13 +1115,20 @@ impl crate::User {
|
|||||||
})
|
})
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
badges: self.badges.unwrap_or_default() as u32,
|
badges: self.badges.unwrap_or_default() as u32,
|
||||||
|
online: revolt_presence::is_online(&self.id).await
|
||||||
|
&& !matches!(
|
||||||
|
self.status,
|
||||||
|
Some(crate::UserStatus {
|
||||||
|
presence: Some(crate::Presence::Invisible),
|
||||||
|
..
|
||||||
|
})
|
||||||
|
),
|
||||||
status: self.status.map(|status| status.into()),
|
status: self.status.map(|status| status.into()),
|
||||||
profile: self.profile.map(|profile| profile.into()),
|
profile: self.profile.map(|profile| profile.into()),
|
||||||
flags: self.flags.unwrap_or_default() as u32,
|
flags: self.flags.unwrap_or_default() as u32,
|
||||||
privileged: self.privileged,
|
privileged: self.privileged,
|
||||||
bot: self.bot.map(|bot| bot.into()),
|
bot: self.bot.map(|bot| bot.into()),
|
||||||
relationship: RelationshipStatus::User,
|
relationship: RelationshipStatus::User,
|
||||||
online: revolt_presence::is_online(&self.id).await,
|
|
||||||
id: self.id,
|
id: self.id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
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;
|
||||||
@@ -44,9 +46,27 @@ impl Reference {
|
|||||||
db.fetch_channel(&self.id).await
|
db.fetch_channel(&self.id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch invite from Ref
|
/// Fetch invite from Ref or create invite to server if discoverable
|
||||||
pub async fn as_invite(&self, db: &Database) -> Result<Invite> {
|
pub async fn as_invite(&self, db: &Database) -> Result<Invite> {
|
||||||
db.fetch_invite(&self.id).await
|
if ulid::Ulid::from_str(&self.id).is_ok() {
|
||||||
|
let server = self.as_server(db).await?;
|
||||||
|
if !server.discoverable {
|
||||||
|
return Err(create_error!(NotFound));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Invite::Server {
|
||||||
|
code: self.id.to_string(),
|
||||||
|
server: server.id,
|
||||||
|
creator: server.owner,
|
||||||
|
channel: server
|
||||||
|
.channels
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
.ok_or(create_error!(NotFound))?,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
db.fetch_invite(&self.id).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch message from Ref
|
/// Fetch message from Ref
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-models"
|
name = "revolt-models"
|
||||||
version = "0.7.2"
|
version = "0.7.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "MIT"
|
||||||
authors = ["Paul Makles <me@insrt.uk>"]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
description = "Revolt Backend: API Models"
|
description = "Revolt Backend: API Models"
|
||||||
|
|
||||||
@@ -19,8 +19,8 @@ default = ["serde", "partials", "rocket"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Core
|
# Core
|
||||||
revolt-config = { version = "0.7.2", path = "../config" }
|
revolt-config = { version = "0.7.4", path = "../config" }
|
||||||
revolt-permissions = { version = "0.7.2", path = "../permissions" }
|
revolt-permissions = { version = "0.7.4", path = "../permissions" }
|
||||||
|
|
||||||
# Utility
|
# Utility
|
||||||
regex = "1"
|
regex = "1"
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Pawel Makles
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-permissions"
|
name = "revolt-permissions"
|
||||||
version = "0.7.2"
|
version = "0.7.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "MIT"
|
||||||
authors = ["Paul Makles <me@insrt.uk>"]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
description = "Revolt Backend: Permission Logic"
|
description = "Revolt Backend: Permission Logic"
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ async-std = { version = "1.8.0", features = ["attributes"] }
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Core
|
# Core
|
||||||
revolt-result = { version = "0.7.2", path = "../result" }
|
revolt-result = { version = "0.7.4", path = "../result" }
|
||||||
|
|
||||||
# Utility
|
# Utility
|
||||||
auto_ops = "0.3.0"
|
auto_ops = "0.3.0"
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Pawel Makles
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-presence"
|
name = "revolt-presence"
|
||||||
version = "0.7.2"
|
version = "0.7.4"
|
||||||
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>"]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-result"
|
name = "revolt-result"
|
||||||
version = "0.7.2"
|
version = "0.7.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "MIT"
|
||||||
authors = ["Paul Makles <me@insrt.uk>"]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
description = "Revolt Backend: Result and Error types"
|
description = "Revolt Backend: Result and Error types"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Pawel Makles
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-delta"
|
name = "revolt-delta"
|
||||||
version = "0.7.2"
|
version = "0.7.4"
|
||||||
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"
|
||||||
|
|||||||
Reference in new issue
Block a user