forked from jmug/stoatchat
@@ -1,3 +1,3 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
export version=0.5.1-alpha.24
|
export version=0.5.2-alpha.0
|
||||||
echo "pub const VERSION: &str = \"${version}\";" > src/version.rs
|
echo "pub const VERSION: &str = \"${version}\";" > src/version.rs
|
||||||
|
|||||||
12
src/database/entities/bots.rs
Normal file
12
src/database/entities/bots.rs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub struct Bot {
|
||||||
|
#[serde(rename = "_id")]
|
||||||
|
pub id: String,
|
||||||
|
pub owner: String,
|
||||||
|
pub token: String,
|
||||||
|
pub public: bool,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub interactions_url: Option<String>,
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ mod microservice;
|
|||||||
mod server;
|
mod server;
|
||||||
mod sync;
|
mod sync;
|
||||||
mod user;
|
mod user;
|
||||||
|
mod bots;
|
||||||
|
|
||||||
use microservice::*;
|
use microservice::*;
|
||||||
|
|
||||||
@@ -16,3 +17,4 @@ pub use message::*;
|
|||||||
pub use server::*;
|
pub use server::*;
|
||||||
pub use sync::*;
|
pub use sync::*;
|
||||||
pub use user::*;
|
pub use user::*;
|
||||||
|
pub use bots::*;
|
||||||
|
|||||||
@@ -37,13 +37,20 @@ pub type PermissionTuple = (
|
|||||||
i32 // channel permission
|
i32 // channel permission
|
||||||
);
|
);
|
||||||
|
|
||||||
|
fn if_false(t: &bool) -> bool {
|
||||||
|
*t == false
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct Role {
|
pub struct Role {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub permissions: PermissionTuple,
|
pub permissions: PermissionTuple,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub colour: Option<String>
|
pub colour: Option<String>,
|
||||||
// Bri'ish API conventions
|
#[serde(skip_serializing_if = "if_false", default)]
|
||||||
|
pub hoist: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub rank: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
|||||||
@@ -73,7 +73,12 @@ pub enum Badges {
|
|||||||
|
|
||||||
impl_op_ex_commutative!(+ |a: &i32, b: &Badges| -> i32 { *a | *b as i32 });
|
impl_op_ex_commutative!(+ |a: &i32, b: &Badges| -> i32 { *a | *b as i32 });
|
||||||
|
|
||||||
// When changing this struct, update notifications/payload.rs#80
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub struct BotInformation {
|
||||||
|
owner: String
|
||||||
|
}
|
||||||
|
|
||||||
|
// When changing this struct, update notifications/payload.rs#113
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
#[serde(rename = "_id")]
|
#[serde(rename = "_id")]
|
||||||
@@ -91,6 +96,11 @@ pub struct User {
|
|||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub profile: Option<UserProfile>,
|
pub profile: Option<UserProfile>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub flags: Option<i32>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub bot: Option<BotInformation>,
|
||||||
|
|
||||||
// ? This should never be pushed to the collection.
|
// ? This should never be pushed to the collection.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub relationship: Option<RelationshipStatus>,
|
pub relationship: Option<RelationshipStatus>,
|
||||||
|
|||||||
@@ -10,6 +10,61 @@ impl<'r> FromRequest<'r> for User {
|
|||||||
type Error = rauth::util::Error;
|
type Error = rauth::util::Error;
|
||||||
|
|
||||||
async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||||
|
let header_bot_token = request
|
||||||
|
.headers()
|
||||||
|
.get("x-bot-token")
|
||||||
|
.next()
|
||||||
|
.map(|x| x.to_string());
|
||||||
|
|
||||||
|
if let Some(bot_token) = header_bot_token {
|
||||||
|
return if let Ok(result) = get_collection("bots")
|
||||||
|
.find_one(
|
||||||
|
doc! {
|
||||||
|
"token": bot_token
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
if let Some(doc) = result {
|
||||||
|
let id = doc.get_str("_id").unwrap();
|
||||||
|
if let Ok(result) = get_collection("users")
|
||||||
|
.find_one(
|
||||||
|
doc! {
|
||||||
|
"_id": &id
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
if let Some(doc) = result {
|
||||||
|
Outcome::Success(from_document(doc).unwrap())
|
||||||
|
} else {
|
||||||
|
Outcome::Failure((Status::Forbidden, rauth::util::Error::InvalidSession))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Outcome::Failure((
|
||||||
|
Status::InternalServerError,
|
||||||
|
rauth::util::Error::DatabaseError {
|
||||||
|
operation: "find_one",
|
||||||
|
with: "user",
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Outcome::Failure((Status::Forbidden, rauth::util::Error::InvalidSession))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Outcome::Failure((
|
||||||
|
Status::InternalServerError,
|
||||||
|
rauth::util::Error::DatabaseError {
|
||||||
|
operation: "find_one",
|
||||||
|
with: "bot",
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let session: Session = request.guard::<Session>().await.unwrap();
|
let session: Session = request.guard::<Session>().await.unwrap();
|
||||||
|
|
||||||
if let Ok(result) = get_collection("users")
|
if let Ok(result) = get_collection("users")
|
||||||
|
|||||||
@@ -57,6 +57,10 @@ pub async fn create_database() {
|
|||||||
.await
|
.await
|
||||||
.expect("Failed to create user_settings collection.");
|
.expect("Failed to create user_settings collection.");
|
||||||
|
|
||||||
|
db.create_collection("bots", None)
|
||||||
|
.await
|
||||||
|
.expect("Failed to create bots collection.");
|
||||||
|
|
||||||
db.create_collection(
|
db.create_collection(
|
||||||
"pubsub",
|
"pubsub",
|
||||||
CreateCollectionOptions::builder()
|
CreateCollectionOptions::builder()
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ struct MigrationInfo {
|
|||||||
revision: i32,
|
revision: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const LATEST_REVISION: i32 = 7;
|
pub const LATEST_REVISION: i32 = 8;
|
||||||
|
|
||||||
pub async fn migrate_database() {
|
pub async fn migrate_database() {
|
||||||
let migrations = get_collection("migrations");
|
let migrations = get_collection("migrations");
|
||||||
@@ -203,6 +203,15 @@ pub async fn run_migrations(revision: i32) -> i32 {
|
|||||||
.expect("Failed to create message index.");
|
.expect("Failed to create message index.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if revision <= 7 {
|
||||||
|
info!("Running migration [revision 7 / 2021-08-11]: Add message text index.");
|
||||||
|
|
||||||
|
get_db()
|
||||||
|
.create_collection("bots", None)
|
||||||
|
.await
|
||||||
|
.expect("Failed to create bots collection.");
|
||||||
|
}
|
||||||
|
|
||||||
// Reminder to update LATEST_REVISION when adding new migrations.
|
// Reminder to update LATEST_REVISION when adding new migrations.
|
||||||
LATEST_REVISION
|
LATEST_REVISION
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,22 @@ pub enum WebSocketError {
|
|||||||
AlreadyAuthenticated,
|
AlreadyAuthenticated,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct BotAuth {
|
||||||
|
pub token: String
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum AuthType {
|
||||||
|
User(Session),
|
||||||
|
Bot(BotAuth)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum ServerboundNotification {
|
pub enum ServerboundNotification {
|
||||||
Authenticate(Session),
|
Authenticate(AuthType),
|
||||||
BeginTyping { channel: String },
|
BeginTyping { channel: String },
|
||||||
EndTyping { channel: String },
|
EndTyping { channel: String },
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::database::*;
|
use crate::database::*;
|
||||||
|
use crate::notifications::events::{AuthType, BotAuth};
|
||||||
use crate::util::variables::WS_HOST;
|
use crate::util::variables::WS_HOST;
|
||||||
|
|
||||||
use super::subscriptions;
|
use super::subscriptions;
|
||||||
@@ -12,8 +13,9 @@ use futures::{pin_mut, prelude::*};
|
|||||||
use hive_pubsub::PubSub;
|
use hive_pubsub::PubSub;
|
||||||
use log::{debug, info};
|
use log::{debug, info};
|
||||||
use many_to_many::ManyToMany;
|
use many_to_many::ManyToMany;
|
||||||
|
use mongodb::bson::doc;
|
||||||
use rauth::{
|
use rauth::{
|
||||||
auth::{Auth, Session},
|
auth::{Auth},
|
||||||
options::Options,
|
options::Options,
|
||||||
};
|
};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -66,15 +68,15 @@ async fn accept(stream: TcpStream) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let session: Arc<Mutex<Option<Session>>> = Arc::new(Mutex::new(None));
|
let user_id: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));
|
||||||
let mutex_generator = || session.clone();
|
let mutex_generator = || user_id.clone();
|
||||||
let fwd = rx.map(Ok).forward(write);
|
let fwd = rx.map(Ok).forward(write);
|
||||||
let incoming = read.try_for_each(async move |msg| {
|
let incoming = read.try_for_each(async move |msg| {
|
||||||
let mutex = mutex_generator();
|
let mutex = mutex_generator();
|
||||||
if let Message::Text(text) = msg {
|
if let Message::Text(text) = msg {
|
||||||
if let Ok(notification) = serde_json::from_str::<ServerboundNotification>(&text) {
|
if let Ok(notification) = serde_json::from_str::<ServerboundNotification>(&text) {
|
||||||
match notification {
|
match notification {
|
||||||
ServerboundNotification::Authenticate(new_session) => {
|
ServerboundNotification::Authenticate(auth) => {
|
||||||
{
|
{
|
||||||
if mutex.lock().unwrap().is_some() {
|
if mutex.lock().unwrap().is_some() {
|
||||||
send(ClientboundNotification::Error(
|
send(ClientboundNotification::Error(
|
||||||
@@ -85,12 +87,34 @@ async fn accept(stream: TcpStream) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(validated_session) =
|
if let Some(id) = match auth {
|
||||||
Auth::new(get_collection("accounts"), Options::new())
|
AuthType::User(new_session) => {
|
||||||
.verify_session(new_session)
|
if let Ok(validated_session) =
|
||||||
.await
|
Auth::new(get_collection("accounts"), Options::new())
|
||||||
{
|
.verify_session(new_session)
|
||||||
let id = validated_session.user_id.clone();
|
.await
|
||||||
|
{
|
||||||
|
Some(validated_session.user_id.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AuthType::Bot(BotAuth { token }) => {
|
||||||
|
if let Ok(doc) = get_collection("bots")
|
||||||
|
.find_one(
|
||||||
|
doc! { "token": token },
|
||||||
|
None
|
||||||
|
).await {
|
||||||
|
if let Some(doc) = doc {
|
||||||
|
Some(doc.get_str("_id").unwrap().to_string())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} {
|
||||||
if let Ok(user) = (Ref { id: id.clone() }).fetch_user().await {
|
if let Ok(user) = (Ref { id: id.clone() }).fetch_user().await {
|
||||||
let was_online = is_online(&id);
|
let was_online = is_online(&id);
|
||||||
{
|
{
|
||||||
@@ -110,7 +134,7 @@ async fn accept(stream: TcpStream) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*mutex.lock().unwrap() = Some(validated_session);
|
*mutex.lock().unwrap() = Some(id.clone());
|
||||||
|
|
||||||
if let Err(_) = subscriptions::generate_subscriptions(&user).await {
|
if let Err(_) = subscriptions::generate_subscriptions(&user).await {
|
||||||
send(ClientboundNotification::Error(
|
send(ClientboundNotification::Error(
|
||||||
@@ -166,8 +190,7 @@ async fn accept(stream: TcpStream) {
|
|||||||
if mutex.lock().unwrap().is_some() {
|
if mutex.lock().unwrap().is_some() {
|
||||||
let user = {
|
let user = {
|
||||||
let mutex = mutex.lock().unwrap();
|
let mutex = mutex.lock().unwrap();
|
||||||
let session = mutex.as_ref().unwrap();
|
mutex.as_ref().unwrap().clone()
|
||||||
session.user_id.clone()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ClientboundNotification::ChannelStartTyping {
|
ClientboundNotification::ChannelStartTyping {
|
||||||
@@ -187,8 +210,7 @@ async fn accept(stream: TcpStream) {
|
|||||||
if mutex.lock().unwrap().is_some() {
|
if mutex.lock().unwrap().is_some() {
|
||||||
let user = {
|
let user = {
|
||||||
let mutex = mutex.lock().unwrap();
|
let mutex = mutex.lock().unwrap();
|
||||||
let session = mutex.as_ref().unwrap();
|
mutex.as_ref().unwrap().clone()
|
||||||
session.user_id.clone()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ClientboundNotification::ChannelStopTyping {
|
ClientboundNotification::ChannelStopTyping {
|
||||||
@@ -219,13 +241,13 @@ async fn accept(stream: TcpStream) {
|
|||||||
|
|
||||||
let mut offline = None;
|
let mut offline = None;
|
||||||
{
|
{
|
||||||
let session = session.lock().unwrap();
|
let user_id = user_id.lock().unwrap();
|
||||||
if let Some(session) = session.as_ref() {
|
if let Some(user_id) = user_id.as_ref() {
|
||||||
let mut users = USERS.write().unwrap();
|
let mut users = USERS.write().unwrap();
|
||||||
users.remove(&session.user_id, &addr);
|
users.remove(&user_id, &addr);
|
||||||
if users.get_left(&session.user_id).is_none() {
|
if users.get_left(&user_id).is_none() {
|
||||||
get_hive().drop_client(&session.user_id).unwrap();
|
get_hive().drop_client(&user_id).unwrap();
|
||||||
offline = Some(session.user_id.clone());
|
offline = Some(user_id.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
89
src/routes/bots/create.rs
Normal file
89
src/routes/bots/create.rs
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
use crate::database::*;
|
||||||
|
use crate::util::result::{Error, Result};
|
||||||
|
use crate::util::variables::MAX_BOT_COUNT;
|
||||||
|
|
||||||
|
use mongodb::bson::{doc, to_document};
|
||||||
|
use regex::Regex;
|
||||||
|
use rocket::serde::json::{Json, Value};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use ulid::Ulid;
|
||||||
|
use nanoid::nanoid;
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
|
// ! FIXME: should be global somewhere; maybe use config(?)
|
||||||
|
// ! tip: CTRL + F, RE_USERNAME
|
||||||
|
lazy_static! {
|
||||||
|
static ref RE_USERNAME: Regex = Regex::new(r"^[a-zA-Z0-9_.]+$").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Validate, Serialize, Deserialize)]
|
||||||
|
pub struct Data {
|
||||||
|
#[validate(length(min = 2, max = 32), regex = "RE_USERNAME")]
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/create", data = "<info>")]
|
||||||
|
pub async fn create_bot(user: User, info: Json<Data>) -> Result<Value> {
|
||||||
|
let info = info.into_inner();
|
||||||
|
info.validate()
|
||||||
|
.map_err(|error| Error::FailedValidation { error })?;
|
||||||
|
|
||||||
|
if get_collection("bots")
|
||||||
|
.count_documents(
|
||||||
|
doc! {
|
||||||
|
"owner": &user.id
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::DatabaseError {
|
||||||
|
operation: "count_documents",
|
||||||
|
with: "bots",
|
||||||
|
})? as usize >= *MAX_BOT_COUNT {
|
||||||
|
return Err(Error::ReachedMaximumBots)
|
||||||
|
}
|
||||||
|
|
||||||
|
let id = Ulid::new().to_string();
|
||||||
|
let token = nanoid!(64);
|
||||||
|
let bot = Bot {
|
||||||
|
id: id.clone(),
|
||||||
|
owner: user.id.clone(),
|
||||||
|
token,
|
||||||
|
public: false,
|
||||||
|
interactions_url: None
|
||||||
|
};
|
||||||
|
|
||||||
|
if User::is_username_taken(&info.name).await? {
|
||||||
|
return Err(Error::UsernameTaken);
|
||||||
|
}
|
||||||
|
|
||||||
|
get_collection("users")
|
||||||
|
.insert_one(
|
||||||
|
doc! {
|
||||||
|
"_id": &id,
|
||||||
|
"username": &info.name,
|
||||||
|
"bot": {
|
||||||
|
"owner": &user.id
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::DatabaseError {
|
||||||
|
operation: "insert_one",
|
||||||
|
with: "user",
|
||||||
|
})?;
|
||||||
|
|
||||||
|
get_collection("bots")
|
||||||
|
.insert_one(
|
||||||
|
to_document(&bot).map_err(|_| Error::DatabaseError { with: "bot", operation: "to_document" })?,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::DatabaseError {
|
||||||
|
operation: "insert_one",
|
||||||
|
with: "user",
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(json!(bot))
|
||||||
|
}
|
||||||
9
src/routes/bots/mod.rs
Normal file
9
src/routes/bots/mod.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
use rocket::Route;
|
||||||
|
|
||||||
|
mod create;
|
||||||
|
|
||||||
|
pub fn routes() -> Vec<Route> {
|
||||||
|
routes![
|
||||||
|
create::create_bot
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ mod root;
|
|||||||
mod servers;
|
mod servers;
|
||||||
mod sync;
|
mod sync;
|
||||||
mod users;
|
mod users;
|
||||||
|
mod bots;
|
||||||
|
|
||||||
pub fn mount(rocket: Rocket<Build>) -> Rocket<Build> {
|
pub fn mount(rocket: Rocket<Build>) -> Rocket<Build> {
|
||||||
rocket
|
rocket
|
||||||
@@ -18,6 +19,7 @@ pub fn mount(rocket: Rocket<Build>) -> Rocket<Build> {
|
|||||||
.mount("/users", users::routes())
|
.mount("/users", users::routes())
|
||||||
.mount("/channels", channels::routes())
|
.mount("/channels", channels::routes())
|
||||||
.mount("/servers", servers::routes())
|
.mount("/servers", servers::routes())
|
||||||
|
.mount("/bots", bots::routes())
|
||||||
.mount("/invites", invites::routes())
|
.mount("/invites", invites::routes())
|
||||||
.mount("/push", push::routes())
|
.mount("/push", push::routes())
|
||||||
.mount("/sync", sync::routes())
|
.mount("/sync", sync::routes())
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ pub struct Data {
|
|||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
#[validate(length(min = 1, max = 32))]
|
#[validate(length(min = 1, max = 32))]
|
||||||
colour: Option<String>,
|
colour: Option<String>,
|
||||||
|
hoist: Option<bool>,
|
||||||
|
rank: Option<i64>,
|
||||||
remove: Option<RemoveRoleField>,
|
remove: Option<RemoveRoleField>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,7 +24,7 @@ pub async fn req(user: User, target: Ref, role_id: String, data: Json<Data>) ->
|
|||||||
data.validate()
|
data.validate()
|
||||||
.map_err(|error| Error::FailedValidation { error })?;
|
.map_err(|error| Error::FailedValidation { error })?;
|
||||||
|
|
||||||
if data.name.is_none() && data.colour.is_none() && data.remove.is_none()
|
if data.name.is_none() && data.colour.is_none() && data.hoist.is_none() && data.rank.is_none() && data.remove.is_none()
|
||||||
{
|
{
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -67,6 +69,16 @@ pub async fn req(user: User, target: Ref, role_id: String, data: Json<Data>) ->
|
|||||||
set_update.insert("colour", colour);
|
set_update.insert("colour", colour);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(hoist) = &data.hoist {
|
||||||
|
set.insert(role_key.clone() + ".hoist", hoist);
|
||||||
|
set_update.insert("hoist", hoist);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(rank) = &data.rank {
|
||||||
|
set.insert(role_key.clone() + ".rank", rank);
|
||||||
|
set_update.insert("rank", rank);
|
||||||
|
}
|
||||||
|
|
||||||
let mut operations = doc! {};
|
let mut operations = doc! {};
|
||||||
if set.len() > 0 {
|
if set.len() > 0 {
|
||||||
operations.insert("$set", &set);
|
operations.insert("$set", &set);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|
||||||
// ! FIXME: should be global somewhere; maybe use config(?)
|
// ! FIXME: should be global somewhere; maybe use config(?)
|
||||||
|
// ! tip: CTRL + F, RE_USERNAME
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref RE_USERNAME: Regex = Regex::new(r"^[a-zA-Z0-9_.]+$").unwrap();
|
static ref RE_USERNAME: Regex = Regex::new(r"^[a-zA-Z0-9_.]+$").unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ pub enum Error {
|
|||||||
InvalidRole,
|
InvalidRole,
|
||||||
Banned,
|
Banned,
|
||||||
|
|
||||||
|
// ? Bot related errors.
|
||||||
|
ReachedMaximumBots,
|
||||||
|
|
||||||
// ? General errors.
|
// ? General errors.
|
||||||
TooManyIds,
|
TooManyIds,
|
||||||
FailedValidation {
|
FailedValidation {
|
||||||
@@ -98,6 +101,8 @@ impl<'r> Responder<'r, 'static> for Error {
|
|||||||
Error::InvalidRole => Status::NotFound,
|
Error::InvalidRole => Status::NotFound,
|
||||||
Error::Banned => Status::Forbidden,
|
Error::Banned => Status::Forbidden,
|
||||||
|
|
||||||
|
Error::ReachedMaximumBots => Status::BadRequest,
|
||||||
|
|
||||||
Error::FailedValidation { .. } => Status::UnprocessableEntity,
|
Error::FailedValidation { .. } => Status::UnprocessableEntity,
|
||||||
Error::DatabaseError { .. } => Status::InternalServerError,
|
Error::DatabaseError { .. } => Status::InternalServerError,
|
||||||
Error::InternalError => Status::InternalServerError,
|
Error::InternalError => Status::InternalServerError,
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ lazy_static! {
|
|||||||
// Application Logic Settings
|
// Application Logic Settings
|
||||||
pub static ref MAX_GROUP_SIZE: usize =
|
pub static ref MAX_GROUP_SIZE: usize =
|
||||||
env::var("REVOLT_MAX_GROUP_SIZE").unwrap_or_else(|_| "50".to_string()).parse().unwrap();
|
env::var("REVOLT_MAX_GROUP_SIZE").unwrap_or_else(|_| "50".to_string()).parse().unwrap();
|
||||||
|
pub static ref MAX_BOT_COUNT: usize =
|
||||||
|
env::var("REVOLT_MAX_BOT_COUNT").unwrap_or_else(|_| "5".to_string()).parse().unwrap();
|
||||||
pub static ref EARLY_ADOPTER_BADGE: i64 =
|
pub static ref EARLY_ADOPTER_BADGE: i64 =
|
||||||
env::var("REVOLT_EARLY_ADOPTER_BADGE").unwrap_or_else(|_| "0".to_string()).parse().unwrap();
|
env::var("REVOLT_EARLY_ADOPTER_BADGE").unwrap_or_else(|_| "0".to_string()).parse().unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
pub const VERSION: &str = "0.5.1-alpha.24";
|
pub const VERSION: &str = "0.5.2-alpha.0";
|
||||||
|
|||||||
Reference in New Issue
Block a user