flabk/src/servek/servek.rs

132 lines
4.1 KiB
Rust

use core::panic;
use std::{fmt::Display, net::SocketAddr};
use axum::{
http::StatusCode,
response::{self, IntoResponse},
Extension, Router,
};
use handlebars::{Handlebars, RenderError};
use tower_cookies::CookieManagerLayer;
use crate::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", include_str!("../../templates/html/login.html"))
.expect("login template");
hb.register_template_string("signup", include_str!("../../templates/html/signup.html"))
.expect("login template");
hb.register_template_string(
"redirect",
include_str!("../../templates/html/html-redirect.html"),
)
.expect("redirect template");
hb.register_template_string("error-partial", r#"<h2 class="error">{{message}}</h2>"#)
.expect("error-partial");
hb.register_template_string("success-partial", r#"<h2 class="success">{{message}}</h2>"#)
.expect("success-partial");
hb.register_template_string("index", include_str!("../../templates/html/index.html"))
.expect("index");
hb.register_template_string("err404", include_str!("../../templates/html/404.html"))
.expect("err404");
hb.register_partial(
"LoggedOut",
include_str!("../../templates/html/nav-loggedout.html"),
)
.expect("LoggedOut");
hb.register_partial(
"LoggedIn",
include_str!("../../templates/html/nav-loggedin.html"),
)
.expect("LoggedIn");
Self { hb, profiler, auth }
}
pub async fn listen_and_serve(self, port: u16) -> ! {
let router = Router::new();
let router = self
.register_html(&router)
.layer(Extension::<Server>(self.clone()))
.layer(CookieManagerLayer::new());
let addr = SocketAddr::from(([127, 0, 0, 1], port));
println!("listening on {}", addr);
axum::Server::bind(&addr)
.serve(router.into_make_service())
.await
.unwrap();
panic!("server stopped prematurely")
}
}
#[derive(Clone, Debug)]
pub(super) enum ServerError {
Internal(String),
NotFound,
BadRequest(String),
}
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),
AuthError::Expired => ServerError::BadRequest("expired token".to_owned()),
}
}
}
impl Display for ServerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self)
}
}
impl IntoResponse for ServerError {
fn into_response(self) -> axum::response::Response {
match self {
ServerError::Internal(err) => (StatusCode::INTERNAL_SERVER_ERROR, err).into_response(),
ServerError::NotFound => (
StatusCode::NOT_FOUND,
response::Html(include_str!("../../templates/html/404.html")),
)
.into_response(),
ServerError::BadRequest(err) => (StatusCode::BAD_REQUEST, err).into_response(),
}
}
}