Compare commits

...

13 Commits

Author SHA1 Message Date
Paul Makles
a9f258de6b Randomly select an avatar from id. 2021-01-16 11:17:18 +00:00
Paul Makles
f6c52de171 Add basic presence tracking. 2021-01-09 20:49:36 +00:00
Paul Makles
5e70ceea01 Write proper user permission code. 2021-01-09 16:50:11 +00:00
Paul Makles
bb3667a83b Move guard and reference code out of entities folder. 2021-01-09 13:59:45 +00:00
Paul Makles
80e3baaa15 Clean up imports for database. 2021-01-09 13:57:37 +00:00
Paul Makles
0d3ef9a3b7 Refractor WS; remove - fom names. 2021-01-09 12:58:15 +00:00
insert
4f9029f7b9 Merge branch 'master' into 'master'
Code cleanup/formatting

See merge request revolt/delta!2
2021-01-05 14:29:42 +00:00
Martin Loffler
bb73f905e6 Formatted code 2021-01-05 15:01:24 +01:00
Martin Loffler
66a2930a6f Code cleanup
Cleaned up
- matches in routes remove_friend and unblock_user
- production warnings in util/variables
2021-01-05 14:49:56 +01:00
Martin Loffler
f24d478454 async_std main attribute
Closes #1
2021-01-04 15:01:18 +01:00
Paul Makles
1b711a88ef Include relationship when fetching a user. 2021-01-03 20:27:23 +00:00
Paul Makles
b87f396f40 Make ready payload send all users we have a relationship with. 2021-01-03 17:43:20 +00:00
Paul Makles
ffff620508 Advertise WebSocket address at root. Add REVOLT_EXTERNAL_WS_URL. 2020-12-31 16:05:39 +00:00
35 changed files with 413 additions and 249 deletions

11
Cargo.lock generated
View File

@@ -36,6 +36,16 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
name = "async-attributes"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efd3d156917d94862e779f356c5acae312b08fd3121e792c857d7928c8088423"
dependencies = [
"quote 1.0.8",
"syn 1.0.56",
]
[[package]]
name = "async-channel"
version = "1.5.1"
@@ -110,6 +120,7 @@ version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f9f84f1280a2b436a2c77c2582602732b6c2f4321d5494d6e799e6c367859a8"
dependencies = [
"async-attributes",
"async-channel",
"async-global-executor",
"async-io",

View File

@@ -13,13 +13,12 @@ impl_ops = "0.1.1"
ctrlc = { version = "3.0", features = ["termination"] }
async-tungstenite = { version = "0.10.0", features = ["async-std-runtime"] }
rauth = { git = "https://gitlab.insrt.uk/insert/rauth" }
async-std = { version = "1.8.0", features = ["tokio02"] }
async-std = { version = "1.8.0", features = ["tokio02", "attributes"] }
hive_pubsub = { version = "0.4.3", features = ["mongo"] }
rocket_cors = { git = "https://github.com/lawliet89/rocket_cors", branch = "master" }
rocket_contrib = { git = "https://github.com/SergioBenitez/Rocket", branch = "master" }
rocket = { git = "https://github.com/SergioBenitez/Rocket", branch = "master", default-features = false }
# ! FIXME: Switch to async-std runtime.
mongodb = { version = "1.1.1", features = ["tokio-runtime"], default-features = false }
once_cell = "1.4.1"

BIN
assets/user_blue.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
assets/user_green.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
assets/user_red.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

BIN
assets/user_yellow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,9 +1,3 @@
use crate::database::get_collection;
use crate::database::guards::reference::Ref;
use mongodb::bson::{doc, from_bson, Bson};
use rauth::auth::Session;
use rocket::http::Status;
use rocket::request::{self, FromRequest, Outcome, Request};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
@@ -31,42 +25,10 @@ pub struct User {
pub username: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub relations: Option<Vec<Relationship>>,
}
#[rocket::async_trait]
impl<'a, 'r> FromRequest<'a, 'r> for User {
type Error = rauth::util::Error;
async fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
let session: Session = try_outcome!(request.guard::<Session>().await);
if let Ok(result) = get_collection("users")
.find_one(
doc! {
"_id": &session.user_id
},
None,
)
.await
{
if let Some(doc) = result {
Outcome::Success(from_bson(Bson::Document(doc)).unwrap())
} else {
Outcome::Failure((Status::Forbidden, rauth::util::Error::InvalidSession))
}
} else {
Outcome::Failure((
Status::InternalServerError,
rauth::util::Error::DatabaseError,
))
}
}
}
impl User {
pub fn as_ref(&self) -> Ref {
Ref {
id: self.id.to_string(),
}
}
// ? This should never be pushed to the collection.
#[serde(skip_serializing_if = "Option::is_none")]
pub relationship: Option<RelationshipStatus>,
#[serde(skip_serializing_if = "Option::is_none")]
pub online: Option<bool>,
}

View File

@@ -1,2 +1,5 @@
pub mod reference;
pub mod user;
pub use reference::Ref;
// pub use user::*;

View File

@@ -1,6 +1,6 @@
use crate::database::entities::*;
use crate::database::get_collection;
use crate::database::*;
use crate::util::result::{Error, Result};
use mongodb::bson::{doc, from_bson, Bson};
use rocket::http::RawStr;
use rocket::request::FromParam;
@@ -42,6 +42,14 @@ impl Ref {
}
}
impl User {
pub fn as_ref(&self) -> Ref {
Ref {
id: self.id.to_string(),
}
}
}
impl<'r> FromParam<'r> for Ref {
type Error = &'r RawStr;

View File

@@ -1 +1,36 @@
use crate::database::*;
use mongodb::bson::{doc, from_bson, Bson};
use rauth::auth::Session;
use rocket::http::Status;
use rocket::request::{self, FromRequest, Outcome, Request};
#[rocket::async_trait]
impl<'a, 'r> FromRequest<'a, 'r> for User {
type Error = rauth::util::Error;
async fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
let session: Session = try_outcome!(request.guard::<Session>().await);
if let Ok(result) = get_collection("users")
.find_one(
doc! {
"_id": &session.user_id
},
None,
)
.await
{
if let Some(doc) = result {
Outcome::Success(from_bson(Bson::Document(doc)).unwrap())
} else {
Outcome::Failure((Status::Forbidden, rauth::util::Error::InvalidSession))
}
} else {
Outcome::Failure((
Status::InternalServerError,
rauth::util::Error::DatabaseError,
))
}
}
}

View File

@@ -30,3 +30,7 @@ pub mod entities;
pub mod guards;
pub mod migrations;
pub mod permissions;
pub use entities::*;
pub use guards::*;
pub use permissions::*;

View File

@@ -1,49 +1,3 @@
use crate::database::entities::User;
use num_enum::TryFromPrimitive;
use std::ops;
pub mod user;
#[derive(Debug, PartialEq, Eq, TryFromPrimitive, Copy, Clone)]
#[repr(u32)]
pub enum UserPermission {
Access = 1,
SendMessage = 2,
Invite = 4,
}
bitfield! {
pub struct UserPermissions(MSB0 [u32]);
u32;
pub get_access, _: 31;
pub get_send_message, _: 30;
pub get_invite, _: 29;
}
impl_op_ex!(+ |a: &UserPermission, b: &UserPermission| -> u32 { *a as u32 | *b as u32 });
impl_op_ex_commutative!(+ |a: &u32, b: &UserPermission| -> u32 { *a | *b as u32 });
pub async fn temp_calc_perm(_user: &User, _target: &User) -> UserPermissions<[u32; 1]> {
// if friends; Access + Message + Invite
// if mutually know each other:
// and has DMs from users enabled -> Access + Message
// otherwise -> Access
// otherwise; None
UserPermissions([UserPermission::Access + UserPermission::SendMessage + UserPermission::Invite])
}
use crate::database::entities::RelationshipStatus;
use crate::database::guards::reference::Ref;
pub fn get_relationship(a: &User, b: &Ref) -> RelationshipStatus {
if a.id == b.id {
return RelationshipStatus::Friend;
}
if let Some(relations) = &a.relations {
if let Some(relationship) = relations.iter().find(|x| x.id == b.id) {
return relationship.status.clone();
}
}
RelationshipStatus::None
}
pub use user::get_relationship;

View File

@@ -0,0 +1,50 @@
use crate::database::*;
use num_enum::TryFromPrimitive;
use std::ops;
#[derive(Debug, PartialEq, Eq, TryFromPrimitive, Copy, Clone)]
#[repr(u32)]
pub enum UserPermission {
Access = 1,
SendMessage = 2,
Invite = 4,
}
bitfield! {
pub struct UserPermissions(MSB0 [u32]);
u32;
pub get_access, _: 31;
pub get_send_message, _: 30;
pub get_invite, _: 29;
}
impl_op_ex!(+ |a: &UserPermission, b: &UserPermission| -> u32 { *a as u32 | *b as u32 });
impl_op_ex_commutative!(+ |a: &u32, b: &UserPermission| -> u32 { *a | *b as u32 });
pub async fn calculate(user: &User, target: &str) -> UserPermissions<[u32; 1]> {
// if friends; Access + Message + Invite
// if mutually know each other:
// and has DMs from users enabled -> Access + Message
// otherwise -> Access
// otherwise; None
if let RelationshipStatus::Friend = get_relationship(&user, &target) {
UserPermissions([UserPermission::Access + UserPermission::SendMessage + UserPermission::Invite])
} else {
UserPermissions([ 0 ])
}
}
pub fn get_relationship(a: &User, b: &str) -> RelationshipStatus {
if a.id == b {
return RelationshipStatus::Friend;
}
if let Some(relations) = &a.relations {
if let Some(relationship) = relations.iter().find(|x| x.id == b) {
return relationship.status.clone();
}
}
RelationshipStatus::None
}

View File

@@ -18,17 +18,13 @@ pub mod notifications;
pub mod routes;
pub mod util;
use async_std::task;
use futures::join;
use log::info;
use rauth;
use rocket_cors::AllowedOrigins;
fn main() {
task::block_on(entry())
}
async fn entry() {
#[async_std::main]
async fn main() {
dotenv::dotenv().ok();
env_logger::init_from_env(env_logger::Env::default().filter_or("RUST_LOG", "info"));

View File

@@ -2,9 +2,8 @@ use rauth::auth::Session;
use serde::{Deserialize, Serialize};
use snafu::Snafu;
use crate::database::entities::{RelationshipStatus, User};
use super::hive::get_hive;
use crate::database::*;
#[derive(Serialize, Deserialize, Debug, Snafu)]
#[serde(tag = "error")]
@@ -12,7 +11,7 @@ pub enum WebSocketError {
#[snafu(display("This error has not been labelled."))]
LabelMe,
#[snafu(display("Internal server error."))]
InternalError,
InternalError { at: String },
#[snafu(display("Invalid session."))]
InvalidSession,
#[snafu(display("User hasn't completed onboarding."))]
@@ -33,7 +32,7 @@ pub enum ClientboundNotification {
Error(WebSocketError),
Authenticated,
Ready {
user: User
users: Vec<User>,
},
/*MessageCreate {
@@ -91,11 +90,17 @@ pub enum ClientboundNotification {
GuildDelete {
id: String,
},*/
UserRelationship {
id: String,
user: String,
status: RelationshipStatus,
},
UserPresence {
id: String,
online: bool
}
}
impl ClientboundNotification {

View File

@@ -1,5 +1,5 @@
use super::{events::ClientboundNotification, websocket};
use crate::database::get_collection;
use crate::database::*;
use futures::FutureExt;
use hive_pubsub::backend::mongo::MongodbPubSub;

View File

@@ -1,4 +1,5 @@
pub mod events;
pub mod hive;
pub mod payload;
pub mod subscriptions;
pub mod websocket;

View File

@@ -0,0 +1,68 @@
use crate::notifications::events::ClientboundNotification;
use crate::{
database::{entities::User, get_collection},
util::result::{Error, Result},
};
use futures::StreamExt;
use mongodb::{
bson::{doc, from_bson, Bson},
options::FindOptions,
};
use super::websocket::is_online;
pub async fn generate_ready(mut user: User) -> Result<ClientboundNotification> {
let mut users = vec![];
if let Some(relationships) = &user.relations {
let user_ids: Vec<String> = relationships
.iter()
.map(|relationship| relationship.id.clone())
.collect();
let mut cursor = get_collection("users")
.find(
doc! {
"_id": {
"$in": user_ids
}
},
FindOptions::builder()
.projection(doc! { "_id": 1, "username": 1 })
.build(),
)
.await
.map_err(|_| Error::DatabaseError {
operation: "find",
with: "users",
})?;
while let Some(result) = cursor.next().await {
if let Ok(doc) = result {
let mut user: User =
from_bson(Bson::Document(doc)).map_err(|_| Error::DatabaseError {
operation: "from_bson",
with: "user",
})?;
user.relationship = Some(
relationships
.iter()
.find(|x| user.id == x.id)
.ok_or_else(|| Error::InternalError)?
.status
.clone(),
);
user.online = Some(is_online(&user.id));
users.push(user);
}
}
}
user.online = Some(is_online(&user.id));
users.push(user);
Ok(ClientboundNotification::Ready { users })
}

View File

@@ -1,4 +1,4 @@
use crate::database::entities::User;
use crate::database::*;
use super::hive::get_hive;
use hive_pubsub::PubSub;

View File

@@ -1,5 +1,4 @@
use crate::database::get_collection;
use crate::database::guards::reference::Ref;
use crate::database::*;
use crate::util::variables::WS_HOST;
use super::subscriptions;
@@ -64,69 +63,104 @@ async fn accept(stream: TcpStream) {
}
};
let mut session: Option<Session> = None;
let session: Arc<Mutex<Option<Session>>> = Arc::new(Mutex::new(None));
let mutex_generator = || { session.clone() };
let fwd = rx.map(Ok).forward(write);
let incoming = read.try_for_each(|msg| {
let incoming = read.try_for_each(async move |msg| {
let mutex = mutex_generator();
//dbg!(&mutex.lock().unwrap());
if let Message::Text(text) = msg {
if let Ok(notification) = serde_json::from_str::<ServerboundNotification>(&text) {
match notification {
ServerboundNotification::Authenticate(new_session) => {
if session.is_some() {
send(ClientboundNotification::Error(
WebSocketError::AlreadyAuthenticated,
));
return future::ok(());
{
if mutex.lock().unwrap().is_some() {
send(ClientboundNotification::Error(
WebSocketError::AlreadyAuthenticated,
));
return Ok(())
}
}
match task::block_on(
Auth::new(get_collection("accounts")).verify_session(new_session),
) {
Ok(validated_session) => {
match task::block_on(
Ref {
id: validated_session.user_id.clone(),
}
.fetch_user(),
) {
Ok(user) => {
if let Ok(mut map) = USERS.write() {
map.insert(validated_session.user_id.clone(), addr);
session = Some(validated_session);
if let Ok(_) = task::block_on(
subscriptions::generate_subscriptions(&user),
) {
send(ClientboundNotification::Authenticated);
send(ClientboundNotification::Ready { user });
} else {
send(ClientboundNotification::Error(
WebSocketError::InternalError,
));
}
} else {
if let Ok(validated_session) = Auth::new(get_collection("accounts"))
.verify_session(new_session)
.await {
let id = validated_session.user_id.clone();
if let Ok(user) = (
Ref {
id: id.clone()
}
)
.fetch_user()
.await {
let was_online = is_online(&id);
{
match USERS.write() {
Ok(mut map) => {
map.insert(id.clone(), addr);
}
Err(_) => {
send(ClientboundNotification::Error(
WebSocketError::InternalError,
WebSocketError::InternalError { at: "Writing users map.".to_string() },
));
return Ok(())
}
}
}
*mutex.lock().unwrap() = Some(validated_session);
if let Err(_) = subscriptions::generate_subscriptions(&user).await {
send(ClientboundNotification::Error(
WebSocketError::InternalError { at: "Generating subscriptions.".to_string() },
));
return Ok(())
}
send(ClientboundNotification::Authenticated);
match super::payload::generate_ready(user).await {
Ok(payload) => {
send(payload);
if !was_online {
ClientboundNotification::UserPresence {
id: id.clone(),
online: true
}
.publish(id)
.await
.ok();
}
}
Err(_) => {
send(ClientboundNotification::Error(
WebSocketError::OnboardingNotFinished,
WebSocketError::InternalError { at: "Generating payload.".to_string() },
));
return Ok(())
}
}
}
Err(_) => {
} else {
send(ClientboundNotification::Error(
WebSocketError::InvalidSession,
WebSocketError::OnboardingNotFinished,
));
}
} else {
send(ClientboundNotification::Error(
WebSocketError::InvalidSession,
));
}
}
}
}
}
future::ok(())
Ok(())
});
pin_mut!(fwd, incoming);
@@ -135,7 +169,8 @@ async fn accept(stream: TcpStream) {
info!("User {} disconnected.", &addr);
CONNECTIONS.lock().unwrap().remove(&addr);
if let Some(session) = session {
let session = session.lock().unwrap();
if let Some(session) = session.as_ref() {
let mut users = USERS.write().unwrap();
users.remove(&session.user_id, &addr);
if users.get_left(&session.user_id).is_none() {
@@ -173,3 +208,7 @@ pub fn publish(ids: Vec<String>, notification: ClientboundNotification) {
}
}
}
pub fn is_online(user: &String) -> bool {
USERS.read().unwrap().get_left(&user).is_some()
}

View File

@@ -1,6 +1,6 @@
use crate::database::entities::User;
use crate::database::get_collection;
use crate::database::*;
use crate::util::result::{Error, Result};
use mongodb::bson::doc;
use mongodb::options::{Collation, FindOneOptions};
use rauth::auth::Session;
@@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize};
use validator::Validate;
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();
}
#[derive(Validate, Serialize, Deserialize)]

View File

@@ -1,4 +1,5 @@
use crate::database::entities::User;
use crate::database::*;
use rauth::auth::Session;
use rocket_contrib::json::JsonValue;

View File

@@ -1,4 +1,6 @@
use crate::util::variables::{DISABLE_REGISTRATION, HCAPTCHA_SITEKEY, USE_EMAIL, USE_HCAPTCHA};
use crate::util::variables::{
DISABLE_REGISTRATION, EXTERNAL_WS_URL, HCAPTCHA_SITEKEY, USE_EMAIL, USE_HCAPTCHA,
};
use mongodb::bson::doc;
use rocket_contrib::json::JsonValue;
@@ -14,6 +16,7 @@ pub async fn root() -> JsonValue {
"key": HCAPTCHA_SITEKEY.to_string()
},
"email": *USE_EMAIL,
}
},
"ws": *EXTERNAL_WS_URL,
})
}

View File

@@ -1,41 +1,40 @@
use crate::{
database::{
entities::{RelationshipStatus, User},
get_collection,
guards::reference::Ref,
permissions::get_relationship,
},
util::result::Error,
};
use crate::{
notifications::{events::ClientboundNotification, hive},
util::result::Result,
};
use crate::database::*;
use crate::notifications::{events::ClientboundNotification, hive};
use crate::util::result::{Error, Result};
use futures::try_join;
use mongodb::bson::doc;
use mongodb::options::{FindOneOptions, Collation};
use mongodb::options::{Collation, FindOneOptions};
use rocket_contrib::json::JsonValue;
#[put("/<username>/friend")]
pub async fn req(user: User, username: String) -> Result<JsonValue> {
let col = get_collection("users");
let doc = col.find_one(
doc! {
"username": username
},
FindOneOptions::builder()
.collation(Collation::builder().locale("en").strength(2).build())
.build(),
)
.await
.map_err(|_| Error::DatabaseError { operation: "find_one", with: "user" })?
.ok_or_else(|| Error::UnknownUser)?;
let doc = col
.find_one(
doc! {
"username": username
},
FindOneOptions::builder()
.collation(Collation::builder().locale("en").strength(2).build())
.build(),
)
.await
.map_err(|_| Error::DatabaseError {
operation: "find_one",
with: "user",
})?
.ok_or_else(|| Error::UnknownUser)?;
let target_id = doc
.get_str("_id")
.map_err(|_| Error::DatabaseError { operation: "get_str(_id)", with: "user" })?;
let target_id = doc.get_str("_id").map_err(|_| Error::DatabaseError {
operation: "get_str(_id)",
with: "user",
})?;
match get_relationship(&user, &Ref { id: target_id.to_string() }) {
match get_relationship(
&user,
&target_id,
) {
RelationshipStatus::User => return Err(Error::NoEffect),
RelationshipStatus::Friend => return Err(Error::AlreadyFriends),
RelationshipStatus::Outgoing => return Err(Error::AlreadySentRequest),

View File

@@ -1,11 +1,7 @@
use crate::{
database::entities::RelationshipStatus, database::entities::User, database::get_collection,
database::guards::reference::Ref, database::permissions::get_relationship, util::result::Error,
};
use crate::{
notifications::{events::ClientboundNotification, hive},
util::result::Result,
};
use crate::database::*;
use crate::notifications::{events::ClientboundNotification, hive};
use crate::util::result::{Error, Result};
use futures::try_join;
use mongodb::bson::doc;
use rocket_contrib::json::JsonValue;
@@ -14,7 +10,7 @@ use rocket_contrib::json::JsonValue;
pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
let col = get_collection("users");
match get_relationship(&user, &target) {
match get_relationship(&user, &target.id) {
RelationshipStatus::User | RelationshipStatus::Blocked => Err(Error::NoEffect),
RelationshipStatus::BlockedOther => {
col.update_one(

View File

@@ -1,6 +1,6 @@
use crate::database::entities::User;
use crate::database::get_collection;
use crate::database::*;
use crate::util::result::{Error, Result};
use futures::StreamExt;
use mongodb::bson::doc;
use rocket_contrib::json::JsonValue;

View File

@@ -1,8 +1,9 @@
use crate::database::{entities::User, guards::reference::Ref, permissions::get_relationship};
use crate::database::*;
use crate::util::result::Result;
use rocket_contrib::json::JsonValue;
#[get("/<target>/relationship")]
pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
Ok(json!({ "status": get_relationship(&user, &target) }))
Ok(json!({ "status": get_relationship(&user, &target.id) }))
}

View File

@@ -1,5 +1,6 @@
use crate::database::entities::User;
use crate::database::*;
use crate::util::result::Result;
use rocket_contrib::json::JsonValue;
#[get("/relationships")]

View File

@@ -1,6 +1,6 @@
use crate::database::entities::User;
use crate::database::guards::reference::Ref;
use crate::{database::*, notifications::websocket::is_online};
use crate::util::result::{Error, Result};
use rocket_contrib::json::JsonValue;
#[get("/<target>")]
@@ -9,14 +9,29 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
if user.id != target.id {
// Check whether we are allowed to fetch this user.
let perm = crate::database::permissions::temp_calc_perm(&user, &target).await;
let perm = permissions::user::calculate(&user, &target.id).await;
if !perm.get_access() {
Err(Error::LabelMe)?
}
// Only return user relationships if the target is the caller.
target.relations = None;
// Add relevant relationship
if let Some(relationships) = &user.relations {
target.relationship = relationships
.iter()
.find(|x| x.id == user.id)
.map(|x| x.status.clone())
.or_else(|| Some(RelationshipStatus::None));
} else {
target.relationship = Some(RelationshipStatus::None);
}
} else {
target.relationship = Some(RelationshipStatus::User);
}
target.online = Some(is_online(&target.id));
Ok(json!(target))
}

View File

@@ -1,7 +1,43 @@
use rocket::response::NamedFile;
use std::path::Path;
#[get("/<_target>/avatar")]
pub async fn req(_target: String) -> Option<NamedFile> {
NamedFile::open(Path::new("avatar.png")).await.ok()
use crate::database::Ref;
#[get("/<target>/avatar")]
pub async fn req(target: Ref) -> Option<NamedFile> {
match target.id.chars().nth(25).unwrap() {
'0' |
'1' |
'2' |
'3' |
'4' |
'5' |
'6' |
'7' => NamedFile::open(Path::new("assets/user_red.png")).await.ok(),
'8' |
'9' |
'A' |
'C' |
'B' |
'D' |
'E' |
'F' => NamedFile::open(Path::new("assets/user_green.png")).await.ok(),
'G' |
'H' |
'J' |
'K' |
'M' |
'N' |
'P' |
'Q' => NamedFile::open(Path::new("assets/user_blue.png")).await.ok(),
'R' |
'S' |
'T' |
'V' |
'W' |
'X' |
'Y' |
'Z' => NamedFile::open(Path::new("assets/user_yellow.png")).await.ok(),
_ => unreachable!()
}
}

View File

@@ -1,12 +1,12 @@
use rocket::Route;
mod add_friend;
mod get_avatar;
mod block_user;
mod fetch_dms;
mod fetch_relationship;
mod fetch_relationships;
mod fetch_user;
mod get_avatar;
mod open_dm;
mod remove_friend;
mod unblock_user;
@@ -16,11 +16,9 @@ pub fn routes() -> Vec<Route> {
// User Information
fetch_user::req,
get_avatar::req,
// Direct Messaging
fetch_dms::req,
open_dm::req,
// Relationships
fetch_relationships::req,
fetch_relationship::req,

View File

@@ -1,7 +1,6 @@
use crate::database::entities::{Channel, User};
use crate::database::get_collection;
use crate::database::guards::reference::Ref;
use crate::database::*;
use crate::util::result::{Error, Result};
use mongodb::bson::doc;
use rocket_contrib::json::JsonValue;
use ulid::Ulid;

View File

@@ -1,11 +1,7 @@
use crate::{
database::entities::RelationshipStatus, database::entities::User, database::get_collection,
database::guards::reference::Ref, database::permissions::get_relationship, util::result::Error,
};
use crate::{
notifications::{events::ClientboundNotification, hive},
util::result::Result,
};
use crate::database::*;
use crate::notifications::{events::ClientboundNotification, hive};
use crate::util::result::{Error, Result};
use futures::try_join;
use hive_pubsub::PubSub;
use mongodb::bson::doc;
@@ -15,11 +11,7 @@ use rocket_contrib::json::JsonValue;
pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
let col = get_collection("users");
match get_relationship(&user, &target) {
RelationshipStatus::Blocked
| RelationshipStatus::BlockedOther
| RelationshipStatus::User
| RelationshipStatus::None => Err(Error::NoEffect),
match get_relationship(&user, &target.id) {
RelationshipStatus::Friend
| RelationshipStatus::Outgoing
| RelationshipStatus::Incoming => {
@@ -80,5 +72,6 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
}),
}
}
_ => Err(Error::NoEffect),
}
}

View File

@@ -1,11 +1,7 @@
use crate::{
database::entities::RelationshipStatus, database::entities::User, database::get_collection,
database::guards::reference::Ref, database::permissions::get_relationship, util::result::Error,
};
use crate::{
notifications::{events::ClientboundNotification, hive},
util::result::Result,
};
use crate::database::*;
use crate::notifications::{events::ClientboundNotification, hive};
use crate::util::result::{Error, Result};
use futures::try_join;
use hive_pubsub::PubSub;
use mongodb::bson::doc;
@@ -15,15 +11,9 @@ use rocket_contrib::json::JsonValue;
pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
let col = get_collection("users");
match get_relationship(&user, &target) {
RelationshipStatus::None
| RelationshipStatus::User
| RelationshipStatus::BlockedOther
| RelationshipStatus::Incoming
| RelationshipStatus::Outgoing
| RelationshipStatus::Friend => Err(Error::NoEffect),
match get_relationship(&user, &target.id) {
RelationshipStatus::Blocked => {
match get_relationship(&target.fetch_user().await?, &user.as_ref()) {
match get_relationship(&target.fetch_user().await?, &user.id) {
RelationshipStatus::Blocked => {
col.update_one(
doc! {
@@ -115,5 +105,6 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
_ => Err(Error::InternalError),
}
}
_ => Err(Error::NoEffect),
}
}

View File

@@ -9,6 +9,8 @@ lazy_static! {
env::var("REVOLT_MONGO_URI").expect("Missing REVOLT_MONGO_URI environment variable.");
pub static ref PUBLIC_URL: String =
env::var("REVOLT_PUBLIC_URL").expect("Missing REVOLT_PUBLIC_URL environment variable.");
pub static ref EXTERNAL_WS_URL: String =
env::var("REVOLT_EXTERNAL_WS_URL").expect("Missing REVOLT_EXTERNAL_WS_URL environment variable.");
pub static ref HCAPTCHA_KEY: String =
env::var("REVOLT_HCAPTCHA_KEY").unwrap_or_else(|_| "0x0000000000000000000000000000000000000000".to_string());
pub static ref HCAPTCHA_SITEKEY: String =
@@ -40,12 +42,8 @@ lazy_static! {
pub fn preflight_checks() {
if *USE_EMAIL == false {
#[cfg(not(debug_assertions))]
{
if !env::var("REVOLT_UNSAFE_NO_EMAIL").map_or(false, |v| v == *"1") {
panic!(
"Not letting you run this in production, set REVOLT_UNSAFE_NO_EMAIL=1 to run."
);
}
if !env::var("REVOLT_UNSAFE_NO_EMAIL").map_or(false, |v| v == *"1") {
panic!("Running in production without email is not recommended, set REVOLT_UNSAFE_NO_EMAIL=1 to override.");
}
#[cfg(debug_assertions)]
@@ -54,10 +52,8 @@ pub fn preflight_checks() {
if *USE_HCAPTCHA == false {
#[cfg(not(debug_assertions))]
{
if !env::var("REVOLT_UNSAFE_NO_CAPTCHA").map_or(false, |v| v == *"1") {
panic!("Not letting you run this in production, set REVOLT_UNSAFE_NO_CAPTCHA=1 to run.");
}
if !env::var("REVOLT_UNSAFE_NO_CAPTCHA").map_or(false, |v| v == *"1") {
panic!("Running in production without CAPTCHA is not recommended, set REVOLT_UNSAFE_NO_CAPTCHA=1 to override.");
}
#[cfg(debug_assertions)]