Compare commits

..

No commits in common. "017a64b18b19b2e03a50a0401126e13a3d478eb6" and "e8c029c9d002a169ec2e9be3be86e9d886bc7591" have entirely different histories.

7 changed files with 48 additions and 130 deletions

View File

@ -20,4 +20,3 @@ L'option `-d <dir>` permet de changer le dossier de données/config (par exemple
gmarche -d mon/autre/dossier/ start|init gmarche -d mon/autre/dossier/ start|init
Pour lancer l'instance dans un sous-dossier de l'URL (i.e. `http://example.tld/gmarche`), changez `root_url` dans la config. La valeur doit toujours commencer et terminer par un `/`.

View File

@ -12,7 +12,6 @@ pub struct Config {
pub cookies_https_only: bool, pub cookies_https_only: bool,
pub cookies_domain: Option<String>, pub cookies_domain: Option<String>,
pub listen: SocketAddr, pub listen: SocketAddr,
pub root_url: String,
} }
impl Default for Config { impl Default for Config {
@ -22,7 +21,6 @@ impl Default for Config {
cookies_https_only: false, cookies_https_only: false,
cookies_domain: None, cookies_domain: None,
listen: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 10353), listen: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 10353),
root_url: String::from("/"),
} }
} }
} }

View File

@ -17,56 +17,25 @@ pub async fn start_server(
let config = Arc::new(config); let config = Arc::new(config);
let mut app = tide::new(); let mut app = tide::new();
app.at(&format!("{}static", config.root_url)) app.at("/static")
.serve_dir(opt.dir.0.join(static_files::STATIC_DIR)) .serve_dir(opt.dir.0.join(static_files::STATIC_DIR))
.unwrap(); .unwrap();
app.at(&config.root_url).get({ app.at("/").get({
let config = config.clone();
let templates = templates.clone(); let templates = templates.clone();
let dbs = dbs.clone(); let dbs = dbs.clone();
move |req: tide::Request<()>| { move |req: tide::Request<()>| serve_index(req, templates.clone(), dbs.clone(), &[], None)
serve_index(
req,
config.clone(),
templates.clone(),
dbs.clone(),
&[],
None,
)
}
}); });
app.at(&config.root_url).post({ app.at("/").post({
let config = config.clone();
let templates = templates.clone(); let templates = templates.clone();
let dbs = dbs.clone(); let dbs = dbs.clone();
move |req: tide::Request<()>| { move |req: tide::Request<()>| handle_post_index(req, templates.clone(), dbs.clone())
handle_post_index(req, config.clone(), templates.clone(), dbs.clone())
}
}); });
app.at(&format!("{}ad/:ad", config.root_url)).post({ app.at("/ad/:ad").get({
let config = config.clone();
let templates = templates.clone(); let templates = templates.clone();
let dbs = dbs.clone(); let dbs = dbs.clone();
move |req: tide::Request<()>| { move |req: tide::Request<()>| serve_index(req, templates.clone(), dbs.clone(), &[], None)
handle_post_index(req, config.clone(), templates.clone(), dbs.clone())
}
}); });
app.at(&format!("{}ad/:ad", config.root_url)).get({ app.at("/admin").get({
let config = config.clone();
let templates = templates.clone();
let dbs = dbs.clone();
move |req: tide::Request<()>| {
serve_index(
req,
config.clone(),
templates.clone(),
dbs.clone(),
&[],
None,
)
}
});
app.at(&format!("{}admin", config.root_url)).get({
let config = config.clone(); let config = config.clone();
let templates = templates.clone(); let templates = templates.clone();
let dbs = dbs.clone(); let dbs = dbs.clone();
@ -74,38 +43,18 @@ pub async fn start_server(
handle_admin(req, config.clone(), templates.clone(), dbs.clone()) handle_admin(req, config.clone(), templates.clone(), dbs.clone())
} }
}); });
app.at(&format!("{}admin/ad/:ad", config.root_url)).get({ app.at("/admin").post({
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(&format!("{}admin", config.root_url)).post({
let config = config.clone();
let templates = templates.clone();
let dbs = dbs.clone();
move |req: tide::Request<()>| {
handle_post_admin(req, config.clone(), templates.clone(), dbs.clone())
}
});
app.at(&format!("{}admin/ad/:ad", config.root_url)).post({
let config = config.clone(); let config = config.clone();
move |req: tide::Request<()>| { move |req: tide::Request<()>| {
handle_post_admin(req, config.clone(), templates.clone(), dbs.clone()) handle_post_admin(req, config.clone(), templates.clone(), dbs.clone())
} }
}); });
app.at(&format!("{}admin/logout", config.root_url)).get({ app.at("/admin/logout").get(handle_admin_logout);
let config = config.clone();
move |req: tide::Request<()>| handle_admin_logout(req, config.clone())
});
app.listen(config.listen).await.unwrap(); app.listen(config.listen).await.unwrap();
} }
async fn serve_index<'a>( async fn serve_index<'a>(
req: tide::Request<()>, req: tide::Request<()>,
config: Arc<config::Config>,
templates: Arc<Templates<'static>>, templates: Arc<Templates<'static>>,
dbs: db::Dbs, dbs: db::Dbs,
errors: &[ErrorTemplate<'a>], errors: &[ErrorTemplate<'a>],
@ -121,7 +70,6 @@ async fn serve_index<'a>(
"index.html", "index.html",
&IndexTemplate { &IndexTemplate {
lang: "fr", lang: "fr",
root_url: &config.root_url,
errors, errors,
ads: &to_json( ads: &to_json(
dbs.ads dbs.ads
@ -147,7 +95,6 @@ async fn serve_index<'a>(
async fn serve_admin<'a>( async fn serve_admin<'a>(
req: tide::Request<()>, req: tide::Request<()>,
config: Arc<config::Config>,
templates: Arc<Templates<'static>>, templates: Arc<Templates<'static>>,
dbs: db::Dbs, dbs: db::Dbs,
errors: &[ErrorTemplate<'a>], errors: &[ErrorTemplate<'a>],
@ -162,7 +109,6 @@ async fn serve_admin<'a>(
"admin.html", "admin.html",
&AdminTemplate { &AdminTemplate {
lang: "fr", lang: "fr",
root_url: &config.root_url,
errors, errors,
ads: &to_json( ads: &to_json(
dbs.ads dbs.ads
@ -187,7 +133,6 @@ async fn serve_admin<'a>(
async fn serve_admin_login<'a>( async fn serve_admin_login<'a>(
_req: tide::Request<()>, _req: tide::Request<()>,
config: Arc<config::Config>,
templates: Arc<Templates<'static>>, templates: Arc<Templates<'static>>,
errors: &[ErrorTemplate<'a>], errors: &[ErrorTemplate<'a>],
) -> tide::Result<tide::Response> { ) -> tide::Result<tide::Response> {
@ -198,11 +143,7 @@ async fn serve_admin_login<'a>(
.hb .hb
.render( .render(
"admin_login.html", "admin_login.html",
&AdminLoginTemplate { &AdminLoginTemplate { lang: "fr", errors },
lang: "fr",
root_url: &config.root_url,
errors,
},
) )
.unwrap_or_else(|e| e.to_string()), .unwrap_or_else(|e| e.to_string()),
) )
@ -211,23 +152,17 @@ async fn serve_admin_login<'a>(
async fn handle_post_index( async fn handle_post_index(
mut req: tide::Request<()>, mut req: tide::Request<()>,
config: Arc<config::Config>,
templates: Arc<Templates<'static>>, templates: Arc<Templates<'static>>,
dbs: db::Dbs, dbs: db::Dbs,
) -> tide::Result<tide::Response> { ) -> tide::Result<tide::Response> {
match req.body_form::<Query>().await? { match req.body_form::<Query>().await? {
Query::NewAdQuery(query) => { Query::NewAdQuery(query) => handle_new_ad(req, templates.clone(), dbs.clone(), query).await,
handle_new_ad(req, config.clone(), templates.clone(), dbs.clone(), query).await Query::RmAdQuery(query) => handle_rm_ad(req, templates.clone(), dbs.clone(), query).await,
}
Query::RmAdQuery(query) => {
handle_rm_ad(req, config.clone(), templates.clone(), dbs.clone(), query).await
}
} }
} }
async fn handle_new_ad( async fn handle_new_ad(
req: tide::Request<()>, req: tide::Request<()>,
config: Arc<config::Config>,
templates: Arc<Templates<'static>>, templates: Arc<Templates<'static>>,
dbs: db::Dbs, dbs: db::Dbs,
query: NewAdQuery, query: NewAdQuery,
@ -245,7 +180,6 @@ async fn handle_new_ad(
Ok(pubkey) => pubkey, Ok(pubkey) => pubkey,
Err(e) => return serve_index( Err(e) => return serve_index(
req, req,
config,
templates, templates,
dbs, dbs,
&[ErrorTemplate { &[ErrorTemplate {
@ -274,12 +208,11 @@ async fn handle_new_ad(
) )
.unwrap(); .unwrap();
dbs.ads.flush().unwrap(); dbs.ads.flush().unwrap();
Ok(tide::Redirect::new(&config.root_url).into()) Ok(tide::Redirect::new("/").into())
} }
async fn handle_rm_ad( async fn handle_rm_ad(
req: tide::Request<()>, req: tide::Request<()>,
config: Arc<config::Config>,
templates: Arc<Templates<'static>>, templates: Arc<Templates<'static>>,
dbs: db::Dbs, dbs: db::Dbs,
query: RmAdQuery, query: RmAdQuery,
@ -311,7 +244,6 @@ async fn handle_rm_ad(
} else { } else {
return serve_index( return serve_index(
req, req,
config,
templates, templates,
dbs, dbs,
&[ErrorTemplate { &[ErrorTemplate {
@ -325,7 +257,7 @@ async fn handle_rm_ad(
} }
} }
} }
Ok(tide::Redirect::new(&config.root_url).into()) Ok(tide::Redirect::new("/").into())
} }
async fn handle_admin( async fn handle_admin(
@ -336,11 +268,10 @@ async fn handle_admin(
) -> tide::Result<tide::Response> { ) -> tide::Result<tide::Response> {
if let Some(psw) = req.cookie("admin") { if let Some(psw) = req.cookie("admin") {
if config.admin_passwords.contains(&String::from(psw.value())) { if config.admin_passwords.contains(&String::from(psw.value())) {
serve_admin(req, config, templates, dbs, &[]).await serve_admin(req, templates, dbs, &[]).await
} else { } else {
serve_admin_login( serve_admin_login(
req, req,
config,
templates, templates,
&[ErrorTemplate { &[ErrorTemplate {
text: "Mot de passe administrateur invalide", text: "Mot de passe administrateur invalide",
@ -349,7 +280,7 @@ async fn handle_admin(
.await .await
} }
} else { } else {
serve_admin_login(req, config, templates, &[]).await serve_admin_login(req, templates, &[]).await
} }
} }
@ -370,14 +301,13 @@ async fn handle_post_admin(
dbs.ads.flush().unwrap(); dbs.ads.flush().unwrap();
} }
} }
Ok(tide::Redirect::new(&format!("{}admin", config.root_url)).into()) Ok(tide::Redirect::new("/admin").into())
} }
_ => serve_admin(req, config, templates, dbs, &[]).await, _ => serve_admin(req, templates, dbs, &[]).await,
} }
} else { } else {
serve_admin_login( serve_admin_login(
req, req,
config,
templates, templates,
&[ErrorTemplate { &[ErrorTemplate {
text: "Mot de passe administrateur invalide", text: "Mot de passe administrateur invalide",
@ -387,19 +317,16 @@ async fn handle_post_admin(
} }
} else if let AdminQuery::LoginQuery(query) = req.body_form::<AdminQuery>().await? { } else if let AdminQuery::LoginQuery(query) = req.body_form::<AdminQuery>().await? {
if config.admin_passwords.contains(&query.psw) { if config.admin_passwords.contains(&query.psw) {
serve_admin(req, config.clone(), templates, dbs, &[]) serve_admin(req, templates, dbs, &[]).await.map(|mut r| {
.await let mut cookie = tide::http::Cookie::new("admin", query.psw);
.map(|mut r| { cookie.set_http_only(Some(true));
let mut cookie = tide::http::Cookie::new("admin", query.psw); cookie.set_path("/");
cookie.set_http_only(Some(true)); r.insert_cookie(cookie);
cookie.set_path(config.root_url.clone()); r
r.insert_cookie(cookie); })
r
})
} else { } else {
serve_admin_login( serve_admin_login(
req, req,
config,
templates, templates,
&[ErrorTemplate { &[ErrorTemplate {
text: "Mot de passe administrateur invalide", text: "Mot de passe administrateur invalide",
@ -408,17 +335,14 @@ async fn handle_post_admin(
.await .await
} }
} else { } else {
serve_admin_login(req, config, templates, &[]).await serve_admin_login(req, templates, &[]).await
} }
} }
async fn handle_admin_logout( async fn handle_admin_logout(req: tide::Request<()>) -> tide::Result<tide::Response> {
req: tide::Request<()>,
config: Arc<config::Config>,
) -> tide::Result<tide::Response> {
let mut r: tide::Response = tide::Redirect::new("/").into(); let mut r: tide::Response = tide::Redirect::new("/").into();
if let Some(mut cookie) = req.cookie("admin") { if let Some(mut cookie) = req.cookie("admin") {
cookie.set_path(config.root_url.clone()); cookie.set_path("/");
r.remove_cookie(cookie); r.remove_cookie(cookie);
} }
Ok(r) Ok(r)

View File

@ -49,7 +49,6 @@ pub fn load_templates<'reg>(dir: &Path) -> Templates<'reg> {
#[derive(Serialize)] #[derive(Serialize)]
pub struct IndexTemplate<'a> { pub struct IndexTemplate<'a> {
pub lang: &'a str, pub lang: &'a str,
pub root_url: &'a str,
pub errors: &'a [ErrorTemplate<'a>], pub errors: &'a [ErrorTemplate<'a>],
pub ads: &'a Json, pub ads: &'a Json,
pub new_ad_form_refill: Option<NewAdQuery>, pub new_ad_form_refill: Option<NewAdQuery>,
@ -58,14 +57,12 @@ pub struct IndexTemplate<'a> {
#[derive(Serialize)] #[derive(Serialize)]
pub struct AdminLoginTemplate<'a> { pub struct AdminLoginTemplate<'a> {
pub lang: &'a str, pub lang: &'a str,
pub root_url: &'a str,
pub errors: &'a [ErrorTemplate<'a>], pub errors: &'a [ErrorTemplate<'a>],
} }
#[derive(Serialize)] #[derive(Serialize)]
pub struct AdminTemplate<'a> { pub struct AdminTemplate<'a> {
pub lang: &'a str, pub lang: &'a str,
pub root_url: &'a str,
pub errors: &'a [ErrorTemplate<'a>], pub errors: &'a [ErrorTemplate<'a>],
pub ads: &'a Json, pub ads: &'a Json,
} }

View File

@ -3,14 +3,14 @@
<head> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<title>Administration | ĞMarché</title> <title>Administration | ĞMarché</title>
<link rel="stylesheet" href="{{root_url}}static/style1.css"/> <link rel="stylesheet" href="/static/style1.css"/>
<link rel="shortcut icon" href="{{root_url}}static/icon.png"/> <link rel="shortcut icon" href="/static/icon.png"/>
<script type="text/javascript" src="{{root_url}}static/script1.js"></script> <script type="text/javascript" src="/static/script1.js"></script>
</head> </head>
<body> <body>
<div class="center page"> <div class="center page">
<header> <header>
<a href="{{root_url}}"><img id="banner" alt="Bannière ĞMarché" src="{{root_url}}static/banner.jpg"/></a> <a href="/"><img id="banner" alt="Bannière ĞMarché" src="/static/banner.jpg"/></a>
</header> </header>
<main> <main>
@ -38,11 +38,11 @@
{{#each ads}} {{#each ads}}
<tr> <tr>
<td><input type="radio" name="ad" value="{{this.id}}" aria-label="Sélectionner l'annonce" required/></td> <td><input type="radio" name="ad" value="{{this.id}}" aria-label="Sélectionner l'annonce" required/></td>
<td onclick="ad_detail(event,'{{this.id}}')" title="Afficher le détail"><a href="{{../root_url}}admin/ad/{{this.id}}">{{this.ad.title}}</a></td> <td onclick="ad_detail(event,'{{this.id}}')" title="Afficher le détail"><a href="/admin/ad/{{this.id}}">{{this.ad.title}}</a></td>
<td>{{this.ad.quantity}}</td> <td>{{this.ad.quantity}}</td>
<td>{{this.ad.author}}</td> <td>{{this.ad.author}}</td>
</tr> </tr>
<tr id="ad-detail-{{this.id}}" class="ad-detail{{#unless this.selected}} ad-detail-no{{/unless}}"> <tr id="ad-detail-{{this.id}}" class="ad-detail ad-detail-no">
<td colspan="4"><p> <td colspan="4"><p>
{{#if this.ad.pubkey}}Clé publique&nbsp;: {{this.ad.pubkey}}<br/>{{/if}} {{#if this.ad.pubkey}}Clé publique&nbsp;: {{this.ad.pubkey}}<br/>{{/if}}
Prix&nbsp;: {{this.ad.price}} Prix&nbsp;: {{this.ad.price}}
@ -73,7 +73,7 @@
CopyLeft 2020 Pascal Engélibert<br/> CopyLeft 2020 Pascal Engélibert<br/>
Image de fond&nbsp;: Claudia Peters, FreeImages.com</p> Image de fond&nbsp;: Claudia Peters, FreeImages.com</p>
<p><a href="{{root_url}}">Accueil</a> &#8211; <a href="{{root_url}}admin/logout">Verrouiller</a></p> <p><a href="/">Accueil</a> &#8211; <a href="/admin/logout">Verrouiller</a></p>
</footer> </footer>
</div> </div>
</body> </body>

View File

@ -3,13 +3,13 @@
<head> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<title>Administration | ĞMarché</title> <title>Administration | ĞMarché</title>
<link rel="stylesheet" href="{{root_url}}static/style1.css"/> <link rel="stylesheet" href="/static/style1.css"/>
<link rel="shortcut icon" href="{{root_url}}static/icon.png"/> <link rel="shortcut icon" href="/static/icon.png"/>
</head> </head>
<body> <body>
<div class="center page"> <div class="center page">
<header> <header>
<a href="{{root_url}}"><img id="banner" alt="Bannière ĞMarché" src="{{root_url}}static/banner.jpg"/></a> <a href="/"><img id="banner" alt="Bannière ĞMarché" src="/static/banner.jpg"/></a>
</header> </header>
<main> <main>
@ -28,7 +28,7 @@
</div> </div>
{{/if}} {{/if}}
<form method="post"> <form action="/admin" method="post">
<input type="hidden" name="a" value="login" autocomplete="off"/> <input type="hidden" name="a" value="login" autocomplete="off"/>
<label for="f_psw">Mot de passe&nbsp;:</label> <label for="f_psw">Mot de passe&nbsp;:</label>
<input type="password" id="f_psw" name="psw" autofocus/> <input type="password" id="f_psw" name="psw" autofocus/>
@ -45,7 +45,7 @@
CopyLeft 2020 Pascal Engélibert<br/> CopyLeft 2020 Pascal Engélibert<br/>
Image de fond&nbsp;: Claudia Peters, FreeImages.com</p> Image de fond&nbsp;: Claudia Peters, FreeImages.com</p>
<p><a href="{{root_url}}">Accueil</a></p> <p><a href="/">Accueil</a></p>
</footer> </footer>
</div> </div>
</body> </body>

View File

@ -3,14 +3,14 @@
<head> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<title>ĞMarché</title> <title>ĞMarché</title>
<link rel="stylesheet" href="{{root_url}}static/style1.css"/> <link rel="stylesheet" href="/static/style1.css"/>
<link rel="shortcut icon" href="{{root_url}}static/icon.png"/> <link rel="shortcut icon" href="/static/icon.png"/>
<script type="text/javascript" src="{{root_url}}static/script1.js"></script> <script type="text/javascript" src="/static/script1.js"></script>
</head> </head>
<body> <body>
<div class="center page"> <div class="center page">
<header> <header>
<a href="{{root_url}}"><img id="banner" alt="Bannière ĞMarché" src="{{root_url}}static/banner.jpg"/></a> <a href="/"><img id="banner" alt="Bannière ĞMarché" src="/static/banner.jpg"/></a>
</header> </header>
<main> <main>
@ -42,7 +42,7 @@
{{#each ads}} {{#each ads}}
<tr> <tr>
<td><input type="radio" name="ad" value="{{this.id}}" aria-label="Sélectionner l'annonce" required/></td> <td><input type="radio" name="ad" value="{{this.id}}" aria-label="Sélectionner l'annonce" required/></td>
<td onclick="ad_detail(event,'{{this.id}}')" title="Afficher le détail"><a href="{{../root_url}}ad/{{this.id}}">{{this.ad.title}}</a></td> <td onclick="ad_detail(event,'{{this.id}}')" title="Afficher le détail"><a href="/ad/{{this.id}}">{{this.ad.title}}</a></td>
<td>{{this.ad.quantity}}</td> <td>{{this.ad.quantity}}</td>
<td>{{this.ad.author}}</td> <td>{{this.ad.author}}</td>
</tr> </tr>
@ -69,7 +69,7 @@
<p>Il n'y a pas encore d'annonce ici.</p> <p>Il n'y a pas encore d'annonce ici.</p>
{{/if}} {{/if}}
<img id="stand" alt="Marché" src="{{root_url}}static/standgm.png"/> <img id="stand" alt="Marché" src="/static/standgm.png"/>
<form method="post"> <form method="post">
<input type="hidden" name="a" value="new_ad" autocomplete="off"/> <input type="hidden" name="a" value="new_ad" autocomplete="off"/>
@ -102,7 +102,7 @@
CopyLeft 2020 Pascal Engélibert<br/> CopyLeft 2020 Pascal Engélibert<br/>
Image de fond&nbsp;: Claudia Peters, FreeImages.com</p> Image de fond&nbsp;: Claudia Peters, FreeImages.com</p>
<p><a href="{{root_url}}admin">Administration</a></p> <p><a href="/admin">Administration</a></p>
</footer> </footer>
</div> </div>
</body> </body>