auth: Split off vote data from the user

Now that the application is going to have multiple pages, vote data makes no sense
to keep with the user.
The user struct will be used everywhere to check for authentication, which is not
the case for previous votes.

Create a new struct and function in src/vote.rs to retrieve existing votes and
use them in places where user.votes was used previously.
Remove vote-related code from src/auth.rs and the week number dependence that it
required.
This commit is contained in:
trotFunky 2024-07-28 14:36:52 +01:00
parent 1419bb6d51
commit 1b4a934398
7 changed files with 47 additions and 35 deletions

View file

@ -19,6 +19,7 @@ A list of things that could be implemented/added to the application, some of the
- [x] Use fairings for the different elements - [x] Use fairings for the different elements
- [ ] Use guards for User calls ? - [ ] Use guards for User calls ?
- [ ] Use SQLite Row ID for User IDs rather than regular IDs, for randomness ? - [ ] Use SQLite Row ID for User IDs rather than regular IDs, for randomness ?
- [x] Split user from vote data
# Dependencies # Dependencies

View file

@ -9,7 +9,7 @@ use argon2::{Argon2, PasswordHash, PasswordVerifier};
use blake2::{Blake2b512, Digest}; use blake2::{Blake2b512, Digest};
use blake2::digest::FixedOutput; use blake2::digest::FixedOutput;
use rocket::fairing::AdHoc; use rocket::fairing::AdHoc;
use crate::database_records::{AuthTokens, PlayerLoginInfo, Vote}; use crate::database_records::{AuthTokens, PlayerLoginInfo};
use crate::{database, week}; use crate::{database, week};
use database::Db; use database::Db;
@ -21,11 +21,9 @@ pub struct User {
pub is_admin: bool, pub is_admin: bool,
pub id: u16, pub id: u16,
pub name: String, pub name: String,
pub has_week_vote: bool,
pub votes: Vec<Vote>
} }
pub async fn get_user(week: u8, db: &mut Connection<Db>, cookies: &CookieJar<'_>) -> User { pub async fn get_user(db: &mut Connection<Db>, cookies: &CookieJar<'_>) -> User {
let auth_token: Option<String> = match cookies.get_private("auth_token") { let auth_token: Option<String> = match cookies.get_private("auth_token") {
Some(cookie) => Some(cookie.value().to_string()), Some(cookie) => Some(cookie.value().to_string()),
None => None None => None
@ -87,27 +85,12 @@ pub async fn get_user(week: u8, db: &mut Connection<Db>, cookies: &CookieJar<'_>
(String::new(), false) (String::new(), false)
}; };
// TODO: Move to src/vote.rs
let votes: Vec<Vote> = if logged_in && !is_admin {
sqlx::query_as("SELECT Votes.* FROM Votes JOIN Truths ON Votes.truth_id == Truths.id AND Truths.week == $1 WHERE voter_id == $2 ORDER BY Truths.number;")
.bind(week)
.bind(&id_str)
.fetch_all(&mut ***db).await.unwrap_or_else(|error| {
error!("Error while getting votes : {error}");
Vec::<Vote>::new()
})
} else {
Vec::<Vote>::new()
};
if logged_in { if logged_in {
User { User {
logged_in, logged_in,
is_admin, is_admin,
id: id_str.parse::<u16>().unwrap(), id: id_str.parse::<u16>().unwrap(),
name, name,
has_week_vote: if votes.is_empty() { false } else { true },
votes
} }
} else { } else {
User { User {
@ -115,8 +98,6 @@ pub async fn get_user(week: u8, db: &mut Connection<Db>, cookies: &CookieJar<'_>
is_admin: false, is_admin: false,
id: 0, id: 0,
name, name,
has_week_vote: false,
votes
} }
} }
} }

View file

@ -18,7 +18,7 @@ pub struct TruthUpdateForm {
#[post("/<week>/edit/<truth_number>", data="<form>")] #[post("/<week>/edit/<truth_number>", data="<form>")]
pub async fn edit_truth(week: u8, truth_number: u8, form: Form<TruthUpdateForm>, pub async fn edit_truth(week: u8, truth_number: u8, form: Form<TruthUpdateForm>,
mut db: Connection<database::Db>, cookies: &CookieJar<'_>) -> Redirect { mut db: Connection<database::Db>, cookies: &CookieJar<'_>) -> Redirect {
let user = auth::get_user(week, &mut db, cookies).await; let user = auth::get_user(&mut db, cookies).await;
if !user.is_admin { if !user.is_admin {
cookies.add(("toast_error", "Vous n'avez pas la permission de changer la vérité.")); cookies.add(("toast_error", "Vous n'avez pas la permission de changer la vérité."));
return Redirect::to(uri!(week::week(week))); return Redirect::to(uri!(week::week(week)));
@ -57,7 +57,7 @@ pub async fn edit_truth(week: u8, truth_number: u8, form: Form<TruthUpdateForm>,
#[post("/<week>/new_truth", data="<form>")] #[post("/<week>/new_truth", data="<form>")]
pub async fn create_truth(week: u8, form: Form<TruthUpdateForm>, pub async fn create_truth(week: u8, form: Form<TruthUpdateForm>,
mut db: Connection<database::Db>, cookies: &CookieJar<'_>) -> Redirect { mut db: Connection<database::Db>, cookies: &CookieJar<'_>) -> Redirect {
let user = auth::get_user(week, &mut db, cookies).await; let user = auth::get_user(&mut db, cookies).await;
if !user.is_admin { if !user.is_admin {
cookies.add(("toast_error", "Vous n'avez pas la permission d'ajouter de vérité.")); cookies.add(("toast_error", "Vous n'avez pas la permission d'ajouter de vérité."));
return Redirect::to(uri!(week::week(week))); return Redirect::to(uri!(week::week(week)));

View file

@ -12,6 +12,29 @@ use rocket_db_pools::{sqlx, Connection};
use crate::{auth, database, week}; use crate::{auth, database, week};
use crate::database_records::{Vote, VotingData}; use crate::database_records::{Vote, VotingData};
#[derive(Serialize, Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct WeeklyUserVotes {
pub has_week_vote: bool,
pub votes: Vec<Vote>
}
pub async fn get_weekly_user_votes(week: u8, user: &auth::User, db: &mut Connection<database::Db>) -> WeeklyUserVotes {
let votes: Vec<Vote> = if user.logged_in && !user.is_admin {
sqlx::query_as("SELECT Votes.* FROM Votes JOIN Truths ON Votes.truth_id == Truths.id AND Truths.week == $1 WHERE voter_id == $2 ORDER BY Truths.number;")
.bind(week)
.bind(user.id)
.fetch_all(&mut ***db).await.unwrap_or_else(|error| {
error!("Error while getting votes : {error}");
Vec::<Vote>::new()
})
} else {
Vec::<Vote>::new()
};
WeeklyUserVotes {has_week_vote: if votes.is_empty() { false } else { true }, votes}
}
#[derive(FromForm)] #[derive(FromForm)]
pub struct VoteForm { pub struct VoteForm {
truth_votes: HashMap<u32, u16> truth_votes: HashMap<u32, u16>
@ -20,7 +43,7 @@ pub struct VoteForm {
#[post("/<week>/vote", data="<form>")] #[post("/<week>/vote", data="<form>")]
pub async fn vote(week: u8, form: Form<VoteForm>, pub async fn vote(week: u8, form: Form<VoteForm>,
mut db: Connection<database::Db>, cookies: &CookieJar<'_>) -> Redirect { mut db: Connection<database::Db>, cookies: &CookieJar<'_>) -> Redirect {
let user = auth::get_user(week, &mut db, cookies).await; let user = auth::get_user(&mut db, cookies).await;
if !user.logged_in { if !user.logged_in {
cookies.add(("toast_error", "Vous n'avez pas la permission de changer de vote.")); cookies.add(("toast_error", "Vous n'avez pas la permission de changer de vote."));
@ -37,9 +60,11 @@ pub async fn vote(week: u8, form: Form<VoteForm>,
} }
); );
let existing_votes = get_weekly_user_votes(week, &user, &mut db).await;
let mut had_error = false; let mut had_error = false;
for (truth_id, voted_id) in filtered_votes { for (truth_id, voted_id) in filtered_votes {
match user.votes.iter().find(|vote: &&Vote| {vote.truth_id == *truth_id}) { match existing_votes.votes.iter().find(|vote: &&Vote| {vote.truth_id == *truth_id}) {
Some(vote) => { Some(vote) => {
if *voted_id == vote.voted_id { if *voted_id == vote.voted_id {
continue; continue;
@ -97,7 +122,7 @@ pub struct VoteData {
// TODO: Cache vote count ? Maintain in state ? // TODO: Cache vote count ? Maintain in state ?
#[get("/<week>/votes", format = "application/json")] #[get("/<week>/votes", format = "application/json")]
pub async fn fetch_vote_data(week: u8, mut db: Connection<database::Db>, cookies: &CookieJar<'_>) -> Option<Json<VoteData>> { pub async fn fetch_vote_data(week: u8, mut db: Connection<database::Db>, cookies: &CookieJar<'_>) -> Option<Json<VoteData>> {
let user = auth::get_user(week, &mut db, cookies).await; let user = auth::get_user(&mut db, cookies).await;
let raw_votes: Vec<VotingData> = sqlx::query_as(" let raw_votes: Vec<VotingData> = sqlx::query_as("
SELECT Players.name as votes_for, Truths.number as truth_number, count(*) as votes FROM Votes SELECT Players.name as votes_for, Truths.number as truth_number, count(*) as votes FROM Votes
JOIN Players ON Votes.voted_id == Players.id JOIN Players ON Votes.voted_id == Players.id

View file

@ -7,14 +7,15 @@ use rocket::response::Redirect;
use rocket_db_pools::{sqlx, Connection}; use rocket_db_pools::{sqlx, Connection};
use rocket_dyn_templates::{context, Template}; use rocket_dyn_templates::{context, Template};
use sqlx::{Acquire, Executor}; use sqlx::{Acquire, Executor};
use crate::auth; use crate::{auth, vote};
use crate::auth::User; use crate::auth::User;
use crate::database::Db; use crate::database::Db;
use crate::database_records::{DisplayTruth, Player, Truth, Week}; use crate::database_records::{DisplayTruth, Player, Truth, Week};
use crate::vote::WeeklyUserVotes;
#[get("/<week_number>")] #[get("/<week_number>")]
pub async fn week(week_number: u8, mut db: Connection<Db>, cookies: &CookieJar<'_>) -> Template { pub async fn week(week_number: u8, mut db: Connection<Db>, cookies: &CookieJar<'_>) -> Template {
let user: User = auth::get_user(week_number, &mut db, cookies).await; let user: User = auth::get_user(&mut db, cookies).await;
let other_players = if user.logged_in { let other_players = if user.logged_in {
match sqlx::query_as("SELECT id, name FROM Players WHERE id <> $1 AND is_admin == 0 ORDER BY name") match sqlx::query_as("SELECT id, name FROM Players WHERE id <> $1 AND is_admin == 0 ORDER BY name")
@ -41,6 +42,8 @@ pub async fn week(week_number: u8, mut db: Connection<Db>, cookies: &CookieJar<'
} }
}; };
let vote_data: WeeklyUserVotes = vote::get_weekly_user_votes(week_number,&user, &mut db).await;
// FIXME : This is fucking *trash* but fucking hell mate // FIXME : This is fucking *trash* but fucking hell mate
if user.is_admin { if user.is_admin {
let truths: Vec<Truth> = match sqlx::query_as("SELECT * FROM Truths WHERE week == $1 ORDER BY number") let truths: Vec<Truth> = match sqlx::query_as("SELECT * FROM Truths WHERE week == $1 ORDER BY number")
@ -58,6 +61,7 @@ pub async fn week(week_number: u8, mut db: Connection<Db>, cookies: &CookieJar<'
truths: truths, truths: truths,
user: user, user: user,
other_players: other_players, other_players: other_players,
vote_data: vote_data
}) })
} else { } else {
let truths: Vec<DisplayTruth> = match sqlx::query_as("SELECT id, number, author_id, rendered_text FROM Truths WHERE week == $1 ORDER BY number") let truths: Vec<DisplayTruth> = match sqlx::query_as("SELECT id, number, author_id, rendered_text FROM Truths WHERE week == $1 ORDER BY number")
@ -75,6 +79,7 @@ pub async fn week(week_number: u8, mut db: Connection<Db>, cookies: &CookieJar<'
truths: truths, truths: truths,
user: user, user: user,
other_players: other_players, other_players: other_players,
vote_data: vote_data
}) })
} }
} }
@ -82,7 +87,7 @@ pub async fn week(week_number: u8, mut db: Connection<Db>, cookies: &CookieJar<'
#[post("/<week>/edit", data="<raw_intro>")] #[post("/<week>/edit", data="<raw_intro>")]
pub async fn update_week(week: u8, raw_intro: Form<String>, pub async fn update_week(week: u8, raw_intro: Form<String>,
mut db: Connection<Db>, cookies: &CookieJar<'_>) -> Redirect { mut db: Connection<Db>, cookies: &CookieJar<'_>) -> Redirect {
let user = auth::get_user(week, &mut db, cookies).await; let user = auth::get_user(&mut db, cookies).await;
if !user.is_admin { if !user.is_admin {
cookies.add(("toast_error", "Vous n'avez pas la permission de changer la semaine.")); cookies.add(("toast_error", "Vous n'avez pas la permission de changer la semaine."));
return Redirect::to(uri!(week(week))); return Redirect::to(uri!(week(week)));
@ -118,7 +123,7 @@ pub async fn update_week(week: u8, raw_intro: Form<String>,
#[post("/<week>/set_last")] #[post("/<week>/set_last")]
pub async fn set_last_week(week: u8, mut db: Connection<Db>, cookies: &CookieJar<'_>) -> Redirect { pub async fn set_last_week(week: u8, mut db: Connection<Db>, cookies: &CookieJar<'_>) -> Redirect {
let user = auth::get_user(week, &mut db, cookies).await; let user = auth::get_user(&mut db, cookies).await;
if !user.is_admin { if !user.is_admin {
cookies.add(("toast_error", "Vous n'avez pas la permission de changer la semaine.")); cookies.add(("toast_error", "Vous n'avez pas la permission de changer la semaine."));
return Redirect::to(uri!(week(week))); return Redirect::to(uri!(week(week)));
@ -173,7 +178,7 @@ pub async fn set_last_week(week: u8, mut db: Connection<Db>, cookies: &CookieJar
#[get("/<week>/create")] #[get("/<week>/create")]
pub async fn create_week(week: u8, mut db: Connection<Db>, cookies: &CookieJar<'_>) -> Redirect { pub async fn create_week(week: u8, mut db: Connection<Db>, cookies: &CookieJar<'_>) -> Redirect {
let user = auth::get_user(week, &mut db, cookies).await; let user = auth::get_user(&mut db, cookies).await;
if !user.is_admin { if !user.is_admin {
cookies.add(("toast_error", "Vous n'avez pas la permission de changer la semaine.")); cookies.add(("toast_error", "Vous n'avez pas la permission de changer la semaine."));
return Redirect::to(uri!(week(week - 1))); return Redirect::to(uri!(week(week - 1)));

View file

@ -22,7 +22,7 @@
{%- endmacro display -%} {%- endmacro display -%}
{# Remove the form if all votes are locked, to reduce confusion. #} {# Remove the form if all votes are locked, to reduce confusion. #}
{% set lock_truth_form = user.votes | length + 1 == truths | length and week_data.is_last_week != true %} {% set lock_truth_form = vote_data.votes | length + 1 == truths | length and week_data.is_last_week != true %}
{% block body %} {% block body %}
@ -91,7 +91,7 @@
{% if user.logged_in == true and user.is_admin == false and not lock_truth_form %} {% if user.logged_in == true and user.is_admin == false and not lock_truth_form %}
<br/> <br/>
<button form="truths"> <button form="truths">
{%- if user.logged_in == true and user.has_week_vote == true -%} {%- if user.logged_in == true and vote_data.has_week_vote == true -%}
Changer de vote Changer de vote
{% else %} {% else %}
À voter ! À voter !

View file

@ -1,6 +1,6 @@
{%- set is_disabled = "" -%} {%- set is_disabled = "" -%}
{# If we are not during the active week, prevent changing vote but not sending a missing one. #} {# If we are not during the active week, prevent changing vote but not sending a missing one. #}
{%- if week_data.is_last_week != true and user.votes | filter(attribute="truth_id", value=truth.id) -%} {%- if week_data.is_last_week != true and vote_data.votes | filter(attribute="truth_id", value=truth.id) -%}
{%- set is_disabled = "disabled" -%} {%- set is_disabled = "disabled" -%}
{%- endif -%} {%- endif -%}
@ -19,7 +19,7 @@
{% for player in other_players %} {% for player in other_players %}
{# Check if we should pre-select an existing vote. #} {# Check if we should pre-select an existing vote. #}
{% set_global is_selected = "" %} {% set_global is_selected = "" %}
{% for vote in user.votes %} {% for vote in vote_data.votes %}
{% if truth.id == vote.truth_id and player.id == vote.voted_id %} {% if truth.id == vote.truth_id and player.id == vote.voted_id %}
{% set_global is_selected = "selected" %} {% set_global is_selected = "selected" %}
{% break %} {% break %}