Compare commits
18
Commits
20220606-1
...
20220612-4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11d89b3bf0 | ||
|
|
64bb171cc8 | ||
|
|
6de5ad15c5 | ||
|
|
b5ab16d66f | ||
|
|
b9aad6d38c | ||
|
|
ed5c8159e9 | ||
|
|
2e6e1fdd44 | ||
|
|
cb2e6fb2f7 | ||
|
|
d069acd5a5 | ||
|
|
f8b5c25c17 | ||
|
|
80af18761f | ||
|
|
98e10c3478 | ||
|
|
2051c8ce45 | ||
|
|
0abc12e5fa | ||
|
|
aaceb5bdee | ||
|
|
3de7ee4c7b | ||
|
|
d660127c14 | ||
|
|
7390b3c087 |
No files matched your search
@@ -0,0 +1,79 @@
|
|||||||
|
# MongoDB URI
|
||||||
|
MONGODB=mongodb://localhost
|
||||||
|
|
||||||
|
# URL to where the Revolt app is publicly accessible
|
||||||
|
REVOLT_APP_URL=http://local.revolt.chat:5000
|
||||||
|
|
||||||
|
# URL to where the API is publicly accessible
|
||||||
|
REVOLT_PUBLIC_URL=http://local.revolt.chat:8000
|
||||||
|
VITE_API_URL=http://local.revolt.chat:8000
|
||||||
|
|
||||||
|
# URL to where the WebSocket server is publicly accessible
|
||||||
|
REVOLT_EXTERNAL_WS_URL=ws://local.revolt.chat:9000
|
||||||
|
|
||||||
|
# URL to where Autumn is publicly available
|
||||||
|
AUTUMN_PUBLIC_URL=http://local.revolt.chat:3000
|
||||||
|
|
||||||
|
# URL to where January is publicly available
|
||||||
|
JANUARY_PUBLIC_URL=http://local.revolt.chat:7000
|
||||||
|
|
||||||
|
# URL to where Vortex is publicly available
|
||||||
|
# VOSO_PUBLIC_URL=https://voso.revolt.chat
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
## hCaptcha Settings
|
||||||
|
##
|
||||||
|
|
||||||
|
# If you are sure that you don't want to use hCaptcha, set to 1.
|
||||||
|
REVOLT_UNSAFE_NO_CAPTCHA=1
|
||||||
|
|
||||||
|
# hCaptcha API key
|
||||||
|
# REVOLT_HCAPTCHA_KEY=0x0000000000000000000000000000000000000000
|
||||||
|
|
||||||
|
# hCaptcha site key
|
||||||
|
# REVOLT_HCAPTCHA_SITEKEY=10000000-ffff-ffff-ffff-000000000001
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
## Email Settings
|
||||||
|
##
|
||||||
|
|
||||||
|
# If you are sure that you don't want to use email verification, set to 1.
|
||||||
|
REVOLT_UNSAFE_NO_EMAIL=1
|
||||||
|
|
||||||
|
# SMTP host
|
||||||
|
# REVOLT_SMTP_HOST=smtp.example.com
|
||||||
|
|
||||||
|
# SMTP username
|
||||||
|
# REVOLT_SMTP_USERNAME=noreply@example.com
|
||||||
|
|
||||||
|
# SMTP password
|
||||||
|
# REVOLT_SMTP_PASSWORD=CHANGEME
|
||||||
|
|
||||||
|
# SMTP From header
|
||||||
|
# REVOLT_SMTP_FROM=Revolt <noreply@example.com>
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
## Application Settings
|
||||||
|
##
|
||||||
|
|
||||||
|
# Whether to only allow users to sign up if they have an invite code
|
||||||
|
REVOLT_INVITE_ONLY=0
|
||||||
|
|
||||||
|
# Maximum number of people that can be in a group chat
|
||||||
|
REVOLT_MAX_GROUP_SIZE=150
|
||||||
|
|
||||||
|
# VAPID keys for push notifications
|
||||||
|
# Generate using this guide: https://gitlab.insrt.uk/revolt/delta/-/wikis/vapid
|
||||||
|
# --> Please replace these keys before going into production! <--
|
||||||
|
REVOLT_VAPID_PRIVATE_KEY=LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUJSUWpyTWxLRnBiVWhsUHpUbERvcEliYk1yeVNrNXpKYzVYVzIxSjJDS3hvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFWnkrQkg2TGJQZ2hEa3pEempXOG0rUXVPM3pCajRXT1phdkR6ZU00c0pqbmFwd1psTFE0WAp1ZDh2TzVodU94QWhMQlU3WWRldVovWHlBdFpWZmNyQi9BPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
|
||||||
|
REVOLT_VAPID_PUBLIC_KEY=BGcvgR-i2z4IQ5Mw841vJvkLjt8wY-FjmWrw83jOLCY52qcGZS0OF7nfLzuYbjsQISwVO2HXrmf18gLWVX3Kwfw=
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
## Vortex configuration
|
||||||
|
##
|
||||||
|
|
||||||
|
# VOSO_MANAGE_TOKEN=CHANGEME
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
Rocket.toml
|
Rocket.toml
|
||||||
target
|
target
|
||||||
|
.data
|
||||||
Generated
+251
-793
File diff suppressed because it is too large.
Load diff
@@ -14,7 +14,7 @@ mod websocket;
|
|||||||
#[async_std::main]
|
#[async_std::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
// Configure requirements for Bonfire.
|
// Configure requirements for Bonfire.
|
||||||
let _guard = revolt_quark::setup_logging();
|
revolt_quark::configure!();
|
||||||
database::connect().await;
|
database::connect().await;
|
||||||
|
|
||||||
// Clean up the current region information.
|
// Clean up the current region information.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-delta"
|
name = "revolt-delta"
|
||||||
version = "0.5.3-6"
|
version = "0.5.3-7"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = ["Paul Makles <paulmakles@gmail.com>"]
|
authors = ["Paul Makles <paulmakles@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
@@ -18,7 +18,6 @@ linkify = "0.6.0"
|
|||||||
once_cell = "1.4.1"
|
once_cell = "1.4.1"
|
||||||
env_logger = "0.7.1"
|
env_logger = "0.7.1"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
ctrlc = { version = "3.0", features = ["termination"] }
|
|
||||||
|
|
||||||
# Lang. Utilities
|
# Lang. Utilities
|
||||||
regex = "1"
|
regex = "1"
|
||||||
@@ -44,7 +43,6 @@ async-std = { version = "1.8.0", features = ["tokio1", "tokio02", "attributes"]
|
|||||||
|
|
||||||
# internal util
|
# internal util
|
||||||
lettre = "0.10.0-alpha.4"
|
lettre = "0.10.0-alpha.4"
|
||||||
rauth = { git = "https://github.com/insertish/rauth", rev = "001a9698c56cea79e69e4ae71d7bc2cb48aec1a6" }
|
|
||||||
|
|
||||||
# redis
|
# redis
|
||||||
redis = { version = "0.21.2", features = ["async-std-comp"] }
|
redis = { version = "0.21.2", features = ["async-std-comp"] }
|
||||||
@@ -52,15 +50,14 @@ mobc = { version = "0.7.3" }
|
|||||||
mobc-redis = { version = "0.7.0", default-features = false, features = ["async-std-comp"] }
|
mobc-redis = { version = "0.7.0", default-features = false, features = ["async-std-comp"] }
|
||||||
|
|
||||||
# web
|
# web
|
||||||
rocket_empty = { git = "https://github.com/insertish/rocket_empty", branch = "rc1" }
|
rocket = { version = "0.5.0-rc.2", default-features = false, features = ["json"] }
|
||||||
rocket = { version = "0.5.0-rc.1", default-features = false, features = ["json"] }
|
rocket_empty = { git = "https://github.com/insertish/rocket_empty", branch = "master" }
|
||||||
mongodb = { version = "1.2.2", features = ["async-std-runtime"], default-features = false }
|
rocket_rauth = { git = "https://github.com/insertish/rauth", rev = "ab7cc5a9739f1147375d3a559250f4be2a051565" }
|
||||||
rocket_cors = { git = "https://github.com/lawliet89/rocket_cors", rev = "5843861a88958c16bfaa0b40f0d8910772bcd2f6" }
|
|
||||||
|
|
||||||
# spec generation
|
# spec generation
|
||||||
schemars = "0.8.8"
|
schemars = "0.8.8"
|
||||||
# rocket_okapi = "0.8.0-rc.1"
|
# rocket_okapi = "0.8.0-rc.1"
|
||||||
rocket_okapi = { git = "https://github.com/insertish/okapi", rev = "dcf0df115596ee07a587a7a543cddf3d7944645b", features = [ "swagger" ] }
|
rocket_okapi = { git = "https://github.com/insertish/okapi", rev = "a1048d0c8cd771e424ec97d33d825c32e06aa120", features = [ "swagger" ] }
|
||||||
|
|
||||||
# quark
|
# quark
|
||||||
revolt-quark = { path = "../quark" }
|
revolt-quark = { path = "../quark" }
|
||||||
+21
-119
@@ -6,144 +6,46 @@ extern crate rocket_okapi;
|
|||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
extern crate ctrlc;
|
|
||||||
|
|
||||||
pub mod routes;
|
pub mod routes;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
pub mod version;
|
|
||||||
|
|
||||||
use log::info;
|
use revolt_quark::rauth::RAuth;
|
||||||
use rauth::{
|
|
||||||
config::{Captcha, Config, EmailVerification, SMTPSettings, Template, Templates},
|
|
||||||
logic::Auth,
|
|
||||||
};
|
|
||||||
use revolt_quark::variables::delta::{
|
|
||||||
APP_URL, HCAPTCHA_KEY, INVITE_ONLY, SMTP_FROM, SMTP_HOST, SMTP_PASSWORD, SMTP_USERNAME,
|
|
||||||
USE_EMAIL, USE_HCAPTCHA,
|
|
||||||
};
|
|
||||||
use revolt_quark::DatabaseInfo;
|
use revolt_quark::DatabaseInfo;
|
||||||
use rocket_cors::AllowedOrigins;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
#[async_std::main]
|
#[launch]
|
||||||
async fn main() {
|
async fn rocket() -> _ {
|
||||||
let _guard = revolt_quark::setup_logging();
|
// Configure logging and environment
|
||||||
|
revolt_quark::configure!();
|
||||||
info!(
|
|
||||||
"Starting Revolt server [version {}].",
|
|
||||||
crate::version::VERSION
|
|
||||||
);
|
|
||||||
|
|
||||||
|
// Ensure environment variables are present
|
||||||
revolt_quark::variables::delta::preflight_checks();
|
revolt_quark::variables::delta::preflight_checks();
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
// Setup database
|
||||||
ctrlc::set_handler(move || {
|
|
||||||
// Force ungraceful exit to avoid hang.
|
|
||||||
std::process::exit(0);
|
|
||||||
})
|
|
||||||
.expect("Error setting Ctrl-C handler");
|
|
||||||
|
|
||||||
let cors = rocket_cors::CorsOptions {
|
|
||||||
allowed_origins: AllowedOrigins::All,
|
|
||||||
allowed_methods: [
|
|
||||||
"Get", "Put", "Post", "Delete", "Options", "Head", "Trace", "Connect", "Patch",
|
|
||||||
]
|
|
||||||
.iter()
|
|
||||||
.map(|s| FromStr::from_str(s).unwrap())
|
|
||||||
.collect(),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.to_cors()
|
|
||||||
.expect("Failed to create CORS.");
|
|
||||||
|
|
||||||
let mut config = Config {
|
|
||||||
email_verification: if *USE_EMAIL {
|
|
||||||
EmailVerification::Enabled {
|
|
||||||
smtp: SMTPSettings {
|
|
||||||
from: (*SMTP_FROM).to_string(),
|
|
||||||
host: (*SMTP_HOST).to_string(),
|
|
||||||
username: (*SMTP_USERNAME).to_string(),
|
|
||||||
password: (*SMTP_PASSWORD).to_string(),
|
|
||||||
reply_to: Some("support@revolt.chat".into()),
|
|
||||||
port: None,
|
|
||||||
use_tls: None,
|
|
||||||
},
|
|
||||||
expiry: Default::default(),
|
|
||||||
templates: Templates {
|
|
||||||
verify: Template {
|
|
||||||
title: "Verify your Revolt account.".into(),
|
|
||||||
text: include_str!(crate::asset!("templates/verify.txt")).into(),
|
|
||||||
url: format!("{}/login/verify/", *APP_URL),
|
|
||||||
html: None,
|
|
||||||
},
|
|
||||||
reset: Template {
|
|
||||||
title: "Reset your Revolt password.".into(),
|
|
||||||
text: include_str!(crate::asset!("templates/reset.txt")).into(),
|
|
||||||
url: format!("{}/login/reset/", *APP_URL),
|
|
||||||
html: None,
|
|
||||||
},
|
|
||||||
welcome: None,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
EmailVerification::Disabled
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
if *INVITE_ONLY {
|
|
||||||
config.invite_only = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if *USE_HCAPTCHA {
|
|
||||||
config.captcha = Captcha::HCaptcha {
|
|
||||||
secret: HCAPTCHA_KEY.clone(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let db = DatabaseInfo::Auto.connect().await.unwrap();
|
let db = DatabaseInfo::Auto.connect().await.unwrap();
|
||||||
db.migrate_database().await.unwrap();
|
db.migrate_database().await.unwrap();
|
||||||
|
|
||||||
// This is entirely temporary code until rauth is migrated to quark.
|
// Setup rAuth
|
||||||
// (and / or otherwise gets updated to MongoDB v2 driver)
|
let rauth = RAuth {
|
||||||
let mongo_db = mongodb::Client::with_uri_str(
|
database: db.clone().into(),
|
||||||
&std::env::var("MONGODB").unwrap_or_else(|_| "mongodb://localhost".to_string()),
|
config: revolt_quark::util::rauth::config(),
|
||||||
)
|
};
|
||||||
.await
|
|
||||||
.expect("Failed to init db connection.");
|
|
||||||
|
|
||||||
rauth::entities::sync_models(&mongo_db.database("revolt")).await;
|
|
||||||
|
|
||||||
// Launch background task workers.
|
// Launch background task workers.
|
||||||
async_std::task::spawn(revolt_quark::tasks::start_workers(db.clone()));
|
async_std::task::spawn(revolt_quark::tasks::start_workers(db.clone()));
|
||||||
|
|
||||||
let auth = Auth::new(mongo_db.database("revolt"), config);
|
// Configure CORS
|
||||||
|
let cors = revolt_quark::web::cors::new();
|
||||||
|
|
||||||
|
// Configure Rocket
|
||||||
let rocket = rocket::build();
|
let rocket = rocket::build();
|
||||||
routes::mount(rocket)
|
routes::mount(rocket)
|
||||||
.mount("/", rocket_cors::catch_all_options_routes())
|
.mount("/", revolt_quark::web::cors::catch_all_options_routes())
|
||||||
.mount("/", util::ratelimiter::routes())
|
.mount("/", revolt_quark::web::ratelimiter::routes())
|
||||||
.mount(
|
.mount("/swagger/", revolt_quark::web::swagger::routes())
|
||||||
"/swagger/",
|
.manage(rauth)
|
||||||
rocket_okapi::swagger_ui::make_swagger_ui(&rocket_okapi::swagger_ui::SwaggerUIConfig {
|
|
||||||
url: "../openapi.json".to_owned(),
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.manage(auth)
|
|
||||||
.manage(db)
|
.manage(db)
|
||||||
.manage(cors.clone())
|
.manage(cors.clone())
|
||||||
.attach(util::ratelimiter::RatelimitFairing)
|
.attach(revolt_quark::web::ratelimiter::RatelimitFairing)
|
||||||
.attach(cors)
|
.attach(cors)
|
||||||
.launch()
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve asset
|
|
||||||
macro_rules! asset {
|
|
||||||
($path:literal) => {
|
|
||||||
concat!(env!("CARGO_MANIFEST_DIR"), "/assets/", $path)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) use asset;
|
|
||||||
@@ -5,7 +5,9 @@ use revolt_quark::{
|
|||||||
message::{Masquerade, Reply, SendableEmbed},
|
message::{Masquerade, Reply, SendableEmbed},
|
||||||
Message, User,
|
Message, User,
|
||||||
},
|
},
|
||||||
perms, Db, Error, Permission, Ref, Result,
|
perms,
|
||||||
|
web::idempotency::IdempotencyKey,
|
||||||
|
Db, Error, Permission, Ref, Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
@@ -14,8 +16,6 @@ use serde::{Deserialize, Serialize};
|
|||||||
use ulid::Ulid;
|
use ulid::Ulid;
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|
||||||
use crate::util::idempotency::IdempotencyKey;
|
|
||||||
|
|
||||||
#[derive(Validate, Serialize, Deserialize, JsonSchema)]
|
#[derive(Validate, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct DataMessageSend {
|
pub struct DataMessageSend {
|
||||||
/// Unique token to prevent duplicate message sending
|
/// Unique token to prevent duplicate message sending
|
||||||
@@ -134,7 +134,19 @@ pub async fn message_send(
|
|||||||
.replace(replies.into_iter().collect::<Vec<String>>());
|
.replace(replies.into_iter().collect::<Vec<String>>());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Add attachments to message.
|
// 4. Process included embeds.
|
||||||
|
let mut embeds = vec![];
|
||||||
|
if let Some(sendable_embeds) = data.embeds {
|
||||||
|
for sendable_embed in sendable_embeds {
|
||||||
|
embeds.push(sendable_embed.into_embed(db, message_id.clone()).await?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !embeds.is_empty() {
|
||||||
|
message.embeds.replace(embeds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Add attachments to message.
|
||||||
let mut attachments = vec![];
|
let mut attachments = vec![];
|
||||||
if let Some(ids) = &data.attachments {
|
if let Some(ids) = &data.attachments {
|
||||||
if !ids.is_empty() {
|
if !ids.is_empty() {
|
||||||
@@ -160,18 +172,6 @@ pub async fn message_send(
|
|||||||
message.attachments.replace(attachments);
|
message.attachments.replace(attachments);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Process included embeds.
|
|
||||||
let mut embeds = vec![];
|
|
||||||
if let Some(sendable_embeds) = data.embeds {
|
|
||||||
for sendable_embed in sendable_embeds {
|
|
||||||
embeds.push(sendable_embed.into_embed(db, message_id.clone()).await?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !embeds.is_empty() {
|
|
||||||
message.embeds.replace(embeds);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Set content
|
// 6. Set content
|
||||||
message.content = data.content;
|
message.content = data.content;
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,9 @@ pub fn mount(mut rocket: Rocket<Build>) -> Rocket<Build> {
|
|||||||
"/channels" => channels::routes(),
|
"/channels" => channels::routes(),
|
||||||
"/servers" => servers::routes(),
|
"/servers" => servers::routes(),
|
||||||
"/invites" => invites::routes(),
|
"/invites" => invites::routes(),
|
||||||
"/auth/account" => rauth::web::account::routes(),
|
"/auth/account" => rocket_rauth::routes::account::routes(),
|
||||||
"/auth/session" => rauth::web::session::routes(),
|
"/auth/session" => rocket_rauth::routes::session::routes(),
|
||||||
|
"/auth/mfa" => rocket_rauth::routes::mfa::routes(),
|
||||||
"/onboard" => onboard::routes(),
|
"/onboard" => onboard::routes(),
|
||||||
"/push" => push::routes(),
|
"/push" => push::routes(),
|
||||||
"/sync" => sync::routes(),
|
"/sync" => sync::routes(),
|
||||||
@@ -100,7 +101,8 @@ fn custom_openapi_spec() -> OpenApi {
|
|||||||
"tags": [
|
"tags": [
|
||||||
"Account",
|
"Account",
|
||||||
"Session",
|
"Session",
|
||||||
"Onboarding"
|
"Onboarding",
|
||||||
|
"MFA"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -117,7 +119,7 @@ fn custom_openapi_spec() -> OpenApi {
|
|||||||
openapi: OpenApi::default_version(),
|
openapi: OpenApi::default_version(),
|
||||||
info: Info {
|
info: Info {
|
||||||
title: "Revolt API".to_owned(),
|
title: "Revolt API".to_owned(),
|
||||||
description: Some("User-first privacy focused chat platform.".to_owned()),
|
description: Some("Open source user-first chat platform.".to_owned()),
|
||||||
terms_of_service: Some("https://revolt.chat/terms".to_owned()),
|
terms_of_service: Some("https://revolt.chat/terms".to_owned()),
|
||||||
contact: Some(Contact {
|
contact: Some(Contact {
|
||||||
name: Some("Revolt Support".to_owned()),
|
name: Some("Revolt Support".to_owned()),
|
||||||
@@ -130,7 +132,7 @@ fn custom_openapi_spec() -> OpenApi {
|
|||||||
url: Some("https://github.com/revoltchat/delta/blob/master/LICENSE".to_owned()),
|
url: Some("https://github.com/revoltchat/delta/blob/master/LICENSE".to_owned()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
version: "0.5.3-rc.1".to_owned(),
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
servers: vec![
|
servers: vec![
|
||||||
@@ -242,6 +244,11 @@ fn custom_openapi_spec() -> OpenApi {
|
|||||||
description: Some("Create and manage sessions".to_owned()),
|
description: Some("Create and manage sessions".to_owned()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
Tag {
|
||||||
|
name: "MFA".to_owned(),
|
||||||
|
description: Some("Multi-factor Authentication".to_owned()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
Tag {
|
Tag {
|
||||||
name: "Onboarding".to_owned(),
|
name: "Onboarding".to_owned(),
|
||||||
description: Some(
|
description: Some(
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use crate::util::regex::RE_USERNAME;
|
use crate::util::regex::RE_USERNAME;
|
||||||
use revolt_quark::{models::User, Database, EmptyResponse, Error, Result};
|
use revolt_quark::{models::User, rauth::models::Session, Database, EmptyResponse, Error, Result};
|
||||||
|
|
||||||
use rauth::entities::Session;
|
|
||||||
use rocket::{serde::json::Json, State};
|
use rocket::{serde::json::Json, State};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use rauth::entities::Session;
|
use revolt_quark::{models::User, rauth::models::Session};
|
||||||
use revolt_quark::models::User;
|
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
use revolt_quark::{EmptyResponse, Error, Result};
|
use revolt_quark::{
|
||||||
|
rauth::{
|
||||||
use rauth::{
|
models::{Session, WebPushSubscription},
|
||||||
entities::{Model, Session, WebPushSubscription},
|
RAuth,
|
||||||
logic::Auth,
|
},
|
||||||
|
EmptyResponse, Error, Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
use rocket::{serde::json::Json, State};
|
use rocket::{serde::json::Json, State};
|
||||||
|
|
||||||
/// # Push Subscribe
|
/// # Push Subscribe
|
||||||
@@ -14,13 +16,13 @@ use rocket::{serde::json::Json, State};
|
|||||||
#[openapi(tag = "Web Push")]
|
#[openapi(tag = "Web Push")]
|
||||||
#[post("/subscribe", data = "<data>")]
|
#[post("/subscribe", data = "<data>")]
|
||||||
pub async fn req(
|
pub async fn req(
|
||||||
auth: &State<Auth>,
|
rauth: &State<RAuth>,
|
||||||
mut session: Session,
|
mut session: Session,
|
||||||
data: Json<WebPushSubscription>,
|
data: Json<WebPushSubscription>,
|
||||||
) -> Result<EmptyResponse> {
|
) -> Result<EmptyResponse> {
|
||||||
session.subscription = Some(data.into_inner());
|
session.subscription = Some(data.into_inner());
|
||||||
session
|
session
|
||||||
.save(&auth.db, None)
|
.save(&rauth)
|
||||||
.await
|
.await
|
||||||
.map(|_| EmptyResponse)
|
.map(|_| EmptyResponse)
|
||||||
.map_err(|_| Error::DatabaseError {
|
.map_err(|_| Error::DatabaseError {
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
use revolt_quark::{EmptyResponse, Error, Result};
|
use revolt_quark::{
|
||||||
|
rauth::{models::Session, RAuth},
|
||||||
use rauth::{
|
EmptyResponse, Error, Result,
|
||||||
entities::{Model, Session},
|
|
||||||
logic::Auth,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use rocket::State;
|
use rocket::State;
|
||||||
|
|
||||||
/// # Unsubscribe
|
/// # Unsubscribe
|
||||||
@@ -11,10 +10,10 @@ use rocket::State;
|
|||||||
/// Remove the Web Push subscription associated with the current session.
|
/// Remove the Web Push subscription associated with the current session.
|
||||||
#[openapi(tag = "Web Push")]
|
#[openapi(tag = "Web Push")]
|
||||||
#[post("/unsubscribe")]
|
#[post("/unsubscribe")]
|
||||||
pub async fn req(auth: &State<Auth>, mut session: Session) -> Result<EmptyResponse> {
|
pub async fn req(rauth: &State<RAuth>, mut session: Session) -> Result<EmptyResponse> {
|
||||||
session.subscription = None;
|
session.subscription = None;
|
||||||
session
|
session
|
||||||
.save(&auth.db, None)
|
.save(&rauth)
|
||||||
.await
|
.await
|
||||||
.map(|_| EmptyResponse)
|
.map(|_| EmptyResponse)
|
||||||
.map_err(|_| Error::DatabaseError {
|
.map_err(|_| Error::DatabaseError {
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ pub struct RevoltConfig {
|
|||||||
#[get("/")]
|
#[get("/")]
|
||||||
pub async fn root() -> Result<Json<RevoltConfig>> {
|
pub async fn root() -> Result<Json<RevoltConfig>> {
|
||||||
Ok(Json(RevoltConfig {
|
Ok(Json(RevoltConfig {
|
||||||
revolt: crate::version::VERSION.to_string(),
|
revolt: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
features: RevoltFeatures {
|
features: RevoltFeatures {
|
||||||
captcha: CaptchaFeature {
|
captcha: CaptchaFeature {
|
||||||
enabled: *USE_HCAPTCHA,
|
enabled: *USE_HCAPTCHA,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use revolt_quark::models::User;
|
use revolt_quark::models::User;
|
||||||
use revolt_quark::r#impl::generic::users::user_settings::UserSettingsImpl;
|
use revolt_quark::r#impl::UserSettingsImpl;
|
||||||
use revolt_quark::{Db, EmptyResponse, Result};
|
use revolt_quark::{Db, EmptyResponse, Result};
|
||||||
|
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
|
|||||||
@@ -1,16 +1,20 @@
|
|||||||
use revolt_quark::models::User;
|
use revolt_quark::models::User;
|
||||||
use revolt_quark::{Database, Error, Result};
|
use revolt_quark::{Database, Error, Ref, Result};
|
||||||
|
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
use rocket::State;
|
use rocket::State;
|
||||||
|
|
||||||
/// # Send Friend Request / Accept Request
|
/// # Accept Friend Request
|
||||||
///
|
///
|
||||||
/// Send a friend request to another user or accept another user's friend request.
|
/// Accept another user's friend request.
|
||||||
#[openapi(tag = "Relationships")]
|
#[openapi(tag = "Relationships")]
|
||||||
#[put("/<username>/friend")]
|
#[put("/<target>/friend")]
|
||||||
pub async fn req(db: &State<Database>, user: User, username: String) -> Result<Json<User>> {
|
pub async fn req(db: &State<Database>, user: User, target: Ref) -> Result<Json<User>> {
|
||||||
let mut target = db.fetch_user_by_username(&username).await?;
|
let mut target = if let Ok(user) = db.fetch_user_by_username(&target.id).await {
|
||||||
|
user
|
||||||
|
} else {
|
||||||
|
target.as_user(db).await?
|
||||||
|
};
|
||||||
|
|
||||||
if user.bot.is_some() || target.bot.is_some() {
|
if user.bot.is_some() || target.bot.is_some() {
|
||||||
return Err(Error::IsBot);
|
return Err(Error::IsBot);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use crate::util::regex::RE_USERNAME;
|
use crate::util::regex::RE_USERNAME;
|
||||||
use rauth::entities::Account;
|
use revolt_quark::{models::User, rauth::models::Account, Database, Error, Result};
|
||||||
use revolt_quark::{models::User, Database, Error, Result};
|
|
||||||
use rocket::{serde::json::Json, State};
|
use rocket::{serde::json::Json, State};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|||||||
@@ -59,18 +59,6 @@ impl rocket_okapi::response::OpenApiResponderInner for CachedFile {
|
|||||||
pub async fn req(target: String) -> CachedFile {
|
pub async fn req(target: String) -> CachedFile {
|
||||||
CachedFile((
|
CachedFile((
|
||||||
ContentType::PNG,
|
ContentType::PNG,
|
||||||
match target.chars().last().unwrap() {
|
revolt_quark::util::pfp::avatar(target.chars().last().unwrap()),
|
||||||
// 0123456789ABCDEFGHJKMNPQRSTVWXYZ
|
|
||||||
'0' | '1' | '2' | '3' | 'S' | 'Z' => {
|
|
||||||
include_bytes!(crate::asset!("user/2.png")).to_vec()
|
|
||||||
}
|
|
||||||
'4' | '5' | '6' | '7' | 'T' => include_bytes!(crate::asset!("user/3.png")).to_vec(),
|
|
||||||
'8' | '9' | 'A' | 'B' => include_bytes!(crate::asset!("user/4.png")).to_vec(),
|
|
||||||
'C' | 'D' | 'E' | 'F' | 'V' => include_bytes!(crate::asset!("user/5.png")).to_vec(),
|
|
||||||
'G' | 'H' | 'J' | 'K' | 'W' => include_bytes!(crate::asset!("user/6.png")).to_vec(),
|
|
||||||
'M' | 'N' | 'P' | 'Q' | 'X' => include_bytes!(crate::asset!("user/7.png")).to_vec(),
|
|
||||||
/*'0' | '1' | '2' | '3' | 'R' | 'Y'*/
|
|
||||||
_ => include_bytes!(crate::asset!("user/1.png")).to_vec(),
|
|
||||||
},
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -13,6 +13,7 @@ mod find_mutual;
|
|||||||
mod get_default_avatar;
|
mod get_default_avatar;
|
||||||
mod open_dm;
|
mod open_dm;
|
||||||
mod remove_friend;
|
mod remove_friend;
|
||||||
|
mod send_friend_request;
|
||||||
mod unblock_user;
|
mod unblock_user;
|
||||||
|
|
||||||
pub fn routes() -> (Vec<Route>, OpenApi) {
|
pub fn routes() -> (Vec<Route>, OpenApi) {
|
||||||
@@ -33,5 +34,6 @@ pub fn routes() -> (Vec<Route>, OpenApi) {
|
|||||||
remove_friend::req,
|
remove_friend::req,
|
||||||
block_user::req,
|
block_user::req,
|
||||||
unblock_user::req,
|
unblock_user::req,
|
||||||
|
send_friend_request::req,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
use revolt_quark::models::User;
|
||||||
|
use revolt_quark::{Database, Error, Result};
|
||||||
|
|
||||||
|
use rocket::serde::json::Json;
|
||||||
|
use rocket::State;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// # User Lookup Information
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema)]
|
||||||
|
pub struct DataSendFriendRequest {
|
||||||
|
username: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Send Friend Request
|
||||||
|
///
|
||||||
|
/// Send a friend request to another user.
|
||||||
|
#[openapi(tag = "Relationships")]
|
||||||
|
#[post("/friend", data = "<data>")]
|
||||||
|
pub async fn req(
|
||||||
|
db: &State<Database>,
|
||||||
|
user: User,
|
||||||
|
data: Json<DataSendFriendRequest>,
|
||||||
|
) -> Result<Json<User>> {
|
||||||
|
let mut target = db.fetch_user_by_username(&data.username).await?;
|
||||||
|
|
||||||
|
if user.bot.is_some() || target.bot.is_some() {
|
||||||
|
return Err(Error::IsBot);
|
||||||
|
}
|
||||||
|
|
||||||
|
user.add_friend(db, &mut target).await?;
|
||||||
|
Ok(Json(target.with_auto_perspective(db, &user).await))
|
||||||
|
}
|
||||||
@@ -1,3 +1 @@
|
|||||||
pub mod idempotency;
|
|
||||||
pub mod ratelimiter;
|
|
||||||
pub mod regex;
|
pub mod regex;
|
||||||
@@ -1 +0,0 @@
|
|||||||
pub const VERSION: &str = "0.5.3-5-patch.2";
|
|
||||||
+26
-8
@@ -8,8 +8,20 @@ edition = "2021"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
mongo = [ "mongodb" ]
|
mongo = [ "mongodb" ]
|
||||||
rocket_impl = [ "rocket", "rocket_empty" ]
|
rocket_impl = [
|
||||||
test = [ "async-std", "mongo", "mongodb/async-std-runtime", "rocket_impl", "rauth" ]
|
"rocket",
|
||||||
|
"rocket_empty",
|
||||||
|
"rocket_cors",
|
||||||
|
|
||||||
|
"lru",
|
||||||
|
"dashmap",
|
||||||
|
|
||||||
|
"rauth/database-mongodb",
|
||||||
|
"rauth/rocket_impl",
|
||||||
|
"rauth/okapi_impl"
|
||||||
|
]
|
||||||
|
|
||||||
|
test = [ "async-std", "mongo", "mongodb/async-std-runtime", "rocket_impl" ]
|
||||||
default = [ "test" ]
|
default = [ "test" ]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
@@ -26,8 +38,8 @@ bson = { version = "2.1.0", features = ["chrono-0_4"] }
|
|||||||
|
|
||||||
# Spec Generation
|
# Spec Generation
|
||||||
schemars = "0.8.8"
|
schemars = "0.8.8"
|
||||||
okapi = { git = "https://github.com/insertish/okapi", rev = "dcf0df115596ee07a587a7a543cddf3d7944645b" }
|
okapi = { git = "https://github.com/insertish/okapi", rev = "a1048d0c8cd771e424ec97d33d825c32e06aa120" }
|
||||||
rocket_okapi = { git = "https://github.com/insertish/okapi", rev = "dcf0df115596ee07a587a7a543cddf3d7944645b" }
|
rocket_okapi = { git = "https://github.com/insertish/okapi", rev = "a1048d0c8cd771e424ec97d33d825c32e06aa120" }
|
||||||
# okapi = "0.7.0-rc.1"
|
# okapi = "0.7.0-rc.1"
|
||||||
# rocket_okapi = "0.8.0-rc.1"
|
# rocket_okapi = "0.8.0-rc.1"
|
||||||
|
|
||||||
@@ -58,15 +70,21 @@ reqwest = "0.11.10"
|
|||||||
bitfield = "0.13.2"
|
bitfield = "0.13.2"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
|
||||||
|
lru = { version = "0.7.6", optional = true }
|
||||||
|
dashmap = { version = "5.2.0", optional = true }
|
||||||
|
|
||||||
# Web Push
|
# Web Push
|
||||||
base64 = "0.13.0"
|
base64 = "0.13.0"
|
||||||
web-push = "0.7.2"
|
web-push = "0.7.2"
|
||||||
|
|
||||||
# Implementations
|
# Implementations
|
||||||
rauth = { optional = true, git = "https://github.com/insertish/rauth", rev = "001a9698c56cea79e69e4ae71d7bc2cb48aec1a6" }
|
rocket_http = { optional = true, version = "0.5.0-rc.2" }
|
||||||
rocket = { optional = true, version = "=0.5.0-rc.1", default-features = false, features = ["json"] }
|
rocket = { optional = true, version = "0.5.0-rc.2", default-features = false, features = ["json"] }
|
||||||
rocket_http = { optional = true, version = "=0.5.0-rc.1" }
|
rocket_empty = { optional = true, git = "https://github.com/insertish/rocket_empty", branch = "master" }
|
||||||
rocket_empty = { optional = true, git = "https://github.com/insertish/rocket_empty", branch = "rc1" }
|
rocket_cors = { optional = true, git = "https://github.com/lawliet89/rocket_cors", rev = "5843861a88958c16bfaa0b40f0d8910772bcd2f6" }
|
||||||
|
|
||||||
|
# rAuth
|
||||||
|
rauth = { git = "https://github.com/insertish/rauth", rev = "ab7cc5a9739f1147375d3a559250f4be2a051565", features = [ "async-std-runtime" ] }
|
||||||
|
|
||||||
# Sentry
|
# Sentry
|
||||||
sentry = "0.25.0"
|
sentry = "0.25.0"
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
You requested to have your account deleted, if you did not perform this action please take measures to secure your account immediately.
|
||||||
|
|
||||||
|
Confirm here: {{url}}
|
||||||
|
|
||||||
|
Sent by Revolt.
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,8 +1,7 @@
|
|||||||
use std::env;
|
use std::env;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use crate::r#impl::dummy::DummyDb;
|
use crate::r#impl::{DummyDb, MongoDb};
|
||||||
use crate::r#impl::mongo::MongoDb;
|
|
||||||
use crate::AbstractDatabase;
|
use crate::AbstractDatabase;
|
||||||
|
|
||||||
/// Database information to use to create a client
|
/// Database information to use to create a client
|
||||||
@@ -61,3 +60,14 @@ impl Deref for Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Database> for rauth::Database {
|
||||||
|
fn from(val: Database) -> Self {
|
||||||
|
match val {
|
||||||
|
Database::Dummy(_) => rauth::Database::default(),
|
||||||
|
Database::MongoDb(MongoDb(client)) => {
|
||||||
|
rauth::Database::MongoDb(rauth::database::MongoDb(client.database("revolt")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ use std::collections::HashSet;
|
|||||||
|
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use ulid::Ulid;
|
use ulid::Ulid;
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
events::client::EventV1,
|
events::client::EventV1,
|
||||||
@@ -17,7 +18,7 @@ use crate::{
|
|||||||
january::{Embed, Text},
|
january::{Embed, Text},
|
||||||
push::PushNotification,
|
push::PushNotification,
|
||||||
},
|
},
|
||||||
Database, Result,
|
Database, Error, Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl Message {
|
impl Message {
|
||||||
@@ -232,6 +233,9 @@ impl From<SystemMessage> for String {
|
|||||||
|
|
||||||
impl SendableEmbed {
|
impl SendableEmbed {
|
||||||
pub async fn into_embed(self, db: &Database, message_id: String) -> Result<Embed> {
|
pub async fn into_embed(self, db: &Database, message_id: String) -> Result<Embed> {
|
||||||
|
self.validate()
|
||||||
|
.map_err(|error| Error::FailedValidation { error })?;
|
||||||
|
|
||||||
let media = if let Some(id) = self.media {
|
let media = if let Some(id) = self.media {
|
||||||
Some(
|
Some(
|
||||||
db.find_and_use_attachment(&id, "attachments", "message", &message_id)
|
db.find_and_use_attachment(&id, "attachments", "message", &message_id)
|
||||||
|
|||||||
@@ -8,9 +8,6 @@ use crate::{perms, Database, Error, Result};
|
|||||||
|
|
||||||
use futures::try_join;
|
use futures::try_join;
|
||||||
use impl_ops::impl_op_ex_commutative;
|
use impl_ops::impl_op_ex_commutative;
|
||||||
use okapi::openapi3::{SecurityScheme, SecuritySchemeData};
|
|
||||||
use rocket_okapi::gen::OpenApiGenerator;
|
|
||||||
use rocket_okapi::request::{OpenApiFromRequest, RequestHeaderInput};
|
|
||||||
use std::ops;
|
use std::ops;
|
||||||
|
|
||||||
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 });
|
||||||
@@ -155,8 +152,36 @@ impl User {
|
|||||||
|
|
||||||
/// Update a user's username
|
/// Update a user's username
|
||||||
pub async fn update_username(&mut self, db: &Database, username: String) -> Result<()> {
|
pub async fn update_username(&mut self, db: &Database, username: String) -> Result<()> {
|
||||||
|
// Trim surrounding spaces
|
||||||
let username = username.trim().to_string();
|
let username = username.trim().to_string();
|
||||||
|
|
||||||
|
// Make sure username is still at least 3 characters
|
||||||
|
if username.len() < 2 {
|
||||||
|
return Err(Error::InvalidUsername);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the username for validation
|
||||||
|
let username_lowercase = username.to_lowercase();
|
||||||
|
|
||||||
|
// Ensure the username itself isn't blocked
|
||||||
|
const BLOCKED_USERNAMES: &[&str] = &["admin", "revolt"];
|
||||||
|
|
||||||
|
for username in BLOCKED_USERNAMES {
|
||||||
|
if username_lowercase == *username {
|
||||||
|
return Err(Error::InvalidUsername);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure none of the following substrings show up in the username
|
||||||
|
const BLOCKED_SUBSTRINGS: &[&str] = &["@", "#", ":", "```", "\n"];
|
||||||
|
|
||||||
|
for substr in BLOCKED_SUBSTRINGS {
|
||||||
|
if username_lowercase.contains(substr) {
|
||||||
|
return Err(Error::InvalidUsername);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the username isn't taken
|
||||||
if db.is_username_taken(&username).await? {
|
if db.is_username_taken(&username).await? {
|
||||||
return Err(Error::UsernameTaken);
|
return Err(Error::UsernameTaken);
|
||||||
}
|
}
|
||||||
@@ -359,72 +384,3 @@ impl User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use rauth::entities::Session;
|
|
||||||
use rocket::http::Status;
|
|
||||||
use rocket::request::{self, FromRequest, Outcome, Request};
|
|
||||||
|
|
||||||
#[rocket::async_trait]
|
|
||||||
impl<'r> FromRequest<'r> for User {
|
|
||||||
type Error = rauth::util::Error;
|
|
||||||
|
|
||||||
async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
|
||||||
let user: &Option<User> = request
|
|
||||||
.local_cache_async(async {
|
|
||||||
let db = request
|
|
||||||
.rocket()
|
|
||||||
.state::<Database>()
|
|
||||||
.expect("Database state not reachable!");
|
|
||||||
|
|
||||||
let header_bot_token = request
|
|
||||||
.headers()
|
|
||||||
.get("x-bot-token")
|
|
||||||
.next()
|
|
||||||
.map(|x| x.to_string());
|
|
||||||
|
|
||||||
if let Some(bot_token) = header_bot_token {
|
|
||||||
if let Ok(user) = User::from_token(db, &bot_token, UserHint::Bot).await {
|
|
||||||
return Some(user);
|
|
||||||
}
|
|
||||||
} else if let Outcome::Success(session) = request.guard::<Session>().await {
|
|
||||||
// This uses a guard so can't really easily be refactored into from_token at this stage.
|
|
||||||
if let Ok(user) = db.fetch_user(&session.user_id).await {
|
|
||||||
return Some(user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
|
|
||||||
if let Some(user) = user {
|
|
||||||
Outcome::Success(user.clone())
|
|
||||||
} else {
|
|
||||||
Outcome::Failure((Status::Unauthorized, rauth::util::Error::InvalidSession))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'r> OpenApiFromRequest<'r> for User {
|
|
||||||
fn from_request_input(
|
|
||||||
_gen: &mut OpenApiGenerator,
|
|
||||||
_name: String,
|
|
||||||
_required: bool,
|
|
||||||
) -> rocket_okapi::Result<RequestHeaderInput> {
|
|
||||||
let mut requirements = schemars::Map::new();
|
|
||||||
requirements.insert("Api Key".to_owned(), vec![]);
|
|
||||||
|
|
||||||
Ok(RequestHeaderInput::Security(
|
|
||||||
"Api Key".to_owned(),
|
|
||||||
SecurityScheme {
|
|
||||||
data: SecuritySchemeData::ApiKey {
|
|
||||||
name: "x-session-token".to_owned(),
|
|
||||||
location: "header".to_owned(),
|
|
||||||
},
|
|
||||||
description: Some("Session Token".to_owned()),
|
|
||||||
extensions: schemars::Map::new(),
|
|
||||||
},
|
|
||||||
requirements,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,10 @@
|
|||||||
pub mod dummy;
|
mod dummy;
|
||||||
pub mod generic;
|
mod generic;
|
||||||
pub mod mongo;
|
mod mongo;
|
||||||
|
|
||||||
|
#[cfg(feature = "rocket_impl")]
|
||||||
|
mod rocket;
|
||||||
|
|
||||||
|
pub use self::generic::users::user_settings::UserSettingsImpl;
|
||||||
|
pub use dummy::DummyDb;
|
||||||
|
pub use mongo::MongoDb;
|
||||||
@@ -2,7 +2,6 @@ use crate::r#impl::mongo::MongoDb;
|
|||||||
|
|
||||||
use super::scripts::LATEST_REVISION;
|
use super::scripts::LATEST_REVISION;
|
||||||
|
|
||||||
use log::info;
|
|
||||||
use mongodb::bson::doc;
|
use mongodb::bson::doc;
|
||||||
use mongodb::options::CreateCollectionOptions;
|
use mongodb::options::CreateCollectionOptions;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ use std::time::Duration;
|
|||||||
|
|
||||||
use bson::Bson;
|
use bson::Bson;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use log::info;
|
|
||||||
use mongodb::{
|
use mongodb::{
|
||||||
bson::{doc, from_bson, from_document, to_document, Document},
|
bson::{doc, from_bson, from_document, to_document, Document},
|
||||||
options::FindOptions,
|
options::FindOptions,
|
||||||
@@ -17,7 +16,7 @@ struct MigrationInfo {
|
|||||||
revision: i32,
|
revision: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const LATEST_REVISION: i32 = 15;
|
pub const LATEST_REVISION: i32 = 16;
|
||||||
|
|
||||||
pub async fn migrate_database(db: &MongoDb) {
|
pub async fn migrate_database(db: &MongoDb) {
|
||||||
let migrations = db.col::<Document>("migrations");
|
let migrations = db.col::<Document>("migrations");
|
||||||
@@ -603,6 +602,15 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if revision <= 15 {
|
||||||
|
info!("Running migration [revision 15 / 04-06-2022]: Migrate rAuth to latest version.");
|
||||||
|
|
||||||
|
let db = rauth::Database::MongoDb(rauth::database::MongoDb(db.db()));
|
||||||
|
db.run_migration(rauth::Migration::M2022_06_03EnsureUpToSpec)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
// Need to migrate fields on attachments, change `user_id`, `object_id`, etc to `parent`.
|
// Need to migrate fields on attachments, change `user_id`, `object_id`, etc to `parent`.
|
||||||
|
|
||||||
// Reminder to update LATEST_REVISION when adding new migrations.
|
// Reminder to update LATEST_REVISION when adding new migrations.
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
use okapi::openapi3::{SecurityScheme, SecuritySchemeData};
|
||||||
|
use rauth::models::Session;
|
||||||
|
use rocket_okapi::gen::OpenApiGenerator;
|
||||||
|
use rocket_okapi::request::{OpenApiFromRequest, RequestHeaderInput};
|
||||||
|
|
||||||
|
use rocket::http::Status;
|
||||||
|
use rocket::request::{self, FromRequest, Outcome, Request};
|
||||||
|
|
||||||
|
use crate::models::user::UserHint;
|
||||||
|
use crate::models::User;
|
||||||
|
use crate::Database;
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
|
impl<'r> FromRequest<'r> for User {
|
||||||
|
type Error = rauth::Error;
|
||||||
|
|
||||||
|
async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||||
|
let user: &Option<User> = request
|
||||||
|
.local_cache_async(async {
|
||||||
|
let db = request.rocket().state::<Database>().expect("`Database`");
|
||||||
|
|
||||||
|
let header_bot_token = request
|
||||||
|
.headers()
|
||||||
|
.get("x-bot-token")
|
||||||
|
.next()
|
||||||
|
.map(|x| x.to_string());
|
||||||
|
|
||||||
|
if let Some(bot_token) = header_bot_token {
|
||||||
|
if let Ok(user) = User::from_token(db, &bot_token, UserHint::Bot).await {
|
||||||
|
return Some(user);
|
||||||
|
}
|
||||||
|
} else if let Outcome::Success(session) = request.guard::<Session>().await {
|
||||||
|
// This uses a guard so can't really easily be refactored into from_token at this stage.
|
||||||
|
if let Ok(user) = db.fetch_user(&session.user_id).await {
|
||||||
|
return Some(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
if let Some(user) = user {
|
||||||
|
Outcome::Success(user.clone())
|
||||||
|
} else {
|
||||||
|
Outcome::Failure((Status::Unauthorized, rauth::Error::InvalidSession))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'r> OpenApiFromRequest<'r> for User {
|
||||||
|
fn from_request_input(
|
||||||
|
_gen: &mut OpenApiGenerator,
|
||||||
|
_name: String,
|
||||||
|
_required: bool,
|
||||||
|
) -> rocket_okapi::Result<RequestHeaderInput> {
|
||||||
|
let mut requirements = schemars::Map::new();
|
||||||
|
requirements.insert("Session Token".to_owned(), vec![]);
|
||||||
|
|
||||||
|
Ok(RequestHeaderInput::Security(
|
||||||
|
"Session Token".to_owned(),
|
||||||
|
SecurityScheme {
|
||||||
|
data: SecuritySchemeData::ApiKey {
|
||||||
|
name: "x-session-token".to_owned(),
|
||||||
|
location: "header".to_owned(),
|
||||||
|
},
|
||||||
|
description: Some("Used to authenticate as a user.".to_owned()),
|
||||||
|
extensions: schemars::Map::new(),
|
||||||
|
},
|
||||||
|
requirements,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
-33
@@ -15,9 +15,10 @@ extern crate lazy_static;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate bitfield;
|
extern crate bitfield;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub extern crate bson;
|
extern crate bson;
|
||||||
|
|
||||||
pub use iso8601_timestamp::Timestamp;
|
pub use iso8601_timestamp::Timestamp;
|
||||||
|
pub use rauth;
|
||||||
pub use redis_kiss;
|
pub use redis_kiss;
|
||||||
|
|
||||||
pub mod events;
|
pub mod events;
|
||||||
@@ -26,11 +27,14 @@ pub mod models;
|
|||||||
pub mod presence;
|
pub mod presence;
|
||||||
pub mod tasks;
|
pub mod tasks;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
pub mod util;
|
||||||
|
|
||||||
|
#[cfg(feature = "rocket_impl")]
|
||||||
|
pub mod web;
|
||||||
|
|
||||||
mod database;
|
mod database;
|
||||||
mod permissions;
|
mod permissions;
|
||||||
mod traits;
|
mod traits;
|
||||||
mod util;
|
|
||||||
|
|
||||||
pub use database::*;
|
pub use database::*;
|
||||||
pub use traits::*;
|
pub use traits::*;
|
||||||
@@ -38,38 +42,20 @@ pub use traits::*;
|
|||||||
pub use permissions::defn::*;
|
pub use permissions::defn::*;
|
||||||
pub use permissions::{get_relationship, perms};
|
pub use permissions::{get_relationship, perms};
|
||||||
|
|
||||||
pub use util::r#ref::Ref;
|
pub use util::{
|
||||||
pub use util::result::{Error, Result};
|
r#ref::Ref,
|
||||||
pub use util::variables;
|
result::{Error, Result},
|
||||||
|
variables,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "rocket_impl")]
|
#[cfg(feature = "rocket_impl")]
|
||||||
pub use rocket_empty::EmptyResponse;
|
pub use web::{Db, EmptyResponse};
|
||||||
|
|
||||||
#[cfg(feature = "rocket_impl")]
|
/// Resolve asset
|
||||||
use rocket::State;
|
macro_rules! asset {
|
||||||
|
($path:literal) => {
|
||||||
#[cfg(feature = "rocket_impl")]
|
concat!(env!("CARGO_MANIFEST_DIR"), "/assets/", $path)
|
||||||
pub type Db = State<Database>;
|
};
|
||||||
|
|
||||||
/// Configure logging and common Rust variables
|
|
||||||
pub fn setup_logging() -> sentry::ClientInitGuard {
|
|
||||||
dotenv::dotenv().ok();
|
|
||||||
|
|
||||||
if std::env::var("RUST_LOG").is_err() {
|
|
||||||
std::env::set_var("RUST_LOG", "info");
|
|
||||||
}
|
|
||||||
|
|
||||||
if std::env::var("ROCKET_ADDRESS").is_err() {
|
|
||||||
std::env::set_var("ROCKET_ADDRESS", "0.0.0.0");
|
|
||||||
}
|
|
||||||
|
|
||||||
pretty_env_logger::init();
|
|
||||||
|
|
||||||
sentry::init((
|
|
||||||
"https://62fd0e02c5354905b4e286757f4beb16@sentry.insert.moe/4",
|
|
||||||
sentry::ClientOptions {
|
|
||||||
release: sentry::release_name!(),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) use asset;
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
use crate::{models::channel::PartialChannel, Database};
|
use crate::{models::channel::PartialChannel, Database};
|
||||||
|
|
||||||
use deadqueue::limited::Queue;
|
use deadqueue::limited::Queue;
|
||||||
use log::info;
|
|
||||||
use mongodb::bson::doc;
|
use mongodb::bson::doc;
|
||||||
use std::{collections::HashMap, time::Duration};
|
use std::{collections::HashMap, time::Duration};
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ pub async fn start_workers(db: Database) {
|
|||||||
task::spawn(ack::worker(db.clone()));
|
task::spawn(ack::worker(db.clone()));
|
||||||
task::spawn(last_message_id::worker(db.clone()));
|
task::spawn(last_message_id::worker(db.clone()));
|
||||||
task::spawn(process_embeds::worker(db.clone()));
|
task::spawn(process_embeds::worker(db.clone()));
|
||||||
task::spawn(web_push::worker(db.clone()));
|
task::spawn(web_push::worker(db.clone().into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use deadqueue::limited::Queue;
|
use deadqueue::limited::Queue;
|
||||||
use log::error;
|
|
||||||
|
|
||||||
/// Task information
|
/// Task information
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
|
use crate::bson::doc;
|
||||||
use crate::util::variables::delta::VAPID_PRIVATE_KEY;
|
use crate::util::variables::delta::VAPID_PRIVATE_KEY;
|
||||||
use crate::{bson::doc, r#impl::mongo::MongoDb, Database};
|
|
||||||
|
|
||||||
use deadqueue::limited::Queue;
|
use deadqueue::limited::Queue;
|
||||||
use futures::StreamExt;
|
use rauth::Database;
|
||||||
use rauth::entities::Session;
|
|
||||||
use web_push::{
|
use web_push::{
|
||||||
ContentEncoding, SubscriptionInfo, SubscriptionKeys, VapidSignatureBuilder, WebPushClient,
|
ContentEncoding, SubscriptionInfo, SubscriptionKeys, VapidSignatureBuilder, WebPushClient,
|
||||||
WebPushMessageBuilder,
|
WebPushMessageBuilder,
|
||||||
@@ -43,90 +42,69 @@ pub async fn worker(db: Database) {
|
|||||||
let key = base64::decode_config(VAPID_PRIVATE_KEY.clone(), base64::URL_SAFE)
|
let key = base64::decode_config(VAPID_PRIVATE_KEY.clone(), base64::URL_SAFE)
|
||||||
.expect("valid `VAPID_PRIVATE_KEY`");
|
.expect("valid `VAPID_PRIVATE_KEY`");
|
||||||
|
|
||||||
if let Database::MongoDb(MongoDb(db)) = db {
|
loop {
|
||||||
loop {
|
let task = Q.pop().await;
|
||||||
let task = Q.pop().await;
|
|
||||||
|
|
||||||
// ! FIXME: this is hard-coded until rauth is merged into quark
|
if let Ok(sessions) = db.find_sessions_with_subscription(&task.recipients).await {
|
||||||
if let Ok(mut cursor) = db
|
for session in sessions {
|
||||||
.database("revolt")
|
if let Some(sub) = session.subscription {
|
||||||
.collection::<Session>("sessions")
|
let subscription = SubscriptionInfo {
|
||||||
.find(
|
endpoint: sub.endpoint,
|
||||||
doc! {
|
keys: SubscriptionKeys {
|
||||||
"user_id": {
|
auth: sub.auth,
|
||||||
"$in": task.recipients
|
p256dh: sub.p256dh,
|
||||||
},
|
},
|
||||||
"subscription": {
|
};
|
||||||
"$exists": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
while let Some(Ok(session)) = cursor.next().await {
|
|
||||||
if let Some(sub) = session.subscription {
|
|
||||||
let subscription = SubscriptionInfo {
|
|
||||||
endpoint: sub.endpoint,
|
|
||||||
keys: SubscriptionKeys {
|
|
||||||
auth: sub.auth,
|
|
||||||
p256dh: sub.p256dh,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
match WebPushMessageBuilder::new(&subscription) {
|
match WebPushMessageBuilder::new(&subscription) {
|
||||||
Ok(mut builder) => {
|
Ok(mut builder) => {
|
||||||
match VapidSignatureBuilder::from_pem(
|
match VapidSignatureBuilder::from_pem(
|
||||||
std::io::Cursor::new(&key),
|
std::io::Cursor::new(&key),
|
||||||
&subscription,
|
&subscription,
|
||||||
) {
|
) {
|
||||||
Ok(sig_builder) => match sig_builder.build() {
|
Ok(sig_builder) => match sig_builder.build() {
|
||||||
Ok(signature) => {
|
Ok(signature) => {
|
||||||
builder.set_vapid_signature(signature);
|
builder.set_vapid_signature(signature);
|
||||||
builder.set_payload(
|
builder.set_payload(
|
||||||
ContentEncoding::AesGcm,
|
ContentEncoding::AesGcm,
|
||||||
task.payload.as_bytes(),
|
task.payload.as_bytes(),
|
||||||
);
|
);
|
||||||
|
|
||||||
match builder.build() {
|
match builder.build() {
|
||||||
Ok(msg) => match client.send(msg).await {
|
Ok(msg) => match client.send(msg).await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
info!(
|
info!(
|
||||||
"Sent Web Push notification to {:?}.",
|
"Sent Web Push notification to {:?}.",
|
||||||
session.id
|
session.id
|
||||||
)
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
error!(
|
|
||||||
"Hit error sending Web Push! {:?}",
|
|
||||||
err
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
error!(
|
|
||||||
"Failed to build message for {}! {:?}",
|
|
||||||
session.user_id, err
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("Hit error sending Web Push! {:?}", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
error!(
|
||||||
|
"Failed to build message for {}! {:?}",
|
||||||
|
session.user_id, err
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => error!(
|
}
|
||||||
"Failed to build signature for {}! {:?}",
|
|
||||||
session.user_id, err
|
|
||||||
),
|
|
||||||
},
|
|
||||||
Err(err) => error!(
|
Err(err) => error!(
|
||||||
"Failed to create signature builder for {}! {:?}",
|
"Failed to build signature for {}! {:?}",
|
||||||
session.user_id, err
|
session.user_id, err
|
||||||
),
|
),
|
||||||
}
|
},
|
||||||
|
Err(err) => error!(
|
||||||
|
"Failed to create signature builder for {}! {:?}",
|
||||||
|
session.user_id, err
|
||||||
|
),
|
||||||
}
|
}
|
||||||
Err(err) => error!(
|
|
||||||
"Invalid subscription information for {}! {:?}",
|
|
||||||
session.user_id, err
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
Err(err) => error!(
|
||||||
|
"Invalid subscription information for {}! {:?}",
|
||||||
|
session.user_id, err
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/// Configure logging and common Rust variables
|
||||||
|
pub fn setup_logging(release: &'static str) -> sentry::ClientInitGuard {
|
||||||
|
dotenv::dotenv().ok();
|
||||||
|
|
||||||
|
if std::env::var("RUST_LOG").is_err() {
|
||||||
|
std::env::set_var("RUST_LOG", "info");
|
||||||
|
}
|
||||||
|
|
||||||
|
if std::env::var("ROCKET_ADDRESS").is_err() {
|
||||||
|
std::env::set_var("ROCKET_ADDRESS", "0.0.0.0");
|
||||||
|
}
|
||||||
|
|
||||||
|
pretty_env_logger::init();
|
||||||
|
info!("Starting {release}");
|
||||||
|
|
||||||
|
sentry::init((
|
||||||
|
"https://62fd0e02c5354905b4e286757f4beb16@sentry.insert.moe/4",
|
||||||
|
sentry::ClientOptions {
|
||||||
|
release: Some(release.into()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! configure {
|
||||||
|
() => {
|
||||||
|
let _sentry = revolt_quark::util::log::setup_logging(concat!(
|
||||||
|
env!("CARGO_PKG_NAME"),
|
||||||
|
"@",
|
||||||
|
env!("CARGO_PKG_VERSION")
|
||||||
|
));
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
|
pub mod log;
|
||||||
pub mod manipulation;
|
pub mod manipulation;
|
||||||
|
pub mod pfp;
|
||||||
|
pub mod rauth;
|
||||||
pub mod r#ref;
|
pub mod r#ref;
|
||||||
pub mod result;
|
pub mod result;
|
||||||
pub mod value;
|
pub mod value;
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
pub fn avatar(v: char) -> Vec<u8> {
|
||||||
|
match v {
|
||||||
|
// 0123456789ABCDEFGHJKMNPQRSTVWXYZ
|
||||||
|
'0' | '1' | '2' | '3' | 'S' | 'Z' => include_bytes!(crate::asset!("user/2.png")).to_vec(),
|
||||||
|
'4' | '5' | '6' | '7' | 'T' => include_bytes!(crate::asset!("user/3.png")).to_vec(),
|
||||||
|
'8' | '9' | 'A' | 'B' => include_bytes!(crate::asset!("user/4.png")).to_vec(),
|
||||||
|
'C' | 'D' | 'E' | 'F' | 'V' => include_bytes!(crate::asset!("user/5.png")).to_vec(),
|
||||||
|
'G' | 'H' | 'J' | 'K' | 'W' => include_bytes!(crate::asset!("user/6.png")).to_vec(),
|
||||||
|
'M' | 'N' | 'P' | 'Q' | 'X' => include_bytes!(crate::asset!("user/7.png")).to_vec(),
|
||||||
|
/*'0' | '1' | '2' | '3' | 'R' | 'Y'*/
|
||||||
|
_ => include_bytes!(crate::asset!("user/1.png")).to_vec(),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
use super::variables::delta::{
|
||||||
|
APP_URL, HCAPTCHA_KEY, INVITE_ONLY, SMTP_FROM, SMTP_HOST, SMTP_PASSWORD, SMTP_USERNAME,
|
||||||
|
USE_EMAIL, USE_HCAPTCHA,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::rauth::config::{
|
||||||
|
Captcha, Config, EmailVerificationConfig, SMTPSettings, Template, Templates,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn config() -> Config {
|
||||||
|
let mut config = Config {
|
||||||
|
email_verification: if *USE_EMAIL {
|
||||||
|
EmailVerificationConfig::Enabled {
|
||||||
|
smtp: SMTPSettings {
|
||||||
|
from: (*SMTP_FROM).to_string(),
|
||||||
|
host: (*SMTP_HOST).to_string(),
|
||||||
|
username: (*SMTP_USERNAME).to_string(),
|
||||||
|
password: (*SMTP_PASSWORD).to_string(),
|
||||||
|
reply_to: Some("support@revolt.chat".into()),
|
||||||
|
port: None,
|
||||||
|
use_tls: None,
|
||||||
|
},
|
||||||
|
expiry: Default::default(),
|
||||||
|
templates: Templates {
|
||||||
|
verify: Template {
|
||||||
|
title: "Verify your Revolt account.".into(),
|
||||||
|
text: include_str!(crate::asset!("templates/verify.txt")).into(),
|
||||||
|
url: format!("{}/login/verify/", *APP_URL),
|
||||||
|
html: None,
|
||||||
|
},
|
||||||
|
reset: Template {
|
||||||
|
title: "Reset your Revolt password.".into(),
|
||||||
|
text: include_str!(crate::asset!("templates/reset.txt")).into(),
|
||||||
|
url: format!("{}/login/reset/", *APP_URL),
|
||||||
|
html: None,
|
||||||
|
},
|
||||||
|
deletion: Template {
|
||||||
|
title: "Confirm account deletion.".into(),
|
||||||
|
text: include_str!(crate::asset!("templates/deletion.txt")).into(),
|
||||||
|
url: format!("{}/delete/", *APP_URL),
|
||||||
|
html: None,
|
||||||
|
},
|
||||||
|
welcome: None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
EmailVerificationConfig::Disabled
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
if *INVITE_ONLY {
|
||||||
|
config.invite_only = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if *USE_HCAPTCHA {
|
||||||
|
config.captcha = Captcha::HCaptcha {
|
||||||
|
secret: HCAPTCHA_KEY.clone(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
config
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ pub enum Error {
|
|||||||
|
|
||||||
// ? User related errors.
|
// ? User related errors.
|
||||||
UsernameTaken,
|
UsernameTaken,
|
||||||
|
InvalidUsername,
|
||||||
UnknownUser,
|
UnknownUser,
|
||||||
AlreadyFriends,
|
AlreadyFriends,
|
||||||
AlreadySentRequest,
|
AlreadySentRequest,
|
||||||
@@ -128,6 +129,7 @@ impl<'r> Responder<'r, 'static> for Error {
|
|||||||
Error::AlreadyOnboarded => Status::Forbidden,
|
Error::AlreadyOnboarded => Status::Forbidden,
|
||||||
|
|
||||||
Error::UnknownUser => Status::NotFound,
|
Error::UnknownUser => Status::NotFound,
|
||||||
|
Error::InvalidUsername => Status::BadRequest,
|
||||||
Error::UsernameTaken => Status::Conflict,
|
Error::UsernameTaken => Status::Conflict,
|
||||||
Error::AlreadyFriends => Status::Conflict,
|
Error::AlreadyFriends => Status::Conflict,
|
||||||
Error::AlreadySentRequest => Status::Conflict,
|
Error::AlreadySentRequest => Status::Conflict,
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
use log::warn;
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
// Application Settings
|
// Application Settings
|
||||||
pub static ref PUBLIC_URL: String =
|
pub static ref PUBLIC_URL: String =
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
pub use rocket_cors::catch_all_options_routes;
|
||||||
|
use rocket_cors::{AllowedOrigins, Cors};
|
||||||
|
|
||||||
|
pub fn new() -> Cors {
|
||||||
|
rocket_cors::CorsOptions {
|
||||||
|
allowed_origins: AllowedOrigins::All,
|
||||||
|
allowed_methods: [
|
||||||
|
"Get", "Put", "Post", "Delete", "Options", "Head", "Trace", "Connect", "Patch",
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.map(|s| FromStr::from_str(s).unwrap())
|
||||||
|
.collect(),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
.to_cors()
|
||||||
|
.expect("Failed to create CORS.")
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
|
use crate::{Error, Result};
|
||||||
|
|
||||||
use async_std::sync::Mutex;
|
use async_std::sync::Mutex;
|
||||||
use mongodb::bson::doc;
|
|
||||||
use revolt_quark::{Error, Result};
|
|
||||||
use rocket::http::Status;
|
use rocket::http::Status;
|
||||||
use rocket::request::{FromRequest, Outcome};
|
use rocket::request::{FromRequest, Outcome};
|
||||||
use rocket_okapi::gen::OpenApiGenerator;
|
use rocket_okapi::gen::OpenApiGenerator;
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
use crate::Database;
|
||||||
|
use rocket::State;
|
||||||
|
|
||||||
|
pub mod cors;
|
||||||
|
pub mod idempotency;
|
||||||
|
pub mod ratelimiter;
|
||||||
|
pub mod swagger;
|
||||||
|
|
||||||
|
pub use rocket_empty::EmptyResponse;
|
||||||
|
pub type Db = State<Database>;
|
||||||
@@ -8,6 +8,7 @@ use std::hash::Hasher;
|
|||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
use crate::rauth::models::Session;
|
||||||
use rocket::fairing::{Fairing, Info, Kind};
|
use rocket::fairing::{Fairing, Info, Kind};
|
||||||
use rocket::http::uri::Origin;
|
use rocket::http::uri::Origin;
|
||||||
use rocket::http::{Method, Status};
|
use rocket::http::{Method, Status};
|
||||||
@@ -22,8 +23,6 @@ use serde::Serialize;
|
|||||||
|
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
|
|
||||||
use log::info;
|
|
||||||
|
|
||||||
/// Ratelimit Bucket
|
/// Ratelimit Bucket
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
struct Entry {
|
struct Entry {
|
||||||
@@ -207,10 +206,9 @@ impl<'r> FromRequest<'r> for Ratelimiter {
|
|||||||
let ratelimiter = request
|
let ratelimiter = request
|
||||||
.local_cache_async(async {
|
.local_cache_async(async {
|
||||||
use rocket::outcome::Outcome;
|
use rocket::outcome::Outcome;
|
||||||
let identifier = if let Outcome::Success(session) =
|
let identifier = if let Outcome::Success(session) = request.guard::<Session>().await
|
||||||
request.guard::<rauth::entities::Session>().await
|
|
||||||
{
|
{
|
||||||
session.id.expect("`id` on User")
|
session.id
|
||||||
} else {
|
} else {
|
||||||
to_real_ip(request)
|
to_real_ip(request)
|
||||||
};
|
};
|
||||||
@@ -304,11 +302,11 @@ impl<'r> FromRequest<'r> for RatelimitInformation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/ratelimit")]
|
#[rocket::get("/ratelimit")]
|
||||||
fn ratelimit_info(info: RatelimitInformation) -> Json<RatelimitInformation> {
|
fn ratelimit_info(info: RatelimitInformation) -> Json<RatelimitInformation> {
|
||||||
Json(info)
|
Json(info)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn routes() -> Vec<rocket::Route> {
|
pub fn routes() -> Vec<rocket::Route> {
|
||||||
routes![ratelimit_info]
|
rocket::routes![ratelimit_info]
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
use rocket::Route;
|
||||||
|
|
||||||
|
pub fn routes() -> Vec<Route> {
|
||||||
|
rocket_okapi::swagger_ui::make_swagger_ui(&rocket_okapi::swagger_ui::SwaggerUIConfig {
|
||||||
|
url: "../openapi.json".to_owned(),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.into()
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
version: "3.3"
|
||||||
|
services:
|
||||||
|
redis:
|
||||||
|
image: eqalpha/keydb
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
||||||
|
database:
|
||||||
|
image: mongo
|
||||||
|
ports:
|
||||||
|
- "27017:27017"
|
||||||
|
volumes:
|
||||||
|
- ./.data/db:/data/db
|
||||||
|
mongo-express:
|
||||||
|
image: mongo-express
|
||||||
|
ports:
|
||||||
|
- "8081:8081"
|
||||||
|
environment:
|
||||||
|
- ME_CONFIG_MONGODB_SERVER=database
|
||||||
Reference in new issue
Block a user