chore: refactor generic web server code into quark

This commit is contained in:
Paul Makles
2022-06-04 19:02:10 +01:00
parent 7390b3c087
commit d660127c14
38 changed files with 170 additions and 170 deletions

26
Cargo.lock generated
View File

@@ -684,16 +684,6 @@ dependencies = [
"cipher",
]
[[package]]
name = "ctrlc"
version = "3.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b37feaa84e6861e00a1f5e5aa8da3ee56d605c9992d33e082786754828e20865"
dependencies = [
"nix",
"winapi 0.3.9",
]
[[package]]
name = "darling"
version = "0.13.4"
@@ -1914,17 +1904,6 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "nix"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f17df307904acd05aa8e32e97bb20f2a0df1728bbc2d771ae8f9a90463441e9"
dependencies = [
"bitflags",
"cfg-if 1.0.0",
"libc",
]
[[package]]
name = "nom"
version = "7.1.1"
@@ -2757,7 +2736,6 @@ dependencies = [
"async-std",
"bitfield",
"chrono",
"ctrlc",
"dashmap",
"dotenv",
"env_logger",
@@ -2778,7 +2756,6 @@ dependencies = [
"reqwest",
"revolt-quark",
"rocket",
"rocket_cors",
"rocket_empty",
"rocket_okapi",
"rocket_rauth",
@@ -2801,6 +2778,7 @@ dependencies = [
"bincode",
"bitfield",
"bson",
"dashmap",
"deadqueue",
"dotenv",
"futures",
@@ -2809,6 +2787,7 @@ dependencies = [
"lazy_static",
"linkify 0.8.1",
"log",
"lru",
"mongodb",
"nanoid",
"num_enum",
@@ -2820,6 +2799,7 @@ dependencies = [
"regex",
"reqwest",
"rocket",
"rocket_cors",
"rocket_empty",
"rocket_http",
"rocket_okapi",

View File

@@ -14,7 +14,7 @@ mod websocket;
#[async_std::main]
async fn main() {
// Configure requirements for Bonfire.
let _guard = revolt_quark::setup_logging();
revolt_quark::configure!();
database::connect().await;
// Clean up the current region information.

View File

@@ -18,7 +18,6 @@ linkify = "0.6.0"
once_cell = "1.4.1"
env_logger = "0.7.1"
lazy_static = "1.4.0"
ctrlc = { version = "3.0", features = ["termination"] }
# Lang. Utilities
regex = "1"
@@ -54,7 +53,6 @@ mobc-redis = { version = "0.7.0", default-features = false, features = ["async-s
rocket = { version = "0.5.0-rc.2", default-features = false, features = ["json"] }
rocket_empty = { git = "https://github.com/insertish/rocket_empty", branch = "master" }
rocket_rauth = { git = "https://github.com/insertish/rauth", rev = "a82cb5d23da4787e75d6f3c795189ce31e1590c4" }
rocket_cors = { git = "https://github.com/lawliet89/rocket_cors", rev = "5843861a88958c16bfaa0b40f0d8910772bcd2f6" }
# spec generation
schemars = "0.8.8"

View File

@@ -6,101 +6,21 @@ extern crate rocket_okapi;
extern crate serde_json;
#[macro_use]
extern crate lazy_static;
extern crate ctrlc;
pub mod routes;
pub mod util;
pub mod version;
use log::info;
use revolt_quark::rauth::config::{
Captcha, Config, EmailVerificationConfig, SMTPSettings, Template, Templates,
};
use revolt_quark::rauth::RAuth;
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 rocket_cors::AllowedOrigins;
use std::str::FromStr;
#[launch]
async fn rocket() -> _ {
let _guard = revolt_quark::setup_logging();
info!(
"Starting Revolt server [version {}].",
crate::version::VERSION
);
// Configure logging and environment
revolt_quark::configure!();
// Ensure environment variables are present
revolt_quark::variables::delta::preflight_checks();
#[cfg(debug_assertions)]
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 {
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,
},
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(),
};
}
// Setup database
let db = DatabaseInfo::Auto.connect().await.unwrap();
db.migrate_database().await.unwrap();
@@ -108,7 +28,7 @@ async fn rocket() -> _ {
// Setup rAuth
let rauth = RAuth {
database: db.clone().into(),
config,
config: revolt_quark::util::rauth::config(),
};
// Launch background task workers.
@@ -117,27 +37,11 @@ async fn rocket() -> _ {
// Configure Rocket
let rocket = rocket::build();
routes::mount(rocket)
.mount("/", rocket_cors::catch_all_options_routes())
.mount("/", util::ratelimiter::routes())
.mount(
"/swagger/",
rocket_okapi::swagger_ui::make_swagger_ui(&rocket_okapi::swagger_ui::SwaggerUIConfig {
url: "../openapi.json".to_owned(),
..Default::default()
}),
)
.mount("/", revolt_quark::web::cors::catch_all_options_routes())
.mount("/", revolt_quark::web::ratelimiter::routes())
.mount("/swagger/", revolt_quark::web::swagger::routes())
.manage(rauth)
.manage(db)
.manage(cors.clone())
.attach(util::ratelimiter::RatelimitFairing)
.attach(cors)
.attach(revolt_quark::web::ratelimiter::RatelimitFairing)
.attach(revolt_quark::web::cors::fairing())
}
/// Resolve asset
macro_rules! asset {
($path:literal) => {
concat!(env!("CARGO_MANIFEST_DIR"), "/assets/", $path)
};
}
pub(crate) use asset;

View File

@@ -5,7 +5,9 @@ use revolt_quark::{
message::{Masquerade, Reply, SendableEmbed},
Message, User,
},
perms, Db, Error, Permission, Ref, Result,
perms,
web::idempotency::IdempotencyKey,
Db, Error, Permission, Ref, Result,
};
use regex::Regex;
@@ -14,8 +16,6 @@ use serde::{Deserialize, Serialize};
use ulid::Ulid;
use validator::Validate;
use crate::util::idempotency::IdempotencyKey;
#[derive(Validate, Serialize, Deserialize, JsonSchema)]
pub struct DataMessageSend {
/// Unique token to prevent duplicate message sending

View File

@@ -76,7 +76,7 @@ pub struct RevoltConfig {
#[get("/")]
pub async fn root() -> Result<Json<RevoltConfig>> {
Ok(Json(RevoltConfig {
revolt: crate::version::VERSION.to_string(),
revolt: env!("CARGO_PKG_VERSION").to_string(),
features: RevoltFeatures {
captcha: CaptchaFeature {
enabled: *USE_HCAPTCHA,

View File

@@ -59,18 +59,6 @@ impl rocket_okapi::response::OpenApiResponderInner for CachedFile {
pub async fn req(target: String) -> CachedFile {
CachedFile((
ContentType::PNG,
match 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(),
},
revolt_quark::util::pfp::avatar(target.chars().last().unwrap()),
))
}

View File

@@ -1,3 +1 @@
pub mod idempotency;
pub mod ratelimiter;
pub mod regex;

View File

@@ -1 +0,0 @@
pub const VERSION: &str = "0.5.3-5-patch.2";

View File

@@ -8,7 +8,18 @@ edition = "2021"
[features]
mongo = [ "mongodb" ]
rocket_impl = [ "rocket", "rocket_empty", "rauth/database-mongodb", "rauth/rocket_impl", "rauth/okapi_impl" ]
rocket_impl = [
"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" ]
@@ -59,6 +70,9 @@ reqwest = "0.11.10"
bitfield = "0.13.2"
lazy_static = "1.4.0"
lru = { version = "0.7.6", optional = true }
dashmap = { version = "5.2.0", optional = true }
# Web Push
base64 = "0.13.0"
web-push = "0.7.2"
@@ -67,6 +81,7 @@ web-push = "0.7.2"
rocket_http = { optional = true, version = "0.5.0-rc.2" }
rocket = { optional = true, version = "0.5.0-rc.2", default-features = false, features = ["json"] }
rocket_empty = { optional = true, git = "https://github.com/insertish/rocket_empty", branch = "master" }
rocket_cors = { optional = true, git = "https://github.com/lawliet89/rocket_cors", rev = "5843861a88958c16bfaa0b40f0d8910772bcd2f6" }
# rAuth
rauth = { git = "https://github.com/insertish/rauth", rev = "a82cb5d23da4787e75d6f3c795189ce31e1590c4", features = [ "async-std-runtime" ] }

View File

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -2,7 +2,6 @@ use crate::r#impl::mongo::MongoDb;
use super::scripts::LATEST_REVISION;
use log::info;
use mongodb::bson::doc;
use mongodb::options::CreateCollectionOptions;

View File

@@ -2,7 +2,6 @@ use std::time::Duration;
use bson::Bson;
use futures::StreamExt;
use log::info;
use mongodb::{
bson::{doc, from_bson, from_document, to_document, Document},
options::FindOptions,

View File

@@ -15,7 +15,7 @@ extern crate lazy_static;
#[macro_use]
extern crate bitfield;
#[macro_use]
pub extern crate bson;
extern crate bson;
pub use iso8601_timestamp::Timestamp;
pub use rauth;
@@ -27,6 +27,7 @@ pub mod models;
pub mod presence;
pub mod tasks;
pub mod types;
pub mod util;
#[cfg(feature = "rocket_impl")]
pub mod web;
@@ -34,7 +35,6 @@ pub mod web;
mod database;
mod permissions;
mod traits;
mod util;
pub use database::*;
pub use traits::*;
@@ -43,10 +43,19 @@ pub use permissions::defn::*;
pub use permissions::{get_relationship, perms};
pub use util::{
log::setup_logging,
r#ref::Ref,
result::{Error, Result},
variables,
};
#[cfg(feature = "rocket_impl")]
pub use web::{Db, EmptyResponse};
/// Resolve asset
macro_rules! asset {
($path:literal) => {
concat!(env!("CARGO_MANIFEST_DIR"), "/assets/", $path)
};
}
pub(crate) use asset;

View File

@@ -2,7 +2,6 @@
use crate::{models::channel::PartialChannel, Database};
use deadqueue::limited::Queue;
use log::info;
use mongodb::bson::doc;
use std::{collections::HashMap, time::Duration};

View File

@@ -6,7 +6,6 @@ use crate::{
};
use deadqueue::limited::Queue;
use log::error;
/// Task information
#[derive(Debug)]

View File

@@ -1,5 +1,5 @@
/// Configure logging and common Rust variables
pub fn setup_logging() -> sentry::ClientInitGuard {
pub fn setup_logging(release: &'static str) -> sentry::ClientInitGuard {
dotenv::dotenv().ok();
if std::env::var("RUST_LOG").is_err() {
@@ -11,12 +11,24 @@ pub fn setup_logging() -> sentry::ClientInitGuard {
}
pretty_env_logger::init();
info!("Starting {release}");
sentry::init((
"https://62fd0e02c5354905b4e286757f4beb16@sentry.insert.moe/4",
sentry::ClientOptions {
release: sentry::release_name!(),
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")
));
};
}

View File

@@ -1,5 +1,7 @@
pub mod log;
pub mod manipulation;
pub mod pfp;
pub mod rauth;
pub mod r#ref;
pub mod result;
pub mod value;

View File

@@ -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(),
}
}

View File

@@ -0,0 +1,57 @@
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,
},
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
}

View File

@@ -1,8 +1,5 @@
use std::env;
#[cfg(debug_assertions)]
use log::warn;
lazy_static! {
// Application Settings
pub static ref PUBLIC_URL: String =

View File

@@ -0,0 +1,19 @@
use std::str::FromStr;
pub use rocket_cors::catch_all_options_routes;
use rocket_cors::{AllowedOrigins, Cors};
pub fn fairing() -> 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,5 +1,6 @@
use crate::{Error, Result};
use async_std::sync::Mutex;
use revolt_quark::{Error, Result};
use rocket::http::Status;
use rocket::request::{FromRequest, Outcome};
use rocket_okapi::gen::OpenApiGenerator;

View File

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

View File

@@ -8,7 +8,7 @@ use std::hash::Hasher;
use std::ops::Add;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use revolt_quark::rauth::models::Session;
use crate::rauth::models::Session;
use rocket::fairing::{Fairing, Info, Kind};
use rocket::http::uri::Origin;
use rocket::http::{Method, Status};
@@ -23,8 +23,6 @@ use serde::Serialize;
use dashmap::DashMap;
use log::info;
/// Ratelimit Bucket
#[derive(Clone, Copy)]
struct Entry {
@@ -304,11 +302,11 @@ impl<'r> FromRequest<'r> for RatelimitInformation {
}
}
#[get("/ratelimit")]
#[rocket::get("/ratelimit")]
fn ratelimit_info(info: RatelimitInformation) -> Json<RatelimitInformation> {
Json(info)
}
pub fn routes() -> Vec<rocket::Route> {
routes![ratelimit_info]
rocket::routes![ratelimit_info]
}

View File

@@ -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()
}