2023-08-30 04:16:43 +02:00
use std ::{ collections ::HashMap , vec } ;
2023-08-26 06:36:12 +02:00
2023-08-30 04:16:43 +02:00
use actix_web ::{
delete , get , post , put ,
web ::{ self , Json } ,
HttpResponse , Responder ,
} ;
2023-08-26 06:36:12 +02:00
2023-08-30 04:16:43 +02:00
use crate ::{ db , AppState } ;
2023-08-26 06:36:12 +02:00
pub ( crate ) mod schemas ;
2023-08-30 04:16:43 +02:00
fn verify_auth ( token : & str , data : & AppState ) -> schemas ::AuthReturn {
let mut auth_return = schemas ::AuthReturn {
moderator : false ,
admin : false ,
} ;
if token = = data . auth_tokens . admin {
auth_return . admin = true ;
auth_return . moderator = true ;
} else if token = = data . auth_tokens . moderator {
auth_return . moderator = true ;
}
return auth_return ;
}
#[ utoipa::path(
params (
schemas ::AuthParams
) ,
responses (
( status = 200 , description = " OK " , body = AuthReturn ) ,
) ,
security (
( " api_key " = [ ] )
) ,
) ]
#[ get( " /api/v3/auth " ) ]
pub ( crate ) async fn auth (
data : web ::Data < AppState > ,
params : web ::Query < schemas ::AuthParamsOptional > ,
) -> impl Responder {
let params : schemas ::AuthParamsOptional = params . into_inner ( ) ;
let mut auth_return = schemas ::AuthReturn {
moderator : false ,
admin : false ,
} ;
if let Some ( auth_token ) = params . token . clone ( ) {
if auth_token = = data . auth_tokens . admin {
auth_return . admin = true ;
auth_return . moderator = true ;
} else if auth_token = = data . auth_tokens . moderator {
auth_return . moderator = true ;
}
}
Json ( auth_return )
}
2023-08-26 06:36:12 +02:00
#[ utoipa::path(
params (
) ,
responses (
( status = 200 , description = " OK " , body = FullViewData ) ,
) ,
security (
( " api_key " = [ ] )
) ,
) ]
2023-08-30 04:16:43 +02:00
#[ get( " /api/v3/full_view_data " ) ]
pub ( crate ) async fn full_view_data ( data : web ::Data < AppState > ) -> impl Responder {
2023-08-26 06:36:12 +02:00
let start = chrono ::Local ::now ( ) ; // DEBUG
// SQL Queries
// TODO: Optimize by utilizing some SQL magic, for now this has to do
2023-08-30 04:16:43 +02:00
let db_games : Vec < db ::schemas ::Game > = sqlx ::query_as ( " SELECT * FROM public.games ORDER BY id " )
. fetch_all ( & data . db )
. await
. expect ( " Error Fetching Data from DB " ) ;
let db_groups : Vec < db ::schemas ::GameGroup > =
sqlx ::query_as ( " SELECT * FROM public.game_groups ORDER BY id " )
. fetch_all ( & data . db )
. await
. expect ( " Error Fetching Data from DB " ) ;
let db_empires : Vec < db ::schemas ::Empire > =
sqlx ::query_as ( " SELECT * FROM public.empires ORDER BY id " )
. fetch_all ( & data . db )
. await
. expect ( " Error Fetching Data from DB " ) ;
let db_ethics : Vec < db ::schemas ::Ethic > =
sqlx ::query_as ( " SELECT * FROM public.ethics ORDER BY id " )
. fetch_all ( & data . db )
. await
. expect ( " Error Fetching Data from DB " ) ;
let db_empire_ethics : Vec < db ::schemas ::EmpireEthic > =
sqlx ::query_as ( " SELECT * FROM public.empire_ethics ORDER BY empires_id " )
. fetch_all ( & data . db )
. await
. expect ( " Error Fetching Data from DB " ) ;
let db_portrait_groups : Vec < db ::schemas ::PortraitGroup > =
sqlx ::query_as ( " SELECT * FROM public.portrait_groups ORDER BY id " )
. fetch_all ( & data . db )
. await
. expect ( " Error Fetching Data from DB " ) ;
let db_portraits : Vec < db ::schemas ::Portrait > =
sqlx ::query_as ( " SELECT * FROM public.portraits ORDER BY id " )
. fetch_all ( & data . db )
. await
. expect ( " Error Fetching Data from DB " ) ;
2023-08-26 06:36:12 +02:00
let mut parsed_data : schemas ::FullViewData = schemas ::FullViewData {
games : HashMap ::new ( ) ,
ethics : HashMap ::new ( ) ,
species : HashMap ::new ( ) ,
} ;
// Data processing
// Species Vector
db_portrait_groups . iter ( ) . for_each ( | species | {
let new_data = schemas ::Species {
id : species . id ,
displayName : species . name . clone ( ) ,
portraits : HashMap ::new ( ) ,
} ;
2023-08-30 04:16:43 +02:00
parsed_data
. species
. entry ( species . id )
. and_modify ( | d | * d = new_data . clone ( ) )
. or_insert ( new_data ) ;
2023-08-26 06:36:12 +02:00
} ) ;
db_portraits . iter ( ) . for_each ( | portrait | {
let new_data = schemas ::Portrait {
id : portrait . id ,
hires : portrait . hires . clone ( ) ,
lores : portrait . lores . clone ( ) ,
} ;
2023-08-30 04:16:43 +02:00
parsed_data
. species
. get_mut ( & portrait . group_id )
. unwrap ( )
. portraits
. entry ( portrait . id )
. and_modify ( | d | * d = new_data . clone ( ) )
. or_insert ( new_data ) ;
2023-08-26 06:36:12 +02:00
} ) ;
// Games Vector and Children
db_games . iter ( ) . for_each ( | game | {
let new_data = schemas ::ChellarisGame {
id : game . id ,
name : game . name . clone ( ) ,
empires : HashMap ::new ( ) ,
groups : HashMap ::new ( ) ,
} ;
2023-08-30 04:16:43 +02:00
parsed_data
. games
. entry ( game . id )
. and_modify ( | d | * d = new_data . clone ( ) )
. or_insert ( new_data ) ;
2023-08-26 06:36:12 +02:00
} ) ;
db_groups . iter ( ) . for_each ( | group | {
let new_data = schemas ::ChellarisGameGroup {
id : group . id ,
name : group . name . clone ( ) ,
} ;
2023-08-30 04:16:43 +02:00
parsed_data
. games
. get_mut ( & group . game_id )
. unwrap ( )
. groups
. entry ( group . id )
. and_modify ( | d | * d = new_data . clone ( ) )
. or_insert ( new_data ) ;
2023-08-26 06:36:12 +02:00
} ) ;
db_empires . iter ( ) . for_each ( | empire | {
let new_data = schemas ::ChellarisEmpire {
id : empire . id ,
gestalt : empire . gestalt . unwrap_or ( false ) ,
2023-08-30 04:16:43 +02:00
machine : false ,
2023-08-26 06:36:12 +02:00
group : empire . group_id ,
empire_portrait : empire . empire_portrait_id ,
empire_portrait_group : empire . empire_portrait_group_id ,
discord_user : empire . discord_user . clone ( ) ,
ethics : HashMap ::new ( ) ,
} ;
2023-08-30 04:16:43 +02:00
parsed_data
. games
. get_mut ( & empire . group_game_id )
. unwrap ( )
. empires
. entry ( empire . id )
. and_modify ( | d | * d = new_data . clone ( ) )
. or_insert ( new_data ) ;
2023-08-26 06:36:12 +02:00
} ) ;
// Ethics Vector
db_ethics . iter ( ) . for_each ( | ethic | {
let new_data = schemas ::Ethic {
id : ethic . id ,
displayName : ethic . name . clone ( ) ,
machine : ethic . machine_ethic ,
} ;
2023-08-30 04:16:43 +02:00
parsed_data
. ethics
. entry ( ethic . id )
. and_modify ( | d | * d = new_data . clone ( ) )
. or_insert ( new_data ) ;
2023-08-26 06:36:12 +02:00
} ) ;
db_empire_ethics . iter ( ) . for_each ( | empire_ethic | {
let game_id = empire_ethic . empires_group_game_id ;
let id = empire_ethic . empires_id ;
let new_data = schemas ::EmpireEthic {
id : empire_ethic . ethics_id ,
2023-08-30 04:16:43 +02:00
displayName : parsed_data . ethics [ & empire_ethic . ethics_id ]
. displayName
. clone ( ) ,
2023-08-26 06:36:12 +02:00
machine : parsed_data . ethics [ & empire_ethic . ethics_id ] . machine ,
fanatic : empire_ethic . ethics_fanatic ,
} ;
2023-08-30 04:16:43 +02:00
if new_data . machine {
parsed_data
. games
. get_mut ( & game_id )
. unwrap ( )
. empires
. get_mut ( & id )
. unwrap ( )
. machine = true ;
}
parsed_data
. games
. get_mut ( & game_id )
. unwrap ( )
. empires
. get_mut ( & id )
. unwrap ( )
. ethics
. entry ( empire_ethic . ethics_id )
. and_modify ( | d | * d = new_data . clone ( ) )
. or_insert ( new_data ) ;
2023-08-26 06:36:12 +02:00
} ) ;
println! ( " {:?} ms " , ( chrono ::Local ::now ( ) - start ) . to_std ( ) . unwrap ( ) ) ; // DEBUG
2023-08-30 04:16:43 +02:00
Json ( parsed_data )
}
// Data Fetching Endpoints for Admin/Moderator Menu
#[ utoipa::path(
params ( ) ,
responses (
( status = 200 , description = " OK " , body = HashMap < i32 , ChellarisGameLite > ) ,
) ,
security (
( " api_key " = [ ] )
) ,
) ]
#[ get( " /api/v3/list_games " ) ]
pub ( crate ) async fn list_games ( data : web ::Data < AppState > ) -> impl Responder {
// SQL Queries
// TODO: Optimize by utilizing some SQL magic, for now this has to do
let db_games : Vec < db ::schemas ::Game > = sqlx ::query_as ( " SELECT * FROM public.games ORDER BY id " )
. fetch_all ( & data . db )
. await
. expect ( " Error Fetching Data from DB " ) ;
let mut parsed_data : HashMap < i32 , schemas ::ChellarisGameLite > = HashMap ::new ( ) ;
// Data processing
// Games Vector
db_games . iter ( ) . for_each ( | game | {
let new_data = schemas ::ChellarisGameLite {
id : game . id ,
name : game . name . clone ( ) ,
} ;
parsed_data
. entry ( game . id )
. and_modify ( | d | * d = new_data . clone ( ) )
. or_insert ( new_data ) ;
} ) ;
Json ( parsed_data )
}
#[ utoipa::path(
params (
schemas ::GetGameParam
) ,
responses (
( status = 200 , description = " OK " , body = HashMap < i32 , ChellarisGameLite > ) ,
) ,
security (
( " api_key " = [ ] )
) ,
) ]
#[ get( " /api/v3/game " ) ]
pub ( crate ) async fn get_game_data (
data : web ::Data < AppState > ,
params : web ::Query < schemas ::GetGameParam > ,
) -> impl Responder {
let params : schemas ::GetGameParam = params . into_inner ( ) ;
// SQL Queries
// TODO: Optimize by utilizing some SQL magic, for now this has to do
let db_games : Vec < db ::schemas ::Game > ;
if let Some ( game_id ) = params . game_id {
db_games = match sqlx ::query_as! (
db ::schemas ::Game ,
" SELECT * FROM public.games WHERE id = $1 ORDER BY id " ,
game_id as i32
)
. fetch_one ( & data . db )
. await
{
Ok ( data ) = > vec! [ data ] ,
Err ( _ ) = > vec! [ ] ,
} ;
} else {
db_games = sqlx ::query_as ( " SELECT * FROM public.games ORDER BY id " )
. fetch_all ( & data . db )
. await
. expect ( " Error Fetching Data from DB " ) ;
}
let mut parsed_data : HashMap < i32 , schemas ::ChellarisGameLite > = HashMap ::new ( ) ;
// Data processing
// Games Vector
db_games . iter ( ) . for_each ( | game | {
let new_data = schemas ::ChellarisGameLite {
id : game . id ,
name : game . name . clone ( ) ,
} ;
parsed_data
. entry ( game . id )
. and_modify ( | d | * d = new_data . clone ( ) )
. or_insert ( new_data ) ;
} ) ;
2023-08-26 06:36:12 +02:00
Json ( parsed_data )
}
2023-08-30 04:16:43 +02:00
#[ utoipa::path(
request_body = PostGameParams ,
responses (
( status = 200 , description = " OK " , body = ChellarisGameLite ) ,
( status = 422 , description = " Missing Game Name " ) ,
( status = 401 , description = " Auth Token Invalid " ) ,
) ,
security (
( " api_key " = [ ] )
) ,
) ]
#[ post( " /api/v3/game " ) ]
pub ( crate ) async fn create_game (
data : web ::Data < AppState > ,
params : web ::Json < schemas ::PostGameParams > ,
) -> impl Responder {
let params = params . into_inner ( ) ;
let user_auth : schemas ::AuthReturn = verify_auth ( & params . auth . token , & data ) ;
// SQL Queries
// TODO: Optimize by utilizing some SQL magic, for now this has to do
if user_auth . admin | | user_auth . moderator {
if params . game_name ! = " " {
let db_game : db ::schemas ::Game ;
db_game = match sqlx ::query_as! (
db ::schemas ::Game ,
" INSERT INTO public.games(name) VALUES ($1) RETURNING * " ,
params . game_name
)
. fetch_one ( & data . db )
. await
{
Ok ( data ) = > data ,
Err ( _ ) = > return HttpResponse ::UnprocessableEntity ( ) . finish ( ) ,
} ;
let parsed_game : schemas ::ChellarisGameLite = schemas ::ChellarisGameLite {
id : db_game . id ,
name : db_game . name ,
} ;
return HttpResponse ::Ok ( ) . json ( parsed_game ) ;
} else {
return HttpResponse ::UnprocessableEntity ( ) . finish ( ) ;
}
} else {
return HttpResponse ::Unauthorized ( ) . finish ( ) ;
}
}
#[ utoipa::path(
params (
schemas ::DeleteGameParam ,
) ,
request_body = AuthParams ,
responses (
( status = 200 , description = " OK " ) ,
( status = 422 , description = " Missing Game ID " ) ,
( status = 401 , description = " Auth Token Invalid " ) ,
) ,
security (
( " api_key " = [ ] )
) ,
) ]
#[ delete( " /api/v3/game " ) ]
pub ( crate ) async fn delete_game (
data : web ::Data < AppState > ,
auth_params : web ::Json < schemas ::AuthParams > ,
param : web ::Query < schemas ::DeleteGameParam > ,
) -> impl Responder {
let auth_params = auth_params . into_inner ( ) ;
let param = param . into_inner ( ) ;
let user_auth : schemas ::AuthReturn = verify_auth ( & auth_params . token , & data ) ;
// SQL Queries
// TODO: Optimize by utilizing some SQL magic, for now this has to do
if user_auth . admin | | user_auth . moderator {
match sqlx ::query! (
" DELETE FROM public.games WHERE id = $1 " ,
param . game_id as i32
)
. execute ( & data . db )
. await
{
Ok ( _ ) = > { } ,
Err ( _ ) = > return HttpResponse ::UnprocessableEntity ( ) . finish ( ) ,
} ;
return HttpResponse ::Ok ( ) . into ( ) ;
} else {
return HttpResponse ::Unauthorized ( ) . finish ( ) ;
}
}
// Data Manipulation Endpoints
// Moderator & Admin
// Add/Update/Remove Empire
// Add/Update/Remove Group
// Add/Update/Remove Game
// Admin
// Add/Update/Remove Portrait
// Add/Update/Remove Species
// Add/Update/Remove Ethics