Delete & Post Game Endpoints. Various other changes
This commit is contained in:
parent
790a7dd8ee
commit
07e6df6b64
8 changed files with 493 additions and 107 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,5 @@
|
||||||
/target
|
/target
|
||||||
|
|
||||||
config.toml
|
config.toml
|
||||||
|
|
||||||
|
.env
|
37
Cargo.lock
generated
37
Cargo.lock
generated
|
@ -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"
|
||||||
|
|
|
@ -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"] }
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
59
src/main.rs
59
src/main.rs
|
@ -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");
|
||||||
});
|
});
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
420
src/v3/mod.rs
420
src/v3/mod.rs
|
@ -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(¶ms.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
|
||||||
|
|
|
@ -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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in a new issue