1342 lines
42 KiB
Rust
1342 lines
42 KiB
Rust
use std::{collections::HashMap, vec};
|
|
|
|
use actix_web::{
|
|
delete, get, post, put,
|
|
web::{self, Json},
|
|
HttpRequest, HttpResponse, Responder,
|
|
};
|
|
use sqlx::QueryBuilder;
|
|
|
|
use crate::{db, AppState};
|
|
|
|
pub(crate) mod schemas;
|
|
|
|
fn verify_auth(token: Option<&str>, data: &AppState) -> schemas::AuthReturn {
|
|
let mut auth_return = schemas::AuthReturn {
|
|
moderator: false,
|
|
admin: false,
|
|
};
|
|
|
|
if let Some(token) = token {
|
|
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;
|
|
}
|
|
|
|
fn get_auth_header<'a>(req: &'a HttpRequest) -> Option<&'a str> {
|
|
req.headers().get("x-api-key")?.to_str().ok()
|
|
}
|
|
|
|
#[utoipa::path(
|
|
responses(
|
|
(status = 200, description = "OK", body = AuthReturn),
|
|
),
|
|
security(
|
|
("api_key" = [])
|
|
),
|
|
)]
|
|
#[get("/api/v3/auth")]
|
|
pub(crate) async fn auth(data: web::Data<AppState>, req: HttpRequest) -> impl Responder {
|
|
let auth_token = get_auth_header(&req);
|
|
|
|
let auth_return = verify_auth(auth_token, &data);
|
|
|
|
Json(auth_return)
|
|
}
|
|
|
|
#[utoipa::path(
|
|
responses(
|
|
(status = 200, description = "OK", body = FullViewData),
|
|
),
|
|
security(
|
|
("api_key" = [])
|
|
),
|
|
)]
|
|
#[get("/api/v3/full_view_data")]
|
|
pub(crate) async fn full_view_data(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 db_groups: Vec<db::schemas::GameGroup> =
|
|
sqlx::query_as("SELECT * FROM public.game_groups ORDER BY id")
|
|
.fetch_all(&data.db)
|
|
.await
|
|
.expect("Error Fetching Data from DB");
|
|
|
|
let db_empires: Vec<db::schemas::Empire> =
|
|
sqlx::query_as("SELECT * FROM public.empires ORDER BY id")
|
|
.fetch_all(&data.db)
|
|
.await
|
|
.expect("Error Fetching Data from DB");
|
|
|
|
let db_ethics: Vec<db::schemas::Ethic> =
|
|
sqlx::query_as("SELECT * FROM public.ethics ORDER BY id")
|
|
.fetch_all(&data.db)
|
|
.await
|
|
.expect("Error Fetching Data from DB");
|
|
|
|
let db_empire_ethics: Vec<db::schemas::EmpireEthic> =
|
|
sqlx::query_as("SELECT * FROM public.empire_ethics ORDER BY empire_id")
|
|
.fetch_all(&data.db)
|
|
.await
|
|
.expect("Error Fetching Data from DB");
|
|
|
|
let db_portrait_groups: Vec<db::schemas::PortraitGroup> =
|
|
sqlx::query_as("SELECT * FROM public.portrait_groups ORDER BY id")
|
|
.fetch_all(&data.db)
|
|
.await
|
|
.expect("Error Fetching Data from DB");
|
|
|
|
let db_portraits: Vec<db::schemas::Portrait> =
|
|
sqlx::query_as("SELECT * FROM public.portraits ORDER BY id")
|
|
.fetch_all(&data.db)
|
|
.await
|
|
.expect("Error Fetching Data from DB");
|
|
|
|
let mut parsed_data: schemas::FullViewData = schemas::FullViewData {
|
|
games: HashMap::new(),
|
|
ethics: HashMap::new(),
|
|
phenotypes: HashMap::new(),
|
|
};
|
|
|
|
// Data processing
|
|
// Species Vector
|
|
db_portrait_groups.iter().for_each(|species| {
|
|
let new_data = schemas::Phenotype {
|
|
id: species.id,
|
|
display: species.name.clone(),
|
|
species: HashMap::new(),
|
|
};
|
|
|
|
parsed_data
|
|
.phenotypes
|
|
.entry(species.id)
|
|
.and_modify(|d| *d = new_data.clone())
|
|
.or_insert(new_data);
|
|
});
|
|
|
|
db_portraits.iter().for_each(|portrait| {
|
|
let new_data = schemas::Species {
|
|
id: portrait.id,
|
|
hires: portrait.hires.clone(),
|
|
lores: portrait.lores.clone(),
|
|
};
|
|
|
|
parsed_data
|
|
.phenotypes
|
|
.get_mut(&portrait.group_id)
|
|
.unwrap()
|
|
.species
|
|
.entry(portrait.id)
|
|
.and_modify(|d| *d = new_data.clone())
|
|
.or_insert(new_data);
|
|
});
|
|
|
|
// Games Vector and Children
|
|
db_games.iter().for_each(|game| {
|
|
let new_data = schemas::ChellarisGameLegacy {
|
|
id: game.id,
|
|
name: game.name.clone(),
|
|
empires: HashMap::new(),
|
|
groups: HashMap::new(),
|
|
};
|
|
|
|
parsed_data
|
|
.games
|
|
.entry(game.id)
|
|
.and_modify(|d| *d = new_data.clone())
|
|
.or_insert(new_data);
|
|
});
|
|
|
|
db_groups.iter().for_each(|group| {
|
|
let new_data = schemas::ChellarisGameGroupLegacy {
|
|
id: group.id,
|
|
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);
|
|
});
|
|
|
|
db_empires.iter().for_each(|empire| {
|
|
let new_data = schemas::ChellarisEmpireLegacy {
|
|
id: empire.id,
|
|
gestalt: empire.gestalt,
|
|
machine: empire.portrait_group_id.to_string() == dotenv!("MACHINE_GROUP_ID"),
|
|
group: empire.group_id,
|
|
portrait_id: empire.portrait_id,
|
|
portrait_group_id: empire.portrait_group_id,
|
|
discord_user: None,
|
|
ethics: HashMap::new(),
|
|
};
|
|
|
|
parsed_data
|
|
.games
|
|
.get_mut(&empire.game_id)
|
|
.unwrap()
|
|
.empires
|
|
.entry(empire.id)
|
|
.and_modify(|d| *d = new_data.clone())
|
|
.or_insert(new_data);
|
|
});
|
|
|
|
// Ethics Vector
|
|
db_ethics.iter().for_each(|ethic| {
|
|
let new_data = schemas::Ethic {
|
|
id: ethic.id,
|
|
display: ethic.name.clone(),
|
|
gestalt: ethic.gestalt_ethic,
|
|
};
|
|
|
|
parsed_data
|
|
.ethics
|
|
.entry(ethic.id)
|
|
.and_modify(|d| *d = new_data.clone())
|
|
.or_insert(new_data);
|
|
});
|
|
|
|
db_empire_ethics.iter().for_each(|empire_ethic| {
|
|
let game_id = empire_ethic.empire_game_id;
|
|
let id = empire_ethic.empire_id;
|
|
|
|
let new_data = schemas::EmpireEthicLegacy {
|
|
ethic_id: empire_ethic.ethics_id,
|
|
display: parsed_data.ethics[&empire_ethic.ethics_id].display.clone(),
|
|
gestalt: parsed_data.ethics[&empire_ethic.ethics_id].gestalt,
|
|
fanatic: empire_ethic.fanatic,
|
|
};
|
|
|
|
if new_data.gestalt {
|
|
parsed_data
|
|
.games
|
|
.get_mut(&game_id)
|
|
.unwrap()
|
|
.empires
|
|
.get_mut(&id)
|
|
.unwrap()
|
|
.gestalt = 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);
|
|
});
|
|
|
|
Json(parsed_data)
|
|
}
|
|
|
|
// Data Fetching Endpoints for Admin/Moderator Menu
|
|
|
|
// Game Endpoints
|
|
|
|
#[utoipa::path(
|
|
params(),
|
|
responses(
|
|
(status = 200, description = "OK", body = HashMap<i32, ChellarisGameFlat>),
|
|
),
|
|
security(
|
|
("api_key" = [])
|
|
),
|
|
)]
|
|
#[get("/api/v3/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::ChellarisGameFlat> = HashMap::new();
|
|
|
|
// Data processing
|
|
// Games Vector
|
|
db_games.iter().for_each(|game| {
|
|
let new_data = schemas::ChellarisGameFlat {
|
|
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 = ChellarisGame),
|
|
),
|
|
security(
|
|
("api_key" = [])
|
|
),
|
|
)]
|
|
#[get("/api/v3/game")]
|
|
pub(crate) async fn get_game_data(
|
|
data: web::Data<AppState>,
|
|
path_param: web::Query<schemas::GetGameParam>,
|
|
req: HttpRequest,
|
|
) -> impl Responder {
|
|
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
|
|
// TODO: Optimize by utilizing some SQL magic, for now this has to do
|
|
let db_game: db::schemas::Game = match sqlx::query_as!(
|
|
db::schemas::Game,
|
|
"SELECT * FROM public.games WHERE id = $1",
|
|
param.game_id
|
|
)
|
|
.fetch_one(&data.db)
|
|
.await
|
|
{
|
|
Ok(data) => data,
|
|
Err(_) => return HttpResponse::UnprocessableEntity().finish(),
|
|
};
|
|
|
|
let db_empires: Vec<db::schemas::Empire> = sqlx::query_as!(
|
|
db::schemas::Empire,
|
|
"SELECT * FROM public.empires WHERE game_id = $1",
|
|
param.game_id
|
|
)
|
|
.fetch_all(&data.db)
|
|
.await
|
|
.unwrap_or(vec![]);
|
|
|
|
let db_groups: Vec<db::schemas::GameGroup> = sqlx::query_as!(
|
|
db::schemas::GameGroup,
|
|
"SELECT * FROM public.game_groups WHERE game_id = $1",
|
|
param.game_id
|
|
)
|
|
.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,
|
|
game: empire.game_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,
|
|
portrait_id: empire.portrait_id,
|
|
portrait_group_id: empire.portrait_group_id,
|
|
};
|
|
|
|
parsed_data
|
|
.empires
|
|
.entry(empire.id)
|
|
.and_modify(|d| *d = new_empire.clone())
|
|
.or_insert(new_empire);
|
|
});
|
|
|
|
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(
|
|
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>,
|
|
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,
|
|
"INSERT INTO public.games(name) VALUES ($1) RETURNING * ",
|
|
params.game_name
|
|
)
|
|
.fetch_one(&data.db)
|
|
.await
|
|
{
|
|
Ok(data) => data,
|
|
Err(_) => return HttpResponse::UnprocessableEntity().finish(),
|
|
};
|
|
|
|
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,
|
|
name: db_game.name,
|
|
};
|
|
|
|
return HttpResponse::Ok().json(parsed_game);
|
|
} else {
|
|
return HttpResponse::UnprocessableEntity().finish();
|
|
}
|
|
} else {
|
|
return HttpResponse::Unauthorized().finish();
|
|
}
|
|
}
|
|
|
|
#[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(
|
|
params(
|
|
schemas::DeleteGameParam,
|
|
),
|
|
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>,
|
|
req: HttpRequest,
|
|
param: web::Query<schemas::DeleteGameParam>,
|
|
) -> 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.games WHERE id = $1", param.game_id)
|
|
.execute(&data.db)
|
|
.await
|
|
{
|
|
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,
|
|
),
|
|
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(),
|
|
};
|
|
|
|
return HttpResponse::Ok().into();
|
|
} else {
|
|
return HttpResponse::Unauthorized().finish();
|
|
}
|
|
}
|
|
|
|
// Empire Endpoints
|
|
|
|
#[utoipa::path(
|
|
params(
|
|
schemas::GetEmpireParams
|
|
),
|
|
responses(
|
|
(status = 200, description = "OK", body = ChellarisEmpire),
|
|
),
|
|
security(
|
|
("api_key" = [])
|
|
),
|
|
)]
|
|
#[get("/api/v3/empire")]
|
|
pub(crate) async fn get_empire(
|
|
data: web::Data<AppState>,
|
|
params: web::Query<schemas::GetEmpireParams>,
|
|
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 {
|
|
let db_empire: db::schemas::Empire;
|
|
|
|
let mut db_empire_query =
|
|
QueryBuilder::<sqlx::Postgres>::new("SELECT * FROM public.empires WHERE");
|
|
|
|
db_empire_query.push(" id = ").push_bind(params.empire_id);
|
|
db_empire_query
|
|
.push(" AND game_id = ")
|
|
.push_bind(params.game_id);
|
|
|
|
db_empire = match db_empire_query
|
|
.build_query_as::<db::schemas::Empire>()
|
|
.fetch_one(&data.db)
|
|
.await
|
|
{
|
|
Ok(data) => data,
|
|
Err(e) => {
|
|
return HttpResponse::UnprocessableEntity().body(format!("{:#?}", e.to_string()))
|
|
}
|
|
};
|
|
|
|
let mut db_ethics: HashMap<i32, schemas::EmpireEthic> = HashMap::new();
|
|
|
|
let mut db_ethic_query =
|
|
QueryBuilder::<sqlx::Postgres>::new("SELECT * FROM public.empire_ethics WHERE");
|
|
|
|
db_ethic_query
|
|
.push(" empire_id = ")
|
|
.push_bind(params.empire_id);
|
|
db_ethic_query
|
|
.push(" AND empire_game_id = ")
|
|
.push_bind(params.game_id);
|
|
|
|
match db_ethic_query
|
|
.build_query_as::<db::schemas::EmpireEthic>()
|
|
.fetch_all(&data.db)
|
|
.await
|
|
{
|
|
Ok(data) => {
|
|
for ethic in data {
|
|
db_ethics.insert(
|
|
ethic.ethics_id,
|
|
schemas::EmpireEthic {
|
|
ethic_id: ethic.ethics_id,
|
|
fanatic: ethic.fanatic,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
Err(e) => {
|
|
return HttpResponse::UnprocessableEntity().body(format!("{:#?}", e.to_string()))
|
|
}
|
|
};
|
|
|
|
// Empire Ethic Creation
|
|
let parsed_empire: schemas::ChellarisEmpire = schemas::ChellarisEmpire {
|
|
id: db_empire.id,
|
|
group: db_empire.group_id,
|
|
game: db_empire.game_id,
|
|
name: db_empire.name,
|
|
discord_user: db_empire.discord_user,
|
|
machine: db_empire.portrait_group_id.to_string() == dotenv!("MACHINE_GROUP_ID"),
|
|
gestalt: db_empire.gestalt,
|
|
portrait_id: db_empire.portrait_id,
|
|
portrait_group_id: db_empire.portrait_group_id,
|
|
ethics: db_ethics,
|
|
};
|
|
|
|
return HttpResponse::Ok().json(parsed_empire);
|
|
} else {
|
|
return HttpResponse::Unauthorized().finish();
|
|
}
|
|
}
|
|
|
|
#[utoipa::path(
|
|
request_body = PostEmpireParams,
|
|
responses(
|
|
(status = 200, description = "OK", body = ChellarisEmpireFlat),
|
|
(status = 422, description = "Missing Game Name"),
|
|
(status = 401, description = "Auth Token Invalid"),
|
|
),
|
|
security(
|
|
("api_key" = [])
|
|
),
|
|
)]
|
|
#[post("/api/v3/empire")]
|
|
pub(crate) async fn create_empire(
|
|
data: web::Data<AppState>,
|
|
params: web::Json<schemas::PostEmpireParams>,
|
|
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 {
|
|
let mut all_params_present = true;
|
|
|
|
if params.empire_name == "" {
|
|
all_params_present = false;
|
|
}
|
|
|
|
if all_params_present {
|
|
// Basic Empire Creation
|
|
let db_empire: db::schemas::Empire;
|
|
|
|
let mut db_empire_query =
|
|
QueryBuilder::<sqlx::Postgres>::new("INSERT INTO public.empires(");
|
|
|
|
db_empire_query.push("group_id");
|
|
db_empire_query.push(", game_id");
|
|
db_empire_query.push(", name");
|
|
db_empire_query.push(", gestalt");
|
|
db_empire_query.push(", portrait_id");
|
|
db_empire_query.push(", portrait_group_id");
|
|
db_empire_query.push(", discord_user");
|
|
db_empire_query.push(") VALUES (");
|
|
|
|
db_empire_query.push("").push_bind(params.group_id);
|
|
db_empire_query.push(", ").push_bind(params.game_id);
|
|
db_empire_query.push(", ").push_bind(params.empire_name);
|
|
db_empire_query.push(", ").push_bind(params.gestalt);
|
|
db_empire_query.push(", ").push_bind(params.portrait_id);
|
|
db_empire_query
|
|
.push(", ")
|
|
.push_bind(params.portrait_group_id);
|
|
|
|
if let Some(discord_user) = params.discord_user {
|
|
db_empire_query.push(", ").push_bind(discord_user);
|
|
} else {
|
|
let val: Option<String> = None;
|
|
db_empire_query.push(", ").push_bind(val);
|
|
}
|
|
|
|
db_empire_query.push(") RETURNING * ");
|
|
|
|
db_empire = match db_empire_query
|
|
.build_query_as::<db::schemas::Empire>()
|
|
.fetch_one(&data.db)
|
|
.await
|
|
{
|
|
Ok(data) => data,
|
|
Err(e) => {
|
|
return HttpResponse::UnprocessableEntity()
|
|
.body(format!("{:#?}", e.to_string()))
|
|
}
|
|
};
|
|
|
|
let mut db_ethics: HashMap<i32, schemas::EmpireEthic> = HashMap::new();
|
|
|
|
for ethic in params.ethics {
|
|
let mut db_ethic_query =
|
|
QueryBuilder::<sqlx::Postgres>::new("INSERT INTO public.empire_ethics(");
|
|
|
|
db_ethic_query.push("empire_id").push(", empire_game_id");
|
|
db_ethic_query.push(", ethics_id").push(", fanatic");
|
|
|
|
db_ethic_query.push(") VALUES (");
|
|
|
|
db_ethic_query.push("").push_bind(db_empire.id);
|
|
db_ethic_query.push(", ").push_bind(db_empire.game_id);
|
|
db_ethic_query.push(", ").push_bind(ethic.ethic_id);
|
|
db_ethic_query.push(", ").push_bind(ethic.fanatic);
|
|
|
|
db_ethic_query.push(") RETURNING * ");
|
|
|
|
match db_ethic_query
|
|
.build_query_as::<db::schemas::EmpireEthic>()
|
|
.fetch_one(&data.db)
|
|
.await
|
|
{
|
|
Ok(data) => db_ethics.insert(
|
|
data.ethics_id,
|
|
schemas::EmpireEthic {
|
|
ethic_id: data.ethics_id,
|
|
fanatic: data.fanatic,
|
|
},
|
|
),
|
|
Err(e) => {
|
|
return HttpResponse::UnprocessableEntity()
|
|
.body(format!("{:#?}", e.to_string()))
|
|
}
|
|
};
|
|
}
|
|
|
|
// Empire Ethic Creation
|
|
let parsed_empire: schemas::ChellarisEmpire = schemas::ChellarisEmpire {
|
|
id: db_empire.id,
|
|
group: db_empire.group_id,
|
|
game: db_empire.game_id,
|
|
name: db_empire.name,
|
|
discord_user: db_empire.discord_user,
|
|
machine: db_empire.portrait_group_id.to_string() == dotenv!("MACHINE_GROUP_ID"),
|
|
gestalt: db_empire.gestalt,
|
|
portrait_id: db_empire.portrait_id,
|
|
portrait_group_id: db_empire.portrait_group_id,
|
|
ethics: db_ethics,
|
|
};
|
|
|
|
return HttpResponse::Ok().json(parsed_empire);
|
|
} else {
|
|
return HttpResponse::UnprocessableEntity().finish();
|
|
}
|
|
} else {
|
|
return HttpResponse::Unauthorized().finish();
|
|
}
|
|
}
|
|
|
|
#[utoipa::path(
|
|
request_body = UpdateEmpireParams,
|
|
responses(
|
|
(status = 200, description = "OK", body = ChellarisEmpireFlat),
|
|
(status = 422, description = "Missing Game Name"),
|
|
(status = 401, description = "Auth Token Invalid"),
|
|
),
|
|
security(
|
|
("api_key" = [])
|
|
),
|
|
)]
|
|
#[put("/api/v3/empire")]
|
|
pub(crate) async fn edit_empire(
|
|
data: web::Data<AppState>,
|
|
params: web::Json<schemas::UpdateEmpireParams>,
|
|
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 {
|
|
let mut any_param_present = false;
|
|
|
|
let mut db_empire_query = QueryBuilder::<sqlx::Postgres>::new("UPDATE public.empires SET ");
|
|
let mut db_empire_separated = db_empire_query.separated(", ");
|
|
|
|
// More often than not does nothing but is required to ensure data is handled properly
|
|
db_empire_separated
|
|
.push(" game_id = ")
|
|
.push_bind_unseparated(params.game_id);
|
|
|
|
if let Some(new_group) = params.group_id {
|
|
any_param_present = true;
|
|
db_empire_separated
|
|
.push(" group_id = ")
|
|
.push_bind_unseparated(new_group);
|
|
}
|
|
|
|
if let Some(new_name) = params.empire_name {
|
|
if new_name != "" {
|
|
any_param_present = true;
|
|
db_empire_separated
|
|
.push(" name = ")
|
|
.push_bind_unseparated(new_name);
|
|
}
|
|
}
|
|
|
|
if let Some(new_gestalt) = params.gestalt {
|
|
any_param_present = true;
|
|
db_empire_separated
|
|
.push(" gestalt = ")
|
|
.push_bind_unseparated(new_gestalt);
|
|
}
|
|
|
|
if let Some(new_portrait) = params.portrait_id {
|
|
any_param_present = true;
|
|
db_empire_separated
|
|
.push(" portrait_id = ")
|
|
.push_bind_unseparated(new_portrait);
|
|
}
|
|
|
|
if let Some(new_portrait_group) = params.portrait_group_id {
|
|
any_param_present = true;
|
|
db_empire_separated
|
|
.push(" portrait_group_id = ")
|
|
.push_bind_unseparated(new_portrait_group);
|
|
}
|
|
|
|
if let Some(new_discord_user) = params.discord_user {
|
|
if new_discord_user != "" {
|
|
any_param_present = true;
|
|
db_empire_separated
|
|
.push(" discord_user = ")
|
|
.push_bind_unseparated(new_discord_user);
|
|
}
|
|
else {
|
|
any_param_present = true;
|
|
let val: Option<String> = None;
|
|
db_empire_separated
|
|
.push(" discord_user = ").push_bind_unseparated(val);
|
|
}
|
|
}
|
|
|
|
let db_empire: db::schemas::Empire;
|
|
|
|
if any_param_present {
|
|
db_empire_separated
|
|
.push_unseparated(" WHERE id = ")
|
|
.push_bind_unseparated(params.empire_id);
|
|
db_empire_separated
|
|
.push_unseparated(" AND game_id = ")
|
|
.push_bind_unseparated(params.game_id);
|
|
db_empire_separated.push_unseparated(" RETURNING * ");
|
|
|
|
db_empire = match db_empire_query
|
|
.build_query_as::<db::schemas::Empire>()
|
|
.fetch_one(&data.db)
|
|
.await
|
|
{
|
|
Ok(data) => data,
|
|
Err(e) => {
|
|
return HttpResponse::UnprocessableEntity()
|
|
.body(format!("{:#?}", e.to_string()))
|
|
}
|
|
};
|
|
} else {
|
|
let mut db_empire_query =
|
|
QueryBuilder::<sqlx::Postgres>::new("SELECT * FROM public.empires");
|
|
db_empire_separated
|
|
.push_unseparated(" WHERE id = ")
|
|
.push_bind_unseparated(params.empire_id);
|
|
db_empire_separated
|
|
.push_unseparated(" AND game_id = ")
|
|
.push_bind_unseparated(params.game_id);
|
|
|
|
db_empire = match db_empire_query
|
|
.build_query_as::<db::schemas::Empire>()
|
|
.fetch_one(&data.db)
|
|
.await
|
|
{
|
|
Ok(data) => data,
|
|
Err(e) => {
|
|
return HttpResponse::UnprocessableEntity()
|
|
.body(format!("{:#?}", e.to_string()))
|
|
}
|
|
};
|
|
}
|
|
|
|
let mut ethics_changed = false;
|
|
let mut db_ethics: HashMap<i32, schemas::EmpireEthic> = HashMap::new();
|
|
|
|
if let Some(new_ethics) = params.ethics {
|
|
if new_ethics.len() != 0 {
|
|
ethics_changed = true;
|
|
|
|
let mut db_ethic_wipe =
|
|
QueryBuilder::<sqlx::Postgres>::new("DELETE FROM public.empire_ethics");
|
|
db_ethic_wipe
|
|
.push(" WHERE empire_id = ")
|
|
.push_bind(params.empire_id);
|
|
db_ethic_wipe
|
|
.push(" AND empire_game_id = ")
|
|
.push_bind(params.game_id);
|
|
|
|
match db_ethic_wipe.build().execute(&data.db).await {
|
|
Ok(_) => {}
|
|
Err(e) => {
|
|
return HttpResponse::UnprocessableEntity()
|
|
.body(format!("{:#?}", e.to_string()))
|
|
}
|
|
};
|
|
|
|
for ethic in new_ethics {
|
|
let mut db_ethic_query =
|
|
QueryBuilder::<sqlx::Postgres>::new("INSERT INTO public.empire_ethics(");
|
|
|
|
db_ethic_query.push("empire_id").push(", empire_game_id");
|
|
db_ethic_query.push(", ethics_id").push(", fanatic");
|
|
|
|
db_ethic_query.push(") VALUES (");
|
|
|
|
db_ethic_query.push("").push_bind(db_empire.id);
|
|
db_ethic_query.push(", ").push_bind(db_empire.game_id);
|
|
db_ethic_query.push(", ").push_bind(ethic.ethic_id);
|
|
db_ethic_query.push(", ").push_bind(ethic.fanatic);
|
|
|
|
db_ethic_query.push(") RETURNING * ");
|
|
|
|
match db_ethic_query.build().execute(&data.db).await {
|
|
Ok(_) => {}
|
|
Err(e) => {
|
|
return HttpResponse::UnprocessableEntity()
|
|
.body(format!("{:#?}", e.to_string()))
|
|
}
|
|
};
|
|
}
|
|
}
|
|
}
|
|
let mut db_ethic_query =
|
|
QueryBuilder::<sqlx::Postgres>::new("SELECT * FROM public.empire_ethics");
|
|
db_ethic_query
|
|
.push(" WHERE empire_id = ")
|
|
.push_bind(params.empire_id);
|
|
db_ethic_query
|
|
.push(" AND empire_game_id = ")
|
|
.push_bind(params.game_id);
|
|
|
|
match db_ethic_query
|
|
.build_query_as::<db::schemas::EmpireEthic>()
|
|
.fetch_all(&data.db)
|
|
.await
|
|
{
|
|
Ok(data) => data.iter().for_each(|db_ethic| {
|
|
db_ethics.insert(
|
|
db_ethic.ethics_id,
|
|
schemas::EmpireEthic {
|
|
ethic_id: db_ethic.ethics_id,
|
|
fanatic: db_ethic.fanatic,
|
|
},
|
|
);
|
|
}),
|
|
Err(e) => {
|
|
return HttpResponse::UnprocessableEntity().body(format!("{:#?}", e.to_string()))
|
|
}
|
|
};
|
|
|
|
if any_param_present || ethics_changed {
|
|
let parsed_empire: schemas::ChellarisEmpire = schemas::ChellarisEmpire {
|
|
id: db_empire.id,
|
|
group: db_empire.group_id,
|
|
game: db_empire.game_id,
|
|
name: db_empire.name,
|
|
discord_user: db_empire.discord_user,
|
|
machine: db_empire.portrait_group_id.to_string() == dotenv!("MACHINE_GROUP_ID"),
|
|
gestalt: db_empire.gestalt,
|
|
portrait_id: db_empire.portrait_id,
|
|
portrait_group_id: db_empire.portrait_group_id,
|
|
ethics: db_ethics,
|
|
};
|
|
return HttpResponse::Ok().json(parsed_empire);
|
|
} else {
|
|
return HttpResponse::UnprocessableEntity().finish();
|
|
}
|
|
} else {
|
|
return HttpResponse::Unauthorized().finish();
|
|
}
|
|
}
|
|
|
|
#[utoipa::path(
|
|
params(
|
|
schemas::DeleteEmpireParams,
|
|
),
|
|
responses(
|
|
(status = 200, description = "OK"),
|
|
(status = 422, description = "Missing Game ID"),
|
|
(status = 401, description = "Auth Token Invalid"),
|
|
),
|
|
security(
|
|
("api_key" = [])
|
|
),
|
|
)]
|
|
#[delete("/api/v3/empire")]
|
|
pub(crate) async fn delete_empire(
|
|
data: web::Data<AppState>,
|
|
req: HttpRequest,
|
|
param: web::Query<schemas::DeleteEmpireParams>,
|
|
) -> 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.empires WHERE id = $1 AND game_id = $2",
|
|
param.empire_id,
|
|
param.game_id
|
|
)
|
|
.execute(&data.db)
|
|
.await
|
|
{
|
|
Ok(_) => {}
|
|
Err(_) => return HttpResponse::UnprocessableEntity().finish(),
|
|
};
|
|
|
|
return HttpResponse::Ok().into();
|
|
} else {
|
|
return HttpResponse::Unauthorized().finish();
|
|
}
|
|
}
|
|
|
|
// Ethics Endpoints
|
|
// Data Manipulation Admin Only
|
|
|
|
#[utoipa::path(
|
|
responses(
|
|
(status = 200, description = "OK", body = ChellarisEthics),
|
|
),
|
|
)]
|
|
#[get("/api/v3/ethics")]
|
|
pub(crate) async fn get_ethics(data: web::Data<AppState>) -> impl Responder {
|
|
// SQL Queries
|
|
// TODO: Optimize by utilizing some SQL magic, for now this has to do
|
|
let mut db_ethics: HashMap<i32, schemas::Ethic> = HashMap::new();
|
|
|
|
let mut db_ethic_query = QueryBuilder::<sqlx::Postgres>::new("SELECT * FROM public.ethics");
|
|
|
|
match db_ethic_query
|
|
.build_query_as::<db::schemas::Ethic>()
|
|
.fetch_all(&data.db)
|
|
.await
|
|
{
|
|
Ok(data) => {
|
|
for ethic in data {
|
|
db_ethics.insert(
|
|
ethic.id,
|
|
schemas::Ethic {
|
|
id: ethic.id,
|
|
gestalt: ethic.gestalt_ethic,
|
|
display: ethic.name,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
Err(e) => return HttpResponse::UnprocessableEntity().body(format!("{:#?}", e.to_string())),
|
|
};
|
|
|
|
// Empire Ethic Creation
|
|
let parsed_data: schemas::ChellarisEthics = schemas::ChellarisEthics { ethics: db_ethics };
|
|
|
|
return HttpResponse::Ok().json(parsed_data);
|
|
}
|
|
|
|
// Species & Portrait Endpoints
|
|
// Data Manipulation Admin Only
|
|
|
|
#[utoipa::path(
|
|
responses(
|
|
(status = 200, description = "OK", body = ChellarisPhenotypes),
|
|
),
|
|
)]
|
|
#[get("/api/v3/phenotypes")]
|
|
pub(crate) async fn get_phenotypes(data: web::Data<AppState>) -> impl Responder {
|
|
// SQL Queries
|
|
// TODO: Optimize by utilizing some SQL magic, for now this has to do
|
|
let mut db_phenotypes: HashMap<i32, schemas::Phenotype> = HashMap::new();
|
|
|
|
let mut db_phenotype_query = QueryBuilder::<sqlx::Postgres>::new("SELECT * FROM public.portrait_groups");
|
|
|
|
match db_phenotype_query
|
|
.build_query_as::<db::schemas::PortraitGroup>()
|
|
.fetch_all(&data.db)
|
|
.await
|
|
{
|
|
Ok(data) => {
|
|
for phenotype in data {
|
|
db_phenotypes.insert(
|
|
phenotype.id,
|
|
schemas::Phenotype {
|
|
id: phenotype.id,
|
|
display: phenotype.name,
|
|
species: HashMap::new(),
|
|
},
|
|
);
|
|
}
|
|
}
|
|
Err(e) => return HttpResponse::UnprocessableEntity().body(format!("{:#?}", e.to_string())),
|
|
};
|
|
|
|
let mut db_species_query = QueryBuilder::<sqlx::Postgres>::new("SELECT * FROM public.portraits");
|
|
|
|
match db_species_query
|
|
.build_query_as::<db::schemas::Portrait>()
|
|
.fetch_all(&data.db)
|
|
.await
|
|
{
|
|
Ok(data) => {
|
|
for species in data {
|
|
if let Some(phenotype) = db_phenotypes.get_mut(&species.group_id) {
|
|
phenotype.species.insert(
|
|
species.id,
|
|
schemas::Species {
|
|
id: species.id,
|
|
hires: species.hires,
|
|
lores: species.lores
|
|
}
|
|
);
|
|
}
|
|
}
|
|
}
|
|
Err(e) => return HttpResponse::UnprocessableEntity().body(format!("{:#?}", e.to_string())),
|
|
};
|
|
|
|
// Empire Ethic Creation
|
|
let parsed_data: schemas::ChellarisPhenotypes = schemas::ChellarisPhenotypes { phenotypes: db_phenotypes };
|
|
|
|
return HttpResponse::Ok().json(parsed_data);
|
|
}
|