Game & Group Put + Delete Endpoints
This commit is contained in:
parent
8fd4321944
commit
eb18526f51
3 changed files with 442 additions and 119 deletions
44
src/main.rs
44
src/main.rs
|
@ -12,7 +12,7 @@ use chrono::Local;
|
||||||
use actix_web::{middleware::Logger, web, App, HttpServer, Result};
|
use actix_web::{middleware::Logger, web, App, HttpServer, Result};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::{PgPool, Pool, Postgres, Connection};
|
use sqlx::{PgPool, Pool, Postgres, Connection};
|
||||||
use utoipa::OpenApi;
|
use utoipa::{OpenApi, openapi::security::{SecurityScheme, ApiKey, ApiKeyValue}, Modify};
|
||||||
use utoipa_swagger_ui::{Config, SwaggerUi, Url};
|
use utoipa_swagger_ui::{Config, SwaggerUi, Url};
|
||||||
|
|
||||||
mod db;
|
mod db;
|
||||||
|
@ -97,6 +97,18 @@ async fn main() -> Result<()> {
|
||||||
|
|
||||||
println!("DATABASE_URL: {}", env::var("DATABASE_URL").unwrap()); // DBEUG
|
println!("DATABASE_URL: {}", env::var("DATABASE_URL").unwrap()); // DBEUG
|
||||||
|
|
||||||
|
struct ApiSecurity;
|
||||||
|
|
||||||
|
impl Modify for ApiSecurity {
|
||||||
|
fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
|
||||||
|
let components = openapi.components.as_mut().unwrap(); // we can unwrap safely since there already is components registered.
|
||||||
|
components.add_security_scheme(
|
||||||
|
"api_key",
|
||||||
|
SecurityScheme::ApiKey(ApiKey::Header(ApiKeyValue::new("x-api-key"))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(OpenApi)]
|
#[derive(OpenApi)]
|
||||||
#[openapi(
|
#[openapi(
|
||||||
paths(
|
paths(
|
||||||
|
@ -128,25 +140,35 @@ async fn main() -> Result<()> {
|
||||||
v3::list_games,
|
v3::list_games,
|
||||||
v3::get_game_data,
|
v3::get_game_data,
|
||||||
v3::create_game,
|
v3::create_game,
|
||||||
v3::delete_game
|
v3::edit_game,
|
||||||
|
v3::delete_game,
|
||||||
|
v3::create_group,
|
||||||
|
v3::edit_group,
|
||||||
|
v3::delete_group
|
||||||
),
|
),
|
||||||
components(schemas(
|
components(schemas(
|
||||||
v3::schemas::AuthParamsOptional,
|
|
||||||
v3::schemas::AuthParams,
|
|
||||||
v3::schemas::AuthReturn,
|
v3::schemas::AuthReturn,
|
||||||
v3::schemas::GetGameParam,
|
v3::schemas::GetGameParam,
|
||||||
v3::schemas::PostGameParams,
|
v3::schemas::PostGameParams,
|
||||||
|
v3::schemas::UpdateGameParams,
|
||||||
v3::schemas::DeleteGameParam,
|
v3::schemas::DeleteGameParam,
|
||||||
|
v3::schemas::PostGroupParams,
|
||||||
|
v3::schemas::UpdateGroupParams,
|
||||||
|
v3::schemas::DeleteGroupParams,
|
||||||
v3::schemas::FullViewData,
|
v3::schemas::FullViewData,
|
||||||
v3::schemas::Ethic,
|
v3::schemas::Ethic,
|
||||||
v3::schemas::EmpireEthic,
|
v3::schemas::EmpireEthic,
|
||||||
|
v3::schemas::ChellarisGameLegacy,
|
||||||
|
v3::schemas::ChellarisGameFlat,
|
||||||
v3::schemas::ChellarisGame,
|
v3::schemas::ChellarisGame,
|
||||||
v3::schemas::ChellarisGameLite,
|
|
||||||
v3::schemas::Species,
|
v3::schemas::Species,
|
||||||
v3::schemas::ChellarisGameGroup,
|
v3::schemas::ChellarisGameGroupLegacy,
|
||||||
|
v3::schemas::ChellarisGroupFlat,
|
||||||
v3::schemas::Portrait,
|
v3::schemas::Portrait,
|
||||||
v3::schemas::ChellarisEmpire
|
v3::schemas::ChellarisEmpire,
|
||||||
))
|
v3::schemas::ChellarisEmpireFlat
|
||||||
|
)),
|
||||||
|
modifiers(&ApiSecurity)
|
||||||
)]
|
)]
|
||||||
struct ApiDocV3;
|
struct ApiDocV3;
|
||||||
|
|
||||||
|
@ -188,7 +210,11 @@ async fn main() -> Result<()> {
|
||||||
.service(v3::list_games)
|
.service(v3::list_games)
|
||||||
.service(v3::get_game_data)
|
.service(v3::get_game_data)
|
||||||
.service(v3::create_game)
|
.service(v3::create_game)
|
||||||
|
.service(v3::edit_game)
|
||||||
.service(v3::delete_game)
|
.service(v3::delete_game)
|
||||||
|
.service(v3::create_group)
|
||||||
|
.service(v3::edit_group)
|
||||||
|
.service(v3::delete_group)
|
||||||
// Swagger UI
|
// Swagger UI
|
||||||
.service(
|
.service(
|
||||||
SwaggerUi::new(concat!(api_base!(), "/swagger/{_:.*}"))
|
SwaggerUi::new(concat!(api_base!(), "/swagger/{_:.*}"))
|
||||||
|
@ -226,7 +252,7 @@ async fn main() -> Result<()> {
|
||||||
|
|
||||||
while is_alive.load(Ordering::Relaxed) {
|
while is_alive.load(Ordering::Relaxed) {
|
||||||
let thread = tokio::spawn(async {
|
let thread = tokio::spawn(async {
|
||||||
thread::sleep(Duration::from_millis(100));
|
sleep(Duration::from_millis(100));
|
||||||
});
|
});
|
||||||
|
|
||||||
let _ = thread.await;
|
let _ = thread.await;
|
||||||
|
|
420
src/v3/mod.rs
420
src/v3/mod.rs
|
@ -3,33 +3,36 @@ use std::{collections::HashMap, vec};
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
delete, get, post, put,
|
delete, get, post, put,
|
||||||
web::{self, Json},
|
web::{self, Json},
|
||||||
HttpResponse, Responder,
|
HttpResponse, Responder, HttpRequest,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{db, AppState};
|
use crate::{db, AppState};
|
||||||
|
|
||||||
pub(crate) mod schemas;
|
pub(crate) mod schemas;
|
||||||
|
|
||||||
fn verify_auth(token: &str, data: &AppState) -> schemas::AuthReturn {
|
fn verify_auth(token: Option<&str>, data: &AppState) -> schemas::AuthReturn {
|
||||||
let mut auth_return = schemas::AuthReturn {
|
let mut auth_return = schemas::AuthReturn {
|
||||||
moderator: false,
|
moderator: false,
|
||||||
admin: false,
|
admin: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if token == data.auth_tokens.admin {
|
if let Some(token) = token {
|
||||||
auth_return.admin = true;
|
if token == data.auth_tokens.admin {
|
||||||
auth_return.moderator = true;
|
auth_return.admin = true;
|
||||||
} else if token == data.auth_tokens.moderator {
|
auth_return.moderator = true;
|
||||||
auth_return.moderator = true;
|
} else if token == data.auth_tokens.moderator {
|
||||||
|
auth_return.moderator = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return auth_return;
|
return auth_return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_auth_header<'a>(req: &'a HttpRequest) -> Option<&'a str> {
|
||||||
|
req.headers().get("x-api-key")?.to_str().ok()
|
||||||
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
params(
|
|
||||||
schemas::AuthParams
|
|
||||||
),
|
|
||||||
responses(
|
responses(
|
||||||
(status = 200, description = "OK", body = AuthReturn),
|
(status = 200, description = "OK", body = AuthReturn),
|
||||||
),
|
),
|
||||||
|
@ -40,31 +43,16 @@ fn verify_auth(token: &str, data: &AppState) -> schemas::AuthReturn {
|
||||||
#[get("/api/v3/auth")]
|
#[get("/api/v3/auth")]
|
||||||
pub(crate) async fn auth(
|
pub(crate) async fn auth(
|
||||||
data: web::Data<AppState>,
|
data: web::Data<AppState>,
|
||||||
params: web::Query<schemas::AuthParamsOptional>,
|
req: HttpRequest,
|
||||||
) -> impl Responder {
|
) -> impl Responder {
|
||||||
let params: schemas::AuthParamsOptional = params.into_inner();
|
let auth_token = get_auth_header(&req);
|
||||||
|
|
||||||
let mut auth_return = schemas::AuthReturn {
|
let auth_return = verify_auth(auth_token, &data);
|
||||||
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)
|
Json(auth_return)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
params(
|
|
||||||
|
|
||||||
),
|
|
||||||
responses(
|
responses(
|
||||||
(status = 200, description = "OK", body = FullViewData),
|
(status = 200, description = "OK", body = FullViewData),
|
||||||
),
|
),
|
||||||
|
@ -74,8 +62,6 @@ pub(crate) async fn auth(
|
||||||
)]
|
)]
|
||||||
#[get("/api/v3/full_view_data")]
|
#[get("/api/v3/full_view_data")]
|
||||||
pub(crate) async fn full_view_data(data: web::Data<AppState>) -> impl Responder {
|
pub(crate) async fn full_view_data(data: web::Data<AppState>) -> impl Responder {
|
||||||
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
|
||||||
|
|
||||||
|
@ -146,7 +132,7 @@ pub(crate) async fn full_view_data(data: web::Data<AppState>) -> impl Responder
|
||||||
let new_data = schemas::Portrait {
|
let new_data = schemas::Portrait {
|
||||||
id: portrait.id,
|
id: portrait.id,
|
||||||
hires: portrait.hires.clone(),
|
hires: portrait.hires.clone(),
|
||||||
lores: portrait.lores.clone(),
|
lores: Some(portrait.lores.clone()),
|
||||||
};
|
};
|
||||||
|
|
||||||
parsed_data
|
parsed_data
|
||||||
|
@ -161,7 +147,7 @@ pub(crate) async fn full_view_data(data: web::Data<AppState>) -> impl Responder
|
||||||
|
|
||||||
// Games Vector and Children
|
// Games Vector and Children
|
||||||
db_games.iter().for_each(|game| {
|
db_games.iter().for_each(|game| {
|
||||||
let new_data = schemas::ChellarisGame {
|
let new_data = schemas::ChellarisGameLegacy {
|
||||||
id: game.id,
|
id: game.id,
|
||||||
name: game.name.clone(),
|
name: game.name.clone(),
|
||||||
empires: HashMap::new(),
|
empires: HashMap::new(),
|
||||||
|
@ -176,7 +162,7 @@ pub(crate) async fn full_view_data(data: web::Data<AppState>) -> impl Responder
|
||||||
});
|
});
|
||||||
|
|
||||||
db_groups.iter().for_each(|group| {
|
db_groups.iter().for_each(|group| {
|
||||||
let new_data = schemas::ChellarisGameGroup {
|
let new_data = schemas::ChellarisGameGroupLegacy {
|
||||||
id: group.id,
|
id: group.id,
|
||||||
name: group.name.clone(),
|
name: group.name.clone(),
|
||||||
};
|
};
|
||||||
|
@ -194,12 +180,12 @@ pub(crate) async fn full_view_data(data: web::Data<AppState>) -> impl Responder
|
||||||
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,
|
||||||
machine: false,
|
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,
|
||||||
discord_user: empire.discord_user.clone(),
|
discord_user: None,
|
||||||
ethics: HashMap::new(),
|
ethics: HashMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -265,13 +251,13 @@ pub(crate) async fn full_view_data(data: web::Data<AppState>) -> impl Responder
|
||||||
.or_insert(new_data);
|
.or_insert(new_data);
|
||||||
});
|
});
|
||||||
|
|
||||||
println!("{:?} ms", (chrono::Local::now() - start).to_std().unwrap()); // DEBUG
|
|
||||||
|
|
||||||
Json(parsed_data)
|
Json(parsed_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data Fetching Endpoints for Admin/Moderator Menu
|
// Data Fetching Endpoints for Admin/Moderator Menu
|
||||||
|
|
||||||
|
// Game Endpoints
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
params(),
|
params(),
|
||||||
responses(
|
responses(
|
||||||
|
@ -281,7 +267,7 @@ pub(crate) async fn full_view_data(data: web::Data<AppState>) -> impl Responder
|
||||||
("api_key" = [])
|
("api_key" = [])
|
||||||
),
|
),
|
||||||
)]
|
)]
|
||||||
#[get("/api/v3/list_games")]
|
#[get("/api/v3/games")]
|
||||||
pub(crate) async fn list_games(data: web::Data<AppState>) -> impl Responder {
|
pub(crate) async fn list_games(data: web::Data<AppState>) -> impl Responder {
|
||||||
// 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
|
||||||
|
@ -291,12 +277,12 @@ pub(crate) async fn list_games(data: web::Data<AppState>) -> impl Responder {
|
||||||
.await
|
.await
|
||||||
.expect("Error Fetching Data from DB");
|
.expect("Error Fetching Data from DB");
|
||||||
|
|
||||||
let mut parsed_data: HashMap<i32, schemas::ChellarisGameLite> = HashMap::new();
|
let mut parsed_data: HashMap<i32, schemas::ChellarisGameFlat> = HashMap::new();
|
||||||
|
|
||||||
// Data processing
|
// Data processing
|
||||||
// Games Vector
|
// Games Vector
|
||||||
db_games.iter().for_each(|game| {
|
db_games.iter().for_each(|game| {
|
||||||
let new_data = schemas::ChellarisGameLite {
|
let new_data = schemas::ChellarisGameFlat {
|
||||||
id: game.id,
|
id: game.id,
|
||||||
name: game.name.clone(),
|
name: game.name.clone(),
|
||||||
};
|
};
|
||||||
|
@ -315,7 +301,7 @@ pub(crate) async fn list_games(data: web::Data<AppState>) -> impl Responder {
|
||||||
schemas::GetGameParam
|
schemas::GetGameParam
|
||||||
),
|
),
|
||||||
responses(
|
responses(
|
||||||
(status = 200, description = "OK", body = HashMap<i32, ChellarisGameLite>),
|
(status = 200, description = "OK", body = ChellarisGame),
|
||||||
),
|
),
|
||||||
security(
|
security(
|
||||||
("api_key" = [])
|
("api_key" = [])
|
||||||
|
@ -324,49 +310,86 @@ pub(crate) async fn list_games(data: web::Data<AppState>) -> impl Responder {
|
||||||
#[get("/api/v3/game")]
|
#[get("/api/v3/game")]
|
||||||
pub(crate) async fn get_game_data(
|
pub(crate) async fn get_game_data(
|
||||||
data: web::Data<AppState>,
|
data: web::Data<AppState>,
|
||||||
params: web::Query<schemas::GetGameParam>,
|
path_param: web::Query<schemas::GetGameParam>,
|
||||||
|
req: HttpRequest,
|
||||||
) -> impl Responder {
|
) -> impl Responder {
|
||||||
let params: schemas::GetGameParam = params.into_inner();
|
let auth_token = get_auth_header(&req);
|
||||||
|
let param = path_param.into_inner();
|
||||||
|
|
||||||
|
let user_auth: schemas::AuthReturn = verify_auth(auth_token.as_deref(), &data);
|
||||||
|
|
||||||
// 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>;
|
let db_game: db::schemas::Game = match sqlx::query_as!(
|
||||||
if let Some(game_id) = params.game_id {
|
db::schemas::Game,
|
||||||
db_games = match sqlx::query_as!(
|
"SELECT * FROM public.games WHERE id = $1",
|
||||||
db::schemas::Game,
|
param.game_id
|
||||||
"SELECT * FROM public.games WHERE id = $1 ORDER BY id",
|
)
|
||||||
game_id as i32
|
.fetch_one(&data.db)
|
||||||
)
|
.await
|
||||||
.fetch_one(&data.db)
|
{
|
||||||
.await
|
Ok(data) => data,
|
||||||
{
|
Err(_) => return HttpResponse::UnprocessableEntity().finish(),
|
||||||
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();
|
let db_empires: Vec<db::schemas::Empire> = sqlx::query_as!(
|
||||||
|
db::schemas::Empire,
|
||||||
|
"SELECT * FROM public.empires WHERE group_game_id = $1",
|
||||||
|
param.game_id
|
||||||
|
)
|
||||||
|
.fetch_all(&data.db)
|
||||||
|
.await
|
||||||
|
.unwrap_or(vec![]);
|
||||||
|
|
||||||
// Data processing
|
let db_groups: Vec<db::schemas::GameGroup> = sqlx::query_as!(
|
||||||
// Games Vector
|
db::schemas::GameGroup,
|
||||||
db_games.iter().for_each(|game| {
|
"SELECT * FROM public.game_groups WHERE game_id = $1",
|
||||||
let new_data = schemas::ChellarisGameLite {
|
param.game_id
|
||||||
id: game.id,
|
)
|
||||||
name: game.name.clone(),
|
.fetch_all(&data.db)
|
||||||
|
.await
|
||||||
|
.unwrap_or(vec![]);
|
||||||
|
|
||||||
|
// Data Processing
|
||||||
|
let mut parsed_data: schemas::ChellarisGame = schemas::ChellarisGame {
|
||||||
|
id: db_game.id,
|
||||||
|
name: db_game.name,
|
||||||
|
empires: HashMap::new(),
|
||||||
|
groups: HashMap::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
db_empires.iter().for_each(|empire| {
|
||||||
|
let new_empire = schemas::ChellarisEmpireFlat {
|
||||||
|
id: empire.id,
|
||||||
|
group: empire.group_id,
|
||||||
|
name: if user_auth.moderator || user_auth.admin {empire.name.clone()} else {"[REDACTED]".to_string()},
|
||||||
|
discord_user: if user_auth.moderator || user_auth.admin {empire.discord_user.clone()} else {None},
|
||||||
|
gestalt: empire.gestalt,
|
||||||
|
empire_portrait: empire.empire_portrait_id,
|
||||||
|
empire_portrait_group: empire.empire_portrait_group_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
parsed_data
|
parsed_data
|
||||||
.entry(game.id)
|
.empires
|
||||||
.and_modify(|d| *d = new_data.clone())
|
.entry(empire.id)
|
||||||
.or_insert(new_data);
|
.and_modify(|d| *d = new_empire.clone())
|
||||||
|
.or_insert(new_empire);
|
||||||
});
|
});
|
||||||
|
|
||||||
Json(parsed_data)
|
db_groups.iter().for_each(|group| {
|
||||||
|
let new_group = schemas::ChellarisGroupFlat {
|
||||||
|
id: group.id,
|
||||||
|
name: group.name.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
parsed_data
|
||||||
|
.groups
|
||||||
|
.entry(group.id)
|
||||||
|
.and_modify(|d| *d = new_group.clone())
|
||||||
|
.or_insert(new_group);
|
||||||
|
});
|
||||||
|
|
||||||
|
return HttpResponse::Ok().json(parsed_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
|
@ -384,10 +407,12 @@ pub(crate) async fn get_game_data(
|
||||||
pub(crate) async fn create_game(
|
pub(crate) async fn create_game(
|
||||||
data: web::Data<AppState>,
|
data: web::Data<AppState>,
|
||||||
params: web::Json<schemas::PostGameParams>,
|
params: web::Json<schemas::PostGameParams>,
|
||||||
|
req: HttpRequest,
|
||||||
) -> impl Responder {
|
) -> impl Responder {
|
||||||
|
let auth_token = get_auth_header(&req);
|
||||||
let params = params.into_inner();
|
let params = params.into_inner();
|
||||||
|
|
||||||
let user_auth: schemas::AuthReturn = verify_auth(¶ms.auth.token, &data);
|
let user_auth: schemas::AuthReturn = verify_auth(auth_token.as_deref(), &data);
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -407,7 +432,18 @@ pub(crate) async fn create_game(
|
||||||
Err(_) => return HttpResponse::UnprocessableEntity().finish(),
|
Err(_) => return HttpResponse::UnprocessableEntity().finish(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let parsed_game: schemas::ChellarisGameLite = schemas::ChellarisGameLite {
|
match sqlx::query!(
|
||||||
|
"INSERT INTO public.game_groups(name, game_id) VALUES ($1, $2)",
|
||||||
|
"N/A", db_game.id
|
||||||
|
)
|
||||||
|
.execute(&data.db)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(_) => return HttpResponse::UnprocessableEntity().finish(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let parsed_game: schemas::ChellarisGameFlat = schemas::ChellarisGameFlat {
|
||||||
id: db_game.id,
|
id: db_game.id,
|
||||||
name: db_game.name,
|
name: db_game.name,
|
||||||
};
|
};
|
||||||
|
@ -421,6 +457,61 @@ pub(crate) async fn create_game(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[utoipa::path(
|
||||||
|
request_body = UpdateGameParams,
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "OK", body = ChellarisGameLite),
|
||||||
|
(status = 422, description = "Missing Game Name"),
|
||||||
|
(status = 401, description = "Auth Token Invalid"),
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("api_key" = [])
|
||||||
|
),
|
||||||
|
)]
|
||||||
|
#[put("/api/v3/game")]
|
||||||
|
pub(crate) async fn edit_game(
|
||||||
|
data: web::Data<AppState>,
|
||||||
|
params: web::Json<schemas::UpdateGameParams>,
|
||||||
|
req: HttpRequest,
|
||||||
|
) -> impl Responder {
|
||||||
|
let auth_token = get_auth_header(&req);
|
||||||
|
let params = params.into_inner();
|
||||||
|
|
||||||
|
let user_auth: schemas::AuthReturn = verify_auth(auth_token.as_deref(), &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,
|
||||||
|
"UPDATE public.games SET name = $1 WHERE id = $2 RETURNING * ;",
|
||||||
|
params.game_name, params.game_id
|
||||||
|
)
|
||||||
|
.fetch_one(&data.db)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(data) => data,
|
||||||
|
Err(_) => return HttpResponse::UnprocessableEntity().finish(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let parsed_game: schemas::ChellarisGameFlat = schemas::ChellarisGameFlat {
|
||||||
|
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(
|
#[utoipa::path(
|
||||||
params(
|
params(
|
||||||
schemas::DeleteGameParam,
|
schemas::DeleteGameParam,
|
||||||
|
@ -438,25 +529,177 @@ pub(crate) async fn create_game(
|
||||||
#[delete("/api/v3/game")]
|
#[delete("/api/v3/game")]
|
||||||
pub(crate) async fn delete_game(
|
pub(crate) async fn delete_game(
|
||||||
data: web::Data<AppState>,
|
data: web::Data<AppState>,
|
||||||
auth_params: web::Json<schemas::AuthParams>,
|
req: HttpRequest,
|
||||||
param: web::Query<schemas::DeleteGameParam>,
|
param: web::Query<schemas::DeleteGameParam>,
|
||||||
) -> impl Responder {
|
) -> impl Responder {
|
||||||
let auth_params = auth_params.into_inner();
|
let auth_token = get_auth_header(&req);
|
||||||
let param = param.into_inner();
|
let param = param.into_inner();
|
||||||
|
|
||||||
let user_auth: schemas::AuthReturn = verify_auth(&auth_params.token, &data);
|
let user_auth: schemas::AuthReturn = verify_auth(auth_token, &data);
|
||||||
|
|
||||||
// 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
|
||||||
if user_auth.admin || user_auth.moderator {
|
if user_auth.admin || user_auth.moderator {
|
||||||
match sqlx::query!(
|
match sqlx::query!("DELETE FROM public.games WHERE id = $1", param.game_id)
|
||||||
"DELETE FROM public.games WHERE id = $1",
|
.execute(&data.db)
|
||||||
param.game_id as i32
|
.await
|
||||||
)
|
|
||||||
.execute(&data.db)
|
|
||||||
.await
|
|
||||||
{
|
{
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
|
Err(e) => {
|
||||||
|
println!("{:#?}", e);
|
||||||
|
return HttpResponse::UnprocessableEntity().finish();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return HttpResponse::Ok().into();
|
||||||
|
} else {
|
||||||
|
return HttpResponse::Unauthorized().finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group Endpoints
|
||||||
|
|
||||||
|
#[utoipa::path(
|
||||||
|
request_body = PostGroupParams,
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "OK", body = ChellarisGroupFlat),
|
||||||
|
(status = 422, description = "Missing Game Name"),
|
||||||
|
(status = 401, description = "Auth Token Invalid"),
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("api_key" = [])
|
||||||
|
),
|
||||||
|
)]
|
||||||
|
#[post("/api/v3/group")]
|
||||||
|
pub(crate) async fn create_group(
|
||||||
|
data: web::Data<AppState>,
|
||||||
|
params: web::Json<schemas::PostGroupParams>,
|
||||||
|
req: HttpRequest,
|
||||||
|
) -> impl Responder {
|
||||||
|
let auth_token = get_auth_header(&req);
|
||||||
|
let params = params.into_inner();
|
||||||
|
|
||||||
|
let user_auth: schemas::AuthReturn = verify_auth(auth_token.as_deref(), &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.group_name != "" {
|
||||||
|
let db_group: db::schemas::GameGroup;
|
||||||
|
|
||||||
|
db_group = match sqlx::query_as!(
|
||||||
|
db::schemas::GameGroup,
|
||||||
|
"INSERT INTO public.game_groups(name, game_id) VALUES ($1, $2) RETURNING * ",
|
||||||
|
params.group_name, params.game_id
|
||||||
|
)
|
||||||
|
.fetch_one(&data.db)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(data) => data,
|
||||||
|
Err(_) => return HttpResponse::UnprocessableEntity().finish(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let parsed_group: schemas::ChellarisGroupFlat = schemas::ChellarisGroupFlat {
|
||||||
|
id: db_group.id,
|
||||||
|
name: db_group.name,
|
||||||
|
};
|
||||||
|
|
||||||
|
return HttpResponse::Ok().json(parsed_group);
|
||||||
|
} else {
|
||||||
|
return HttpResponse::UnprocessableEntity().finish();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return HttpResponse::Unauthorized().finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[utoipa::path(
|
||||||
|
request_body = UpdateGroupParams,
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "OK", body = ChellarisGroupFlat),
|
||||||
|
(status = 422, description = "Missing Game Name"),
|
||||||
|
(status = 401, description = "Auth Token Invalid"),
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("api_key" = [])
|
||||||
|
),
|
||||||
|
)]
|
||||||
|
#[put("/api/v3/group")]
|
||||||
|
pub(crate) async fn edit_group(
|
||||||
|
data: web::Data<AppState>,
|
||||||
|
params: web::Json<schemas::UpdateGroupParams>,
|
||||||
|
req: HttpRequest,
|
||||||
|
) -> impl Responder {
|
||||||
|
let auth_token = get_auth_header(&req);
|
||||||
|
let params = params.into_inner();
|
||||||
|
|
||||||
|
let user_auth: schemas::AuthReturn = verify_auth(auth_token.as_deref(), &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.group_name != "" {
|
||||||
|
let db_group: db::schemas::GameGroup;
|
||||||
|
|
||||||
|
db_group = match sqlx::query_as!(
|
||||||
|
db::schemas::GameGroup,
|
||||||
|
"UPDATE public.game_groups SET name = $1 WHERE id = $2 AND game_id = $3 RETURNING * ;",
|
||||||
|
params.group_name, params.group_id, params.game_id
|
||||||
|
)
|
||||||
|
.fetch_one(&data.db)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(data) => data,
|
||||||
|
Err(_) => return HttpResponse::UnprocessableEntity().finish(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let parsed_group: schemas::ChellarisGroupFlat = schemas::ChellarisGroupFlat {
|
||||||
|
id: db_group.id,
|
||||||
|
name: db_group.name,
|
||||||
|
};
|
||||||
|
|
||||||
|
return HttpResponse::Ok().json(parsed_group);
|
||||||
|
} else {
|
||||||
|
return HttpResponse::UnprocessableEntity().finish();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return HttpResponse::Unauthorized().finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[utoipa::path(
|
||||||
|
params(
|
||||||
|
schemas::DeleteGroupParams,
|
||||||
|
),
|
||||||
|
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/group")]
|
||||||
|
pub(crate) async fn delete_group(
|
||||||
|
data: web::Data<AppState>,
|
||||||
|
req: HttpRequest,
|
||||||
|
param: web::Query<schemas::DeleteGroupParams>,
|
||||||
|
) -> impl Responder {
|
||||||
|
let auth_token = get_auth_header(&req);
|
||||||
|
let param = param.into_inner();
|
||||||
|
|
||||||
|
let user_auth: schemas::AuthReturn = verify_auth(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 {
|
||||||
|
match sqlx::query!("DELETE FROM public.game_groups WHERE id = $1 AND game_id = $2 AND name != 'N/A'", param.group_id, param.game_id)
|
||||||
|
.execute(&data.db)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => {}
|
||||||
Err(_) => return HttpResponse::UnprocessableEntity().finish(),
|
Err(_) => return HttpResponse::UnprocessableEntity().finish(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -466,12 +709,15 @@ pub(crate) async fn delete_game(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Empire Endpoints
|
||||||
|
|
||||||
|
|
||||||
// Data Manipulation Endpoints
|
// Data Manipulation Endpoints
|
||||||
|
|
||||||
// Moderator & Admin
|
// Moderator & Admin
|
||||||
// Add/Update/Remove Empire
|
// Add/Update/Remove Empire
|
||||||
// Add/Update/Remove Group
|
// Update Group
|
||||||
// Add/Update/Remove Game
|
// Update Game
|
||||||
|
|
||||||
// Admin
|
// Admin
|
||||||
// Add/Update/Remove Portrait
|
// Add/Update/Remove Portrait
|
||||||
|
|
|
@ -1,20 +1,8 @@
|
||||||
use std::collections::HashMap;
|
use std::{collections::HashMap};
|
||||||
|
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use utoipa::{ToSchema, IntoParams};
|
use utoipa::{ToSchema, IntoParams};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, ToSchema, Debug, IntoParams)]
|
|
||||||
pub struct AuthParamsOptional {
|
|
||||||
#[schema(example = "1357")]
|
|
||||||
pub token: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, ToSchema, Debug, IntoParams)]
|
|
||||||
pub struct AuthParams {
|
|
||||||
#[schema(example = "1357")]
|
|
||||||
pub token: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, ToSchema, Debug)]
|
#[derive(Serialize, Deserialize, ToSchema, Debug)]
|
||||||
pub struct AuthReturn {
|
pub struct AuthReturn {
|
||||||
#[schema(example = false)]
|
#[schema(example = false)]
|
||||||
|
@ -23,14 +11,24 @@ pub struct AuthReturn {
|
||||||
pub admin: bool,
|
pub admin: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Game Structs
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, ToSchema, Debug, IntoParams)]
|
#[derive(Serialize, Deserialize, ToSchema, Debug, IntoParams)]
|
||||||
pub struct GetGameParam {
|
pub struct GetGameParam {
|
||||||
pub game_id: Option<usize>,
|
#[schema(example = 0)]
|
||||||
|
pub game_id: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, ToSchema, Debug)]
|
#[derive(Serialize, Deserialize, ToSchema, Debug)]
|
||||||
pub struct PostGameParams {
|
pub struct PostGameParams {
|
||||||
pub auth: AuthParams,
|
#[schema(example = "Game XY")]
|
||||||
|
pub game_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, ToSchema, Debug)]
|
||||||
|
pub struct UpdateGameParams {
|
||||||
|
#[schema(example = 0)]
|
||||||
|
pub game_id: i32,
|
||||||
#[schema(example = "Game XY")]
|
#[schema(example = "Game XY")]
|
||||||
pub game_name: String,
|
pub game_name: String,
|
||||||
}
|
}
|
||||||
|
@ -38,44 +36,97 @@ pub struct PostGameParams {
|
||||||
#[derive(Serialize, Deserialize, ToSchema, Debug, IntoParams)]
|
#[derive(Serialize, Deserialize, ToSchema, Debug, IntoParams)]
|
||||||
pub struct DeleteGameParam {
|
pub struct DeleteGameParam {
|
||||||
#[schema(example = 0)]
|
#[schema(example = 0)]
|
||||||
pub game_id: usize,
|
pub game_id: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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, ChellarisGameLegacy>,
|
||||||
pub ethics: HashMap<i32, Ethic>,
|
pub ethics: HashMap<i32, Ethic>,
|
||||||
pub species: HashMap<i32, Species>,
|
pub species: HashMap<i32, Species>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, ToSchema, Debug, Clone)]
|
||||||
|
pub struct ChellarisGameLegacy {
|
||||||
|
pub id: i32,
|
||||||
|
pub name: String,
|
||||||
|
pub groups: HashMap<i32, ChellarisGameGroupLegacy>,
|
||||||
|
pub empires: HashMap<i32, ChellarisEmpire>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, ToSchema, Debug, Clone)]
|
||||||
|
pub struct ChellarisGameFlat {
|
||||||
|
pub id: i32,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, ToSchema, Debug, Clone)]
|
#[derive(Serialize, ToSchema, Debug, Clone)]
|
||||||
pub struct ChellarisGame {
|
pub struct ChellarisGame {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub groups: HashMap<i32, ChellarisGameGroup>,
|
pub empires: HashMap<i32, ChellarisEmpireFlat>,
|
||||||
pub empires: HashMap<i32, ChellarisEmpire>,
|
pub groups: HashMap<i32, ChellarisGroupFlat>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group Structs
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, ToSchema, Debug)]
|
||||||
|
pub struct PostGroupParams {
|
||||||
|
#[schema(example = "Group XY")]
|
||||||
|
pub group_name: String,
|
||||||
|
#[schema(example = 0)]
|
||||||
|
pub game_id: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, ToSchema, Debug)]
|
||||||
|
pub struct UpdateGroupParams {
|
||||||
|
#[schema(example = 0)]
|
||||||
|
pub group_id: i32,
|
||||||
|
#[schema(example = 0)]
|
||||||
|
pub game_id: i32,
|
||||||
|
#[schema(example = "Group XY")]
|
||||||
|
pub group_name: String
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, ToSchema, Debug, IntoParams)]
|
||||||
|
pub struct DeleteGroupParams {
|
||||||
|
#[schema(example = 0)]
|
||||||
|
pub group_id: i32,
|
||||||
|
#[schema(example = 0)]
|
||||||
|
pub game_id: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, ToSchema, Debug, Clone)]
|
#[derive(Serialize, ToSchema, Debug, Clone)]
|
||||||
pub struct ChellarisGameLite {
|
pub struct ChellarisGroupFlat {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, ToSchema, Debug, Clone)]
|
#[derive(Serialize, ToSchema, Debug, Clone)]
|
||||||
pub struct ChellarisGameGroup {
|
pub struct ChellarisGameGroupLegacy {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, ToSchema, Debug, Clone)]
|
||||||
|
pub struct ChellarisEmpireFlat {
|
||||||
|
pub id: i32,
|
||||||
|
pub group: i32,
|
||||||
|
pub name: String,
|
||||||
|
pub discord_user: Option<String>,
|
||||||
|
pub gestalt: bool,
|
||||||
|
pub empire_portrait: Option<i32>,
|
||||||
|
pub empire_portrait_group: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, ToSchema, Debug, Clone)]
|
#[derive(Serialize, ToSchema, Debug, Clone)]
|
||||||
pub struct ChellarisEmpire {
|
pub struct ChellarisEmpire {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub gestalt: bool,
|
pub gestalt: bool,
|
||||||
pub machine: bool,
|
pub machine: bool,
|
||||||
pub group: i32,
|
pub group: i32,
|
||||||
pub empire_portrait: i32,
|
pub empire_portrait: Option<i32>,
|
||||||
pub empire_portrait_group: i32,
|
pub empire_portrait_group: Option<i32>,
|
||||||
pub discord_user: Option<String>,
|
pub discord_user: Option<String>,
|
||||||
pub ethics: HashMap<i32, EmpireEthic>,
|
pub ethics: HashMap<i32, EmpireEthic>,
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue