custom errors

This commit is contained in:
catvayor 2024-06-17 09:51:44 +02:00
parent 6dd6e46aab
commit bf9d82ed9f
25 changed files with 414 additions and 114 deletions

61
Cargo.lock generated
View file

@ -122,6 +122,16 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "bstr"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
dependencies = [
"memchr",
"serde",
]
[[package]] [[package]]
name = "bytemuck" name = "bytemuck"
version = "1.16.0" version = "1.16.0"
@ -432,6 +442,19 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "globset"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
dependencies = [
"aho-corasick",
"bstr",
"log",
"regex-automata 0.4.6",
"regex-syntax 0.8.3",
]
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.3.26" version = "0.3.26"
@ -1154,6 +1177,41 @@ dependencies = [
"uncased", "uncased",
] ]
[[package]]
name = "rust-embed"
version = "8.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19549741604902eb99a7ed0ee177a0663ee1eda51a29f71401f166e47e77806a"
dependencies = [
"rust-embed-impl",
"rust-embed-utils",
"walkdir",
]
[[package]]
name = "rust-embed-impl"
version = "8.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb9f96e283ec64401f30d3df8ee2aaeb2561f34c824381efa24a35f79bf40ee4"
dependencies = [
"proc-macro2",
"quote",
"rust-embed-utils",
"syn",
"walkdir",
]
[[package]]
name = "rust-embed-utils"
version = "8.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38c74a686185620830701348de757fd36bef4aa9680fd23c49fc539ddcc1af32"
dependencies = [
"globset",
"sha2",
"walkdir",
]
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.24" version = "0.1.24"
@ -1564,11 +1622,12 @@ dependencies = [
[[package]] [[package]]
name = "traque" name = "traque"
version = "0.1.0" version = "0.2.4"
dependencies = [ dependencies = [
"rand", "rand",
"rocket", "rocket",
"rocket_dyn_templates", "rocket_dyn_templates",
"rust-embed",
] ]
[[package]] [[package]]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "traque" name = "traque"
version = "0.1.0" version = "0.2.4"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -8,6 +8,10 @@ edition = "2021"
[dependencies] [dependencies]
rand = "0.8.5" rand = "0.8.5"
[dependencies.rust-embed]
version = "8.4.0"
features = ["include-exclude"]
[dependencies.rocket] [dependencies.rocket]
version = "0.5.0" version = "0.5.0"
features = ["json"] features = ["json"]

View file

@ -3,13 +3,16 @@ extern crate rocket;
use rocket::{ use rocket::{
fairing::AdHoc, fairing::AdHoc,
fs::{relative, FileServer}, fs::{relative, FileServer},
response::stream::Event, http::Status,
response::{content, stream::Event},
tokio::{ tokio::{
self, select, self, select,
time::{self, Duration}, time::{self, Duration},
}, },
Request,
}; };
use rocket_dyn_templates::Template; use rocket_dyn_templates::Template;
use rust_embed::Embed;
use std::{ use std::{
collections::VecDeque, collections::VecDeque,
sync::{Arc, RwLock}, sync::{Arc, RwLock},
@ -68,6 +71,27 @@ fn clean_expired_evt(
} }
} }
#[derive(Embed)]
#[folder = "static/errors"]
#[include = "*.html"]
#[exclude = "*.jpg"]
struct ErrorPages;
#[catch(default)]
fn handler(status: Status, _: &Request) -> content::RawHtml<String> {
content::RawHtml(
std::str::from_utf8(
ErrorPages::get(format!("{}.html", status.code).as_str())
//501 not implemented
.unwrap_or(ErrorPages::get("501.html").unwrap())
.data
.as_ref(),
)
.unwrap()
.to_string(),
)
}
#[launch] #[launch]
async fn rocket() -> _ { async fn rocket() -> _ {
let rocket = rocket::build(); let rocket = rocket::build();
@ -106,7 +130,8 @@ async fn rocket() -> _ {
.manage(admin_evt_queue.clone()) .manage(admin_evt_queue.clone())
.mount("/", routes![index]) .mount("/", routes![index])
.mount("/track", track::routes()) .mount("/track", track::routes())
.mount("/admin", admin::routes()); .mount("/admin", admin::routes())
.register("/", catchers![handler]);
if config.serve_static { if config.serve_static {
rocket = rocket.mount("/", FileServer::from(relative!("static"))); rocket = rocket.mount("/", FileServer::from(relative!("static")));
} }

View file

@ -1,4 +1,5 @@
use rocket::{ use rocket::{
http::Status,
response::stream::{Event, EventStream}, response::stream::{Event, EventStream},
tokio::{ tokio::{
self, select, self, select,
@ -10,15 +11,20 @@ use rocket_dyn_templates::{context, Template};
use crate::global::{TrackedState::*, *}; use crate::global::{TrackedState::*, *};
#[get("/")]
fn no_id() -> Status {
Status::ImATeapot
}
#[get("/<id>?<gpslog>&<dbg>")] #[get("/<id>?<gpslog>&<dbg>")]
fn tracked_view( fn tracked_view(
id: &str, id: &str,
gpslog: Option<bool>, gpslog: Option<bool>,
dbg: Option<bool>, dbg: Option<bool>,
tracking: &State<Tracking>, tracking: &State<Tracking>,
) -> Option<Template> { ) -> Result<Template, Status> {
if let Some(tracked) = tracking.get(&id.to_string()) { if let Some(tracked) = tracking.get(&id.to_string()) {
Some(Template::render( Ok(Template::render(
match tracked.read().unwrap().state { match tracked.read().unwrap().state {
Vieux { .. } => "vieux", Vieux { .. } => "vieux",
Conscrit { .. } => "conscrit", Conscrit { .. } => "conscrit",
@ -31,7 +37,7 @@ fn tracked_view(
}, },
)) ))
} else { } else {
None Err(Status::BadRequest)
} }
} }
@ -41,10 +47,10 @@ fn tracked_events<'a>(
evt_queue: &'a State<TrackingEventQueue>, evt_queue: &'a State<TrackingEventQueue>,
config: &State<Config>, config: &State<Config>,
mut shutdown: Shutdown, mut shutdown: Shutdown,
) -> Option<EventStream![Event + 'a]> { ) -> Result<EventStream![Event + 'a], Status> {
if evt_queue.contains_key(&id.to_string()) { if evt_queue.contains_key(&id.to_string()) {
let timeout = Duration::from_millis(config.event_timeout); let timeout = Duration::from_millis(config.event_timeout);
Some(EventStream! { Ok(EventStream! {
let mut interval = time::interval(timeout); let mut interval = time::interval(timeout);
loop { loop {
select!{ select!{
@ -59,7 +65,7 @@ fn tracked_events<'a>(
} }
}) })
} else { } else {
None Err(Status::BadRequest)
} }
} }
@ -71,10 +77,13 @@ fn store_pos(
tracking: &State<Tracking>, tracking: &State<Tracking>,
evt_queues: &State<TrackingEventQueue>, evt_queues: &State<TrackingEventQueue>,
admin_queue: &State<AdminEventQueue>, admin_queue: &State<AdminEventQueue>,
) { ) -> Status {
if let Some(tracked) = tracking.get(&id.to_string()) { if let Some(tracked) = tracking.get(&id.to_string()) {
tracked.write().unwrap().pos = (lat, long); tracked.write().unwrap().pos = (lat, long);
state_update(&tracked.read().unwrap(), &evt_queues, &admin_queue); state_update(&tracked.read().unwrap(), &evt_queues, &admin_queue);
Status::Accepted
} else {
Status::BadRequest
} }
} }
@ -86,19 +95,23 @@ fn set_state(
tracking: &State<Tracking>, tracking: &State<Tracking>,
evt_queues: &State<TrackingEventQueue>, evt_queues: &State<TrackingEventQueue>,
admin_queue: &State<AdminEventQueue>, admin_queue: &State<AdminEventQueue>,
) -> Option<()> { ) -> Status {
let tracked = &mut tracking.get(&id.to_string()).unwrap().write().unwrap(); if let Some(tracked) = tracking.get(&id.to_string()) {
if let Vieux { let tracked = &mut tracked.write().unwrap();
ref mut invisible, if let Vieux {
ref mut color, ref mut invisible,
} = tracked.state ref mut color,
{ } = tracked.state
*invisible = inv; {
*color = col; *invisible = inv;
state_update(&tracked, &evt_queues, &admin_queue); *color = col;
Some(()) state_update(&tracked, &evt_queues, &admin_queue);
Status::Accepted
} else {
Status::MethodNotAllowed
}
} else { } else {
None Status::BadRequest
} }
} }
@ -109,43 +122,47 @@ pub async fn activate_invisibility(
evt_queues: &State<TrackingEventQueue>, evt_queues: &State<TrackingEventQueue>,
admin_queue: &State<AdminEventQueue>, admin_queue: &State<AdminEventQueue>,
config: &State<Config>, config: &State<Config>,
) -> Option<()> { ) -> Status {
let tracked = &mut tracking.get(&id.to_string()).unwrap().write().unwrap(); if let Some(tracked) = tracking.get(&id.to_string()) {
if let Conscrit { let tracked = &mut tracked.write().unwrap();
ref mut invisible, if let Conscrit {
ref mut invisibility_codes, ref mut invisible,
.. ref mut invisibility_codes,
} = tracked.state ..
{ } = tracked.state
if *invisibility_codes > 0 { {
*invisibility_codes -= 1; if *invisibility_codes > 0 {
*invisible = true; *invisibility_codes -= 1;
state_update(&tracked, &evt_queues, &admin_queue); *invisible = true;
let track_clone = (*tracking).clone(); state_update(&tracked, &evt_queues, &admin_queue);
let queue_clone = (*evt_queues).clone(); let track_clone = (*tracking).clone();
let admin_clone = (*admin_queue).clone(); let queue_clone = (*evt_queues).clone();
let id_str = id.to_string(); let admin_clone = (*admin_queue).clone();
let timeout = Duration::from_millis(config.bonus_timeout); let id_str = id.to_string();
tokio::spawn(async move { let timeout = Duration::from_millis(config.bonus_timeout);
sleep(timeout).await; tokio::spawn(async move {
if let Conscrit { sleep(timeout).await;
ref mut invisible, .. if let Conscrit {
} = track_clone.get(&id_str).unwrap().write().unwrap().state ref mut invisible, ..
{ } = track_clone.get(&id_str).unwrap().write().unwrap().state
*invisible = false; {
} *invisible = false;
state_update( }
&track_clone.get(&id_str).unwrap().read().unwrap(), state_update(
&queue_clone, &track_clone.get(&id_str).unwrap().read().unwrap(),
&admin_clone, &queue_clone,
); &admin_clone,
}); );
Some(()) });
Status::Accepted
} else {
Status::PaymentRequired
}
} else { } else {
None Status::MethodNotAllowed
} }
} else { } else {
None Status::BadRequest
} }
} }
@ -156,43 +173,47 @@ pub async fn activate_blur(
evt_queues: &State<TrackingEventQueue>, evt_queues: &State<TrackingEventQueue>,
admin_queue: &State<AdminEventQueue>, admin_queue: &State<AdminEventQueue>,
config: &State<Config>, config: &State<Config>,
) -> Option<()> { ) -> Status {
let tracked = &mut tracking.get(&id.to_string()).unwrap().write().unwrap(); if let Some(tracked) = tracking.get(&id.to_string()) {
if let Conscrit { let tracked = &mut tracked.write().unwrap();
ref mut blurred, if let Conscrit {
ref mut blur_codes, ref mut blurred,
.. ref mut blur_codes,
} = tracked.state ..
{ } = tracked.state
if *blur_codes > 0 { {
*blur_codes -= 1; if *blur_codes > 0 {
*blurred = true; *blur_codes -= 1;
state_update(&tracked, &evt_queues, &admin_queue); *blurred = true;
let track_clone = (*tracking).clone(); state_update(&tracked, &evt_queues, &admin_queue);
let queue_clone = (*evt_queues).clone(); let track_clone = (*tracking).clone();
let admin_clone = (*admin_queue).clone(); let queue_clone = (*evt_queues).clone();
let id_str = id.to_string(); let admin_clone = (*admin_queue).clone();
let timeout = Duration::from_millis(config.bonus_timeout); let id_str = id.to_string();
tokio::spawn(async move { let timeout = Duration::from_millis(config.bonus_timeout);
sleep(timeout).await; tokio::spawn(async move {
if let Conscrit { sleep(timeout).await;
ref mut blurred, .. if let Conscrit {
} = track_clone.get(&id_str).unwrap().write().unwrap().state ref mut blurred, ..
{ } = track_clone.get(&id_str).unwrap().write().unwrap().state
*blurred = false; {
} *blurred = false;
state_update( }
&track_clone.get(&id_str).unwrap().read().unwrap(), state_update(
&queue_clone, &track_clone.get(&id_str).unwrap().read().unwrap(),
&admin_clone, &queue_clone,
); &admin_clone,
}); );
Some(()) });
Status::Accepted
} else {
Status::PaymentRequired
}
} else { } else {
None Status::MethodNotAllowed
} }
} else { } else {
None Status::BadRequest
} }
} }
@ -204,5 +225,6 @@ pub fn routes() -> Vec<Route> {
set_state, set_state,
activate_invisibility, activate_invisibility,
activate_blur, activate_blur,
no_id,
] ]
} }

19
static/errors/400.html Normal file
View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="color-scheme" content="light dark">
<title>400 Bad Request</title>
</head>
<body align="center">
<div role="main" align="center">
<h1>400 Bad Request</h1>
<img src="/errors/cat/400.jpg"/>
<p>Conscrit ! Cet identifiant ne correspond à aucune équipe.</p>
<hr />
</div>
<div role="contentinfo" align="center">
<small>Crédit à <a href="https://http.cat">http.cat</a> pour l'image.</small>
</div>
</body>
</html>

19
static/errors/401.html Normal file
View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="color-scheme" content="light dark">
<title>401 Unauthorized</title>
</head>
<body align="center">
<div role="main" align="center">
<h1>401 Unauthorized</h1>
<img src="/errors/cat/401.jpg"/>
<p>Il faut s'identifié pour accéder à cette ressource.</p>
<hr />
</div>
<div role="contentinfo" align="center">
<small>Crédit à <a href="https://http.cat">http.cat</a> pour l'image.</small>
</div>
</body>
</html>

19
static/errors/402.html Normal file
View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="color-scheme" content="light dark">
<title>402 Payment Required</title>
</head>
<body align="center">
<div role="main" align="center">
<h1>402 Payment Required</h1>
<img src="/errors/cat/402.jpg"/>
<p>Toute les utilisations de ce bonus ont été utilisées.</p>
<hr />
</div>
<div role="contentinfo" align="center">
<small>Crédit à <a href="https://http.cat">http.cat</a> pour l'image.</small>
</div>
</body>
</html>

19
static/errors/404.html Normal file
View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="color-scheme" content="light dark">
<title>404 Not Found</title>
</head>
<body align="center">
<div role="main" align="center">
<h1>404 Not Found</h1>
<img src="/errors/cat/404.jpg"/>
<p>Page non trouvé</p>
<hr />
</div>
<div role="contentinfo" align="center">
<small>Crédit à <a href="https://http.cat">http.cat</a> pour l'image.</small>
</div>
</body>
</html>

19
static/errors/405.html Normal file
View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="color-scheme" content="light dark">
<title>405 Method Not Allowed</title>
</head>
<body align="center">
<div role="main" align="center">
<h1>405 Method Not Allowed</h1>
<img src="/errors/cat/405.jpg"/>
<p>Cette équipe n'a pas le droit d'utiliser cet endpoint.</p>
<hr />
</div>
<div role="contentinfo" align="center">
<small>Crédit à <a href="https://http.cat">http.cat</a> pour l'image.</small>
</div>
</body>
</html>

19
static/errors/418.html Normal file
View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="color-scheme" content="light dark">
<title>418 Im a teapot</title>
</head>
<body align="center">
<div role="main" align="center">
<h1>418 Im a teapot</h1>
<img src="/errors/cat/418.jpg"/>
<p>Il y a quelque-chose ici, mais je montrerai rien.</p>
<hr />
</div>
<div role="contentinfo" align="center">
<small>Crédit à <a href="https://http.cat">http.cat</a> pour l'image.</small>
</div>
</body>
</html>

19
static/errors/498.html Normal file
View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="color-scheme" content="light dark">
<title>498 Token expired/invalid</title>
</head>
<body align="center">
<div role="main" align="center">
<h1>498 Token expired/invalid</h1>
<img src="/errors/cat/498.jpg"/>
<p>Le token d'authentification est invalide.</p>
<hr />
</div>
<div role="contentinfo" align="center">
<small>Crédit à <a href="https://http.cat">http.cat</a> pour l'image.</small>
</div>
</body>
</html>

19
static/errors/500.html Normal file
View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="color-scheme" content="light dark">
<title>500 Internal Server Error</title>
</head>
<body align="center">
<div role="main" align="center">
<h1>500 Internal Server Error</h1>
<img src="/errors/cat/500.jpg"/>
<p>Erreur Interne</p>
<hr />
</div>
<div role="contentinfo" align="center">
<small>Crédit à <a href="https://http.cat">http.cat</a> pour l'image.</small>
</div>
</body>
</html>

19
static/errors/501.html Normal file
View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="color-scheme" content="light dark">
<title>501 Not Implemented</title>
</head>
<body align="center">
<div role="main" align="center">
<h1>501 Not Implemented</h1>
<img src="/errors/cat/501.jpg"/>
<p>Une erreur inconnue a été rencontré.</p>
<hr />
</div>
<div role="contentinfo" align="center">
<small>Crédit à <a href="https://http.cat">http.cat</a> pour l'image.</small>
</div>
</body>
</html>

19
static/errors/503.html Normal file
View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="color-scheme" content="light dark">
<title>503 Service Unavailable</title>
</head>
<body align="center">
<div role="main" align="center">
<h1>503 Service Unavailable</h1>
<img src="/errors/cat/503.jpg"/>
<p>La traque n'est pas encore prête, on arrive bientôt...</p>
<hr />
</div>
<div role="contentinfo" align="center">
<small>Crédit à <a href="https://http.cat">http.cat</a> pour l'image.</small>
</div>
</body>
</html>

BIN
static/errors/cat/400.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
static/errors/cat/401.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
static/errors/cat/402.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
static/errors/cat/404.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
static/errors/cat/405.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
static/errors/cat/418.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
static/errors/cat/498.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
static/errors/cat/500.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
static/errors/cat/501.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
static/errors/cat/503.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View file

@ -45,30 +45,30 @@
<body> <body>
<div id="map"></div> <div id="map"></div>
<div id="right"> <div id="right">
<div class="tableFixHead"> <div class="tableFixHead">
<table> <table>
<thead><tr> <thead><tr>
<th>Nom</th> <th>Nom</th>
<th>Mallette</th> <th>Mallette</th>
<th>Tracker</th> <th>Tracker</th>
<th>Invisible</th> <th>Invisible</th>
<th>Brouillé</th> <th>Brouillé</th>
<th>Code Invisibilité</th> <th>Code Invisibilité</th>
<th>Code Brouillage</th> <th>Code Brouillage</th>
<th>Couleur</th> <th>Couleur</th>
<!-- <th>Last Update</th> --> <!-- <th>Last Update</th> -->
</tr></thead> </tr></thead>
<tbody id="teamInfos"> <tbody id="teamInfos">
</tbody> </tbody>
</table> </table>
</div> </div>
<input id="popup"/><button id="sendPopup">Send popup to all clients</button><br/> <input id="popup"/><button id="sendPopup">Send popup to all clients</button><br/>
<br/> <br/>
<a href="https://dgnum.eu"><img src="/dgnum-logo.png" height=50px /></a><br/> <a href="https://dgnum.eu"><img src="/dgnum-logo.png" height=50px /></a><br/>
<span style="font-size: 0.8em">Merci à la <a href="https://dgnum.eu">Délégation Générale NUMérique de l'ENS</a>, qui héberge ce site.</span> <span style="font-size: 0.8em">Merci à la <a href="https://dgnum.eu">Délégation Générale NUMérique de l'ENS</a>, qui héberge ce site.</span>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
setup_map(); setup_map();