Compare commits

...

2 Commits

Author SHA1 Message Date
Pascal Engélibert 017a64b18b Fix root_url, admin ad selection 2020-12-17 17:52:07 +01:00
Pascal Engélibert 6816f2f9fe root_url 2020-12-17 17:13:11 +01:00
7 changed files with 130 additions and 48 deletions

View File

@ -20,3 +20,4 @@ L'option `-d <dir>` permet de changer le dossier de données/config (par exemple
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,6 +12,7 @@ pub struct Config {
pub cookies_https_only: bool,
pub cookies_domain: Option<String>,
pub listen: SocketAddr,
pub root_url: String,
}
impl Default for Config {
@ -21,6 +22,7 @@ impl Default for Config {
cookies_https_only: false,
cookies_domain: None,
listen: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 10353),
root_url: String::from("/"),
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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