Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ jobs:
include:
- { os: "macos-latest", target: "aarch64-apple-darwin" }
- { os: "ubuntu-latest", target: "x86_64-unknown-linux-gnu" }
- { os: "ubuntu-latest", target: "x86_64-unknown-linux-musl" }
- { os: "ubuntu-24.04-arm", target: "aarch64-unknown-linux-gnu" }
- { os: "ubuntu-24.04-arm", target: "aarch64-unknown-linux-musl" }
- { os: "windows-latest", target: "x86_64-pc-windows-msvc" }

runs-on: ${{ matrix.os }}
Expand All @@ -40,7 +42,7 @@ jobs:
if: contains(matrix.os, 'ubuntu')
uses: awalsh128/cache-apt-pkgs-action@latest
with:
packages: libwebkit2gtk-4.1-dev build-essential curl wget file libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev lld
packages: libwebkit2gtk-4.1-dev build-essential curl wget file libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev lld musl-tools # don't wanna branch for musl
version: latest # could be anything

- name: install rust target
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@
**/Cargo.lock
assets/tailwind.css

.**-apollo-sid.cookie
.*-apollo-sid.cookie
6 changes: 6 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,9 @@ The role of this file is to describe common mistakes and confusion points that a
**NOTE**: only read this if the code change will effect core parts with `dioxus`

you can find in-depth `dioxus` docs under <./.docs/dioxus.md>

---

## surprises encountered

- `make check` also runs `cargo check -F web --target wasm32-unknown-unknown`; in fresh environments the `wasm32-unknown-unknown` target may be missing, so run `rustup target add wasm32-unknown-unknown` first if needed.
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ server-build:
cp target/x86_64-unknown-linux-gnu/release/apollo apollo-server-x64-linux-gnu

clean:
rm .user-*-apollo-sid.cookie | echo 'no sid-cookies to delete'
rm .*-apollo-sid.cookie || echo 'no sid-cookies to delete'
rm apollo-state.cbor.encrypted || echo 'no default state-save to delete'
cargo clean

help list:
Expand Down
17 changes: 11 additions & 6 deletions src/backend/endpoints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ pub async fn join(username: String) -> Result<SetHeader<SetCookie>, HttpError> {
_ = TEAMS
.write()
.await
.insert(username.clone(), SolvedPuzzles::new());
.insert(username.clone(), TeamAttempts::new());
}
// allowed to log in, but don't reset progress

Expand Down Expand Up @@ -220,21 +220,26 @@ pub async fn submit_solution(
.or_internal_server_error("nem sikerült ellenőrizni a feladatmegoldást")?
};
solution.zeroize();
is_solution_valid.or_forbidden("érvénytelen megoldás ehhez a feladathoz")?;
let is_correct = is_solution_valid;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why new var?


let mut teams_lock = TEAMS.write().await;

let team_solved = teams_lock
let team_attempts = teams_lock
.get_mut(&username)
.or_internal_server_error("nincs ehhez a csapatnévhez előrehaladás rendelve")?;

team_solved
.insert(puzzle_id)
(!team_has_solved_puzzle(team_attempts, &puzzle_id))
.or_forbidden("ezt a feladatot már megoldottad")?;

team_attempts.push(SolveAttempt::now(puzzle_id, is_correct));
drop(teams_lock);

#[cfg(feature = "server_state_save")]
tokio::spawn(state_save::save_state());

Ok(String::from("hurrá, sikeresen elmentettük a megoldásod!"))
if is_correct {
Ok(String::from("hurrá, sikeresen elmentettük a megoldásod!"))
} else {
HttpError::forbidden("érvénytelen megoldás ehhez a feladathoz")
}
}
60 changes: 55 additions & 5 deletions src/backend/models.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use dioxus::fullstack::serde;
use std::collections::{HashMap, HashSet};
use std::collections::HashMap;
use std::time::SystemTime;

// SECURITY: SecretString, with manual impls?
#[derive(Clone, PartialOrd, Ord, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
Expand All @@ -20,7 +21,56 @@ pub type PuzzleSolutionHash = String;
pub type PuzzlesExisting = HashMap<PuzzleId, PuzzleValue>;
/// all the puzzles with their values and solutions
pub type PuzzleSolutions = HashMap<PuzzleId, Puzzle>;
/// solved puzzles of a team
pub type SolvedPuzzles = HashSet<PuzzleId>;
/// progress of each team, which puzzles they've solved
pub type TeamsState = HashMap<String, SolvedPuzzles>;

#[derive(Clone, PartialOrd, Ord, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
#[serde(crate = "dioxus::fullstack::serde")]
pub struct SolveAttempt {
pub puzzle_id: PuzzleId,
pub attempted_at: SystemTime,
pub correct: bool,
}
impl SolveAttempt {
pub fn now(puzzle_id: PuzzleId, correct: bool) -> Self {
Self {
puzzle_id,
attempted_at: SystemTime::now(),
correct,
}
}
}

/// all solve attempts of a team
pub type TeamAttempts = Vec<SolveAttempt>;
/// progress of each team, which puzzles they attempted and when
pub type TeamsState = HashMap<String, TeamAttempts>;

pub fn team_has_solved_puzzle(team_attempts: &[SolveAttempt], puzzle_id: &str) -> bool {
team_attempts
.iter()
.any(|attempt| attempt.puzzle_id == puzzle_id && attempt.correct)
}

#[cfg(test)]
mod tests {
use super::{SolveAttempt, team_has_solved_puzzle};
use std::time::UNIX_EPOCH;

#[test]
fn marks_puzzle_as_solved_only_for_correct_attempts() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesn't actually test anything, have a look at insta then

let team_attempts = vec![
SolveAttempt {
puzzle_id: "p1".into(),
attempted_at: UNIX_EPOCH,
correct: false,
},
SolveAttempt {
puzzle_id: "p1".into(),
attempted_at: UNIX_EPOCH,
correct: true,
},
];

assert!(team_has_solved_puzzle(&team_attempts, "p1"));
assert!(!team_has_solved_puzzle(&team_attempts, "p2"));
}
}
Loading