flabk/src/servek/servek.rs

136 lines
3.8 KiB
Rust

use core::panic;
use std::{convert::Infallible, fmt::Display};
use handlebars::{Handlebars, RenderError};
use warp::{
hyper::StatusCode,
reject::{MethodNotAllowed, Reject},
Filter, Rejection, Reply,
};
use crate::{
model,
svc::{
auth::{Auth, AuthError},
profiles::{Profiler, UserError},
},
};
#[derive(Clone)]
pub struct Server {
pub(super) hb: Handlebars<'static>,
pub(super) profiler: Profiler,
pub(super) auth: Auth,
}
impl Server {
pub fn new(profiler: Profiler, auth: Auth) -> Self {
let mut hb = Handlebars::new();
hb.register_template_string("profile", include_str!("../../templates/html/profile.html"))
.expect("profile template");
hb.register_template_string(
"login-error",
include_str!("../../templates/html/login-error.html"),
)
.expect("login-error template");
Self { hb, profiler, auth }
}
pub async fn listen_and_serve(self, port: u16) -> ! {
println!("starting server on port {}", port);
warp::serve(self.html().await.recover(Self::handle_rejection))
.run(([127, 0, 0, 1], port))
.await;
panic!("server stopped prematurely")
}
async fn handle_rejection(err: Rejection) -> Result<impl Reply, Infallible> {
let code;
let message;
if err.is_not_found() {
code = StatusCode::NOT_FOUND;
message = "not found";
} else if let Some(err) = err.find::<ServerError>() {
match err {
ServerError::Internal(err) => {
println!("internal server error: {}", err);
code = StatusCode::INTERNAL_SERVER_ERROR;
message = "internal server error";
}
ServerError::NotFound => {
code = StatusCode::NOT_FOUND;
message = "not found";
}
ServerError::BadRequest(err) => {
code = StatusCode::BAD_REQUEST;
message = err;
}
}
} else if let Some(err) = err.find::<MethodNotAllowed>() {
println!("MethodNotAllowed: {:#?}", err);
code = StatusCode::NOT_FOUND;
message = "not found";
} else {
// We should have expected this... Just log and say its a 500
println!("FIXME: unhandled rejection: {:?}", err);
code = StatusCode::INTERNAL_SERVER_ERROR;
message = "internal server error"
}
Ok(warp::reply::with_status(
warp::reply::json(&model::Error::error(message)),
code,
))
}
}
#[derive(Clone, Debug)]
pub(super) enum ServerError {
Internal(String),
NotFound,
BadRequest(String),
}
impl ServerError {
pub(super) fn rejection(self) -> Rejection {
warp::reject::custom(self)
}
}
impl Reject for ServerError {}
impl From<RenderError> for ServerError {
fn from(r: RenderError) -> Self {
Self::Internal(r.to_string())
}
}
impl From<UserError> for ServerError {
fn from(u: UserError) -> Self {
match u {
UserError::Duplicate => Self::BadRequest("duplicate entry exists".to_owned()),
UserError::NotFound => Self::NotFound,
UserError::Other(o) => Self::Internal(format!("UserError: {}", o)),
}
}
}
impl From<AuthError> for ServerError {
fn from(a: AuthError) -> Self {
match a {
AuthError::InvalidCredentials => {
ServerError::BadRequest("invalid credentials".to_owned())
}
AuthError::ServerError(err) => ServerError::Internal(err),
}
}
}
impl Display for ServerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self)
}
}