Skip to content

Commit

Permalink
team join error pages [deploy]
Browse files Browse the repository at this point in the history
  • Loading branch information
mbund committed Sep 19, 2024
1 parent e045c5e commit 89d7c5b
Show file tree
Hide file tree
Showing 13 changed files with 461 additions and 85 deletions.
3 changes: 1 addition & 2 deletions examples/standalone/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,11 @@ divisions:
description: Undergraduate university students globally
email_regex: ^.*.edu$
requirement: Must verify a valid university .edu email address. Max of up to 4 players
max_players: 4
- name: OSU
description: OSU undergraduate students
email_regex: ^.*\.\d+@(buckeyemail\.)?osu\.edu$
requirement: Must verify a valid OSU email address
max_players: 1
max_players: 4
discord:
guild_id: 1160610137703186636
client_id: 1160076447977848945
Expand Down
38 changes: 19 additions & 19 deletions rhombus/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
Expand Down Expand Up @@ -74,11 +74,11 @@ dialog[open] {
.tooltip {
@apply rounded-lg bg-secondary px-3 py-2 font-medium slide-in-from-bottom-full;
transform-origin: var(--kb-tooltip-content-transform-origin);
animation: contentHide 100ms ease-in forwards;
animation: contentHide 100ms ease-in forwards;
}

.tooltip[data-expanded] {
animation: contentShow 100ms ease-out;
animation: contentShow 100ms ease-out;
}

#loader:not(.htmx-request) {
Expand All @@ -90,22 +90,22 @@ dialog[open] {
}

@keyframes contentShow {
from {
opacity: 0;
transform: scale(0.96);
}
to {
opacity: 1;
transform: scale(1);
}
from {
opacity: 0;
transform: scale(0.96);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes contentHide {
from {
opacity: 1;
transform: scale(1);
}
to {
opacity: 0;
transform: scale(0.96);
}
from {
opacity: 1;
transform: scale(1);
}
to {
opacity: 0;
transform: scale(0.96);
}
}
182 changes: 128 additions & 54 deletions rhombus/src/internal/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,68 +138,138 @@ pub async fn route_signin(
Extension(page): Extension<PageMeta>,
params: Query<SignInParams>,
) -> Response<Body> {
let (invite_token_cookie, team_name) = if let Some(url_invite_token) = &params.token {
let team = state
let mut invite_token_cookie = Cookie::build(("rhombus-invite-token", ""))
.path("/")
.max_age(time::Duration::hours(-1))
.same_site(SameSite::Lax)
.http_only(true);

let mut team_name = None;

if let Some(url_invite_token) = &params.token {
if let Some(team_meta) = state
.db
.get_team_meta_from_invite_token(url_invite_token)
.await
.unwrap_or(None);
.unwrap_or(None)
{
let new_team = state.db.get_team_from_id(team_meta.id).await.unwrap();

// You cannot join a team in which the owner has left (and is on a different team themselves). This doesn't
// need an error page, it should just count as an invalid invite token and not register.
if !new_team.users.is_empty() {
if let Some(user) = &user {
// you cannot join a team if your current team has more than just you on it
let old_team = state.db.get_team_from_id(user.team_id).await.unwrap();
if old_team.users.len() > 1 {
// return Json(json!({
// "message": "You cannot join a team while your old team has other members in it."
// }))
// .into_response();

let html = state
.jinja
.get_template("join-error-existing-team.html")
.unwrap()
.render(context! {
global => state.global_page_meta,
page,
title => format!("Team Join Error | {}", state.global_page_meta.title),
user,
team => old_team,
})
.unwrap();
return Html(html).into_response();
}

if let (Some(team), Some(user)) = (&team, &user) {
// you cannot join a team if your current team has more than just you on it
let old_team = state.db.get_team_from_id(user.team_id).await.unwrap();
if old_team.users.len() > 1 {
return Redirect::temporary("/team").into_response();
}
// you cannot join a team if the new team if, as a result of you joining it would
// lead to the team having more players than the minimum required by any division
let team_divisions = state.db.get_team_divisions(team_meta.id).await.unwrap();
let user_divisions = state.db.get_user_divisions(user.id).await.unwrap();

let max_players = state
.divisions
.iter()
.filter(|division| team_divisions.contains(&division.id))
.filter_map(|division| match division.max_players {
MaxDivisionPlayers::Unlimited => None,
MaxDivisionPlayers::Limited(max) => Some(max),
})
.min()
.map(MaxDivisionPlayers::Limited)
.unwrap_or(MaxDivisionPlayers::Unlimited);
match max_players {
MaxDivisionPlayers::Unlimited => {}
MaxDivisionPlayers::Limited(max_players) => {
if new_team.users.len() >= max_players.get() as usize {
// return Json(json!({
// "message": "The team you are trying to join already has the maximum number of players allowed by their division."
// }))
// .into_response();

let html = state
.jinja
.get_template("join-error-max.html")
.unwrap()
.render(context! {
global => state.global_page_meta,
page,
title => format!("Team Join Error | {}", state.global_page_meta.title),
user,
team => new_team,
max_players,
})
.unwrap();
return Html(html).into_response();
}
}
}

// you cannot join a team if the new team if, as a result of you joining it would
// lead to the team having more players than the minimum required by any division
let team_divisions = state.db.get_team_divisions(team.id).await.unwrap();
let user_divisions = state.db.get_user_divisions(user.id).await.unwrap();
let new_team = state.db.get_team_from_id(team.id).await.unwrap();
let min_players = state
.divisions
.iter()
.filter(|division| {
team_divisions.contains(&division.id) && user_divisions.contains(&division.id)
})
.filter_map(|division| match division.max_players {
MaxDivisionPlayers::Unlimited => None,
MaxDivisionPlayers::Limited(max) => Some(max),
})
.min()
.map(MaxDivisionPlayers::Limited)
.unwrap_or(MaxDivisionPlayers::Unlimited);
match min_players {
MaxDivisionPlayers::Unlimited => {}
MaxDivisionPlayers::Limited(min_players) => {
if new_team.users.len() >= min_players.get() as usize {
return Redirect::temporary("/team").into_response();
let needs_divisions = team_divisions
.iter()
.filter(|division_id| !user_divisions.contains(division_id))
.collect::<Vec<_>>();

if !needs_divisions.is_empty() {
// return Json(json!({
// "message": format!("To join this division you also need to join the following divisions: {}", needs_divisions.iter().map(|division_id| state.divisions.iter().find(|division| division.id == **division_id).unwrap().name.clone()).collect::<Vec<_>>().join(", "))
// }))
// .into_response();

let html = state
.jinja
.get_template("join-error-division.html")
.unwrap()
.render(context! {
global => state.global_page_meta,
page,
title => format!("Team Join Error | {}", state.global_page_meta.title),
user,
divisions => state.divisions,
user_divisions => user_divisions,
team_divisions => team_divisions,
team => new_team,
})
.unwrap();
return Html(html).into_response();
}

state
.db
.add_user_to_team(user.id, team_meta.id, Some(old_team.id))
.await
.unwrap();
return Redirect::to("/team").into_response();
}
}

state.db.add_user_to_team(user.id, team.id).await.unwrap();
return Redirect::to("/team").into_response();
invite_token_cookie = Cookie::build(("rhombus-invite-token", url_invite_token))
.path("/")
.max_age(time::Duration::hours(1))
.same_site(SameSite::Lax)
.http_only(true);
team_name = Some(team_meta.name.clone());
}
}

(
Cookie::build(("rhombus-invite-token", url_invite_token))
.path("/")
.max_age(time::Duration::hours(1))
.same_site(SameSite::Lax)
.http_only(true),
team.map(|t| t.name.clone()),
)
} else {
(
Cookie::build(("rhombus-invite-token", ""))
.path("/")
.max_age(time::Duration::hours(-1))
.same_site(SameSite::Lax)
.http_only(true),
None,
)
};

let auth_options = state.settings.read().await.auth.clone();
Expand Down Expand Up @@ -1028,7 +1098,11 @@ async fn sign_in_cookie(
.await
.unwrap_or(None)
{
state.db.add_user_to_team(user_id, team.id).await.unwrap();
state
.db
.add_user_to_team(user_id, team.id, None)
.await
.unwrap();
}
}

Expand Down
15 changes: 13 additions & 2 deletions rhombus/src/internal/database/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,22 @@ impl Database for DbCache {
get_team_from_id(&self.inner, team_id).await
}

async fn add_user_to_team(&self, user_id: i64, team_id: i64) -> Result<()> {
let result = self.inner.add_user_to_team(user_id, team_id).await;
async fn add_user_to_team(
&self,
user_id: i64,
team_id: i64,
old_team_id: Option<i64>,
) -> Result<()> {
let result = self
.inner
.add_user_to_team(user_id, team_id, old_team_id)
.await;
if result.is_ok() {
TEAM_CACHE.remove(&team_id);
USER_CACHE.remove(&user_id);
if let Some(old_team_id) = old_team_id {
TEAM_CACHE.remove(&old_team_id);
}
}
result
}
Expand Down
7 changes: 6 additions & 1 deletion rhombus/src/internal/database/libsql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -912,7 +912,12 @@ impl<T: LibSQLConnection + Send + Sync> Database for T {
}))
}

async fn add_user_to_team(&self, user_id: i64, team_id: i64) -> Result<()> {
async fn add_user_to_team(
&self,
user_id: i64,
team_id: i64,
_old_team_id: Option<i64>,
) -> Result<()> {
let tx = self.transaction().await?;

tx.execute(
Expand Down
7 changes: 6 additions & 1 deletion rhombus/src/internal/database/postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,12 @@ impl Database for Postgres {
todo!()
}

async fn add_user_to_team(&self, _user_id: i64, _team_id: i64) -> Result<()> {
async fn add_user_to_team(
&self,
_user_id: i64,
_team_id: i64,
_old_team_id: Option<i64>,
) -> Result<()> {
todo!()
}

Expand Down
7 changes: 6 additions & 1 deletion rhombus/src/internal/database/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,12 @@ pub trait Database {
async fn get_team_meta_from_invite_token(&self, invite_token: &str)
-> Result<Option<TeamMeta>>;
async fn get_team_from_id(&self, team_id: i64) -> Result<Team>;
async fn add_user_to_team(&self, user_id: i64, team_id: i64) -> Result<()>;
async fn add_user_to_team(
&self,
user_id: i64,
team_id: i64,
old_team_id: Option<i64>,
) -> Result<()>;
async fn solve_challenge(
&self,
user_id: i64,
Expand Down
Loading

0 comments on commit 89d7c5b

Please sign in to comment.