Delete & Post Game Endpoints. Various other changes

This commit is contained in:
Neshura 2023-08-30 04:16:43 +02:00
parent 790a7dd8ee
commit 07e6df6b64
Signed by: Neshura
GPG key ID: B6983AAA6B9A7A6C
8 changed files with 493 additions and 107 deletions

2
.gitignore vendored
View file

@ -1,3 +1,5 @@
/target /target
config.toml config.toml
.env

37
Cargo.lock generated
View file

@ -403,6 +403,8 @@ dependencies = [
"actix-web", "actix-web",
"chrono", "chrono",
"ctrlc", "ctrlc",
"dotenv",
"dotenv_codegen",
"env_logger", "env_logger",
"schemars", "schemars",
"serde", "serde",
@ -592,6 +594,35 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "dotenv"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
[[package]]
name = "dotenv_codegen"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56966279c10e4f8ee8c22123a15ed74e7c8150b658b26c619c53f4a56eb4a8aa"
dependencies = [
"dotenv_codegen_implementation",
"proc-macro-hack",
]
[[package]]
name = "dotenv_codegen_implementation"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e737a3522cd45f6adc19b644ce43ef53e1e9045f2d2de425c1f468abd4cf33"
dependencies = [
"dotenv",
"proc-macro-hack",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]] [[package]]
name = "dotenvy" name = "dotenvy"
version = "0.15.7" version = "0.15.7"
@ -1404,6 +1435,12 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "proc-macro-hack"
version = "0.5.20+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.66" version = "1.0.66"

View file

@ -9,6 +9,8 @@ edition = "2021"
actix-web = "4.3.1" actix-web = "4.3.1"
chrono = "0.4.26" chrono = "0.4.26"
ctrlc = "3.4.0" ctrlc = "3.4.0"
dotenv = "0.15.0"
dotenv_codegen = "0.15.0"
env_logger = "0.10.0" env_logger = "0.10.0"
schemars = "0.8.10" schemars = "0.8.10"
serde = { version = "1.0.185", features = ["derive"] } serde = { version = "1.0.185", features = ["derive"] }

View file

@ -2,20 +2,4 @@ use std::error::Error;
use sqlx::{Postgres, postgres::PgConnectOptions, PgPool, Pool}; use sqlx::{Postgres, postgres::PgConnectOptions, PgPool, Pool};
use crate::{PostgresConfig};
pub(crate) mod schemas; pub(crate) mod schemas;
pub(crate) async fn connect_postgres(config: PostgresConfig) -> Result<Pool<Postgres>, Box<dyn Error>> {
let connection_settings = PgConnectOptions::new()
.host(&config.host)
.port(config.port)
.username(&config.user)
.password(&config.password)
.database(&config.db);
let pool = PgPool::connect_with(connection_settings).await?;
return Ok(pool);
}

View file

@ -1,4 +1,11 @@
use std::{error::Error, fs, net::Ipv4Addr, net::Ipv6Addr, thread, time::Duration, sync::{Mutex, Arc, atomic::{AtomicBool, Ordering}}}; #[macro_use]
extern crate dotenv_codegen;
extern crate dotenv;
use dotenv::dotenv;
use tokio::time::sleep;
use std::{env, error::Error, fs, net::Ipv4Addr, net::Ipv6Addr, thread, time::Duration, sync::{Mutex, Arc, atomic::{AtomicBool, Ordering}}};
use chrono::Local; use chrono::Local;
@ -8,8 +15,6 @@ use sqlx::{PgPool, Pool, Postgres, Connection};
use utoipa::OpenApi; use utoipa::OpenApi;
use utoipa_swagger_ui::{Config, SwaggerUi, Url}; use utoipa_swagger_ui::{Config, SwaggerUi, Url};
use crate::db::connect_postgres;
mod db; mod db;
mod v2; mod v2;
mod v3; mod v3;
@ -34,19 +39,9 @@ macro_rules! api_base_3 {
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub(crate) struct ConfigToml { pub(crate) struct ConfigToml {
database: PostgresConfig,
auth: AuthenticationTokens, auth: AuthenticationTokens,
} }
#[derive(Serialize, Deserialize, Debug, Clone)]
pub(crate) struct PostgresConfig {
host: String,
port: u16,
user: String,
password: String,
db: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub(crate) struct AuthenticationTokens { pub(crate) struct AuthenticationTokens {
moderator: String, moderator: String,
@ -71,21 +66,13 @@ async fn postgres_watchdog(pool: PgPool, is_alive: Arc<AtomicBool>, shutdown: Ar
}; };
match conn.ping().await { match conn.ping().await {
Ok(_) => {eprintln!("Pinged DB Server at {}", Local::now().format("%H:%M:%S"))}, Ok(_) => {println!("Pinged DB Server at {}", Local::now().format("%H:%M:%S"))},
Err(_) => todo!(), Err(_) => {println!("Error pinging Server"); break;},
}; };
let passed = (Local::now() - start).to_std().expect(&format!("Unable to get Time Difference for '{}' and '{}'", start, Local::now())); let passed = (Local::now() - start).to_std().expect(&format!("Unable to get Time Difference for '{}' and '{}'", start, Local::now()));
thread::sleep(Duration::from_secs(5) - passed); sleep(Duration::from_secs(15) - passed).await;
if shutdown.load(Ordering::Relaxed) {
break;
}
thread::sleep(Duration::from_secs(10) - passed);
if shutdown.load(Ordering::Relaxed) {
break;
}
thread::sleep(Duration::from_secs(15) - passed);
} }
is_alive.store(false, Ordering::Relaxed); is_alive.store(false, Ordering::Relaxed);
} }
@ -93,6 +80,7 @@ async fn postgres_watchdog(pool: PgPool, is_alive: Arc<AtomicBool>, shutdown: Ar
#[actix_web::main] #[actix_web::main]
async fn main() -> Result<()> { async fn main() -> Result<()> {
env_logger::init(); env_logger::init();
dotenv().ok();
let shutdown: Arc<AtomicBool> = Arc::new(AtomicBool::new(false)); let shutdown: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
@ -107,6 +95,8 @@ async fn main() -> Result<()> {
let config: ConfigToml = toml::from_str(&toml_str).expect("Failed to parse config.toml"); let config: ConfigToml = toml::from_str(&toml_str).expect("Failed to parse config.toml");
println!("DATABASE_URL: {}", env::var("DATABASE_URL").unwrap()); // DBEUG
#[derive(OpenApi)] #[derive(OpenApi)]
#[openapi( #[openapi(
paths( paths(
@ -134,12 +124,24 @@ async fn main() -> Result<()> {
#[openapi( #[openapi(
paths( paths(
v3::full_view_data, v3::full_view_data,
v3::auth,
v3::list_games,
v3::get_game_data,
v3::create_game,
v3::delete_game
), ),
components(schemas( components(schemas(
v3::schemas::AuthParamsOptional,
v3::schemas::AuthParams,
v3::schemas::AuthReturn,
v3::schemas::GetGameParam,
v3::schemas::PostGameParams,
v3::schemas::DeleteGameParam,
v3::schemas::FullViewData, v3::schemas::FullViewData,
v3::schemas::Ethic, v3::schemas::Ethic,
v3::schemas::EmpireEthic, v3::schemas::EmpireEthic,
v3::schemas::ChellarisGame, v3::schemas::ChellarisGame,
v3::schemas::ChellarisGameLite,
v3::schemas::Species, v3::schemas::Species,
v3::schemas::ChellarisGameGroup, v3::schemas::ChellarisGameGroup,
v3::schemas::Portrait, v3::schemas::Portrait,
@ -157,7 +159,7 @@ async fn main() -> Result<()> {
loop { loop {
let db_auth_tokens = config.auth.clone(); let db_auth_tokens = config.auth.clone();
let pool = connect_postgres(config.database.clone()).await.unwrap(); let pool = PgPool::connect(dotenv!("DATABASE_URL")).await.unwrap();
let pool_copy = pool.clone(); let pool_copy = pool.clone();
let swagger_config = Config::new(openapi_urls.clone()); let swagger_config = Config::new(openapi_urls.clone());
@ -182,6 +184,11 @@ async fn main() -> Result<()> {
.service(v2::portraits) .service(v2::portraits)
// API v3 Endpoints // API v3 Endpoints
.service(v3::full_view_data) .service(v3::full_view_data)
.service(v3::auth)
.service(v3::list_games)
.service(v3::get_game_data)
.service(v3::create_game)
.service(v3::delete_game)
// Swagger UI // Swagger UI
.service( .service(
SwaggerUi::new(concat!(api_base!(), "/swagger/{_:.*}")) SwaggerUi::new(concat!(api_base!(), "/swagger/{_:.*}"))
@ -202,7 +209,7 @@ async fn main() -> Result<()> {
.run(); .run();
let server_thread = tokio::spawn(async { let server_thread = tokio::spawn(async {
eprintln!("Awaiting server"); println!("Awaiting server");
let _ = server.await; let _ = server.await;
println!("Stopped awaiting server"); println!("Stopped awaiting server");
}); });

View file

@ -13,7 +13,7 @@ pub(crate) mod schemas;
("api_key" = []) ("api_key" = [])
), ),
)] )]
#[get("/empire_ethics")] #[get("/api/v2/empire_ethics")]
pub(crate) async fn empire_ethics( pub(crate) async fn empire_ethics(
data: web::Data<AppState> data: web::Data<AppState>
) -> impl Responder { ) -> impl Responder {
@ -51,7 +51,7 @@ pub(crate) async fn empire_ethics(
("api_key" = []) ("api_key" = [])
), ),
)] )]
#[get("/empires")] #[get("/api/v2/empires")]
pub(crate) async fn empires( pub(crate) async fn empires(
data: web::Data<AppState>, data: web::Data<AppState>,
params: web::Query<schemas::AuthParams> params: web::Query<schemas::AuthParams>
@ -98,7 +98,7 @@ pub(crate) async fn empires(
("api_key" = []) ("api_key" = [])
), ),
)] )]
#[get("/ethics")] #[get("/api/v2/ethics")]
pub(crate) async fn ethics( pub(crate) async fn ethics(
data: web::Data<AppState> data: web::Data<AppState>
) -> impl Responder { ) -> impl Responder {
@ -132,7 +132,7 @@ pub(crate) async fn ethics(
("api_key" = []) ("api_key" = [])
), ),
)] )]
#[get("/game_groups")] #[get("/api/v2/game_groups")]
pub(crate) async fn game_groups( pub(crate) async fn game_groups(
data: web::Data<AppState> data: web::Data<AppState>
) -> impl Responder { ) -> impl Responder {
@ -166,7 +166,7 @@ pub(crate) async fn game_groups(
("api_key" = []) ("api_key" = [])
), ),
)] )]
#[get("/games")] #[get("/api/v2/games")]
pub(crate) async fn games( pub(crate) async fn games(
data: web::Data<AppState> data: web::Data<AppState>
) -> impl Responder { ) -> impl Responder {
@ -199,7 +199,7 @@ pub(crate) async fn games(
("api_key" = []) ("api_key" = [])
), ),
)] )]
#[get("/portrait_groups")] #[get("/api/v2/portrait_groups")]
pub(crate) async fn portrait_groups( pub(crate) async fn portrait_groups(
data: web::Data<AppState> data: web::Data<AppState>
) -> impl Responder { ) -> impl Responder {
@ -232,7 +232,7 @@ pub(crate) async fn portrait_groups(
("api_key" = []) ("api_key" = [])
), ),
)] )]
#[get("/portraits")] #[get("/api/v2/portraits")]
pub(crate) async fn portraits( pub(crate) async fn portraits(
data: web::Data<AppState> data: web::Data<AppState>
) -> impl Responder { ) -> impl Responder {

View file

@ -1,11 +1,66 @@
use std::{vec, collections::HashMap}; use std::{collections::HashMap, vec};
use actix_web::{web::{self, Json}, Responder, get}; use actix_web::{
delete, get, post, put,
web::{self, Json},
HttpResponse, Responder,
};
use crate::{AppState, db}; use crate::{db, AppState};
pub(crate) mod schemas; pub(crate) mod schemas;
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)
}
#[utoipa::path( #[utoipa::path(
params( params(
@ -17,56 +72,53 @@ pub(crate) mod schemas;
("api_key" = []) ("api_key" = [])
), ),
)] )]
#[get("/full_view_data")] #[get("/api/v3/full_view_data")]
pub(crate) async fn full_view_data( pub(crate) async fn full_view_data(data: web::Data<AppState>) -> impl Responder {
data: web::Data<AppState>
) -> impl Responder {
let start = chrono::Local::now(); // DEBUG let start = chrono::Local::now(); // DEBUG
// SQL Queries // SQL Queries
// TODO: Optimize by utilizing some SQL magic, for now this has to do // TODO: Optimize by utilizing some SQL magic, for now this has to do
let db_games: Vec<db::schemas::Game> = sqlx::query_as( let db_games: Vec<db::schemas::Game> = sqlx::query_as("SELECT * FROM public.games ORDER BY id")
"SELECT * FROM public.games ORDER BY id", .fetch_all(&data.db)
) .await
.fetch_all(&data.db) .expect("Error Fetching Data from DB");
.await.expect("Error Fetching Data from DB");
let db_groups: Vec<db::schemas::GameGroup> = sqlx::query_as( let db_groups: Vec<db::schemas::GameGroup> =
"SELECT * FROM public.game_groups ORDER BY id", sqlx::query_as("SELECT * FROM public.game_groups ORDER BY id")
) .fetch_all(&data.db)
.fetch_all(&data.db) .await
.await.expect("Error Fetching Data from DB"); .expect("Error Fetching Data from DB");
let db_empires: Vec<db::schemas::Empire> = sqlx::query_as( let db_empires: Vec<db::schemas::Empire> =
"SELECT * FROM public.empires ORDER BY id", sqlx::query_as("SELECT * FROM public.empires ORDER BY id")
) .fetch_all(&data.db)
.fetch_all(&data.db) .await
.await.expect("Error Fetching Data from DB"); .expect("Error Fetching Data from DB");
let db_ethics: Vec<db::schemas::Ethic> = sqlx::query_as( let db_ethics: Vec<db::schemas::Ethic> =
"SELECT * FROM public.ethics ORDER BY id", sqlx::query_as("SELECT * FROM public.ethics ORDER BY id")
) .fetch_all(&data.db)
.fetch_all(&data.db) .await
.await.expect("Error Fetching Data from DB"); .expect("Error Fetching Data from DB");
let db_empire_ethics: Vec<db::schemas::EmpireEthic> = sqlx::query_as( let db_empire_ethics: Vec<db::schemas::EmpireEthic> =
"SELECT * FROM public.empire_ethics ORDER BY empires_id", sqlx::query_as("SELECT * FROM public.empire_ethics ORDER BY empires_id")
) .fetch_all(&data.db)
.fetch_all(&data.db) .await
.await.expect("Error Fetching Data from DB"); .expect("Error Fetching Data from DB");
let db_portrait_groups: Vec<db::schemas::PortraitGroup> = sqlx::query_as( let db_portrait_groups: Vec<db::schemas::PortraitGroup> =
"SELECT * FROM public.portrait_groups ORDER BY id", sqlx::query_as("SELECT * FROM public.portrait_groups ORDER BY id")
) .fetch_all(&data.db)
.fetch_all(&data.db) .await
.await.expect("Error Fetching Data from DB"); .expect("Error Fetching Data from DB");
let db_portraits: Vec<db::schemas::Portrait> = sqlx::query_as( let db_portraits: Vec<db::schemas::Portrait> =
"SELECT * FROM public.portraits ORDER BY id", sqlx::query_as("SELECT * FROM public.portraits ORDER BY id")
) .fetch_all(&data.db)
.fetch_all(&data.db) .await
.await.expect("Error Fetching Data from DB"); .expect("Error Fetching Data from DB");
let mut parsed_data: schemas::FullViewData = schemas::FullViewData { let mut parsed_data: schemas::FullViewData = schemas::FullViewData {
games: HashMap::new(), games: HashMap::new(),
@ -83,7 +135,11 @@ pub(crate) async fn full_view_data(
portraits: HashMap::new(), portraits: HashMap::new(),
}; };
parsed_data.species.entry(species.id).and_modify(|d| *d = new_data.clone()).or_insert(new_data); parsed_data
.species
.entry(species.id)
.and_modify(|d| *d = new_data.clone())
.or_insert(new_data);
}); });
db_portraits.iter().for_each(|portrait| { db_portraits.iter().for_each(|portrait| {
@ -93,7 +149,14 @@ pub(crate) async fn full_view_data(
lores: portrait.lores.clone(), lores: portrait.lores.clone(),
}; };
parsed_data.species.get_mut(&portrait.group_id).unwrap().portraits.entry(portrait.id).and_modify(|d| *d = new_data.clone()).or_insert(new_data); parsed_data
.species
.get_mut(&portrait.group_id)
.unwrap()
.portraits
.entry(portrait.id)
.and_modify(|d| *d = new_data.clone())
.or_insert(new_data);
}); });
// Games Vector and Children // Games Vector and Children
@ -105,7 +168,11 @@ pub(crate) async fn full_view_data(
groups: HashMap::new(), groups: HashMap::new(),
}; };
parsed_data.games.entry(game.id).and_modify(|d| *d = new_data.clone()).or_insert(new_data); parsed_data
.games
.entry(game.id)
.and_modify(|d| *d = new_data.clone())
.or_insert(new_data);
}); });
db_groups.iter().for_each(|group| { db_groups.iter().for_each(|group| {
@ -114,14 +181,21 @@ pub(crate) async fn full_view_data(
name: group.name.clone(), name: group.name.clone(),
}; };
parsed_data.games.get_mut(&group.game_id).unwrap().groups.entry(group.id).and_modify(|d| *d = new_data.clone()).or_insert(new_data); parsed_data
.games
.get_mut(&group.game_id)
.unwrap()
.groups
.entry(group.id)
.and_modify(|d| *d = new_data.clone())
.or_insert(new_data);
}); });
db_empires.iter().for_each(|empire| { db_empires.iter().for_each(|empire| {
let new_data = schemas::ChellarisEmpire { let new_data = schemas::ChellarisEmpire {
id: empire.id, id: empire.id,
gestalt: empire.gestalt.unwrap_or(false), gestalt: empire.gestalt.unwrap_or(false),
machine: false, // TODO overwrite this later on with correct data machine: false,
group: empire.group_id, group: empire.group_id,
empire_portrait: empire.empire_portrait_id, empire_portrait: empire.empire_portrait_id,
empire_portrait_group: empire.empire_portrait_group_id, empire_portrait_group: empire.empire_portrait_group_id,
@ -129,7 +203,14 @@ pub(crate) async fn full_view_data(
ethics: HashMap::new(), ethics: HashMap::new(),
}; };
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); 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);
}); });
// Ethics Vector // Ethics Vector
@ -140,26 +221,259 @@ pub(crate) async fn full_view_data(
machine: ethic.machine_ethic, machine: ethic.machine_ethic,
}; };
parsed_data.ethics.entry(ethic.id).and_modify(|d| *d = new_data.clone()).or_insert(new_data); parsed_data
.ethics
.entry(ethic.id)
.and_modify(|d| *d = new_data.clone())
.or_insert(new_data);
}); });
println!("{:#?}", parsed_data.ethics); // DEBUG
db_empire_ethics.iter().for_each(|empire_ethic| { db_empire_ethics.iter().for_each(|empire_ethic| {
let game_id = empire_ethic.empires_group_game_id; let game_id = empire_ethic.empires_group_game_id;
let id = empire_ethic.empires_id; let id = empire_ethic.empires_id;
let new_data = schemas::EmpireEthic { let new_data = schemas::EmpireEthic {
id: empire_ethic.ethics_id, id: empire_ethic.ethics_id,
displayName: parsed_data.ethics[&empire_ethic.ethics_id].displayName.clone(), displayName: parsed_data.ethics[&empire_ethic.ethics_id]
.displayName
.clone(),
machine: parsed_data.ethics[&empire_ethic.ethics_id].machine, machine: parsed_data.ethics[&empire_ethic.ethics_id].machine,
fanatic: empire_ethic.ethics_fanatic, fanatic: empire_ethic.ethics_fanatic,
}; };
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); 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);
}); });
println!("{:?} ms", (chrono::Local::now() - start).to_std().unwrap()); // DEBUG println!("{:?} ms", (chrono::Local::now() - start).to_std().unwrap()); // DEBUG
Json(parsed_data) 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);
});
Json(parsed_data)
}
#[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

View file

@ -4,10 +4,43 @@ use serde::{Serialize, Deserialize};
use utoipa::{ToSchema, IntoParams}; use utoipa::{ToSchema, IntoParams};
#[derive(Serialize, Deserialize, ToSchema, Debug, IntoParams)] #[derive(Serialize, Deserialize, ToSchema, Debug, IntoParams)]
pub struct AuthParams { pub struct AuthParamsOptional {
#[schema(example = "1357")]
pub token: Option<String>, pub token: Option<String>,
} }
#[derive(Serialize, Deserialize, ToSchema, Debug, IntoParams)]
pub struct AuthParams {
#[schema(example = "1357")]
pub token: String,
}
#[derive(Serialize, Deserialize, ToSchema, Debug)]
pub struct AuthReturn {
#[schema(example = false)]
pub moderator: bool,
#[schema(example = false)]
pub admin: bool,
}
#[derive(Serialize, Deserialize, ToSchema, Debug, IntoParams)]
pub struct GetGameParam {
pub game_id: Option<usize>,
}
#[derive(Serialize, Deserialize, ToSchema, Debug)]
pub struct PostGameParams {
pub auth: AuthParams,
#[schema(example = "Game XY")]
pub game_name: String,
}
#[derive(Serialize, Deserialize, ToSchema, Debug, IntoParams)]
pub struct DeleteGameParam {
#[schema(example = 0)]
pub game_id: usize,
}
#[derive(Serialize, ToSchema, Debug, Clone)] #[derive(Serialize, ToSchema, Debug, Clone)]
pub struct FullViewData { pub struct FullViewData {
pub games: HashMap<i32, ChellarisGame>, pub games: HashMap<i32, ChellarisGame>,
@ -23,6 +56,12 @@ pub struct ChellarisGame {
pub empires: HashMap<i32, ChellarisEmpire>, pub empires: HashMap<i32, ChellarisEmpire>,
} }
#[derive(Serialize, ToSchema, Debug, Clone)]
pub struct ChellarisGameLite {
pub id: i32,
pub name: String,
}
#[derive(Serialize, ToSchema, Debug, Clone)] #[derive(Serialize, ToSchema, Debug, Clone)]
pub struct ChellarisGameGroup { pub struct ChellarisGameGroup {
pub id: i32, pub id: i32,
@ -69,3 +108,4 @@ pub struct Portrait {
pub hires: String, pub hires: String,
pub lores: Option<String>, pub lores: Option<String>,
} }