forked from jmug/stoatchat
feat: member fetching with roles (#299)
This commit is contained in:
@@ -1,17 +1,25 @@
|
|||||||
use revolt_quark::{
|
use revolt_quark::models::server_member::MemberResponse;
|
||||||
models::{Member, User},
|
use revolt_quark::{models::User, perms, Db, Ref, Result};
|
||||||
perms, Db, Ref, Result,
|
|
||||||
};
|
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
|
|
||||||
/// # Fetch Member
|
/// # Fetch Member
|
||||||
///
|
///
|
||||||
/// Retrieve a member.
|
/// Retrieve a member.
|
||||||
#[openapi(tag = "Server Members")]
|
#[openapi(tag = "Server Members")]
|
||||||
#[get("/<target>/members/<member>")]
|
#[get("/<target>/members/<member>?<roles>")]
|
||||||
pub async fn req(db: &Db, user: User, target: Ref, member: Ref) -> Result<Json<Member>> {
|
pub async fn req(
|
||||||
|
db: &Db,
|
||||||
|
user: User,
|
||||||
|
target: Ref,
|
||||||
|
member: Ref,
|
||||||
|
roles: Option<bool>,
|
||||||
|
) -> Result<Json<MemberResponse>> {
|
||||||
let server = target.as_server(db).await?;
|
let server = target.as_server(db).await?;
|
||||||
perms(&user).server(&server).calc(db).await?;
|
perms(&user).server(&server).calc(db).await?;
|
||||||
|
|
||||||
member.as_member(db, &server.id).await.map(Json)
|
let member_response: MemberResponse = match roles {
|
||||||
|
Some(true) => member.as_member_with_roles(db, &server.id).await?.into(),
|
||||||
|
_ => member.as_member(db, &server.id).await?.into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Json(member_response))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ mod permissions_set_default;
|
|||||||
mod roles_create;
|
mod roles_create;
|
||||||
mod roles_delete;
|
mod roles_delete;
|
||||||
mod roles_edit;
|
mod roles_edit;
|
||||||
|
mod roles_fetch;
|
||||||
mod server_ack;
|
mod server_ack;
|
||||||
mod server_create;
|
mod server_create;
|
||||||
mod server_delete;
|
mod server_delete;
|
||||||
@@ -42,6 +43,7 @@ pub fn routes() -> (Vec<Route>, OpenApi) {
|
|||||||
invites_fetch::req,
|
invites_fetch::req,
|
||||||
roles_create::req,
|
roles_create::req,
|
||||||
roles_edit::req,
|
roles_edit::req,
|
||||||
|
roles_fetch::req,
|
||||||
roles_delete::req,
|
roles_delete::req,
|
||||||
permissions_set::req,
|
permissions_set::req,
|
||||||
permissions_set_default::req,
|
permissions_set_default::req,
|
||||||
|
|||||||
19
crates/delta/src/routes/servers/roles_fetch.rs
Normal file
19
crates/delta/src/routes/servers/roles_fetch.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
use revolt_quark::{models::server::Role, Db, Error, Ref, Result};
|
||||||
|
use rocket::serde::json::Json;
|
||||||
|
|
||||||
|
/// # Fetch Role
|
||||||
|
///
|
||||||
|
/// Fetch a role by its id.
|
||||||
|
#[openapi(tag = "Server Permissions")]
|
||||||
|
#[get("/<target>/roles/<role_id>")]
|
||||||
|
pub async fn req(db: &Db, target: Ref, role_id: String) -> Result<Json<Role>> {
|
||||||
|
let server = target.as_server(db).await?;
|
||||||
|
|
||||||
|
let role = server.roles.get(&role_id);
|
||||||
|
|
||||||
|
if let Some(role) = role {
|
||||||
|
Ok(Json(role.clone()))
|
||||||
|
} else {
|
||||||
|
Err(Error::NotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
use crate::models::server_member::{FieldsMember, Member, MemberCompositeKey, PartialMember};
|
use crate::models::server_member::{
|
||||||
|
FieldsMember, Member, MemberCompositeKey, MemberWithRoles, PartialMember,
|
||||||
|
};
|
||||||
use crate::{AbstractServerMember, Result};
|
use crate::{AbstractServerMember, Result};
|
||||||
|
|
||||||
use super::super::DummyDb;
|
use super::super::DummyDb;
|
||||||
@@ -8,6 +10,9 @@ 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> {
|
||||||
Ok(Member::new(server.into(), user.into()))
|
Ok(Member::new(server.into(), user.into()))
|
||||||
}
|
}
|
||||||
|
async fn fetch_member_with_roles(&self, server: &str, user: &str) -> Result<MemberWithRoles> {
|
||||||
|
Ok(MemberWithRoles::new(server.into(), user.into()))
|
||||||
|
}
|
||||||
|
|
||||||
async fn insert_member(&self, member: &Member) -> Result<()> {
|
async fn insert_member(&self, member: &Member) -> Result<()> {
|
||||||
info!("Create {member:?}");
|
info!("Create {member:?}");
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use iso8601_timestamp::Timestamp;
|
use iso8601_timestamp::Timestamp;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
events::client::EventV1,
|
events::client::EventV1,
|
||||||
models::{
|
models::{
|
||||||
server_member::{FieldsMember, MemberCompositeKey, PartialMember},
|
server::Role,
|
||||||
|
server_member::{FieldsMember, MemberCompositeKey, MemberWithRoles, PartialMember},
|
||||||
Member, Server,
|
Member, Server,
|
||||||
},
|
},
|
||||||
Database, Result,
|
Database, Result,
|
||||||
@@ -81,4 +84,30 @@ impl Member {
|
|||||||
FieldsMember::Timeout => self.timeout = None,
|
FieldsMember::Timeout => self.timeout = None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_roles(&self, roles: HashMap<String, Role>) -> MemberWithRoles {
|
||||||
|
MemberWithRoles {
|
||||||
|
id: self.id.clone(),
|
||||||
|
joined_at: self.joined_at,
|
||||||
|
nickname: self.nickname.clone(),
|
||||||
|
avatar: self.avatar.clone(),
|
||||||
|
roles,
|
||||||
|
timeout: self.timeout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl MemberWithRoles {
|
||||||
|
pub fn new(server_id: String, user_id: String) -> Self {
|
||||||
|
Self {
|
||||||
|
id: MemberCompositeKey {
|
||||||
|
server: server_id,
|
||||||
|
user: user_id,
|
||||||
|
},
|
||||||
|
joined_at: Timestamp::now_utc(),
|
||||||
|
nickname: None,
|
||||||
|
avatar: None,
|
||||||
|
roles: HashMap::new(),
|
||||||
|
timeout: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use bson::Document;
|
use bson::Document;
|
||||||
|
|
||||||
|
use super::super::MongoDb;
|
||||||
|
use crate::models::server_member::MemberWithRoles;
|
||||||
use crate::models::server_member::{FieldsMember, Member, MemberCompositeKey, PartialMember};
|
use crate::models::server_member::{FieldsMember, Member, MemberCompositeKey, PartialMember};
|
||||||
use crate::r#impl::mongo::IntoDocumentPath;
|
use crate::r#impl::mongo::IntoDocumentPath;
|
||||||
use crate::{AbstractServerMember, Error, Result};
|
use crate::{AbstractServer, AbstractServerMember, Error, Result};
|
||||||
|
|
||||||
use super::super::MongoDb;
|
|
||||||
|
|
||||||
static COL: &str = "server_members";
|
static COL: &str = "server_members";
|
||||||
|
|
||||||
@@ -21,6 +21,13 @@ impl AbstractServerMember for MongoDb {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn fetch_member_with_roles(&self, server: &str, user: &str) -> Result<MemberWithRoles> {
|
||||||
|
Ok(self
|
||||||
|
.fetch_member(server, user)
|
||||||
|
.await?
|
||||||
|
.with_roles(self.fetch_server(server).await?.roles))
|
||||||
|
}
|
||||||
|
|
||||||
async fn insert_member(&self, member: &Member) -> Result<()> {
|
async fn insert_member(&self, member: &Member) -> Result<()> {
|
||||||
self.insert_one(COL, member).await.map(|_| ())
|
self.insert_one(COL, member).await.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use iso8601_timestamp::Timestamp;
|
use iso8601_timestamp::Timestamp;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::models::attachment::File;
|
use crate::models::attachment::File;
|
||||||
|
|
||||||
|
use super::server::Role;
|
||||||
|
|
||||||
/// Composite primary key consisting of server and user id
|
/// Composite primary key consisting of server and user id
|
||||||
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, Default)]
|
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, Default)]
|
||||||
pub struct MemberCompositeKey {
|
pub struct MemberCompositeKey {
|
||||||
@@ -41,6 +46,50 @@ pub struct Member {
|
|||||||
pub timeout: Option<Timestamp>,
|
pub timeout: Option<Timestamp>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Representation of a member of a server on Revolt With Role Data
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, OptionalStruct)]
|
||||||
|
#[optional_derive(Serialize, Deserialize, JsonSchema, Debug, Default, Clone)]
|
||||||
|
#[opt_skip_serializing_none]
|
||||||
|
#[opt_some_priority]
|
||||||
|
pub struct MemberWithRoles {
|
||||||
|
/// Unique member id
|
||||||
|
#[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>,
|
||||||
|
/// Avatar attachment
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub avatar: Option<File>,
|
||||||
|
|
||||||
|
/// Member's roles
|
||||||
|
#[serde(skip_serializing_if = "HashMap::is_empty", default)]
|
||||||
|
pub roles: HashMap<String, Role>,
|
||||||
|
/// Timestamp this member is timed out until
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub timeout: Option<Timestamp>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)]
|
||||||
|
pub enum MemberResponse {
|
||||||
|
Members(Member),
|
||||||
|
MembersWithRoles(MemberWithRoles),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Member> for MemberResponse {
|
||||||
|
fn from(value: Member) -> Self {
|
||||||
|
Self::Members(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<MemberWithRoles> for MemberResponse {
|
||||||
|
fn from(value: MemberWithRoles) -> Self {
|
||||||
|
Self::MembersWithRoles(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
/// Optional fields on server member object
|
/// Optional fields on server member object
|
||||||
#[derive(Serialize, Deserialize, JsonSchema, Debug, PartialEq, Eq, Clone)]
|
#[derive(Serialize, Deserialize, JsonSchema, Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum FieldsMember {
|
pub enum FieldsMember {
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
use crate::models::server_member::{FieldsMember, Member, MemberCompositeKey, PartialMember};
|
use crate::models::server_member::{
|
||||||
|
FieldsMember, Member, MemberCompositeKey, MemberWithRoles, PartialMember,
|
||||||
|
};
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -6,6 +8,8 @@ pub trait AbstractServerMember: Sync + Send {
|
|||||||
/// Fetch a server member by their id
|
/// Fetch a server member by their id
|
||||||
async fn fetch_member(&self, server: &str, user: &str) -> Result<Member>;
|
async fn fetch_member(&self, server: &str, user: &str) -> Result<Member>;
|
||||||
|
|
||||||
|
async fn fetch_member_with_roles(&self, server: &str, user: &str) -> Result<MemberWithRoles>;
|
||||||
|
|
||||||
/// Insert a new server member into the database
|
/// Insert a new server member into the database
|
||||||
async fn insert_member(&self, member: &Member) -> Result<()>;
|
async fn insert_member(&self, member: &Member) -> Result<()>;
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ use schemars::schema::{InstanceType, Schema, SchemaObject, SingleOrVec};
|
|||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::models::{
|
use crate::models::server_member::MemberWithRoles;
|
||||||
Channel, Emoji, Invite, Member, Message, Report, Server, ServerBan, User,
|
use crate::models::{Channel, Emoji, Invite, Member, Message, Report, Server, ServerBan, User};
|
||||||
};
|
|
||||||
use crate::{Database, Error, Result};
|
use crate::{Database, Error, Result};
|
||||||
|
|
||||||
/// Reference to some object in the database
|
/// Reference to some object in the database
|
||||||
@@ -66,6 +65,14 @@ impl Ref {
|
|||||||
db.fetch_member(server, &self.id).await
|
db.fetch_member(server, &self.id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn as_member_with_roles(
|
||||||
|
&self,
|
||||||
|
db: &Database,
|
||||||
|
server: &str,
|
||||||
|
) -> Result<MemberWithRoles> {
|
||||||
|
db.fetch_member_with_roles(server, &self.id).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Fetch ban from Ref
|
/// Fetch ban from Ref
|
||||||
pub async fn as_ban(&self, db: &Database, server: &str) -> Result<ServerBan> {
|
pub async fn as_ban(&self, db: &Database, server: &str) -> Result<ServerBan> {
|
||||||
db.fetch_ban(server, &self.id).await
|
db.fetch_ban(server, &self.id).await
|
||||||
|
|||||||
Reference in New Issue
Block a user