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]]
|
||||
name = "rauth"
|
||||
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 = [
|
||||
"async-std",
|
||||
"async-trait",
|
||||
@@ -2986,7 +2986,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rocket_rauth"
|
||||
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 = [
|
||||
"okapi",
|
||||
"rauth",
|
||||
|
||||
@@ -52,7 +52,7 @@ mobc-redis = { version = "0.7.0", default-features = false, features = ["async-s
|
||||
# web
|
||||
rocket = { version = "0.5.0-rc.2", default-features = false, features = ["json"] }
|
||||
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
|
||||
schemars = "0.8.8"
|
||||
|
||||
@@ -100,10 +100,16 @@ pub async fn message_send(
|
||||
}
|
||||
|
||||
// 2. Verify permissions for masquerade.
|
||||
if message.masquerade.is_some() {
|
||||
if let Some(masq) = &message.masquerade {
|
||||
permissions
|
||||
.throw_permission(db, Permission::Masquerade)
|
||||
.await?;
|
||||
|
||||
if masq.colour.is_some() {
|
||||
permissions
|
||||
.throw_permission(db, Permission::ManageRole)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Verify replies are valid.
|
||||
|
||||
@@ -5,7 +5,7 @@ use revolt_quark::{
|
||||
server_member::{FieldsMember, PartialMember},
|
||||
File, Member, User,
|
||||
},
|
||||
perms, Db, Error, Permission, Ref, Result,
|
||||
perms, Db, Error, Permission, Ref, Result, Timestamp,
|
||||
};
|
||||
|
||||
use rocket::serde::json::Json;
|
||||
@@ -22,6 +22,8 @@ pub struct DataMemberEdit {
|
||||
avatar: Option<String>,
|
||||
/// Array of role ids
|
||||
roles: Option<Vec<String>>,
|
||||
/// Timestamp this member is timed out until
|
||||
timeout: Option<Timestamp>,
|
||||
/// Fields to remove from channel object
|
||||
#[validate(length(min = 1))]
|
||||
remove: Option<Vec<FieldsMember>>,
|
||||
@@ -105,13 +107,7 @@ pub async fn req(
|
||||
|
||||
// Check permissions against roles in diff
|
||||
if let Some(roles) = &data.roles {
|
||||
let fallback = vec![];
|
||||
let current_roles = member
|
||||
.roles
|
||||
.as_ref()
|
||||
.unwrap_or(&fallback)
|
||||
.iter()
|
||||
.collect::<HashSet<&String>>();
|
||||
let current_roles = member.roles.iter().collect::<HashSet<&String>>();
|
||||
|
||||
let new_roles = roles.iter().collect::<HashSet<&String>>();
|
||||
let added_roles: Vec<&&String> = new_roles.difference(¤t_roles).collect();
|
||||
@@ -132,12 +128,14 @@ pub async fn req(
|
||||
nickname,
|
||||
avatar,
|
||||
roles,
|
||||
timeout,
|
||||
remove,
|
||||
} = data;
|
||||
|
||||
let mut partial = PartialMember {
|
||||
nickname,
|
||||
roles,
|
||||
timeout,
|
||||
..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" }
|
||||
|
||||
# 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 = "0.25.0"
|
||||
|
||||
@@ -484,10 +484,8 @@ impl State {
|
||||
|
||||
if data.rank.is_some() || data.permissions.is_some() {
|
||||
if let Some(member) = self.cache.members.get(id) {
|
||||
if let Some(roles) = &member.roles {
|
||||
if roles.contains(role_id) {
|
||||
queue_server = Some(id.clone());
|
||||
}
|
||||
if member.roles.contains(role_id) {
|
||||
queue_server = Some(id.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -498,10 +496,8 @@ impl State {
|
||||
}
|
||||
|
||||
if let Some(member) = self.cache.members.get(id) {
|
||||
if let Some(roles) = &member.roles {
|
||||
if roles.contains(role_id) {
|
||||
queue_server = Some(id.clone());
|
||||
}
|
||||
if member.roles.contains(role_id) {
|
||||
queue_server = Some(id.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ use crate::{AbstractServerMember, Result};
|
||||
|
||||
use super::super::DummyDb;
|
||||
|
||||
use iso8601_timestamp::Timestamp;
|
||||
|
||||
#[async_trait]
|
||||
impl AbstractServerMember for DummyDb {
|
||||
async fn fetch_member(&self, server: &str, user: &str) -> Result<Member> {
|
||||
@@ -11,9 +13,11 @@ impl AbstractServerMember for DummyDb {
|
||||
server: server.into(),
|
||||
user: user.into(),
|
||||
},
|
||||
joined_at: Timestamp::now_utc(),
|
||||
nickname: None,
|
||||
avatar: None,
|
||||
roles: None,
|
||||
roles: vec![],
|
||||
timeout: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use iso8601_timestamp::Timestamp;
|
||||
use ulid::Ulid;
|
||||
|
||||
use crate::{
|
||||
@@ -188,7 +189,11 @@ impl Server {
|
||||
server: self.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?;
|
||||
|
||||
@@ -36,27 +36,24 @@ impl Member {
|
||||
|
||||
/// Get this user's current ranking
|
||||
pub fn get_ranking(&self, server: &Server) -> i64 {
|
||||
if let Some(roles) = &self.roles {
|
||||
let mut value = i64::MAX;
|
||||
for role in roles {
|
||||
if let Some(role) = server.roles.get(role) {
|
||||
if role.rank < value {
|
||||
value = role.rank;
|
||||
}
|
||||
let mut value = i64::MAX;
|
||||
for role in &self.roles {
|
||||
if let Some(role) = server.roles.get(role) {
|
||||
if role.rank < value {
|
||||
value = role.rank;
|
||||
}
|
||||
}
|
||||
|
||||
value
|
||||
} else {
|
||||
i64::MAX
|
||||
}
|
||||
|
||||
value
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, field: &FieldsMember) {
|
||||
match field {
|
||||
FieldsMember::Avatar => self.avatar = 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 bson::Bson;
|
||||
use bson::{to_bson, Bson};
|
||||
use futures::StreamExt;
|
||||
use iso8601_timestamp::Timestamp;
|
||||
use mongodb::{
|
||||
bson::{doc, from_bson, from_document, to_document, Document},
|
||||
options::FindOptions,
|
||||
@@ -16,7 +17,7 @@ struct MigrationInfo {
|
||||
revision: i32,
|
||||
}
|
||||
|
||||
pub const LATEST_REVISION: i32 = 17;
|
||||
pub const LATEST_REVISION: i32 = 18;
|
||||
|
||||
pub async fn migrate_database(db: &MongoDb) {
|
||||
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.");
|
||||
}
|
||||
|
||||
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`.
|
||||
|
||||
// Reminder to update LATEST_REVISION when adding new migrations.
|
||||
|
||||
@@ -129,6 +129,7 @@ impl IntoDocumentPath for FieldsMember {
|
||||
FieldsMember::Avatar => "avatar",
|
||||
FieldsMember::Nickname => "nickname",
|
||||
FieldsMember::Roles => "roles",
|
||||
FieldsMember::Timeout => "timeout",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +74,14 @@ pub struct Masquerade {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[validate(length(min = 1, max = 128))]
|
||||
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
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use iso8601_timestamp::Timestamp;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::models::attachment::File;
|
||||
@@ -12,7 +13,7 @@ pub struct MemberCompositeKey {
|
||||
}
|
||||
|
||||
/// 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_name = "PartialMember"]
|
||||
#[opt_skip_serializing_none]
|
||||
@@ -22,6 +23,9 @@ pub struct Member {
|
||||
#[serde(rename = "_id")]
|
||||
pub id: MemberCompositeKey,
|
||||
|
||||
/// Time at which this user joined the server
|
||||
pub joined_at: Timestamp,
|
||||
|
||||
/// Member's nickname
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub nickname: Option<String>,
|
||||
@@ -30,8 +34,11 @@ pub struct Member {
|
||||
pub avatar: Option<File>,
|
||||
|
||||
/// 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")]
|
||||
pub roles: Option<Vec<String>>,
|
||||
pub timeout: Option<Timestamp>,
|
||||
}
|
||||
|
||||
/// Optional fields on server member object
|
||||
@@ -40,6 +47,7 @@ pub enum FieldsMember {
|
||||
Nickname,
|
||||
Avatar,
|
||||
Roles,
|
||||
Timeout,
|
||||
}
|
||||
|
||||
/// Member removal intention
|
||||
|
||||
@@ -55,11 +55,7 @@ async fn calculate_server_permission(
|
||||
let mut permissions: PermissionValue = server.default_permissions.into();
|
||||
|
||||
// 4. Resolve each role in order.
|
||||
let member_roles: HashSet<&String> = if let Some(roles) = member.roles.as_ref() {
|
||||
roles.iter().collect()
|
||||
} else {
|
||||
HashSet::new()
|
||||
};
|
||||
let member_roles: HashSet<&String> = member.roles.iter().collect();
|
||||
|
||||
if !member_roles.is_empty() {
|
||||
let mut roles = server
|
||||
@@ -188,11 +184,7 @@ async fn calculate_channel_permission(
|
||||
}
|
||||
|
||||
// 4. Resolve each role in order.
|
||||
let member_roles: HashSet<&String> = if let Some(roles) = member.roles.as_ref() {
|
||||
roles.iter().collect()
|
||||
} else {
|
||||
HashSet::new()
|
||||
};
|
||||
let member_roles: HashSet<&String> = member.roles.iter().collect();
|
||||
|
||||
if !member_roles.is_empty() {
|
||||
let mut roles = role_permissions
|
||||
|
||||
Reference in New Issue
Block a user