gmarche-rs/src/server.rs

423 lines
9.8 KiB
Rust
Raw Normal View History

2020-12-08 19:53:30 +01:00
use super::{cli, config, db, static_files, templates, utils::*};
2020-10-26 23:43:18 +01:00
use handlebars::to_json;
use serde::{Deserialize, Serialize};
2020-11-02 11:41:55 +01:00
use serde_json::value::Value as Json;
2020-10-26 23:43:18 +01:00
use sha2::{Digest, Sha512Trunc256};
use std::{
convert::{TryFrom, TryInto},
2020-10-26 23:43:18 +01:00
sync::{Arc, RwLock},
};
use warp::Filter;
2020-11-06 15:23:17 +01:00
use warp::Reply;
2020-10-26 23:43:18 +01:00
type PasswordHash = [u8; 32];
#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize)]
struct AdId([u8; 16]);
impl AdId {
fn random() -> Self {
Self(rand::random())
}
}
impl AsRef<[u8]> for AdId {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl TryFrom<&[u8]> for AdId {
type Error = std::array::TryFromSliceError;
fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
Ok(Self(v.try_into()?))
}
}
2020-10-26 23:43:18 +01:00
pub async fn start_server(
config: config::Config,
dbs: db::Dbs,
templates: templates::Templates<'static>,
opt: cli::MainOpt,
) {
let templates = Arc::new(templates);
2020-11-02 11:41:55 +01:00
let cache_ads = Arc::new(RwLock::new(to_json(
dbs.ads
.iter()
.filter_map(|x| {
let (ad_id, ad) = x.ok()?;
Some(AdWithId {
id: hex::encode(ad_id.as_ref()),
ad: bincode::deserialize::<Ad>(&ad).ok()?,
})
2020-11-02 11:41:55 +01:00
})
.collect::<Vec<AdWithId>>(),
)));
2020-10-27 11:15:46 +01:00
2020-11-06 15:23:17 +01:00
let config = Arc::new(config);
let dbs = Arc::new(dbs);
2020-10-26 23:43:18 +01:00
let handle_index = {
2020-11-02 11:41:55 +01:00
let cache_ads = cache_ads.clone();
2020-11-06 15:23:17 +01:00
let templates = templates.clone();
2020-11-02 11:41:55 +01:00
move |errors: &[ErrorTemplate]| {
2020-10-26 23:43:18 +01:00
warp::reply::html(
templates
.hb
2020-11-02 11:41:55 +01:00
.render(
"index.html",
&IndexTemplate {
lang: "fr",
errors,
ads: &*cache_ads.read().unwrap(),
},
)
2020-10-26 23:43:18 +01:00
.unwrap_or_else(|e| e.to_string()),
)
2020-12-08 22:39:02 +01:00
.into_response()
2020-10-26 23:43:18 +01:00
}
};
2020-11-06 15:23:17 +01:00
let handle_admin_login = {
let templates = templates.clone();
move |errors: &[ErrorTemplate]| {
warp::reply::html(
templates
.hb
.render(
"admin_login.html",
&AdminLoginTemplate { lang: "fr", errors },
)
.unwrap_or_else(|e| e.to_string()),
)
2020-12-08 22:39:02 +01:00
.into_response()
2020-11-06 15:23:17 +01:00
}
};
let handle_admin = {
2020-12-08 10:56:32 +01:00
let cache_ads = cache_ads.clone();
2020-11-06 15:23:17 +01:00
move |errors: &[ErrorTemplate]| {
warp::reply::html(
templates
.hb
2020-12-08 10:56:32 +01:00
.render(
"admin.html",
&AdminTemplate {
lang: "fr",
errors,
ads: &*cache_ads.read().unwrap(),
},
)
2020-11-06 15:23:17 +01:00
.unwrap_or_else(|e| e.to_string()),
)
2020-12-08 22:39:02 +01:00
.into_response()
2020-11-06 15:23:17 +01:00
}
};
2020-10-26 23:43:18 +01:00
let route_static = warp::path("static")
.and(warp::get())
.and(warp::fs::dir(opt.dir.0.join(static_files::STATIC_DIR)));
2020-12-08 19:53:30 +01:00
let handle_new_ad =
{
let handle_index = handle_index.clone();
let dbs = dbs.clone();
let cache_ads = cache_ads.clone();
move |query: NewAdQuery| {
let mut hasher = Sha512Trunc256::new();
hasher.update(query.psw);
dbs.ads
.insert(
AdId::random(),
bincode::serialize(&Ad {
author: query.author,
password: hasher.finalize()[..].try_into().unwrap(),
2020-11-02 13:14:09 +01:00
price: query.price,
2020-12-08 19:53:30 +01:00
pubkey: (!query.pubkey.is_empty()).then_some(match format_pubkey(&query.pubkey) {
Ok(pubkey) => pubkey,
Err(e) => return handle_index(&[ErrorTemplate{text: match e {
PubkeyDecodeError::BadChecksum => "La somme de contrôle de la clé publique est incorrecte.",
PubkeyDecodeError::BadFormat => "Le format de la clé publique est incorrect.",
}}])
}),
2020-11-02 18:12:50 +01:00
quantity: query.quantity,
time: 0,
title: query.title,
})
.unwrap(),
)
.unwrap();
2020-12-08 19:53:30 +01:00
dbs.ads.flush().unwrap();
let mut cache_ads = cache_ads.write().unwrap();
*cache_ads = to_json(
dbs.ads
.iter()
.filter_map(|x| {
let (ad_id, ad) = x.ok()?;
Some(AdWithId {
id: hex::encode(ad_id.as_ref()),
ad: bincode::deserialize::<Ad>(&ad).ok()?,
})
})
2020-12-08 19:53:30 +01:00
.collect::<Vec<AdWithId>>(),
);
drop(cache_ads);
2020-12-08 22:39:02 +01:00
redirect_302("/").into_response()
2020-12-08 19:53:30 +01:00
}
};
let handle_rm_ad = {
let handle_index = handle_index.clone();
2020-12-08 10:56:32 +01:00
let dbs = dbs.clone();
let cache_ads = cache_ads.clone();
move |query: RmAdQuery| {
let mut hasher = Sha512Trunc256::new();
hasher.update(query.psw);
let password: PasswordHash = hasher.finalize()[..].try_into().unwrap();
/*query
.ads
.into_iter()
.filter_map(|x| Some(AdId::try_from(hex::decode(x).ok()?.as_ref()).ok()?))
.for_each(|ad_id| {
if let Some(raw) = dbs.ads.get(&ad_id).unwrap() {
if let Ok(ad) = bincode::deserialize::<Ad>(&raw) {
if ad.password == password {
dbs.ads.remove(&ad_id).unwrap();
}
}
}
});*/
if let Ok(ad_id) = hex::decode(query.ad) {
if let Ok(ad_id) = AdId::try_from(ad_id.as_ref()) {
if let Some(raw) = dbs.ads.get(&ad_id).unwrap() {
if let Ok(ad) = bincode::deserialize::<Ad>(&raw) {
if ad.password == password {
dbs.ads.remove(&ad_id).unwrap();
dbs.ads.flush().unwrap();
2020-11-02 11:41:55 +01:00
let mut cache_ads = cache_ads.write().unwrap();
*cache_ads = to_json(
dbs.ads
.iter()
.filter_map(|x| {
let (ad_id, ad) = x.ok()?;
Some(AdWithId {
id: hex::encode(ad_id.as_ref()),
ad: bincode::deserialize::<Ad>(&ad).ok()?,
})
2020-11-02 11:41:55 +01:00
})
.collect::<Vec<AdWithId>>(),
);
2020-11-02 11:41:55 +01:00
} else {
2020-11-02 20:24:39 +01:00
return handle_index(&[ErrorTemplate {
text: "Le mot de passe de l'annonce est incorrect.",
}]);
}
}
}
}
}
2020-12-08 22:39:02 +01:00
redirect_302("/").into_response()
}
};
2020-12-08 10:56:32 +01:00
let handle_admin_rm_ad = {
move |query: AdminRmAdQuery| {
if let Ok(ad_id) = hex::decode(query.ad) {
if let Ok(ad_id) = AdId::try_from(ad_id.as_ref()) {
if dbs.ads.remove(&ad_id).unwrap().is_some() {
dbs.ads.flush().unwrap();
let mut cache_ads = cache_ads.write().unwrap();
*cache_ads = to_json(
dbs.ads
.iter()
.filter_map(|x| {
let (ad_id, ad) = x.ok()?;
Some(AdWithId {
id: hex::encode(ad_id.as_ref()),
ad: bincode::deserialize::<Ad>(&ad).ok()?,
})
})
.collect::<Vec<AdWithId>>(),
);
}
}
}
2020-12-08 22:39:02 +01:00
redirect_302("/admin").into_response()
2020-12-08 10:56:32 +01:00
}
};
2020-11-02 11:41:55 +01:00
let route_index = warp::path::end().and(warp::get().map(move || handle_index(&[])).or(
warp::post().and(
warp::body::form::<Query>().map(move |query: Query| match query {
Query::NewAdQuery(query) => handle_new_ad(query),
Query::RmAdQuery(query) => handle_rm_ad(query),
}),
),
));
2020-10-27 11:15:46 +01:00
2020-11-06 15:23:17 +01:00
let route_admin = warp::path("admin").and(
warp::path::end()
.and(
warp::cookie("admin")
2020-12-08 10:56:32 +01:00
.and(warp::body::form::<AdminQuery>())
2020-11-06 15:23:17 +01:00
.map({
let handle_admin_login = handle_admin_login.clone();
let config = config.clone();
2020-12-08 10:56:32 +01:00
move |psw, query| {
if config.admin_passwords.contains(&psw) {
let AdminQuery::RmAdQuery(query) = query;
handle_admin_rm_ad(query)
} else {
2020-11-06 15:23:17 +01:00
handle_admin_login(&[ErrorTemplate {
text: "Mot de passe administrateur invalide",
}])
}
}
})
2020-12-08 10:56:32 +01:00
.or(warp::cookie("admin").map({
let handle_admin = handle_admin.clone();
let handle_admin_login = handle_admin_login.clone();
let config = config.clone();
move |psw| {
if config.admin_passwords.contains(&psw) {
handle_admin(&[])
} else {
handle_admin_login(&[ErrorTemplate {
text: "Mot de passe administrateur invalide",
}])
}
}
}))
2020-11-06 15:23:17 +01:00
.or(warp::path::end().map({
let handle_admin_login = handle_admin_login.clone();
2020-12-08 19:53:30 +01:00
move || handle_admin_login(&[])
2020-11-06 15:23:17 +01:00
})),
)
.or(warp::path("login").and(warp::path::end()).and(
warp::post()
.and(warp::get().map(move || handle_admin(&[])))
.or(warp::body::form::<AdminLoginQuery>().map({
let config = config.clone();
move |query: AdminLoginQuery| {
if config.admin_passwords.contains(&query.psw) {
warp::reply::with_header(
2020-12-08 22:39:02 +01:00
redirect_302("/admin").into_response(),
2020-11-06 15:23:17 +01:00
"Set-Cookie",
2020-12-08 10:56:32 +01:00
format!(
"admin={}; path=/; HttpOnly",
urlencoding::encode(&query.psw)
),
2020-11-06 15:23:17 +01:00
)
.into_response()
} else {
handle_admin_login(&[ErrorTemplate {
text: "Mot de passe administrateur invalide",
}])
.into_response()
}
}
})),
))
2020-12-08 10:56:32 +01:00
.or(warp::path("logout").and(warp::path::end()).map(|| {
warp::reply::with_header(
2020-12-08 22:39:02 +01:00
redirect_302("/").into_response(),
2020-12-08 10:56:32 +01:00
"Set-Cookie",
"admin=; HttpOnly; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT",
)
.into_response()
})),
2020-11-06 15:23:17 +01:00
);
warp::serve(route_static.or(route_index).or(route_admin))
2020-10-26 23:43:18 +01:00
.run(config.listen)
.await;
}
2020-11-02 11:41:55 +01:00
#[derive(Serialize)]
struct IndexTemplate<'a> {
lang: &'a str,
errors: &'a [ErrorTemplate<'a>],
ads: &'a Json,
}
2020-11-06 15:23:17 +01:00
#[derive(Serialize)]
struct AdminLoginTemplate<'a> {
lang: &'a str,
errors: &'a [ErrorTemplate<'a>],
}
#[derive(Serialize)]
struct AdminTemplate<'a> {
lang: &'a str,
errors: &'a [ErrorTemplate<'a>],
2020-12-08 10:56:32 +01:00
ads: &'a Json,
2020-11-06 15:23:17 +01:00
}
2020-11-02 11:41:55 +01:00
#[derive(Serialize)]
struct ErrorTemplate<'a> {
text: &'a str,
}
2020-10-26 23:43:18 +01:00
#[derive(Clone, Debug, Deserialize)]
struct NewAdQuery {
author: String,
2020-11-02 13:14:09 +01:00
price: String,
psw: String,
2020-10-26 23:43:18 +01:00
pubkey: String,
2020-11-02 18:12:50 +01:00
quantity: String,
2020-10-26 23:43:18 +01:00
title: String,
}
#[derive(Clone, Debug, Deserialize)]
struct RmAdQuery {
ad: String,
psw: String,
}
2020-12-08 10:56:32 +01:00
#[derive(Clone, Debug, Deserialize)]
struct AdminRmAdQuery {
ad: String,
}
2020-11-06 15:23:17 +01:00
#[derive(Clone, Debug, Deserialize)]
struct AdminLoginQuery {
psw: String,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(tag = "a")]
enum Query {
#[serde(rename = "new_ad")]
NewAdQuery(NewAdQuery),
#[serde(rename = "rm_ad")]
RmAdQuery(RmAdQuery),
}
2020-12-08 10:56:32 +01:00
#[derive(Clone, Debug, Deserialize)]
#[serde(tag = "a")]
enum AdminQuery {
#[serde(rename = "rm_ad")]
RmAdQuery(AdminRmAdQuery),
}
#[derive(Clone, Debug, Deserialize, Serialize)]
2020-10-26 23:43:18 +01:00
struct Ad {
author: String,
password: PasswordHash,
2020-11-02 13:14:09 +01:00
price: String,
2020-10-26 23:43:18 +01:00
pubkey: Option<String>,
2020-11-02 18:12:50 +01:00
quantity: String,
2020-10-26 23:43:18 +01:00
time: u64,
title: String,
}
#[derive(Clone, Debug, Serialize)]
struct AdWithId {
id: String,
ad: Ad,
}