refactor: remove quark/web

This commit is contained in:
Paul Makles
2023-08-26 16:19:00 +01:00
parent 3a55d00c6a
commit 51c26e324d
19 changed files with 148 additions and 96 deletions

48
Cargo.lock generated
View File

@@ -63,6 +63,17 @@ dependencies = [
"version_check",
]
[[package]]
name = "ahash"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
dependencies = [
"cfg-if 1.0.0",
"once_cell",
"version_check",
]
[[package]]
name = "aho-corasick"
version = "0.7.18"
@@ -72,6 +83,12 @@ dependencies = [
"memchr",
]
[[package]]
name = "allocator-api2"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
[[package]]
name = "ansi_term"
version = "0.12.1"
@@ -515,7 +532,7 @@ version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f60a2c7c80a7850b56df4b8e98e8e4932c34877b8add4f13e8350499cc1e4572"
dependencies = [
"ahash",
"ahash 0.7.6",
"base64 0.13.0",
"chrono",
"hex",
@@ -1343,7 +1360,7 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
dependencies = [
"ahash",
"ahash 0.7.6",
]
[[package]]
@@ -1352,6 +1369,16 @@ version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3"
[[package]]
name = "hashbrown"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
dependencies = [
"ahash 0.8.3",
"allocator-api2",
]
[[package]]
name = "heck"
version = "0.4.0"
@@ -1815,6 +1842,15 @@ dependencies = [
"hashbrown 0.11.2",
]
[[package]]
name = "lru"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eedb2bdbad7e0634f83989bf596f497b070130daaa398ab22d84c39e266deec5"
dependencies = [
"hashbrown 0.14.0",
]
[[package]]
name = "lru-cache"
version = "0.1.2"
@@ -1872,7 +1908,7 @@ version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e52eb6380b6d2a10eb3434aec0885374490f5b82c8aaf5cd487a183c98be834"
dependencies = [
"ahash",
"ahash 0.7.6",
"metrics-macros",
]
@@ -2906,6 +2942,7 @@ dependencies = [
"indexmap",
"iso8601-timestamp 0.2.11",
"log",
"lru 0.11.0",
"mongodb",
"nanoid",
"once_cell",
@@ -2943,7 +2980,7 @@ dependencies = [
"lettre",
"linkify 0.6.0",
"log",
"lru",
"lru 0.7.6",
"nanoid",
"num_enum 0.5.7",
"once_cell",
@@ -2957,6 +2994,7 @@ dependencies = [
"revolt_rocket_okapi",
"rocket",
"rocket_authifier",
"rocket_cors",
"rocket_empty",
"rocket_prometheus",
"schemars",
@@ -3032,7 +3070,7 @@ dependencies = [
"iso8601-timestamp 0.1.10",
"linkify 0.8.1",
"log",
"lru",
"lru 0.7.6",
"mongodb",
"nanoid",
"num_enum 0.5.7",

View File

@@ -32,6 +32,7 @@ revolt-permissions = { version = "0.6.7", path = "../permissions", features = [
# Utility
log = "0.4"
lru = "0.11.0"
rand = "0.8.5"
ulid = "1.0.0"
nanoid = "0.4.0"

View File

@@ -1,6 +1,9 @@
use crate::{Error, Result};
use std::num::NonZeroUsize;
use revolt_result::{create_error, Error, Result};
use async_std::sync::Mutex;
use once_cell::sync::Lazy;
use revolt_rocket_okapi::gen::OpenApiGenerator;
use revolt_rocket_okapi::request::{OpenApiFromRequest, RequestHeaderInput};
use revolt_rocket_okapi::revolt_okapi::openapi3::{Parameter, ParameterValue};
@@ -8,16 +11,14 @@ use rocket::http::Status;
use rocket::request::{FromRequest, Outcome};
use schemars::schema::{InstanceType, SchemaObject, SingleOrVec};
use serde::{Deserialize, Serialize};
use validator::Validate;
use once_cell::sync::Lazy;
#[derive(Validate, Serialize, Deserialize)]
#[derive(Serialize, Deserialize)]
pub struct IdempotencyKey {
#[validate(length(min = 1, max = 64))]
key: String,
}
static TOKEN_CACHE: Lazy<Mutex<lru::LruCache<String, ()>>> = Lazy::new(|| Mutex::new(lru::LruCache::new(100)));
static TOKEN_CACHE: Lazy<Mutex<lru::LruCache<String, ()>>> =
Lazy::new(|| Mutex::new(lru::LruCache::new(NonZeroUsize::new(1000).unwrap())));
impl IdempotencyKey {
// Backwards compatibility.
@@ -26,7 +27,7 @@ impl IdempotencyKey {
if let Some(v) = v {
let mut cache = TOKEN_CACHE.lock().await;
if cache.get(&v).is_some() {
return Err(Error::DuplicateNonce);
return Err(create_error!(DuplicateNonce));
}
cache.put(v.clone(), ());
@@ -81,14 +82,19 @@ impl<'r> FromRequest<'r> for IdempotencyKey {
.next()
.map(|k| k.to_string())
{
let idempotency = IdempotencyKey { key };
if let Err(error) = idempotency.validate() {
return Outcome::Failure((Status::BadRequest, Error::FailedValidation { error }));
if key.len() > 64 {
return Outcome::Failure((
Status::BadRequest,
create_error!(FailedValidation {
error: "idempotency key too long".to_string(),
}),
));
}
let idempotency = IdempotencyKey { key };
let mut cache = TOKEN_CACHE.lock().await;
if cache.get(&idempotency.key).is_some() {
return Outcome::Failure((Status::Conflict, Error::DuplicateNonce));
return Outcome::Failure((Status::Conflict, create_error!(DuplicateNonce)));
}
cache.put(idempotency.key.clone(), ());

View File

@@ -1,3 +1,4 @@
pub mod bridge;
pub mod idempotency;
pub mod permissions;
pub mod reference;

View File

@@ -51,6 +51,7 @@ lettre = "0.10.0-alpha.4"
rocket = { version = "0.5.0-rc.2", default-features = false, features = [
"json",
] }
rocket_cors = { git = "https://github.com/lawliet89/rocket_cors", rev = "c17e8145baa4790319fdb6a473e465b960f55e7c" }
rocket_empty = { version = "0.1.1", features = ["schema"] }
rocket_authifier = { version = "1.0.7" }
rocket_prometheus = "0.10.0-rc.3"

View File

@@ -8,8 +8,10 @@ extern crate serde_json;
pub mod routes;
pub mod util;
use rocket_cors::{AllowedOrigins, CorsOptions};
use rocket_prometheus::PrometheusMetrics;
use std::net::Ipv4Addr;
use std::str::FromStr;
use async_std::channel::unbounded;
use revolt_quark::authifier::{Authifier, AuthifierEvent};
@@ -62,7 +64,27 @@ async fn rocket() -> _ {
async_std::task::spawn(revolt_quark::tasks::start_workers(legacy_db.clone()));
// Configure CORS
let cors = revolt_quark::web::cors::new();
let 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.");
// Configure Swagger
let swagger = revolt_rocket_okapi::swagger_ui::make_swagger_ui(
&revolt_rocket_okapi::swagger_ui::SwaggerUIConfig {
url: "../openapi.json".to_owned(),
..Default::default()
},
)
.into();
// Configure Rocket
let rocket = rocket::build();
@@ -71,14 +93,14 @@ async fn rocket() -> _ {
routes::mount(rocket)
.attach(prometheus.clone())
.mount("/metrics", prometheus)
.mount("/", revolt_quark::web::cors::catch_all_options_routes())
.mount("/", revolt_quark::web::ratelimiter::routes())
.mount("/swagger/", revolt_quark::web::swagger::routes())
.mount("/", rocket_cors::catch_all_options_routes())
.mount("/", util::ratelimiter::routes())
.mount("/swagger/", swagger)
.manage(authifier)
.manage(db)
.manage(legacy_db)
.manage(cors.clone())
.attach(revolt_quark::web::ratelimiter::RatelimitFairing)
.attach(util::ratelimiter::RatelimitFairing)
.attach(cors)
.configure(rocket::Config {
limits: rocket::data::Limits::default().limit("string", 5.megabytes()),

View File

@@ -1,8 +1,8 @@
use revolt_database::util::idempotency::IdempotencyKey;
use revolt_quark::{
models::{message::DataMessageSend, Message, User},
perms,
types::push::MessageAuthor,
web::idempotency::IdempotencyKey,
Db, Error, Permission, Ref, Result,
};

View File

@@ -1,3 +1,5 @@
use once_cell::sync::Lazy;
use regex::Regex;
use revolt_quark::models::emoji::EmojiParent;
use revolt_quark::models::{Emoji, File, User};
use revolt_quark::variables::delta::MAX_EMOJI_COUNT;
@@ -5,10 +7,13 @@ use revolt_quark::{perms, Db, Error, Permission, Result};
use serde::Deserialize;
use validator::Validate;
use crate::util::regex::RE_EMOJI;
use rocket::serde::json::Json;
/// Regex for valid emoji names
///
/// Alphanumeric and underscores
pub static RE_EMOJI: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[a-z0-9_]+$").unwrap());
/// # Emoji Data
#[derive(Validate, Deserialize, JsonSchema)]
pub struct DataCreateEmoji {
@@ -57,7 +62,9 @@ pub async fn create_emoji(
// ! FIXME: hardcoded upper limit
let emojis = db.fetch_emoji_by_parent_id(&server.id).await?;
if emojis.len() > *MAX_EMOJI_COUNT {
return Err(Error::TooManyEmoji { max: *MAX_EMOJI_COUNT });
return Err(Error::TooManyEmoji {
max: *MAX_EMOJI_COUNT,
});
}
}
EmojiParent::Detached => return Err(Error::InvalidOperation),

View File

@@ -1,4 +1,5 @@
use crate::util::regex::RE_USERNAME;
use once_cell::sync::Lazy;
use regex::Regex;
use revolt_database::{Database, User};
use revolt_models::v0;
use revolt_quark::authifier::models::Session;
@@ -8,6 +9,12 @@ use rocket::{serde::json::Json, State};
use serde::{Deserialize, Serialize};
use validator::Validate;
/// Regex for valid usernames
///
/// Block zero width space
/// Block lookalike characters
pub static RE_USERNAME: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(\p{L}|[\d_.-])+$").unwrap());
/// # New User Data
#[derive(Validate, Serialize, Deserialize, JsonSchema)]
pub struct DataOnboard {

View File

@@ -1,9 +1,16 @@
use crate::util::regex::RE_USERNAME;
use once_cell::sync::Lazy;
use regex::Regex;
use revolt_quark::{authifier::models::Account, models::User, Database, Error, Result};
use rocket::{serde::json::Json, State};
use serde::{Deserialize, Serialize};
use validator::Validate;
/// Regex for valid usernames
///
/// Block zero width space
/// Block lookalike characters
pub static RE_USERNAME: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(\p{L}|[\d_.-])+$").unwrap());
/// # Username Information
#[derive(Validate, Serialize, Deserialize, JsonSchema)]
pub struct DataChangeUsername {

View File

@@ -1,3 +1,5 @@
use once_cell::sync::Lazy;
use regex::Regex;
use revolt_quark::models::user::{FieldsUser, PartialUser, User};
use revolt_quark::models::File;
use revolt_quark::{Database, Error, Ref, Result};
@@ -8,7 +10,11 @@ use rocket::State;
use serde::{Deserialize, Serialize};
use validator::Validate;
use crate::util::regex::RE_DISPLAY_NAME;
/// Regex for valid display names
///
/// Block zero width space
/// Block newline and carriage return
pub static RE_DISPLAY_NAME: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[^\u200B\n\r]+$").unwrap());
/// # Profile Data
#[derive(Validate, Serialize, Deserialize, Debug, JsonSchema)]

View File

@@ -1,8 +1,10 @@
use revolt_database::{util::reference::Reference, Database};
use revolt_database::{
util::{idempotency::IdempotencyKey, reference::Reference},
Database,
};
use revolt_quark::{
models::message::{DataMessageSend, Message},
types::push::MessageAuthor,
web::idempotency::IdempotencyKey,
Db, Error, Result,
};
use rocket::{serde::json::Json, State};

View File

@@ -1 +1 @@
pub mod regex;
pub mod ratelimiter;

View File

@@ -1,14 +1,9 @@
//! Pulled from lightspeed-tv/backend.
//!
//! This will be replaced again in the near future since
//! I don't want duplication between two different projects.
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;
use std::ops::Add;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use crate::authifier::models::Session;
use revolt_quark::authifier::models::Session;
use rocket::fairing::{Fairing, Info, Kind};
use rocket::http::uri::Origin;
use rocket::http::{Method, Status};

View File

@@ -1,19 +0,0 @@
use once_cell::sync::Lazy;
use regex::Regex;
/// Regex for valid display names
///
/// Block zero width space
/// Block newline and carriage return
pub static RE_DISPLAY_NAME: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[^\u200B\n\r]+$").unwrap());
/// Regex for valid usernames
///
/// Block zero width space
/// Block lookalike characters
pub static RE_USERNAME: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(\p{L}|[\d_.-])+$").unwrap());
/// Regex for valid emoji names
///
/// Alphanumeric and underscores
pub static RE_EMOJI: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[a-z0-9_]+$").unwrap());

View File

@@ -1,5 +1,6 @@
use std::collections::HashSet;
use revolt_database::util::idempotency::IdempotencyKey;
use ulid::Ulid;
use crate::{
@@ -11,8 +12,7 @@ use crate::{
},
tasks::{ack::AckEvent, process_embeds},
types::push::MessageAuthor,
variables::delta::{MAX_ATTACHMENT_COUNT, MAX_REPLY_COUNT, MAX_EMBED_COUNT},
web::idempotency::IdempotencyKey,
variables::delta::{MAX_ATTACHMENT_COUNT, MAX_EMBED_COUNT, MAX_REPLY_COUNT},
Database, Error, OverrideField, Ref, Result,
};
@@ -413,7 +413,10 @@ impl Channel {
) -> Result<Message> {
Message::validate_sum(&data.content, data.embeds.as_deref().unwrap_or_default())?;
idempotency.consume_nonce(data.nonce).await?;
idempotency
.consume_nonce(data.nonce)
.await
.map_err(|_| Error::InvalidOperation)?;
// Check the message is not empty
if (data.content.as_ref().map_or(true, |v| v.is_empty()))
@@ -497,16 +500,24 @@ impl Channel {
// Add attachments to message.
let mut attachments = vec![];
if data.attachments.as_ref().is_some_and(|v| v.len() > *MAX_ATTACHMENT_COUNT) {
if data
.attachments
.as_ref()
.is_some_and(|v| v.len() > *MAX_ATTACHMENT_COUNT)
{
return Err(Error::TooManyAttachments {
max: *MAX_ATTACHMENT_COUNT,
});
}
if data.embeds.as_ref().is_some_and(|v| v.len() > *MAX_EMBED_COUNT) {
if data
.embeds
.as_ref()
.is_some_and(|v| v.len() > *MAX_EMBED_COUNT)
{
return Err(Error::TooManyEmbeds {
max: *MAX_EMBED_COUNT
})
max: *MAX_EMBED_COUNT,
});
}
for attachment_id in data.attachments.as_deref().unwrap_or_default() {

View File

@@ -1,19 +0,0 @@
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.")
}

View File

@@ -1,10 +1,5 @@
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>;

View File

@@ -1,9 +0,0 @@
use rocket::Route;
pub fn routes() -> Vec<Route> {
revolt_rocket_okapi::swagger_ui::make_swagger_ui(&revolt_rocket_okapi::swagger_ui::SwaggerUIConfig {
url: "../openapi.json".to_owned(),
..Default::default()
})
.into()
}