Skip to content

Commit

Permalink
Merge pull request #1 from prplnorangesoda/admin
Browse files Browse the repository at this point in the history
Admin
  • Loading branch information
prplnorangesoda authored Sep 11, 2024
2 parents 3e4f4cd + a322e7c commit a32fc12
Show file tree
Hide file tree
Showing 15 changed files with 322 additions and 47 deletions.
1 change: 1 addition & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[submodule "lucyleague-frontend"]
path = lucyleague-frontend
url = https://github.com/prplnorangesoda/lucyleague-frontend
branch = main
53 changes: 48 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ randomizer = "0.1.2"
chrono = { version = "0.4", features = ["serde"] }
actix-files = "0.6"
serde_json = "1.0"
steamopenid = "0.1.0"
steamopenid = "0.3.0"
inquire = "0.7.5"
num-derive = "0.4.2"
num-traits = "0.2.19"
actix-http = "3.9.0"
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# LucyLeague

Here's the backend for (hopefully [lucyleague.net](https://lucyleague.net) or [league.passtime.tf]). Please LET ME KNOW if there's anything stupid here like exposed API keys or insecure thingies.

## Running locally

This project is dockerized. Simply running `docker compose up` after cloning (AND MAKING A .env FILE!) should work out of the box. Submit an issue if this doesn't work!
66 changes: 66 additions & 0 deletions backend-src/apiv1/admin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use std::str::FromStr;

use actix_http::header::TryIntoHeaderValue;
use actix_web::http::header;
use actix_web::{get, http, post, web, Error, HttpResponse, Responder};

use crate::apiv1::apimodels::*;
use crate::db;
use crate::errors::MyError;
use crate::models::*;
use crate::AppState;
use deadpool_postgres::Client;

struct AuthHeader(String);

impl TryIntoHeaderValue for AuthHeader {
type Error = actix_web::error::HttpError;
fn try_into_value(self) -> Result<header::HeaderValue, Self::Error> {
Ok(header::HeaderValue::from_bytes(self.0.as_bytes())?)
}
}

pub struct AuthHeaderStringError;

impl FromStr for AuthHeader {
type Err = AuthHeaderStringError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(AuthHeader("Hi".to_owned()))
}
}
impl http::header::Header for AuthHeader {
fn name() -> header::HeaderName {
header::AUTHORIZATION
}
fn parse<M: actix_web::HttpMessage>(msg: &M) -> Result<Self, actix_web::error::ParseError> {
actix_http::header::from_one_raw_str(msg.headers().get(Self::name()))
}
}

#[post("/api/v1/leagues")]
pub async fn post_league(
league: web::Json<MiniLeague>,
state: web::Data<AppState>,
authorization: web::Header<AuthHeader>,
) -> Result<HttpResponse, Error> {
println!("POST request at /api/v1/leagues");
let client: Client = state.pool.get().await.map_err(MyError::PoolError)?;
let league = league.into_inner();
let response = db::add_league(&client, league).await?;

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

/// Set a user or multiple users to a team.
#[post("/api/v1/users/setteam")]
pub async fn post_users_team(
user_team: web::Json<UserTeamBody>,
state: web::Data<AppState>,
) -> Result<HttpResponse, Error> {
let client = state.pool.get().await.map_err(MyError::PoolError)?;
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))
}
21 changes: 21 additions & 0 deletions backend-src/apiv1/apimodels.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use serde::{Serialize, Deserialize};

use crate::models::*;

#[derive(Serialize, Deserialize)]
pub struct UserTeamBody {
pub league_id: i64,
pub user_steamids: Vec<String>,
pub team_id: i64,
}

#[derive(Serialize, Deserialize)]
pub struct UserResponse {
pub user: User,
pub teams: Vec<Team>,
}
#[derive(Serialize, Deserialize)]
pub struct LeagueResponse {
pub info: League,
pub teams: Vec<Team>,
}
29 changes: 4 additions & 25 deletions backend-src/apiv1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use crate::db;
use crate::errors::MyError;
use crate::models::League;
use crate::models::MiniUser;
use crate::models::Team;
use crate::models::User;
use crate::steamapi;
use crate::PlayerSummaryAccess;
Expand All @@ -14,8 +13,10 @@ use deadpool_postgres::{Client, Pool};
use std::collections::HashMap;

pub mod admin;
mod apimodels;

use apimodels::*;

pub use admin::*;
/*
https://rgl.gg/Login/Default.aspx?push=1&r=40
&dnoa.userSuppliedIdentifier=https%3A%2F%2Fsteamcommunity.com%2Fopenid%2F
Expand All @@ -34,16 +35,7 @@ pub struct AppState {
pub steam_auth_url: String,
pub steam_api_key: String,
}
#[derive(serde::Serialize, serde::Deserialize)]
struct UserResponse {
user: User,
teams: Vec<Team>,
}
#[derive(serde::Serialize, serde::Deserialize)]
struct LeagueResponse {
info: League,
teams: Vec<Team>,
}


#[get("/api/v1/leagues/{league_id}")]
pub async fn get_league(
Expand All @@ -64,19 +56,6 @@ pub async fn get_league(
Ok(HttpResponse::Ok().json(results))
}

#[post("/api/v1/leagues")]
pub async fn post_league(
league: web::Json<MiniLeague>,
state: web::Data<AppState>,
) -> Result<HttpResponse, Error> {
println!("POST request at /api/v1/leagues");
let client: Client = state.pool.get().await.map_err(MyError::PoolError)?;
let league = league.into_inner();
let response = db::add_league(&client, league).await?;

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

#[get("/login/landing")]
pub async fn openid_landing(
query: web::Query<HashMap<String, String>>,
Expand Down
56 changes: 52 additions & 4 deletions backend-src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,66 @@ use tokio_pg_mapper::FromTokioPostgresRow;
use crate::{
authorization::create_authorization_for_user,
errors::MyError,
models::{Authorization, League, MiniLeague, MiniUser, Team, User},
models::{Authorization, League, MiniLeague, MiniUser, Team, User, UserTeam},
};

pub async fn initdb(client: &Client) -> Result<(), MyError> {
let _stmt = include_str!("../sql/initdb.sql");

client
.batch_execute(_stmt)
.await?;
client.batch_execute(_stmt).await?;
Ok(())
}

pub async fn get_team_from_id(client: &Client, team_id: i64) -> Result<Team, MyError> {
let _stmt = "SELECT $table_fields FROM teams WHERE teamid=$1";
let _stmt = _stmt.replace("$table_fields", &Team::sql_table_fields());
let stmt = client.prepare(&_stmt).await.unwrap();

let results = client
.query(&stmt, &[&team_id])
.await?
.iter()
.map(|row| Team::from_row_ref(row).unwrap())
.collect::<Vec<Team>>()
.pop()
.ok_or(MyError::NotFound);

results
}

pub async fn get_team_players(client: &Client, team: Team) -> Result<Vec<User>, MyError> {
let _stmt = "SELECT $table_fields FROM userTeam WHERE teamid=$1 AND leagueid=$2";
let _stmt = _stmt.replace("$table_fields", &UserTeam::sql_table_fields());
let stmt = client.prepare(&_stmt).await.unwrap();

let userids: Vec<i64> = client
.query(&stmt, &[&team.id, &team.leagueid])
.await?
.iter()
.map(|row| UserTeam::from_row_ref(row).unwrap().userid)
.collect();

mass_get_user_from_internal_id(client, &userids).await
}

async fn mass_get_user_from_internal_id(
client: &Client,
userids: &Vec<i64>,
) -> Result<Vec<User>, MyError> {
let _stmt = "SELECT $table_fields FROM users WHERE id IN ($1)";
let _stmt = _stmt.replace("$table_fields", &User::sql_table_fields());
let stmt = client.prepare(&_stmt).await.unwrap();

let users: Vec<User> = client
.query(&stmt, &[userids])
.await?
.iter()
.map(|row| User::from_row_ref(row).unwrap())
.collect();

Ok(users)
}

pub async fn get_league(client: &Client, leagueid: i64) -> Result<League, MyError> {
let _stmt = "SELECT $table_fields FROM leagues WHERE id=$1;";
let _stmt = _stmt.replace("$table_fields", &League::sql_table_fields());
Expand Down
5 changes: 2 additions & 3 deletions backend-src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use tokio_postgres::NoTls;

mod apiv1;
mod authorization;
mod checkpermission;
mod permission;
mod config;
mod db;
mod errors;
Expand Down Expand Up @@ -103,12 +103,11 @@ async fn main() -> io::Result<()> {
)
.service(get_league)
.service(get_all_leagues)
.service(post_league)
.service(admin::post_league)
.service(get_openid)
.service(openid_landing)
.service(
actix_files::Files::new("/", "./lucyleague-frontend/out")
.show_files_listing()
.index_file("index.html"),
)
})
Expand Down
4 changes: 2 additions & 2 deletions backend-src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ pub struct Team {
#[derive(Deserialize, PostgresMapper, Serialize)]
#[pg_mapper(table = "userTeam")]
pub struct UserTeam {
pub userid: String,
pub teamid: String,
pub userid: i64,
pub teamid: i64,
}

#[derive(Deserialize, PostgresMapper, Serialize, Debug)]
Expand Down
Loading

0 comments on commit a32fc12

Please sign in to comment.