Compare commits

..

2 Commits

Author SHA1 Message Date
8a7c321044 Fixed bug related to new Vote struct, id and submit date can be None
(useful for posting new votes, ie without id and submit date)
2024-10-05 15:09:51 +02:00
98dbcd11f1 Added submit date to votes 2024-10-05 15:08:41 +02:00
5 changed files with 64 additions and 87 deletions

View File

@@ -3,7 +3,7 @@ use argon2::{
Argon2,
};
use bytes::{Buf, Bytes};
use chrono::{DateTime, Days, Utc};
use chrono::{DateTime, Days, NaiveTime, Timelike, Utc};
#[cfg(target_os = "linux")]
use daemonize::Daemonize;
use http_body_util::{BodyExt, Full};
@@ -66,9 +66,10 @@ struct Player {
name: String,
}
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Debug)]
struct Vote {
id: i64,
id: Option<i64>,
submit_date: Option<String>,
plus_player_id: i64,
plus_nickname: String,
plus_reason: String,
@@ -100,9 +101,9 @@ async fn service(
req: Request<Incoming>,
db: Arc<Mutex<SqlitePool>>,
) -> Result<Response<Body>, Error> {
match req.method() {
&Method::GET => get(req, db).await,
&Method::POST => post(req, db).await,
match *req.method() {
Method::GET => get(req, db).await,
Method::POST => post(req, db).await,
_ => Ok(Response::builder()
.status(StatusCode::IM_A_TEAPOT)
.body(Body::Empty)
@@ -224,17 +225,13 @@ async fn get_data(
let mut plus_results: HashMap<i64, i64> = HashMap::new();
let mut minus_results: HashMap<i64, i64> = HashMap::new();
let _ = ids.iter().for_each(|x| {
ids.iter().for_each(|x| {
let plus_id = x.0;
if !plus_results.contains_key(&plus_id) {
plus_results.insert(plus_id, 0);
}
plus_results.entry(plus_id).or_insert(0);
*plus_results.get_mut(&plus_id).unwrap() += 1;
let minus_id = x.1;
if !minus_results.contains_key(&minus_id) {
minus_results.insert(minus_id, 0);
}
minus_results.entry(minus_id).or_insert(0);
*minus_results.get_mut(&minus_id).unwrap() += 1;
});
@@ -260,11 +257,10 @@ async fn get_votes(req: &Request<Incoming>, db: Arc<Mutex<SqlitePool>>) -> Vec<V
let date = match headers.get("Date-to-fetch") {
Some(date) => {
let date = date.to_str().unwrap();
let parsed_date = date.parse::<i64>();
if parsed_date.is_err() {
None
if let Ok(parsed_date) = date.parse::<i64>() {
DateTime::from_timestamp_millis(parsed_date)
} else {
DateTime::from_timestamp_millis(parsed_date.unwrap())
None
}
}
None => Some(DateTime::from(SystemTime::now())),
@@ -273,25 +269,14 @@ async fn get_votes(req: &Request<Incoming>, db: Arc<Mutex<SqlitePool>>) -> Vec<V
return Vec::new();
}
let formatted_date = format!("{}", date.unwrap().format("%d/%m/%Y"));
let items = sqlx::query!(
sqlx::query_as!(
Vote,
r#"SELECT * FROM votes WHERE submit_date = ?1 ORDER BY id"#,
formatted_date
)
.fetch_all(&pool)
.await
.unwrap();
items
.iter()
.map(|x| Vote {
id: x.id,
plus_player_id: x.plus_player_id,
plus_nickname: x.plus_nickname.clone(),
plus_reason: x.plus_reason.clone(),
minus_player_id: x.minus_player_id,
minus_nickname: x.minus_nickname.clone(),
minus_reason: x.minus_reason.clone(),
})
.collect()
.unwrap()
}
async fn get_admin(
@@ -301,7 +286,7 @@ async fn get_admin(
) -> Result<Response<Body>, Error> {
let authorised = is_authorised(req, db.clone(), 3).await;
if !authorised {
return get_page(&req, "/unauthorised", db).await;
return get_page(req, "/unauthorised", db).await;
}
match path {
"/admin" => get_page(req, path, db).await,
@@ -320,38 +305,19 @@ async fn get_admin(
}
"/admin/players" => {
let pool = db.clone().lock().unwrap().clone();
let players = sqlx::query!(r#"SELECT id, name FROM players"#)
let players = sqlx::query_as!(Player, r#"SELECT id, name FROM players"#)
.fetch_all(&pool)
.await
.unwrap();
let players: Vec<Player> = players
.iter()
.map(|x| Player {
id: x.id,
name: x.name.clone(),
})
.collect();
let stringed = serde_json::to_string(&players).unwrap_or("".to_string());
Ok(Response::builder().body(Body::new(stringed)).unwrap())
}
"/admin/votes" => {
let pool = db.clone().lock().unwrap().clone();
let votes = sqlx::query!(r#"SELECT * FROM votes"#)
let votes = sqlx::query_as!(Vote, r#"SELECT * FROM votes"#)
.fetch_all(&pool)
.await
.unwrap();
let votes: Vec<Vote> = votes
.iter()
.map(|x| Vote {
id: x.id,
plus_player_id: x.plus_player_id,
plus_nickname: x.plus_nickname.clone(),
plus_reason: x.plus_reason.clone(),
minus_player_id: x.minus_player_id,
minus_nickname: x.minus_nickname.clone(),
minus_reason: x.minus_reason.clone(),
})
.collect();
let stringed = serde_json::to_string(&votes).unwrap_or("".to_string());
Ok(Response::builder().body(Body::new(stringed)).unwrap())
}
@@ -402,7 +368,21 @@ async fn post_vote(
.body(Body::Empty)
.unwrap());
}
ok().await
let date: DateTime<Utc> = DateTime::from(SystemTime::now());
let date = date.checked_add_days(Days::new(1)).unwrap();
let date = date
.with_time(NaiveTime::from_hms_opt(0, 0, 0).unwrap())
.unwrap();
Ok(Response::builder()
.header(
SET_COOKIE,
format!(
"hasvoted=true; Expires={}; Secure; SameSite=Strict",
date.to_rfc2822()
),
)
.body(Body::Empty)
.unwrap())
}
async fn post_player(
@@ -550,16 +530,21 @@ async fn post_admin(
},
"/admin/edit/vote" => match req_json::<Vote>(req).await {
Some(vote) => {
if vote.id.is_none() || vote.submit_date.is_none() {
return bad_request().await;
}
let pool = db.clone().lock().unwrap().clone();
let _ = sqlx::query!(
r#"UPDATE votes
SET plus_player_id = ?1,
plus_nickname = ?2,
plus_reason = ?3,
minus_player_id = ?4,
minus_nickname = ?5,
minus_reason = ?6
WHERE id = ?7"#,
SET submit_date = ?1,
plus_player_id = ?2,
plus_nickname = ?3,
plus_reason = ?4,
minus_player_id = ?5,
minus_nickname = ?6,
minus_reason = ?7
WHERE id = ?8"#,
vote.submit_date,
vote.plus_player_id,
vote.plus_nickname,
vote.plus_reason,
@@ -589,7 +574,7 @@ async fn post_admin(
ok().await
}
_ => bad_request().await,
}
},
_ => bad_request().await,
}
}
@@ -749,14 +734,11 @@ async fn is_authorised(req: &Request<Incoming>, db: Arc<Mutex<SqlitePool>>, leve
let perm = user.permissions as u8;
perm >= level
}
_ => match level {
0 => true,
_ => false,
},
_ => matches!(level, 0),
}
}
fn check_username(username: &String) -> bool {
fn check_username(username: &str) -> bool {
if username.len() > 21 {
return false;
}
@@ -768,7 +750,7 @@ fn check_username(username: &String) -> bool {
true
}
fn check_password(password: &String) -> bool {
fn check_password(password: &str) -> bool {
// one symbol, 10 chars min, one capital letter, one number
if password.len() < 10 {
return false;

View File

@@ -23,7 +23,7 @@
<div>
<h1>Votes</h1>
<h3 id="votes_number"></h3>
<p>id, plus_id, plus_nickname, plus_reason, minus_id, minus_nickname, minus_reason</p>
<p>id, submit_date, plus_id, plus_nickname, plus_reason, minus_id, minus_nickname, minus_reason</p>
<div id="votes"></div>
</div>
</body>

View File

@@ -11,7 +11,6 @@
<link rel="stylesheet" href="static/css/index.css">
</head>
<body>
<a href="/login" class="login" id="login">Se connecter</a>
<div id="app" class="app">
<h1 id="app_title">Vote +</h1>
<label for="player_id">Pour qui votes tu?</label>
@@ -37,5 +36,6 @@
<a href="/results">Résultats</a>
<a href="/archives">Archives</a>
</div>
<a href="/login" class="login" id="login">Se connecter</a>
</body>
</html>

View File

@@ -80,6 +80,8 @@ async function run() {
item.style.display = "flex";
let id = document.createElement("p");
id.textContent = vote["id"];
let submit_date = document.createElement("input");
submit_date.value = vote["submit_date"];
let plus_id = document.createElement("input");
plus_id.type = "number";
plus_id.value = vote["plus_player_id"];
@@ -98,10 +100,11 @@ async function run() {
edit.textContent = "Edit";
let del = document.createElement("button");
del.textContent = "Delete";
item.append(id,plus_id,plus_nickname,plus_reason,minus_id,minus_nickname,minus_reason, edit, del);
item.append(id,submit_date,plus_id,plus_nickname,plus_reason,minus_id,minus_nickname,minus_reason, edit, del);
votesDiv.append(item);
edit.addEventListener("click", async () => {
await fetch("/admin/edit/vote", {method: "POST", body: JSON.stringify({"id": votes[i]["id"],
"submit_date": submit_date.value,
"plus_player_id": parseInt(plus_id.value),
"plus_nickname": plus_nickname.value,
"plus_reason": plus_reason.value,

View File

@@ -1,4 +1,5 @@
let vote = {
submit_date: null,
plus_player_id: null,
plus_nickname: "",
plus_reason: "",
@@ -11,7 +12,7 @@ let current_page = 0;
async function main() {
if (read_cookie()) {
showMessage("Merci pour ton vote!", "Ton vote a bien été prit en compte.", false, "info");
showMessage("Merci pour ton vote!", "Ton vote a bien été pris en compte.", false, "info");
return;
}
let players = await fetch("/data/players").then(r => r.json());
@@ -95,7 +96,6 @@ async function main() {
method: "post", body: JSON.stringify(vote)
})
.then(r => r.status) === 200) {
set_cookie();
showMessage("Merci pour ton vote!", "Ton vote a bien été pris en compte.", false, "info");
}
console.log(vote);
@@ -160,14 +160,6 @@ function showMessage(title, description, canBeDismissed, type) {
})
}
function set_cookie() {
let date = new Date(Date.now());
date.setDate(date.getDate() + 1);
date.setHours(0, 0,0);
console.log(date);
document.cookie = `hasvoted=true; expires=${date.toUTCString()}; path=/`;
}
function read_cookie() {
return document.cookie.includes("hasvoted=true");
}