mirror of
https://github.com/stoatchat/stoatchat.git
synced 2026-07-14 05:26:59 +00:00
User status / profile change route, username change route.
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -2475,7 +2475,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "revolt"
|
||||
version = "0.4.0-alpha.3"
|
||||
version = "0.4.0-alpha.4"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"async-tungstenite",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "revolt"
|
||||
version = "0.4.0-alpha.3"
|
||||
version = "0.4.0-alpha.4"
|
||||
authors = ["Paul Makles <paulmakles@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize};
|
||||
use mongodb::options::{Collation, FindOneOptions};
|
||||
|
||||
use crate::database::*;
|
||||
use validator::Validate;
|
||||
use crate::util::result::{Error, Result};
|
||||
use crate::notifications::websocket::is_online;
|
||||
use crate::database::permissions::user::UserPermissions;
|
||||
@@ -27,16 +28,23 @@ pub struct Relationship {
|
||||
|
||||
/*
|
||||
pub enum Badge {
|
||||
Developer = 1
|
||||
Developer = 1,
|
||||
Translator = 2,
|
||||
}
|
||||
*/
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum UserStatus {
|
||||
Text {
|
||||
text: String
|
||||
}
|
||||
#[derive(Validate, Serialize, Deserialize, Debug)]
|
||||
pub struct UserStatus {
|
||||
#[validate(length(min = 1, max = 128))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
text: Option<String>
|
||||
}
|
||||
|
||||
#[derive(Validate, Serialize, Deserialize, Debug)]
|
||||
pub struct UserProfile {
|
||||
#[validate(length(min = 1, max = 2000))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
content: Option<String>
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
@@ -46,13 +54,13 @@ pub struct User {
|
||||
pub username: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub relations: Option<Vec<Relationship>>,
|
||||
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub badges: Option<i32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub status: Option<Vec<UserStatus>>,
|
||||
pub status: Option<UserStatus>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub profile: Option<String>,
|
||||
pub profile: Option<UserProfile>,
|
||||
|
||||
// ? This should never be pushed to the collection.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
||||
@@ -78,6 +78,10 @@ pub enum ClientboundNotification {
|
||||
user: String
|
||||
},
|
||||
|
||||
UserUpdate {
|
||||
id: String,
|
||||
data: JsonValue,
|
||||
},
|
||||
UserRelationship {
|
||||
id: String,
|
||||
user: String,
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::database::*;
|
||||
use crate::util::result::{Error, Result};
|
||||
use crate::notifications::events::ClientboundNotification;
|
||||
|
||||
use mongodb::bson::doc;
|
||||
use mongodb::bson::{doc, to_document};
|
||||
use validator::Validate;
|
||||
use rocket_contrib::json::Json;
|
||||
use serde::{Serialize, Deserialize};
|
||||
@@ -10,8 +10,10 @@ use serde::{Serialize, Deserialize};
|
||||
#[derive(Validate, Serialize, Deserialize)]
|
||||
pub struct Data {
|
||||
#[validate(length(min = 1, max = 32))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
name: Option<String>,
|
||||
#[validate(length(min = 0, max = 1024))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
description: Option<String>,
|
||||
}
|
||||
|
||||
@@ -36,19 +38,10 @@ pub async fn req(user: User, target: Ref, info: Json<Data>) -> Result<()> {
|
||||
|
||||
match &target {
|
||||
Channel::Group { id, .. } => {
|
||||
let col = get_collection("channels");
|
||||
let mut set = doc! {};
|
||||
if let Some(name) = &info.name {
|
||||
set.insert("name", name.clone());
|
||||
}
|
||||
|
||||
if let Some(description) = &info.description {
|
||||
set.insert("description", description.clone());
|
||||
}
|
||||
|
||||
col.update_one(
|
||||
get_collection("channels")
|
||||
.update_one(
|
||||
doc! { "_id": &id },
|
||||
doc! { "$set": set },
|
||||
doc! { "$set": to_document(&info.0).map_err(|_| Error::DatabaseError { operation: "to_document", with: "data" })? },
|
||||
None
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -9,7 +9,7 @@ use rocket_contrib::json::JsonValue;
|
||||
#[get("/")]
|
||||
pub async fn root() -> JsonValue {
|
||||
json!({
|
||||
"revolt": "0.4.0-alpha.3",
|
||||
"revolt": "0.4.0-alpha.4",
|
||||
"features": {
|
||||
"registration": !*DISABLE_REGISTRATION,
|
||||
"captcha": {
|
||||
|
||||
62
src/routes/users/change_username.rs
Normal file
62
src/routes/users/change_username.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
use crate::database::*;
|
||||
use crate::util::result::{Error, Result};
|
||||
use crate::notifications::events::ClientboundNotification;
|
||||
|
||||
use rauth::auth::{Auth, Session};
|
||||
use regex::Regex;
|
||||
use mongodb::bson::doc;
|
||||
use rocket::State;
|
||||
use validator::Validate;
|
||||
use rocket_contrib::json::Json;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
// ! FIXME: should be global somewhere; maybe use config(?)
|
||||
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")]
|
||||
username: Option<String>,
|
||||
#[validate(length(min = 8, max = 72))]
|
||||
password: String,
|
||||
}
|
||||
|
||||
#[patch("/username", data = "<data>")]
|
||||
pub async fn req(auth: State<'_, Auth>, session: Session, user: User, data: Json<Data>) -> Result<()> {
|
||||
data.validate()
|
||||
.map_err(|error| Error::FailedValidation { error })?;
|
||||
|
||||
auth.verify_password(&session, data.password.clone())
|
||||
.await
|
||||
.map_err(|_| Error::InvalidCredentials)?;
|
||||
|
||||
let mut set = doc! {};
|
||||
if let Some(username) = &data.username {
|
||||
if User::is_username_taken(&username).await? {
|
||||
return Err(Error::UsernameTaken)
|
||||
}
|
||||
|
||||
set.insert("username", username.clone());
|
||||
}
|
||||
|
||||
get_collection("users")
|
||||
.update_one(
|
||||
doc! { "_id": &user.id },
|
||||
doc! { "$set": set },
|
||||
None
|
||||
)
|
||||
.await
|
||||
.map_err(|_| Error::DatabaseError { operation: "update_one", with: "user" })?;
|
||||
|
||||
ClientboundNotification::UserUpdate {
|
||||
id: user.id.clone(),
|
||||
data: json!(data.0)
|
||||
}
|
||||
.publish(user.id.clone())
|
||||
.await
|
||||
.ok();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
41
src/routes/users/edit_user.rs
Normal file
41
src/routes/users/edit_user.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
use crate::database::*;
|
||||
use crate::util::result::{Error, Result};
|
||||
use crate::notifications::events::ClientboundNotification;
|
||||
|
||||
use mongodb::bson::{doc, to_document};
|
||||
use validator::Validate;
|
||||
use rocket_contrib::json::Json;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Validate, Serialize, Deserialize)]
|
||||
pub struct Data {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
status: Option<UserStatus>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
profile: Option<UserProfile>
|
||||
}
|
||||
|
||||
#[patch("/", data = "<data>")]
|
||||
pub async fn req(user: User, data: Json<Data>) -> Result<()> {
|
||||
data.validate()
|
||||
.map_err(|error| Error::FailedValidation { error })?;
|
||||
|
||||
get_collection("users")
|
||||
.update_one(
|
||||
doc! { "_id": &user.id },
|
||||
doc! { "$set": to_document(&data.0).map_err(|_| Error::DatabaseError { operation: "to_document", with: "data" })? },
|
||||
None
|
||||
)
|
||||
.await
|
||||
.map_err(|_| Error::DatabaseError { operation: "update_one", with: "user" })?;
|
||||
|
||||
ClientboundNotification::UserUpdate {
|
||||
id: user.id.clone(),
|
||||
data: json!(data.0)
|
||||
}
|
||||
.publish(user.id.clone())
|
||||
.await
|
||||
.ok();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -2,6 +2,8 @@ use rocket::Route;
|
||||
|
||||
mod add_friend;
|
||||
mod block_user;
|
||||
mod change_username;
|
||||
mod edit_user;
|
||||
mod fetch_dms;
|
||||
mod fetch_profile;
|
||||
mod fetch_relationship;
|
||||
@@ -18,6 +20,8 @@ pub fn routes() -> Vec<Route> {
|
||||
routes![
|
||||
// User Information
|
||||
fetch_user::req,
|
||||
edit_user::req,
|
||||
change_username::req,
|
||||
get_default_avatar::req,
|
||||
get_avatar::req,
|
||||
fetch_profile::req,
|
||||
|
||||
@@ -67,6 +67,8 @@ pub enum Error {
|
||||
MissingPermission,
|
||||
#[snafu(display("Operation cannot be performed on this object."))]
|
||||
InvalidOperation,
|
||||
#[snafu(display("Email or password is incorrect."))]
|
||||
InvalidCredentials,
|
||||
#[snafu(display("Already created an object with this nonce."))]
|
||||
DuplicateNonce,
|
||||
#[snafu(display("Voso is not enabled on this instance."))]
|
||||
@@ -108,6 +110,7 @@ impl<'r> Responder<'r, 'static> for Error {
|
||||
Error::MissingPermission => Status::Forbidden,
|
||||
Error::InvalidOperation => Status::BadRequest,
|
||||
Error::TooManyIds => Status::BadRequest,
|
||||
Error::InvalidCredentials => Status::Forbidden,
|
||||
Error::DuplicateNonce => Status::Conflict,
|
||||
Error::VosoUnavailable => Status::BadRequest,
|
||||
Error::NoEffect => Status::Ok,
|
||||
|
||||
Reference in New Issue
Block a user