forked from jmug/stoatchat
feat: add role colours to masquerades
feat: add `joined_at` property to members feat: add `timeout` property to members (non-functional)
This commit is contained in:
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -2505,7 +2505,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "rauth"
|
name = "rauth"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
source = "git+https://github.com/insertish/rauth?rev=c2acaf6b31213e3969f8b48f9deed3daa125d394#c2acaf6b31213e3969f8b48f9deed3daa125d394"
|
source = "git+https://github.com/insertish/rauth?rev=9c3f9bb58a549a51546bd748274130ac4a667cb4#9c3f9bb58a549a51546bd748274130ac4a667cb4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -2986,7 +2986,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "rocket_rauth"
|
name = "rocket_rauth"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
source = "git+https://github.com/insertish/rauth?rev=c2acaf6b31213e3969f8b48f9deed3daa125d394#c2acaf6b31213e3969f8b48f9deed3daa125d394"
|
source = "git+https://github.com/insertish/rauth?rev=9c3f9bb58a549a51546bd748274130ac4a667cb4#9c3f9bb58a549a51546bd748274130ac4a667cb4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"okapi",
|
"okapi",
|
||||||
"rauth",
|
"rauth",
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ mobc-redis = { version = "0.7.0", default-features = false, features = ["async-s
|
|||||||
# web
|
# web
|
||||||
rocket = { version = "0.5.0-rc.2", default-features = false, features = ["json"] }
|
rocket = { version = "0.5.0-rc.2", default-features = false, features = ["json"] }
|
||||||
rocket_empty = { git = "https://github.com/insertish/rocket_empty", branch = "master" }
|
rocket_empty = { git = "https://github.com/insertish/rocket_empty", branch = "master" }
|
||||||
rocket_rauth = { git = "https://github.com/insertish/rauth", rev = "67a7fa8100c7db8411fb8fb149441418b55c19ef" }
|
rocket_rauth = { git = "https://github.com/insertish/rauth", rev = "9c3f9bb58a549a51546bd748274130ac4a667cb4" }
|
||||||
|
|
||||||
# spec generation
|
# spec generation
|
||||||
schemars = "0.8.8"
|
schemars = "0.8.8"
|
||||||
|
|||||||
@@ -100,10 +100,16 @@ pub async fn message_send(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. Verify permissions for masquerade.
|
// 2. Verify permissions for masquerade.
|
||||||
if message.masquerade.is_some() {
|
if let Some(masq) = &message.masquerade {
|
||||||
permissions
|
permissions
|
||||||
.throw_permission(db, Permission::Masquerade)
|
.throw_permission(db, Permission::Masquerade)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
if masq.colour.is_some() {
|
||||||
|
permissions
|
||||||
|
.throw_permission(db, Permission::ManageRole)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Verify replies are valid.
|
// 3. Verify replies are valid.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use revolt_quark::{
|
|||||||
server_member::{FieldsMember, PartialMember},
|
server_member::{FieldsMember, PartialMember},
|
||||||
File, Member, User,
|
File, Member, User,
|
||||||
},
|
},
|
||||||
perms, Db, Error, Permission, Ref, Result,
|
perms, Db, Error, Permission, Ref, Result, Timestamp,
|
||||||
};
|
};
|
||||||
|
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
@@ -22,6 +22,8 @@ pub struct DataMemberEdit {
|
|||||||
avatar: Option<String>,
|
avatar: Option<String>,
|
||||||
/// Array of role ids
|
/// Array of role ids
|
||||||
roles: Option<Vec<String>>,
|
roles: Option<Vec<String>>,
|
||||||
|
/// Timestamp this member is timed out until
|
||||||
|
timeout: Option<Timestamp>,
|
||||||
/// Fields to remove from channel object
|
/// Fields to remove from channel object
|
||||||
#[validate(length(min = 1))]
|
#[validate(length(min = 1))]
|
||||||
remove: Option<Vec<FieldsMember>>,
|
remove: Option<Vec<FieldsMember>>,
|
||||||
@@ -105,13 +107,7 @@ pub async fn req(
|
|||||||
|
|
||||||
// Check permissions against roles in diff
|
// Check permissions against roles in diff
|
||||||
if let Some(roles) = &data.roles {
|
if let Some(roles) = &data.roles {
|
||||||
let fallback = vec![];
|
let current_roles = member.roles.iter().collect::<HashSet<&String>>();
|
||||||
let current_roles = member
|
|
||||||
.roles
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&fallback)
|
|
||||||
.iter()
|
|
||||||
.collect::<HashSet<&String>>();
|
|
||||||
|
|
||||||
let new_roles = roles.iter().collect::<HashSet<&String>>();
|
let new_roles = roles.iter().collect::<HashSet<&String>>();
|
||||||
let added_roles: Vec<&&String> = new_roles.difference(¤t_roles).collect();
|
let added_roles: Vec<&&String> = new_roles.difference(¤t_roles).collect();
|
||||||
@@ -132,12 +128,14 @@ pub async fn req(
|
|||||||
nickname,
|
nickname,
|
||||||
avatar,
|
avatar,
|
||||||
roles,
|
roles,
|
||||||
|
timeout,
|
||||||
remove,
|
remove,
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
let mut partial = PartialMember {
|
let mut partial = PartialMember {
|
||||||
nickname,
|
nickname,
|
||||||
roles,
|
roles,
|
||||||
|
timeout,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ rocket_empty = { optional = true, git = "https://github.com/insertish/rocket_emp
|
|||||||
rocket_cors = { optional = true, git = "https://github.com/lawliet89/rocket_cors", rev = "5843861a88958c16bfaa0b40f0d8910772bcd2f6" }
|
rocket_cors = { optional = true, git = "https://github.com/lawliet89/rocket_cors", rev = "5843861a88958c16bfaa0b40f0d8910772bcd2f6" }
|
||||||
|
|
||||||
# rAuth
|
# rAuth
|
||||||
rauth = { git = "https://github.com/insertish/rauth", rev = "67a7fa8100c7db8411fb8fb149441418b55c19ef", features = [ "async-std-runtime" ] }
|
rauth = { git = "https://github.com/insertish/rauth", rev = "9c3f9bb58a549a51546bd748274130ac4a667cb4", features = [ "async-std-runtime" ] }
|
||||||
|
|
||||||
# Sentry
|
# Sentry
|
||||||
sentry = "0.25.0"
|
sentry = "0.25.0"
|
||||||
|
|||||||
@@ -484,10 +484,8 @@ impl State {
|
|||||||
|
|
||||||
if data.rank.is_some() || data.permissions.is_some() {
|
if data.rank.is_some() || data.permissions.is_some() {
|
||||||
if let Some(member) = self.cache.members.get(id) {
|
if let Some(member) = self.cache.members.get(id) {
|
||||||
if let Some(roles) = &member.roles {
|
if member.roles.contains(role_id) {
|
||||||
if roles.contains(role_id) {
|
queue_server = Some(id.clone());
|
||||||
queue_server = Some(id.clone());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -498,10 +496,8 @@ impl State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(member) = self.cache.members.get(id) {
|
if let Some(member) = self.cache.members.get(id) {
|
||||||
if let Some(roles) = &member.roles {
|
if member.roles.contains(role_id) {
|
||||||
if roles.contains(role_id) {
|
queue_server = Some(id.clone());
|
||||||
queue_server = Some(id.clone());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ use crate::{AbstractServerMember, Result};
|
|||||||
|
|
||||||
use super::super::DummyDb;
|
use super::super::DummyDb;
|
||||||
|
|
||||||
|
use iso8601_timestamp::Timestamp;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl AbstractServerMember for DummyDb {
|
impl AbstractServerMember for DummyDb {
|
||||||
async fn fetch_member(&self, server: &str, user: &str) -> Result<Member> {
|
async fn fetch_member(&self, server: &str, user: &str) -> Result<Member> {
|
||||||
@@ -11,9 +13,11 @@ impl AbstractServerMember for DummyDb {
|
|||||||
server: server.into(),
|
server: server.into(),
|
||||||
user: user.into(),
|
user: user.into(),
|
||||||
},
|
},
|
||||||
|
joined_at: Timestamp::now_utc(),
|
||||||
nickname: None,
|
nickname: None,
|
||||||
avatar: None,
|
avatar: None,
|
||||||
roles: None,
|
roles: vec![],
|
||||||
|
timeout: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use iso8601_timestamp::Timestamp;
|
||||||
use ulid::Ulid;
|
use ulid::Ulid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -188,7 +189,11 @@ impl Server {
|
|||||||
server: self.id.clone(),
|
server: self.id.clone(),
|
||||||
user: user.id.clone(),
|
user: user.id.clone(),
|
||||||
},
|
},
|
||||||
..Default::default()
|
joined_at: Timestamp::now_utc(),
|
||||||
|
nickname: None,
|
||||||
|
avatar: None,
|
||||||
|
roles: vec![],
|
||||||
|
timeout: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
db.insert_member(&member).await?;
|
db.insert_member(&member).await?;
|
||||||
|
|||||||
@@ -36,27 +36,24 @@ impl Member {
|
|||||||
|
|
||||||
/// Get this user's current ranking
|
/// Get this user's current ranking
|
||||||
pub fn get_ranking(&self, server: &Server) -> i64 {
|
pub fn get_ranking(&self, server: &Server) -> i64 {
|
||||||
if let Some(roles) = &self.roles {
|
let mut value = i64::MAX;
|
||||||
let mut value = i64::MAX;
|
for role in &self.roles {
|
||||||
for role in roles {
|
if let Some(role) = server.roles.get(role) {
|
||||||
if let Some(role) = server.roles.get(role) {
|
if role.rank < value {
|
||||||
if role.rank < value {
|
value = role.rank;
|
||||||
value = role.rank;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
i64::MAX
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
value
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove(&mut self, field: &FieldsMember) {
|
pub fn remove(&mut self, field: &FieldsMember) {
|
||||||
match field {
|
match field {
|
||||||
FieldsMember::Avatar => self.avatar = None,
|
FieldsMember::Avatar => self.avatar = None,
|
||||||
FieldsMember::Nickname => self.nickname = None,
|
FieldsMember::Nickname => self.nickname = None,
|
||||||
FieldsMember::Roles => self.roles = None,
|
FieldsMember::Roles => self.roles.clear(),
|
||||||
|
FieldsMember::Timeout => self.timeout = None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use bson::Bson;
|
use bson::{to_bson, Bson};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
|
use iso8601_timestamp::Timestamp;
|
||||||
use mongodb::{
|
use mongodb::{
|
||||||
bson::{doc, from_bson, from_document, to_document, Document},
|
bson::{doc, from_bson, from_document, to_document, Document},
|
||||||
options::FindOptions,
|
options::FindOptions,
|
||||||
@@ -16,7 +17,7 @@ struct MigrationInfo {
|
|||||||
revision: i32,
|
revision: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const LATEST_REVISION: i32 = 17;
|
pub const LATEST_REVISION: i32 = 18;
|
||||||
|
|
||||||
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");
|
||||||
@@ -644,6 +645,23 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
|
|||||||
.expect("Failed to create emoji parent index.");
|
.expect("Failed to create emoji parent index.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if revision <= 17 {
|
||||||
|
info!("Running migration [revision 17 / 15-07-2022]: Initialise `joined_at` property on server members.");
|
||||||
|
|
||||||
|
db.col::<Document>("server_members")
|
||||||
|
.update_many(
|
||||||
|
doc! {},
|
||||||
|
doc! {
|
||||||
|
"$set": {
|
||||||
|
"joined_at": to_bson(&Timestamp::now_utc()).expect("to_bson")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.expect("Failed to update server members.");
|
||||||
|
}
|
||||||
|
|
||||||
// Need to migrate fields on attachments, change `user_id`, `object_id`, etc to `parent`.
|
// Need to migrate fields on attachments, change `user_id`, `object_id`, etc to `parent`.
|
||||||
|
|
||||||
// Reminder to update LATEST_REVISION when adding new migrations.
|
// Reminder to update LATEST_REVISION when adding new migrations.
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ impl IntoDocumentPath for FieldsMember {
|
|||||||
FieldsMember::Avatar => "avatar",
|
FieldsMember::Avatar => "avatar",
|
||||||
FieldsMember::Nickname => "nickname",
|
FieldsMember::Nickname => "nickname",
|
||||||
FieldsMember::Roles => "roles",
|
FieldsMember::Roles => "roles",
|
||||||
|
FieldsMember::Timeout => "timeout",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,6 +74,14 @@ pub struct Masquerade {
|
|||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
#[validate(length(min = 1, max = 128))]
|
#[validate(length(min = 1, max = 128))]
|
||||||
pub avatar: Option<String>,
|
pub avatar: Option<String>,
|
||||||
|
/// Replace the display role colour shown on this message
|
||||||
|
///
|
||||||
|
/// Must have `ManageRole` permission to use
|
||||||
|
///
|
||||||
|
/// This can be any valid CSS colour
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[validate(length(min = 1, max = 32))]
|
||||||
|
pub colour: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Representation of a Message on Revolt
|
/// Representation of a Message on Revolt
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use iso8601_timestamp::Timestamp;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::models::attachment::File;
|
use crate::models::attachment::File;
|
||||||
@@ -12,7 +13,7 @@ pub struct MemberCompositeKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Representation of a member of a server on Revolt
|
/// Representation of a member of a server on Revolt
|
||||||
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, OptionalStruct, Default)]
|
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, OptionalStruct)]
|
||||||
#[optional_derive(Serialize, Deserialize, JsonSchema, Debug, Default, Clone)]
|
#[optional_derive(Serialize, Deserialize, JsonSchema, Debug, Default, Clone)]
|
||||||
#[optional_name = "PartialMember"]
|
#[optional_name = "PartialMember"]
|
||||||
#[opt_skip_serializing_none]
|
#[opt_skip_serializing_none]
|
||||||
@@ -22,6 +23,9 @@ pub struct Member {
|
|||||||
#[serde(rename = "_id")]
|
#[serde(rename = "_id")]
|
||||||
pub id: MemberCompositeKey,
|
pub id: MemberCompositeKey,
|
||||||
|
|
||||||
|
/// Time at which this user joined the server
|
||||||
|
pub joined_at: Timestamp,
|
||||||
|
|
||||||
/// Member's nickname
|
/// Member's nickname
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub nickname: Option<String>,
|
pub nickname: Option<String>,
|
||||||
@@ -30,8 +34,11 @@ pub struct Member {
|
|||||||
pub avatar: Option<File>,
|
pub avatar: Option<File>,
|
||||||
|
|
||||||
/// Member's roles
|
/// Member's roles
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub roles: Vec<String>,
|
||||||
|
/// Timestamp this member is timed out until
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub roles: Option<Vec<String>>,
|
pub timeout: Option<Timestamp>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Optional fields on server member object
|
/// Optional fields on server member object
|
||||||
@@ -40,6 +47,7 @@ pub enum FieldsMember {
|
|||||||
Nickname,
|
Nickname,
|
||||||
Avatar,
|
Avatar,
|
||||||
Roles,
|
Roles,
|
||||||
|
Timeout,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Member removal intention
|
/// Member removal intention
|
||||||
|
|||||||
@@ -55,11 +55,7 @@ async fn calculate_server_permission(
|
|||||||
let mut permissions: PermissionValue = server.default_permissions.into();
|
let mut permissions: PermissionValue = server.default_permissions.into();
|
||||||
|
|
||||||
// 4. Resolve each role in order.
|
// 4. Resolve each role in order.
|
||||||
let member_roles: HashSet<&String> = if let Some(roles) = member.roles.as_ref() {
|
let member_roles: HashSet<&String> = member.roles.iter().collect();
|
||||||
roles.iter().collect()
|
|
||||||
} else {
|
|
||||||
HashSet::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
if !member_roles.is_empty() {
|
if !member_roles.is_empty() {
|
||||||
let mut roles = server
|
let mut roles = server
|
||||||
@@ -188,11 +184,7 @@ async fn calculate_channel_permission(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4. Resolve each role in order.
|
// 4. Resolve each role in order.
|
||||||
let member_roles: HashSet<&String> = if let Some(roles) = member.roles.as_ref() {
|
let member_roles: HashSet<&String> = member.roles.iter().collect();
|
||||||
roles.iter().collect()
|
|
||||||
} else {
|
|
||||||
HashSet::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
if !member_roles.is_empty() {
|
if !member_roles.is_empty() {
|
||||||
let mut roles = role_permissions
|
let mut roles = role_permissions
|
||||||
|
|||||||
Reference in New Issue
Block a user