Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Development merge into main #10

Merged
merged 7 commits into from
Oct 3, 2024
Merged
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
59 changes: 45 additions & 14 deletions sql/initdb.sql
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ CREATE TABLE IF NOT EXISTS users (
avatarurl VARCHAR(200) NOT NULL,
permissions BIGINT DEFAULT 0 NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
UNIQUE (username),
UNIQUE (steamid)
);

Expand All @@ -17,37 +16,70 @@ CREATE TABLE IF NOT EXISTS leagues (
created_at TIMESTAMPTZ NOT NULL
);

CREATE TABLE IF NOT EXISTS divisions (
id BIGSERIAL PRIMARY KEY,
leagueid BIGSERIAL NOT NULL,
prio INT NOT NULL,
name VARCHAR(50) NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
CONSTRAINT FK_divisions_league FOREIGN KEY (leagueid) references leagues(id)
);

CREATE TABLE IF NOT EXISTS division_admins (
id BIGSERIAL PRIMARY KEY,
divisionid BIGSERIAL NOT NULL,
userid BIGSERIAL NOT NULL,
relation VARCHAR(50) NOT NULL DEFAULT 'Admin',
CONSTRAINT FK_division_admins_user FOREIGN KEY (userid) references users(id),
CONSTRAINT FK_division_admins_division FOREIGN KEY (divisionid) references divisions(id)
);

CREATE TABLE IF NOT EXISTS teams (
id BIGSERIAL PRIMARY KEY,
leagueid BIGSERIAL NOT NULL,
team_tag VARCHAR(6) NOT NULL DEFAULT 'Tag',
team_name VARCHAR(200) NOT NULL DEFAULT 'Unnamed',
created_at TIMESTAMPTZ NOT NULL
);

-- A team can be in multiple leagues at the same time, and have different rosters, while having the same (base) team.
CREATE TABLE IF NOT EXISTS teamDivAssociations (
id BIGSERIAL PRIMARY KEY,
roster_name VARCHAR(50),
teamid BIGSERIAL NOT NULL,
divisionid BIGSERIAL NOT NULL,
points_up BIGINT NOT NULL DEFAULT 0,
points_down BIGINT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL,
CONSTRAINT FK_teams_league FOREIGN KEY (leagueid) references leagues(id)
CONSTRAINT FK_teamDivAssociation_team FOREIGN KEY (teamid) references teams(id),
CONSTRAINT FK_teamDivAssociation_division FOREIGN KEY (divisionid) references divisions(id)
);

CREATE TABLE IF NOT EXISTS userTeam (
leagueid BIGSERIAL NOT NULL,
CREATE TABLE IF NOT EXISTS userTeamAssociation (
id BIGSERIAL PRIMARY KEY,
userid BIGSERIAL NOT NULL,
teamid BIGSERIAL NOT NULL,
teamdivid BIGSERIAL NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
CONSTRAINT FK_userTeam_league FOREIGN KEY (leagueid) references leagues(id),
CONSTRAINT FK_userTeam_user FOREIGN KEY (userid) references users(id),
CONSTRAINT FK_userTeam_team FOREIGN KEY (teamid) references teams(id)
ended_at TIMESTAMPTZ,
is_leader BOOLEAN NOT NULL,
CONSTRAINT FK_userTeamAssociation_teamdivid FOREIGN KEY (teamdivid) references teamDivAssociations(id),
CONSTRAINT FK_userTeamAssociation_user FOREIGN KEY (userid) references users(id)
);

CREATE TABLE IF NOT EXISTS games (
id BIGSERIAL NOT NULL,
id BIGSERIAL PRIMARY KEY,
title VARCHAR(50),
leagueid BIGSERIAL NOT NULL,
teamhomeid BIGSERIAL NOT NULL,
teamawayid BIGSERIAL NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
played_at TIMESTAMPTZ NOT NULL,
CONSTRAINT FK_game_league FOREIGN KEY (leagueid) references leagues(id),
CONSTRAINT FK_game_home FOREIGN KEY (teamhomeid) references teams(id),
CONSTRAINT FK_game_away FOREIGN KEY (teamawayid) references teams(id)
);

CREATE TABLE IF NOT EXISTS authorizations (
id BIGSERIAL PRIMARY KEY,
userid BIGSERIAL NOT NULL,
token VARCHAR(50) NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
Expand All @@ -56,12 +88,11 @@ CREATE TABLE IF NOT EXISTS authorizations (
);

CREATE TABLE IF NOT EXISTS team_invites (
leagueid BIGSERIAL NOT NULL,
teamid BIGSERIAL NOT NULL,
id BIGSERIAL PRIMARY KEY,
teamdivid BIGSERIAL NOT NULL,
to_userid BIGSERIAL NOT NULL,
from_userid BIGSERIAL NOT NULL,
CONSTRAINT FK_team_invites_leagueid FOREIGN KEY (leagueid) references leagues(id),
CONSTRAINT FK_team_invites_teamid FOREIGN KEY (teamid) references teams(id),
CONSTRAINT FK_team_invites_teamdivid FOREIGN KEY (teamdivid) references teamDivAssociations(id),
CONSTRAINT FK_team_invites_to_userid FOREIGN KEY (to_userid) references users(id),
CONSTRAINT FK_team_invites_from_userid FOREIGN KEY (from_userid) references users(id)
);
Expand Down
41 changes: 39 additions & 2 deletions src/apiv1/admin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,43 @@ pub async fn post_league(
Ok(HttpResponse::Created().json(response))
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
pub struct MiniDivision {
pub leagueid: i64,
pub name: String,
}

#[post("/api/v1/admin/divisions")]
pub async fn post_league_divisions(
division: web::Json<MiniDivision>,
state: web::Data<AppState>,
auth: web::Header<AuthHeader>,
) -> Result<HttpResponse, Error> {
log::debug!("POST request at /api/v1/divisions");
log::debug!("Authorization header: {0}", auth.0 .0);

log::trace!("Grabbing pool");
let client = crate::grab_pool(&state).await?;

let user = match db::get_user_from_auth_token(&client, &auth.0 .0).await {
Ok(user) => user,
Err(_) => return Ok(HttpResponse::Unauthorized().body("Error processing permissions")),
};

// if not admin / can't create div
if !user.admin_or_perm(UserPermission::CreateLeague) {
return Ok(HttpResponse::Forbidden().body("Insufficient permissions"));
}

// Actually create the new div
log::info!("Authorization succeeded, creating a new division");
let division = division.into_inner();
log::debug!("Adding division: {0:?}", division);
let response = db::divisions::add_division(&client, division).await?;
log::trace!("OK response, {response:?}");

Ok(HttpResponse::Created().json(response))
}
/// Set a user or multiple users to a team.
#[post("/api/v1/admin/setuserteam")]
pub async fn post_users_team(
Expand All @@ -107,6 +144,6 @@ pub async fn post_users_team(
let user_team = user_team.into_inner();
// fetch the team to get its id
let team = db::get_team_from_id(&client, user_team.team_id).await?;
let users = db::get_team_players(&client, &team).await?;
Ok(HttpResponse::Ok().json(users))
todo!();
Ok(HttpResponse::Ok().finish())
}
5 changes: 0 additions & 5 deletions src/apiv1/apimodels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,3 @@ pub struct UserResponse {
pub user: User,
pub teams: Vec<Team>,
}
#[derive(Serialize, Deserialize)]
pub struct LeagueResponse {
pub info: League,
pub teams: Vec<Team>,
}
79 changes: 68 additions & 11 deletions src/apiv1/leagues.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,64 @@
use crate::db;
use crate::db::divisions::DeepTeamDivAssociation;
use crate::errors::MyError;
use actix_web::body::MessageBody;
use actix_web::{get, web, Error, HttpResponse};
use deadpool_postgres::Client;
use serde::{Deserialize, Serialize};

use crate::apiv1::LeagueResponse;
use crate::models::League;
use crate::apiv1::grab_pool;
use crate::models::{Division, League, Team, TeamDivAssociation, WrappedDivisionAdmin};
use crate::AppState;

#[get("/api/v1/leagues")]
async fn get_all_leagues(state: web::Data<AppState>) -> Result<HttpResponse, Error> {
log::info!("GET request at /api/v1/leagues");
let client = state.pool.get().await.map_err(MyError::PoolError)?;
let client = grab_pool(&state).await?;

let leagues: Vec<League> = db::get_leagues(&client).await?;
let leagues: Vec<League> = db::leagues::get_leagues(&client).await?;

Ok(HttpResponse::Ok().json(leagues))
let mut league_responses: Vec<LeagueReturn> = Vec::with_capacity(leagues.len());

for league in leagues {
let league_divs: Vec<Division> =
db::leagues::get_divs_for_league_id(&client, league.id).await?;

let mut divisions: Vec<DivisionOptionalTeams> = Vec::with_capacity(league_divs.len());

for div in league_divs {
let admins = db::divisions::get_admins_for_div_id_wrapped(&client, div.id).await?;
divisions.push(DivisionOptionalTeams {
info: div,
admins,
teams: None,
})
}
league_responses.push(LeagueReturn {
divisions,
info: league,
})
}

Ok(HttpResponse::Ok().json(league_responses))
}

#[derive(Serialize, Deserialize)]
struct AdminInfo {
inner_user_id: i64,
relation: String,
}

#[derive(Serialize, Deserialize)]
struct DivisionOptionalTeams {
info: Division,
admins: Vec<WrappedDivisionAdmin>,
teams: Option<Vec<DeepTeamDivAssociation>>,
}

#[derive(Serialize, Deserialize)]
struct LeagueReturn {
info: League,
divisions: Vec<DivisionOptionalTeams>,
}

#[get("/api/v1/leagues/{league_id}")]
Expand All @@ -23,15 +67,28 @@ pub async fn get_league(
league_id: web::Path<i64>,
) -> Result<HttpResponse, Error> {
log::debug!("GET request at /api/v1/leagues/league_id");
let client: Client = state.pool.get().await.map_err(MyError::PoolError)?;

let league_info = db::get_league_from_id(&client, *league_id).await?;
let client: Client = grab_pool(&state).await?;

let league_info = db::leagues::get_league_from_id(&client, *league_id).await?;

let league_divs: Vec<Division> =
db::leagues::get_divs_for_league_id(&client, *league_id).await?;

let teams = db::get_teams_with_leagueid(&client, *league_id).await?;
let mut divisions: Vec<DivisionOptionalTeams> = Vec::with_capacity(league_divs.len());
for div in league_divs {
let admins = db::divisions::get_admins_for_div_id_wrapped(&client, div.id).await?;
let teams = db::divisions::get_teams_for_div_id(&client, div.id).await?;
divisions.push(DivisionOptionalTeams {
info: div,
admins,
teams: Some(teams),
})
}

let results = LeagueResponse {
let resp = LeagueReturn {
info: league_info,
teams,
divisions,
};
Ok(HttpResponse::Ok().json(results))
Ok(HttpResponse::Ok().json(resp))
}
10 changes: 7 additions & 3 deletions src/apiv1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub async fn logout(
body: web::Json<LogoutFields>,
state: web::Data<AppState>,
) -> Result<HttpResponse, Error> {
let client = state.pool.get().await.unwrap();
let client = grab_pool(&state).await?;
let user = match db::get_user_from_auth_token(&client, &body.auth_token).await {
Ok(user) => user,
Err(err) => return Ok(HttpResponse::BadRequest().body(format!("{err:?}"))),
Expand Down Expand Up @@ -123,6 +123,10 @@ struct Token {
pub expires: DateTime<Utc>,
}

pub async fn grab_pool(state: &AppState) -> Result<Client, MyError> {
state.pool.get().await.map_err(MyError::PoolError)
}

#[post("/api/v1/verifylogin")]
pub async fn verify_openid_login(
body: web::Json<OpenIdFields>,
Expand Down Expand Up @@ -158,7 +162,7 @@ pub async fn verify_openid_login(
// let openid_sig = inner.get("openid.sig").expect("No openid.sig on request");
let steamid = openid_identity.replace("https://steamcommunity.com/openid/id/", "");
log::info!("Openid landing received from steamid: {steamid}");
let client: Client = state.pool.get().await.map_err(MyError::PoolError)?;
let client: Client = grab_pool(&state).await?;

let auth = match db::get_user_from_steamid(&client, &steamid).await {
// there is a user corresponding
Expand Down Expand Up @@ -202,7 +206,7 @@ pub async fn verify_openid_login(
#[derive(Serialize, Deserialize)]
struct TeamResponse {
pub id: i64,
pub leagueid: i64,
pub divisionid: i64,
pub team_name: String,
pub players: Vec<User>,
}
20 changes: 13 additions & 7 deletions src/apiv1/teams.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::db;
use crate::errors::MyError;
use crate::models::League;
use crate::models::MiniTeam;
use crate::models::TeamDivAssociation;
use crate::models::User;
use crate::steamapi;
use crate::CurrentHost;
Expand All @@ -13,23 +14,28 @@ use deadpool_postgres::{Client, Pool};
use crate::apiv1::TeamResponse;
use crate::AppState;

// this will be retroactively changed to be for a teamDivAssociation and not a root team
// maybe /rootteam/{team_id}?
#[get("/api/v1/teams/{team_id}")]
async fn get_team(state: web::Data<AppState>, path: web::Path<i64>) -> Result<HttpResponse, Error> {
log::info!("GET request at /api/v1/teams/{path}");
let team_id = path.into_inner();
if team_id < 0 {
let team_div_assoc_id = path.into_inner();
if team_div_assoc_id < 0 {
return Err(MyError::NotFound.into());
}
let client = state.pool.get().await.map_err(MyError::PoolError)?;
let team = db::get_team_from_id(&client, team_id).await?;
let players = db::get_team_players(&client, &team).await?;
let team_div_assoc: TeamDivAssociation =
db::get_teamdivassociation_from_id(&client, team_div_assoc_id).await?;
let team = db::get_team_from_id(&client, team_div_assoc.teamid).await?;

let players = db::get_team_players(&client, &team_div_assoc).await?;
let resp = TeamResponse {
id: team.id,
leagueid: team.leagueid,
divisionid: team_div_assoc.divisionid,
team_name: team.team_name,
players,
};
Ok(HttpResponse::Created().json(resp))
Ok(HttpResponse::Ok().json(resp))
}
#[post("/api/v1/teams")]
async fn post_team(
Expand All @@ -40,7 +46,7 @@ async fn post_team(
let team = new_team.into_inner();
let client = state.pool.get().await.map_err(MyError::PoolError)?;
let leagueid = team.leagueid;
let league = match db::get_league_from_id(&client, leagueid).await {
let league = match db::leagues::get_league_from_id(&client, leagueid).await {
Ok(league) => league,
Err(_) => return Ok(HttpResponse::NotFound().body("League not found with id ${leagueid}")),
};
Expand Down
Loading