use super::{cli, config, db, queries::*, static_files, templates::*, utils::*}; use handlebars::to_json; use sha2::{Digest, Sha512Trunc256}; use std::{ convert::{TryFrom, TryInto}, sync::Arc, }; pub async fn start_server( config: config::Config, dbs: db::Dbs, templates: Templates<'static>, opt: cli::MainOpt, ) { let templates = Arc::new(templates); let config = Arc::new(config); let dbs = Arc::new(dbs); let mut app = tide::new(); app.at("/static") .serve_dir(opt.dir.0.join(static_files::STATIC_DIR)) .unwrap(); app.at("/").get({ let templates = templates.clone(); let dbs = dbs.clone(); move |req: tide::Request<()>| serve_index(req, templates.clone(), dbs.clone(), &[], None) }); app.at("/").post({ let templates = templates.clone(); let dbs = dbs.clone(); move |req: tide::Request<()>| handle_post_index(req, templates.clone(), dbs.clone()) }); app.at("/ad/:ad").get({ let templates = templates.clone(); let dbs = dbs.clone(); move |req: tide::Request<()>| serve_index(req, templates.clone(), dbs.clone(), &[], None) }); app.at("/admin").get({ let config = config.clone(); let templates = templates.clone(); let dbs = dbs.clone(); move |req: tide::Request<()>| { handle_admin(req, config.clone(), templates.clone(), dbs.clone()) } }); app.at("/admin").post({ let config = config.clone(); move |req: tide::Request<()>| { handle_post_admin(req, config.clone(), templates.clone(), dbs.clone()) } }); app.at("/admin/logout").get(handle_admin_logout); app.listen(config.listen).await.unwrap(); } async fn serve_index<'a>( req: tide::Request<()>, templates: Arc>, dbs: Arc, errors: &[ErrorTemplate<'a>], new_ad_form_refill: Option, ) -> tide::Result { let selected_ad = req.param("ad").ok(); Ok(tide::Response::builder(200) .content_type(tide::http::mime::HTML) .body( templates .hb .render( "index.html", &IndexTemplate { lang: "fr", errors, ads: &to_json( dbs.ads .iter() .filter_map(|x| { let (ad_id, ad) = x.ok()?; let ad_id = hex::encode(ad_id.as_ref()); Some(AdWithId { ad: bincode::deserialize::(&ad).ok()?, selected: selected_ad.map_or(false, |i| i == ad_id), id: ad_id, }) }) .collect::>(), ), new_ad_form_refill, }, ) .unwrap_or_else(|e| e.to_string()), ) .build()) } async fn serve_admin<'a>( req: tide::Request<()>, templates: Arc>, dbs: Arc, errors: &[ErrorTemplate<'a>], ) -> tide::Result { let selected_ad = req.param("ad").ok(); Ok(tide::Response::builder(200) .content_type(tide::http::mime::HTML) .body( templates .hb .render( "admin.html", &AdminTemplate { lang: "fr", errors, ads: &to_json( dbs.ads .iter() .filter_map(|x| { let (ad_id, ad) = x.ok()?; let ad_id = hex::encode(ad_id.as_ref()); Some(AdWithId { ad: bincode::deserialize::(&ad).ok()?, selected: selected_ad.map_or(false, |i| i == ad_id), id: ad_id, }) }) .collect::>(), ), }, ) .unwrap_or_else(|e| e.to_string()), ) .build()) } async fn serve_admin_login<'a>( _req: tide::Request<()>, templates: Arc>, errors: &[ErrorTemplate<'a>], ) -> tide::Result { Ok(tide::Response::builder(200) .content_type(tide::http::mime::HTML) .body( templates .hb .render( "admin_login.html", &AdminLoginTemplate { lang: "fr", errors }, ) .unwrap_or_else(|e| e.to_string()), ) .build()) } async fn handle_post_index( mut req: tide::Request<()>, templates: Arc>, dbs: Arc, ) -> tide::Result { match req.body_form::().await? { Query::NewAdQuery(query) => handle_new_ad(req, templates.clone(), dbs.clone(), query).await, Query::RmAdQuery(query) => handle_rm_ad(req, templates.clone(), dbs.clone(), query).await, } } async fn handle_new_ad( req: tide::Request<()>, templates: Arc>, dbs: Arc, query: NewAdQuery, ) -> tide::Result { let mut hasher = Sha512Trunc256::new(); hasher.update(&query.psw); dbs.ads .insert( AdId::random(), bincode::serialize(&Ad { pubkey: if query.pubkey.is_empty() { None } else { Some(match format_pubkey(&query.pubkey) { Ok(pubkey) => pubkey, Err(e) => return serve_index( req, templates, dbs, &[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." } }, }], Some(query), ) .await, }) }, author: query.author, password: hasher.finalize()[..].try_into().unwrap(), price: query.price, quantity: query.quantity, time: 0, title: query.title, }) .unwrap(), ) .unwrap(); dbs.ads.flush().unwrap(); Ok(tide::Redirect::new("/").into()) } async fn handle_rm_ad( req: tide::Request<()>, templates: Arc>, dbs: Arc, query: RmAdQuery, ) -> tide::Result { 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::(&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::(&raw) { if ad.password == password { dbs.ads.remove(&ad_id).unwrap(); dbs.ads.flush().unwrap(); } else { return serve_index( req, templates, dbs, &[ErrorTemplate { text: "Le mot de passe de l'annonce est incorrect.", }], None, ) .await; } } } } } Ok(tide::Redirect::new("/").into()) } async fn handle_admin( req: tide::Request<()>, config: Arc, templates: Arc>, dbs: Arc, ) -> tide::Result { if let Some(psw) = req.cookie("admin") { if config.admin_passwords.contains(&String::from(psw.value())) { serve_admin(req, templates, dbs, &[]).await } else { serve_admin_login( req, templates, &[ErrorTemplate { text: "Mot de passe administrateur invalide", }], ) .await } } else { serve_admin_login(req, templates, &[]).await } } async fn handle_post_admin( mut req: tide::Request<()>, config: Arc, templates: Arc>, dbs: Arc, ) -> tide::Result { if let Some(psw) = req.cookie("admin") { if config.admin_passwords.contains(&String::from(psw.value())) { match req.body_form::().await? { AdminQuery::RmAdQuery(query) => { if let Ok(ad_id) = hex::decode(query.ad) { if let Ok(ad_id) = AdId::try_from(ad_id.as_ref()) { dbs.ads.remove(&ad_id).unwrap(); dbs.ads.flush().unwrap(); } } Ok(tide::Redirect::new("/admin").into()) } _ => serve_admin(req, templates, dbs, &[]).await, } } else { serve_admin_login( req, templates, &[ErrorTemplate { text: "Mot de passe administrateur invalide", }], ) .await } } else if let AdminQuery::LoginQuery(query) = req.body_form::().await? { if config.admin_passwords.contains(&query.psw) { serve_admin(req, templates, dbs, &[]).await.map(|mut r| { let mut cookie = tide::http::Cookie::new("admin", query.psw); cookie.set_http_only(Some(true)); cookie.set_path("/"); r.insert_cookie(cookie); r }) } else { serve_admin_login( req, templates, &[ErrorTemplate { text: "Mot de passe administrateur invalide", }], ) .await } } else { serve_admin_login(req, templates, &[]).await } } async fn handle_admin_logout(req: tide::Request<()>) -> tide::Result { let mut r: tide::Response = tide::Redirect::new("/").into(); if let Some(mut cookie) = req.cookie("admin") { cookie.set_path("/"); r.remove_cookie(cookie); } Ok(r) }