mirror of
https://github.com/stoatchat/stoatchat.git
synced 2026-07-12 05:57:00 +00:00
Compare commits
4 Commits
0.3.3-alph
...
0.3.3-alph
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2f14d2d37 | ||
|
|
a7ea29d821 | ||
|
|
b6b51bca26 | ||
|
|
2bafffbbc1 |
@@ -1,2 +1,3 @@
|
||||
target
|
||||
.mongo
|
||||
.env
|
||||
6
Cargo.lock
generated
6
Cargo.lock
generated
@@ -2308,8 +2308,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rauth"
|
||||
version = "0.2.4"
|
||||
source = "git+https://gitlab.insrt.uk/insert/rauth?rev=8c96882057f85d950578a6324abf1f7268862edd#8c96882057f85d950578a6324abf1f7268862edd"
|
||||
version = "0.2.5-alpha.0"
|
||||
source = "git+https://gitlab.insrt.uk/insert/rauth?rev=c52758a5087cd035b0ed9c6eacc942ba5468d2ce#c52758a5087cd035b0ed9c6eacc942ba5468d2ce"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"handlebars",
|
||||
@@ -2454,7 +2454,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "revolt"
|
||||
version = "0.3.3-alpha.2"
|
||||
version = "0.3.3-alpha.5"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"async-tungstenite",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "revolt"
|
||||
version = "0.3.3-alpha.2"
|
||||
version = "0.3.3-alpha.5"
|
||||
authors = ["Paul Makles <paulmakles@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
@@ -13,7 +13,7 @@ many-to-many = "0.1.2"
|
||||
ctrlc = { version = "3.0", features = ["termination"] }
|
||||
async-std = { version = "1.8.0", features = ["tokio02", "attributes"] }
|
||||
async-tungstenite = { version = "0.10.0", features = ["async-std-runtime"] }
|
||||
rauth = { git = "https://gitlab.insrt.uk/insert/rauth", rev = "8c96882057f85d950578a6324abf1f7268862edd" }
|
||||
rauth = { git = "https://gitlab.insrt.uk/insert/rauth", rev = "c52758a5087cd035b0ed9c6eacc942ba5468d2ce" }
|
||||
|
||||
hive_pubsub = { version = "0.4.3", features = ["mongo"] }
|
||||
rocket_cors = { git = "https://github.com/insertish/rocket_cors", branch = "master" }
|
||||
|
||||
21
src/database/entities/autumn.rs
Normal file
21
src/database/entities/autumn.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(tag = "type")]
|
||||
enum Metadata {
|
||||
File,
|
||||
Image { width: isize, height: isize },
|
||||
Video { width: isize, height: isize },
|
||||
Audio,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct File {
|
||||
#[serde(rename = "_id")]
|
||||
pub id: String,
|
||||
filename: String,
|
||||
metadata: Metadata,
|
||||
content_type: String,
|
||||
|
||||
message_id: Option<String>
|
||||
}
|
||||
@@ -19,6 +19,8 @@ pub struct Message {
|
||||
|
||||
pub content: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub attachment: Option<File>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub edited: Option<DateTime>,
|
||||
}
|
||||
|
||||
@@ -31,6 +33,7 @@ impl Message {
|
||||
author,
|
||||
|
||||
content,
|
||||
attachment: None,
|
||||
edited: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,10 @@ mod channel;
|
||||
mod guild;
|
||||
mod message;
|
||||
mod user;
|
||||
mod autumn;
|
||||
|
||||
pub use channel::*;
|
||||
pub use guild::*;
|
||||
pub use message::*;
|
||||
pub use user::*;
|
||||
pub use autumn::*;
|
||||
|
||||
74
src/routes/channels/message_query_stale.rs
Normal file
74
src/routes/channels/message_query_stale.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
use crate::database::*;
|
||||
use crate::util::result::{Error, Result};
|
||||
|
||||
use futures::StreamExt;
|
||||
use mongodb::bson::{doc, from_document};
|
||||
use rocket_contrib::json::{Json, JsonValue};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Options {
|
||||
ids: Vec<String>
|
||||
}
|
||||
|
||||
#[post("/<target>/messages/stale", data = "<data>")]
|
||||
pub async fn req(user: User, target: Ref, data: Json<Options>) -> Result<JsonValue> {
|
||||
if data.ids.len() > 150 {
|
||||
return Err(Error::LabelMe);
|
||||
}
|
||||
|
||||
let target = target.fetch_channel().await?;
|
||||
|
||||
let perm = permissions::PermissionCalculator::new(&user)
|
||||
.with_channel(&target)
|
||||
.for_channel()
|
||||
.await?;
|
||||
if !perm.get_view() {
|
||||
Err(Error::LabelMe)?
|
||||
}
|
||||
|
||||
let mut cursor = get_collection("messages")
|
||||
.find(
|
||||
doc! {
|
||||
"_id": {
|
||||
"$in": &data.ids
|
||||
},
|
||||
"channel": target.id()
|
||||
},
|
||||
None
|
||||
)
|
||||
.await
|
||||
.map_err(|_| Error::DatabaseError {
|
||||
operation: "find",
|
||||
with: "messages",
|
||||
})?;
|
||||
|
||||
let mut updated = vec![];
|
||||
let mut found_ids = vec![];
|
||||
while let Some(result) = cursor.next().await {
|
||||
if let Ok(doc) = result {
|
||||
let msg = from_document::<Message>(doc)
|
||||
.map_err(|_| Error::DatabaseError {
|
||||
operation: "from_document",
|
||||
with: "message",
|
||||
})?;
|
||||
|
||||
found_ids.push(msg.id.clone());
|
||||
if msg.edited.is_some() {
|
||||
updated.push(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut deleted = vec![];
|
||||
for id in &data.ids {
|
||||
if found_ids.iter().find(|x| *x == id).is_none() {
|
||||
deleted.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(json!({
|
||||
"updated": updated,
|
||||
"deleted": deleted
|
||||
}))
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::database::*;
|
||||
use crate::util::result::{Error, Result};
|
||||
|
||||
use mongodb::bson::doc;
|
||||
use mongodb::{bson::{doc, from_document}, options::FindOneOptions};
|
||||
use rocket_contrib::json::{Json, JsonValue};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ulid::Ulid;
|
||||
@@ -14,6 +14,8 @@ pub struct Data {
|
||||
// Maximum length of 36 allows both ULIDs and UUIDs.
|
||||
#[validate(length(min = 1, max = 36))]
|
||||
nonce: String,
|
||||
#[validate(length(min = 1, max = 128))]
|
||||
attachment: Option<String>,
|
||||
}
|
||||
|
||||
#[post("/<target>/messages", data = "<message>")]
|
||||
@@ -37,7 +39,9 @@ pub async fn req(user: User, target: Ref, message: Json<Data>) -> Result<JsonVal
|
||||
doc! {
|
||||
"nonce": &message.nonce
|
||||
},
|
||||
None,
|
||||
FindOneOptions::builder()
|
||||
.projection(doc! { "_id": 1 })
|
||||
.build()
|
||||
)
|
||||
.await
|
||||
.map_err(|_| Error::DatabaseError {
|
||||
@@ -49,12 +53,56 @@ pub async fn req(user: User, target: Ref, message: Json<Data>) -> Result<JsonVal
|
||||
Err(Error::DuplicateNonce)?
|
||||
}
|
||||
|
||||
let id = Ulid::new().to_string();
|
||||
let attachments = get_collection("attachments");
|
||||
let attachment = if let Some(attachment_id) = &message.attachment {
|
||||
if let Some(doc) = attachments
|
||||
.find_one(
|
||||
doc! {
|
||||
"_id": attachment_id,
|
||||
"message_id": {
|
||||
"$exists": false
|
||||
}
|
||||
},
|
||||
None
|
||||
)
|
||||
.await
|
||||
.map_err(|_| Error::DatabaseError {
|
||||
operation: "find_one",
|
||||
with: "attachment",
|
||||
})? {
|
||||
let attachment = from_document::<File>(doc)
|
||||
.map_err(|_| Error::DatabaseError { operation: "from_document", with: "attachment" })?;
|
||||
|
||||
attachments.update_one(
|
||||
doc! {
|
||||
"_id": &attachment.id
|
||||
},
|
||||
doc! {
|
||||
"$set": {
|
||||
"message_id": &id
|
||||
}
|
||||
},
|
||||
None
|
||||
)
|
||||
.await
|
||||
.map_err(|_| Error::DatabaseError { operation: "update_one", with: "attachment" })?;
|
||||
|
||||
Some(attachment)
|
||||
} else {
|
||||
return Err(Error::UnknownAttachment)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let msg = Message {
|
||||
id: Ulid::new().to_string(),
|
||||
id,
|
||||
channel: target.id().to_string(),
|
||||
author: user.id,
|
||||
|
||||
content: message.content.clone(),
|
||||
attachment,
|
||||
nonce: Some(message.nonce.clone()),
|
||||
edited: None,
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@ mod message_delete;
|
||||
mod message_edit;
|
||||
mod message_fetch;
|
||||
mod message_query;
|
||||
mod message_query_stale;
|
||||
mod message_send;
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
@@ -17,6 +18,7 @@ pub fn routes() -> Vec<Route> {
|
||||
delete_channel::req,
|
||||
message_send::req,
|
||||
message_query::req,
|
||||
message_query_stale::req,
|
||||
message_fetch::req,
|
||||
message_edit::req,
|
||||
message_delete::req,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::util::variables::{
|
||||
DISABLE_REGISTRATION, EXTERNAL_WS_URL, HCAPTCHA_SITEKEY, INVITE_ONLY, USE_EMAIL, USE_HCAPTCHA,
|
||||
DISABLE_REGISTRATION, EXTERNAL_WS_URL, HCAPTCHA_SITEKEY, INVITE_ONLY, USE_EMAIL, USE_HCAPTCHA, USE_AUTUMN, AUTUMN_URL
|
||||
};
|
||||
|
||||
use mongodb::bson::doc;
|
||||
@@ -8,7 +8,7 @@ use rocket_contrib::json::JsonValue;
|
||||
#[get("/")]
|
||||
pub async fn root() -> JsonValue {
|
||||
json!({
|
||||
"revolt": "0.3.3-alpha.2",
|
||||
"revolt": "0.3.3-alpha.5",
|
||||
"features": {
|
||||
"registration": !*DISABLE_REGISTRATION,
|
||||
"captcha": {
|
||||
@@ -16,7 +16,11 @@ pub async fn root() -> JsonValue {
|
||||
"key": HCAPTCHA_SITEKEY.to_string()
|
||||
},
|
||||
"email": *USE_EMAIL,
|
||||
"invite_only": *INVITE_ONLY
|
||||
"invite_only": *INVITE_ONLY,
|
||||
"autumn": {
|
||||
"enabled": *USE_AUTUMN,
|
||||
"url": *AUTUMN_URL
|
||||
}
|
||||
},
|
||||
"ws": *EXTERNAL_WS_URL,
|
||||
})
|
||||
|
||||
41
src/routes/users/find_mutual.rs
Normal file
41
src/routes/users/find_mutual.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
use crate::database::*;
|
||||
use crate::util::result::{Error, Result};
|
||||
|
||||
use futures::StreamExt;
|
||||
use mongodb::options::FindOptions;
|
||||
use mongodb::bson::{Document, doc};
|
||||
use rocket_contrib::json::JsonValue;
|
||||
|
||||
#[get("/<target>/mutual")]
|
||||
pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
|
||||
let channels = get_collection("channels")
|
||||
.find(
|
||||
doc! {
|
||||
"$or": [
|
||||
{ "type": "Group" },
|
||||
],
|
||||
"$and": [
|
||||
{ "recipients": &user.id },
|
||||
{ "recipients": &target.id }
|
||||
]
|
||||
},
|
||||
FindOptions::builder()
|
||||
.projection(doc! { "_id": 1 })
|
||||
.build()
|
||||
)
|
||||
.await
|
||||
.map_err(|_| Error::DatabaseError {
|
||||
operation: "find",
|
||||
with: "channels",
|
||||
})?
|
||||
.filter_map(async move |s| s.ok())
|
||||
.collect::<Vec<Document>>()
|
||||
.await
|
||||
.into_iter()
|
||||
.filter_map(|x| x.get_str("_id").ok().map(|x| x.to_string()))
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
Ok(json!({
|
||||
"channels": channels
|
||||
}))
|
||||
}
|
||||
@@ -6,6 +6,7 @@ mod fetch_dms;
|
||||
mod fetch_relationship;
|
||||
mod fetch_relationships;
|
||||
mod fetch_user;
|
||||
mod find_mutual;
|
||||
mod get_avatar;
|
||||
mod get_default_avatar;
|
||||
mod open_dm;
|
||||
@@ -22,6 +23,7 @@ pub fn routes() -> Vec<Route> {
|
||||
fetch_dms::req,
|
||||
open_dm::req,
|
||||
// Relationships
|
||||
find_mutual::req,
|
||||
fetch_relationships::req,
|
||||
fetch_relationship::req,
|
||||
add_friend::req,
|
||||
|
||||
@@ -36,6 +36,8 @@ pub enum Error {
|
||||
// ? Channel related errors.
|
||||
#[snafu(display("This channel does not exist!"))]
|
||||
UnknownChannel,
|
||||
#[snafu(display("Attachment does not exist!"))]
|
||||
UnknownAttachment,
|
||||
#[snafu(display("Cannot edit someone else's message."))]
|
||||
CannotEditMessage,
|
||||
#[snafu(display("Cannot remove yourself from a group, use delete channel instead."))]
|
||||
@@ -84,6 +86,7 @@ impl<'r> Responder<'r, 'static> for Error {
|
||||
Error::NotFriends => Status::Forbidden,
|
||||
|
||||
Error::UnknownChannel => Status::NotFound,
|
||||
Error::UnknownAttachment => Status::BadRequest,
|
||||
Error::CannotEditMessage => Status::Forbidden,
|
||||
Error::CannotRemoveYourself => Status::BadRequest,
|
||||
Error::GroupTooLarge { .. } => Status::Forbidden,
|
||||
|
||||
@@ -7,18 +7,20 @@ lazy_static! {
|
||||
// Application Settings
|
||||
pub static ref MONGO_URI: String =
|
||||
env::var("REVOLT_MONGO_URI").expect("Missing REVOLT_MONGO_URI environment variable.");
|
||||
pub static ref WS_HOST: String =
|
||||
env::var("REVOLT_WS_HOST").unwrap_or_else(|_| "0.0.0.0:9000".to_string());
|
||||
pub static ref PUBLIC_URL: String =
|
||||
env::var("REVOLT_PUBLIC_URL").expect("Missing REVOLT_PUBLIC_URL environment variable.");
|
||||
pub static ref APP_URL: String =
|
||||
env::var("REVOLT_APP_URL").unwrap_or_else(|_| "https://app.revolt.chat".to_string());
|
||||
pub static ref EXTERNAL_WS_URL: String =
|
||||
env::var("REVOLT_EXTERNAL_WS_URL").expect("Missing REVOLT_EXTERNAL_WS_URL environment variable.");
|
||||
pub static ref AUTUMN_URL: String =
|
||||
env::var("AUTUMN_PUBLIC_URL").unwrap_or_else(|_| "https://example.com".to_string());
|
||||
pub static ref HCAPTCHA_KEY: String =
|
||||
env::var("REVOLT_HCAPTCHA_KEY").unwrap_or_else(|_| "0x0000000000000000000000000000000000000000".to_string());
|
||||
pub static ref HCAPTCHA_SITEKEY: String =
|
||||
env::var("REVOLT_HCAPTCHA_SITEKEY").unwrap_or_else(|_| "10000000-ffff-ffff-ffff-000000000001".to_string());
|
||||
pub static ref WS_HOST: String =
|
||||
env::var("REVOLT_WS_HOST").unwrap_or_else(|_| "0.0.0.0:9000".to_string());
|
||||
|
||||
// Application Flags
|
||||
pub static ref DISABLE_REGISTRATION: bool = env::var("REVOLT_DISABLE_REGISTRATION").map_or(false, |v| v == "1");
|
||||
@@ -32,6 +34,7 @@ lazy_static! {
|
||||
);
|
||||
pub static ref USE_HCAPTCHA: bool = env::var("REVOLT_HCAPTCHA_KEY").is_ok();
|
||||
pub static ref USE_PROMETHEUS: bool = env::var("REVOLT_ENABLE_PROMETHEUS").map_or(false, |v| v == "1");
|
||||
pub static ref USE_AUTUMN: bool = env::var("AUTUMN_PUBLIC_URL").is_ok();
|
||||
|
||||
// SMTP Settings
|
||||
pub static ref SMTP_HOST: String =
|
||||
|
||||
Reference in New Issue
Block a user