Ads db, new/rm, better query handling, icon
This commit is contained in:
parent
6a75946509
commit
bcfccd6a0e
|
@ -426,6 +426,8 @@ dependencies = [
|
|||
"bincode",
|
||||
"dirs",
|
||||
"handlebars",
|
||||
"hex",
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
|
@ -519,6 +521,12 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
@ -779,6 +787,16 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
@ -1416,6 +1434,7 @@ dependencies = [
|
|||
"lazy_static",
|
||||
"memchr",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"pin-project-lite",
|
||||
"slab",
|
||||
"tokio-macros",
|
||||
|
|
|
@ -8,10 +8,12 @@ edition = "2018"
|
|||
bincode = "1.3.1"
|
||||
dirs = "3.0.1"
|
||||
handlebars = "3.5.1"
|
||||
hex = "0.4.2"
|
||||
rand = "0.7.3"
|
||||
serde = { version = "1.0.117", features = ["derive"] }
|
||||
serde_json = "1.0.59"
|
||||
sha2 = "0.9.1"
|
||||
sled = "0.34.4"
|
||||
structopt = "0.3.20"
|
||||
tokio = { version = "0.2.22", features = ["macros"] }
|
||||
tokio = { version = "0.2.22", features = ["macros", "rt-threaded"] }
|
||||
warp = "0.2.5"
|
||||
|
|
12
src/db.rs
12
src/db.rs
|
@ -1,15 +1,21 @@
|
|||
use sled::{Db, IVec, Tree};
|
||||
use sled::{Db, Tree};
|
||||
use std::path::Path;
|
||||
|
||||
const DB_DIR: &'static str = "db";
|
||||
|
||||
const DB_NAME_ADS: &'static str = "ads";
|
||||
|
||||
pub struct Dbs {
|
||||
db: Db,
|
||||
pub db: Db,
|
||||
pub ads: Tree,
|
||||
}
|
||||
|
||||
pub fn load_dbs(path: &Path) -> Dbs {
|
||||
//std::fs::create_dir_all(path.join(DB_DIR)).expect("Cannot create db dir");
|
||||
let db = sled::open(path.join(DB_DIR)).expect("Cannot open db");
|
||||
|
||||
Dbs { db }
|
||||
Dbs {
|
||||
ads: db.open_tree(DB_NAME_ADS).unwrap(),
|
||||
db,
|
||||
}
|
||||
}
|
||||
|
|
194
src/server.rs
194
src/server.rs
|
@ -5,13 +5,36 @@ use serde::{Deserialize, Serialize};
|
|||
use serde_json::value::{Map, Value as Json};
|
||||
use sha2::{Digest, Sha512Trunc256};
|
||||
use std::{
|
||||
convert::TryInto,
|
||||
convert::{TryFrom, TryInto},
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
use warp::Filter;
|
||||
|
||||
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()?))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn start_server(
|
||||
config: config::Config,
|
||||
dbs: db::Dbs,
|
||||
|
@ -20,16 +43,28 @@ pub async fn start_server(
|
|||
) {
|
||||
let templates = Arc::new(templates);
|
||||
|
||||
let ads = vec![];
|
||||
|
||||
let mut data = Map::<String, Json>::new();
|
||||
data.insert("lang".into(), to_json("fr"));
|
||||
data.insert("ads".into(), to_json(ads.clone()));
|
||||
|
||||
let ads = Arc::new(RwLock::new(ads));
|
||||
data.insert(
|
||||
"ads".into(),
|
||||
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>>(),
|
||||
),
|
||||
);
|
||||
|
||||
let data = Arc::new(RwLock::new(data));
|
||||
|
||||
let dbs = Arc::new(dbs);
|
||||
|
||||
let handle_index = {
|
||||
let data = data.clone();
|
||||
move || {
|
||||
|
@ -46,45 +81,132 @@ pub async fn start_server(
|
|||
.and(warp::get())
|
||||
.and(warp::fs::dir(opt.dir.0.join(static_files::STATIC_DIR)));
|
||||
|
||||
let route_index = warp::path::end().and(
|
||||
warp::get().map(handle_index.clone()).or(warp::post()
|
||||
.and(warp::body::form::<NewAdQuery>())
|
||||
.map(move |ad: NewAdQuery| {
|
||||
let mut hasher = Sha512Trunc256::new();
|
||||
hasher.update(ad.password);
|
||||
ads.write().unwrap().push(Ad {
|
||||
author: ad.author,
|
||||
password: hasher.finalize()[..].try_into().unwrap(),
|
||||
pubkey: (!ad.pubkey.is_empty()).then_some(ad.pubkey),
|
||||
time: 0,
|
||||
title: ad.title,
|
||||
});
|
||||
data.write()
|
||||
.unwrap()
|
||||
.insert("ads".into(), to_json(&*ads.read().unwrap()));
|
||||
handle_index()
|
||||
})),
|
||||
);
|
||||
let handle_new_ad = {
|
||||
let handle_index = handle_index.clone();
|
||||
let dbs = dbs.clone();
|
||||
let data = data.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(),
|
||||
pubkey: (!query.pubkey.is_empty()).then_some(query.pubkey),
|
||||
time: 0,
|
||||
title: query.title,
|
||||
})
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
dbs.ads.flush().unwrap();
|
||||
data.write().unwrap().insert(
|
||||
"ads".into(),
|
||||
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>>(),
|
||||
),
|
||||
);
|
||||
handle_index()
|
||||
}
|
||||
};
|
||||
|
||||
let handle_rm_ad = {
|
||||
let handle_index = handle_index.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();
|
||||
data.write().unwrap().insert(
|
||||
"ads".into(),
|
||||
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>>(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
handle_index()
|
||||
}
|
||||
};
|
||||
|
||||
let route_index = warp::path::end().and(warp::get().map(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),
|
||||
}),
|
||||
)));
|
||||
|
||||
warp::serve(route_static.or(route_index))
|
||||
.run(config.listen)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct WotdQuery {
|
||||
wotd: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
struct NewAdQuery {
|
||||
author: String,
|
||||
password: String,
|
||||
psw: String,
|
||||
pubkey: String,
|
||||
title: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
struct RmAdQuery {
|
||||
ad: String,
|
||||
psw: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[serde(tag = "a")]
|
||||
enum Query {
|
||||
#[serde(rename = "new_ad")]
|
||||
NewAdQuery(NewAdQuery),
|
||||
#[serde(rename = "rm_ad")]
|
||||
RmAdQuery(RmAdQuery),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
struct Ad {
|
||||
author: String,
|
||||
password: PasswordHash,
|
||||
|
@ -92,3 +214,9 @@ struct Ad {
|
|||
time: u64,
|
||||
title: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
struct AdWithId {
|
||||
id: String,
|
||||
ad: Ad,
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ static STATIC_FILES: &'static [(&str, &[u8])] = &[
|
|||
("style1.css", include_bytes!("../static/style1.css")),
|
||||
("standgm.png", include_bytes!("../static/standgm.png")),
|
||||
("banner.jpg", include_bytes!("../static/banner.jpg")),
|
||||
("icon.png", include_bytes!("../static/icon.png")),
|
||||
];
|
||||
|
||||
pub fn init_static_files(dir: &Path) {
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 9.3 KiB |
|
@ -4,6 +4,7 @@
|
|||
<meta charset="utf-8"/>
|
||||
<title>ĞMarché</title>
|
||||
<link rel="stylesheet" href="/static/style1.css"/>
|
||||
<link rel="shortcut icon" href="/static/icon.png"/>
|
||||
</head>
|
||||
<body>
|
||||
<div class="center">
|
||||
|
@ -13,33 +14,45 @@
|
|||
|
||||
<p>Ceci est une démo du nouveau site ĞMarché en développement. C'est très moche et il n'y a pas tellement de fonctionnalités mais ça avance. ;)</p>
|
||||
|
||||
<table border="1">
|
||||
<thead>
|
||||
<tr><td>Annonce</td><td>Vendeur</td></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each ads}}
|
||||
<tr>
|
||||
<td>{{this.title}}</td>
|
||||
<td>{{#if this.pubkey}}<details><summary>{{this.author}}</summary><br/>{{this.pubkey}}</details>{{else}}{{this.author}}{{/if}}</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
<form method="post">
|
||||
<table border="1">
|
||||
<thead>
|
||||
<tr><td></td><td>Annonce</td><td>Vendeur</td></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each ads}}
|
||||
<tr>
|
||||
<td><input type="radio" name="ad" value="{{this.id}}"/></td>
|
||||
<td>{{this.ad.title}}</td>
|
||||
<td>{{#if this.ad.pubkey}}<details><summary>{{this.ad.author}}</summary><br/>{{this.ad.pubkey}}</details>{{else}}{{this.ad.author}}{{/if}}</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<input type="hidden" name="a" value="rm_ad" autocomplete="off"/>
|
||||
<fieldset>
|
||||
<legend>Supprimer l'annonce sélectionnée</legend>
|
||||
<label for="f_rm_psw">Mot de passe :</label>
|
||||
<input type="password" id="f_rm_psw" name="psw"/><br/>
|
||||
<input type="submit" value="Supprimer"/>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<img id="stand" alt="Marché" src="/static/standgm.png"/>
|
||||
|
||||
<form method="post">
|
||||
<input type="hidden" name="a" value="new_ad" autocomplete="off"/>
|
||||
<fieldset>
|
||||
<legend>Nouvelle annonce</legend>
|
||||
<label for="f_title">Titre :</label>
|
||||
<input type="text" id="f_title" name="title"/><br />
|
||||
<label for="f_author">Votre nom :</label>
|
||||
<input type="text" id="f_author" name="author"/><br />
|
||||
<label for="f_pubkey">Clé publique :</label>
|
||||
<input type="text" id="f_pubkey" name="pubkey"/><br />
|
||||
<label for="f_password">Mot de passe :</label>
|
||||
<input type="text" id="f_password" name="password"/><br />
|
||||
<label for="f_new_title">Titre :</label>
|
||||
<input type="text" id="f_new_title" name="title"/><br />
|
||||
<label for="f_new_author">Votre nom :</label>
|
||||
<input type="text" id="f_new_author" name="author"/><br />
|
||||
<label for="f_new_pubkey">Clé publique :</label>
|
||||
<input type="text" id="f_new_pubkey" name="pubkey"/><br />
|
||||
<label for="f_new_psw">Mot de passe :</label>
|
||||
<input type="text" id="f_new_psw" name="psw"/><br />
|
||||
<input type="submit" value="Publier"/>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
|
Loading…
Reference in New Issue