168 lines
6.4 KiB
Rust
168 lines
6.4 KiB
Rust
use std::{collections::HashMap, str::FromStr};
|
|
|
|
use warp::{
|
|
http::HeaderValue,
|
|
hyper::Uri,
|
|
path::Tail,
|
|
reply::{Html, Response},
|
|
Filter, Rejection, Reply,
|
|
};
|
|
|
|
use crate::svc::auth::AuthError;
|
|
|
|
use super::{
|
|
servek::{Server, ServerError},
|
|
CreateProfileRequest, ErrorTemplate,
|
|
};
|
|
use rust_embed::RustEmbed;
|
|
|
|
#[derive(RustEmbed)]
|
|
#[folder = "static"]
|
|
struct StaticData;
|
|
|
|
impl Server {
|
|
pub(super) async fn html(
|
|
&self,
|
|
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
|
|
Self::index()
|
|
.or(self.profile())
|
|
.or(self.create_profile())
|
|
.or(Server::static_files())
|
|
.or(self.login_page())
|
|
.or(self.login())
|
|
.or(Self::handler_404())
|
|
}
|
|
|
|
fn index() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
|
|
warp::get().and(warp::path::end().map(move || {
|
|
warp::reply::html(include_str!("../../templates/html/index.html").to_owned())
|
|
}))
|
|
}
|
|
|
|
fn handler_404() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
|
|
warp::get().and(warp::path::end().map(move || {
|
|
warp::reply::html(include_str!("../../templates/html/404.html").to_owned())
|
|
}))
|
|
}
|
|
|
|
fn login_with_error(&self, error: String) -> Result<Html<String>, Rejection> {
|
|
self.hb
|
|
.render("login-error", &serde_json::json!(ErrorTemplate { error }))
|
|
.map(|html| warp::reply::html(html))
|
|
.map_err(|e| ServerError::from(e).rejection())
|
|
}
|
|
|
|
fn login_page(
|
|
&self,
|
|
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
|
|
warp::get().and(warp::path::path("login").map(move || {
|
|
warp::reply::html(include_str!("../../templates/html/login.html").to_owned())
|
|
}))
|
|
}
|
|
|
|
fn login(&self) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
|
|
warp::post().and(
|
|
warp::body::content_length_limit(8192).and(
|
|
warp::path::path("login")
|
|
.and(Self::with_server(self.clone()))
|
|
.and(warp::body::form())
|
|
.and_then(|srv: Server, body: HashMap<String, String>| async move {
|
|
let user = body.get("username").ok_or(
|
|
ServerError::BadRequest("no username provided".to_owned()).rejection(),
|
|
)?;
|
|
let pass = body.get("password").ok_or(
|
|
ServerError::BadRequest("no password provided".to_owned()).rejection(),
|
|
)?;
|
|
let token = srv
|
|
.auth
|
|
.login(user.clone(), pass.clone())
|
|
.await
|
|
.map(|html| warp::reply::html(html));
|
|
if let Err(e) = &token {
|
|
if let AuthError::InvalidCredentials = e {
|
|
return srv.login_with_error("invalid credentials".to_owned());
|
|
}
|
|
}
|
|
token.map_err(|e| ServerError::from(e).rejection())
|
|
}),
|
|
),
|
|
)
|
|
}
|
|
|
|
fn with_server(
|
|
srv: Server,
|
|
) -> impl Filter<Extract = (Server,), Error = std::convert::Infallible> + Clone {
|
|
warp::any().map(move || srv.clone())
|
|
}
|
|
|
|
fn profile(&self) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
|
warp::get().and(
|
|
warp::path!("@" / String)
|
|
.and(Self::with_server(self.clone()))
|
|
.and_then(|username: String, srv: Server| async move {
|
|
srv.hb
|
|
.render(
|
|
"profile",
|
|
&serde_json::json!(srv
|
|
.profiler
|
|
.profile(username)
|
|
.await
|
|
.map_err(|e| ServerError::from(e))?),
|
|
)
|
|
.map(|html| warp::reply::html(html))
|
|
.map_err(|e| ServerError::from(e).rejection())
|
|
}),
|
|
)
|
|
}
|
|
|
|
fn create_profile(&self) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
|
warp::post().and(
|
|
warp::body::content_length_limit(8192).and(
|
|
warp::path!("@" / String)
|
|
.and(Self::with_server(self.clone()))
|
|
.and(warp::body::form())
|
|
.and_then(
|
|
|username: String, srv: Server, body: CreateProfileRequest| async move {
|
|
if body.password_hash.len() == 0 {
|
|
return Err(ServerError::BadRequest(
|
|
"cannot have an empty password".to_owned(),
|
|
)
|
|
.rejection());
|
|
}
|
|
let user = srv
|
|
.profiler
|
|
.create_user(username, body.password_hash)
|
|
.await
|
|
.map_err(|e| ServerError::from(e));
|
|
match user {
|
|
Ok(u) => Ok(warp::redirect(
|
|
Uri::from_str(format!("/@/{}", u.username).as_str()).unwrap(),
|
|
)),
|
|
Err(e) => Err(e.rejection()),
|
|
}
|
|
},
|
|
),
|
|
),
|
|
)
|
|
}
|
|
|
|
fn static_files() -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
|
warp::get().and(warp::path("static").and(warp::path::tail()).and_then(
|
|
|path: Tail| async move {
|
|
let asset = match StaticData::get(path.as_str()) {
|
|
Some(a) => a,
|
|
None => return Err(ServerError::NotFound.rejection()),
|
|
};
|
|
let mime = mime_guess::from_path(path.as_str()).first_or_octet_stream();
|
|
|
|
let mut res = Response::new(asset.data.into());
|
|
res.headers_mut().insert(
|
|
"Content-Type",
|
|
HeaderValue::from_str(mime.as_ref()).unwrap(),
|
|
);
|
|
Ok(res)
|
|
},
|
|
))
|
|
}
|
|
}
|