128 lines
4.4 KiB
Rust
128 lines
4.4 KiB
Rust
extern crate chrono;
|
|
extern crate dotenv;
|
|
extern crate getrandom;
|
|
extern crate base64;
|
|
|
|
use std::net::SocketAddr;
|
|
use std::convert::Infallible; // our main dispatcher basically never fails hence why we use this
|
|
use std::env;
|
|
|
|
use tokio;
|
|
use hyper::{
|
|
self,
|
|
Server,
|
|
Response, Request, Body,
|
|
Method, StatusCode,
|
|
service::{make_service_fn, service_fn}
|
|
};
|
|
use mysql_async::Pool;
|
|
use dotenv::dotenv;
|
|
|
|
mod auth;
|
|
use auth::AuthReason;
|
|
|
|
mod routes;
|
|
mod invites;
|
|
mod channels;
|
|
mod members;
|
|
mod messages;
|
|
mod http_params;
|
|
mod perms;
|
|
mod db_types;
|
|
|
|
async fn route_dispatcher(pool: &Pool, resp: &mut Response<Body>, meth: &Method, path: &str, params: serde_json::Value) {
|
|
// At some point we should have some way of hiding this obnoxious complexity
|
|
use routes::resolve_dynamic_route;
|
|
println!("{}: {}", meth, path);
|
|
match (meth, path) {
|
|
(&Method::GET, routes::INVITE_JOIN) => {
|
|
if let Err(_) = invites::route_join_invite_code(pool, resp, params).await {
|
|
*resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
|
|
}
|
|
},
|
|
(&Method::GET, routes::INVITE_CREATE) => {
|
|
if let Err(_) = invites::create_invite(pool, resp).await {
|
|
*resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
|
|
}
|
|
},
|
|
(&Method::GET, routes::CHANNELS_LIST) => channels::list_channels(pool, resp).await,
|
|
(&Method::POST, routes::CHANNELS_CREATE) => channels::create_channel(pool, resp, params).await,
|
|
(&Method::POST, routes::CHANNELS_DELETE) => channels::delete_channel(pool, resp, params).await,
|
|
|
|
(&Method::POST, routes::MESSAGE_SEND) => messages::send_message(pool, resp, params).await,
|
|
_ => {
|
|
// We attempt dynamic routes as fallback for a few reasons
|
|
// 1. theres less of these than there are the static routes
|
|
// 2. because of the above and that this is wholly more expensive than static routse
|
|
// we can justify putting in tons of branches since we're likely to:
|
|
// far jump here, lose cache, and still be be network bound
|
|
// Computatinoal bounds are really of no concern with this api since
|
|
// we're not doing any heavy calculations at any point
|
|
if let Some(route) = resolve_dynamic_route(path) {
|
|
*resp.status_mut() = StatusCode::OK;
|
|
println!("Static part: {}", route.base);
|
|
println!("Dynamic part: {}", route.dynamic);
|
|
}
|
|
else {
|
|
println!("NOT FOUND: {}: {}", meth, path);
|
|
*resp.status_mut() = StatusCode::NOT_FOUND
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn main_responder(request: Request<Body>) -> Result<Response<Body>, hyper::Error>{
|
|
use AuthReason::*;
|
|
let mut response = Response::new(Body::empty());
|
|
|
|
let (parts, mut body) = request.into_parts();
|
|
let method = parts.method;
|
|
let path = parts.uri.path();
|
|
|
|
let params_res = http_params::parse_params(&mut body).await;
|
|
|
|
if let Ok(params) = params_res {
|
|
let pool = Pool::new(&env::var("DATABASE_URL").unwrap());
|
|
if let Ok(auth_result) = auth::wall_entry(path, &pool, ¶ms).await {
|
|
// Deal with permissions errors at this point
|
|
match auth_result {
|
|
OpenAuth | Good => route_dispatcher(&pool, &mut response, &method, path, params).await,
|
|
NoKey => {
|
|
println!("AUTH: NoKey/BadKey");
|
|
*response.status_mut() = StatusCode::UNAUTHORIZED
|
|
},
|
|
}
|
|
}
|
|
else {
|
|
*response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
|
|
}
|
|
}
|
|
else {
|
|
println!("PARSER: Parameter parsing failed");
|
|
*response.status_mut() = StatusCode::BAD_REQUEST;
|
|
}
|
|
Ok(response)
|
|
}
|
|
|
|
async fn shutdown_signal() {
|
|
tokio::signal::ctrl_c()
|
|
.await
|
|
.expect("Failed to capture ctrl-c signal");
|
|
}
|
|
#[tokio::main]
|
|
async fn main() {
|
|
dotenv().ok();
|
|
println!("Servering on localhost:8888");
|
|
let addr = SocketAddr::from(([127,0,0,1], 8888));
|
|
|
|
let service = make_service_fn(|_conn| async {
|
|
Ok::<_, Infallible>(service_fn(main_responder))
|
|
});
|
|
|
|
let server = Server::bind(&addr).serve(service);
|
|
let graceful_shutdown = server.with_graceful_shutdown(shutdown_signal());
|
|
if let Err(e) = graceful_shutdown.await {
|
|
eprintln!("Server shutdown error: {}", e);
|
|
}
|
|
}
|