nuke old api, add html

This commit is contained in:
41666 2022-11-26 21:52:59 -05:00
parent 747998e3c6
commit 29a3bdcda7
6 changed files with 81 additions and 435 deletions

View file

@ -1,109 +0,0 @@
use crate::redispool::RedisPool;
use core::time;
use rocket_db_pools::deadpool_redis::redis::{pipe, AsyncCommands};
use rocket_db_pools::Connection;
use serde::{Deserialize, Serialize};
use std::ops::Sub;
use std::time::SystemTime;
#[derive(Serialize, Deserialize, Debug)]
struct ClassCounts {
world_id: String,
classes: Classes,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Classes {
light_assault: i32,
engineer: i32,
combat_medic: i32,
heavy_assault: i32,
infiltrator: i32,
max: i32,
}
#[get("/w/<world_id>/classes")]
pub async fn get_classes(world_id: String, mut con: Connection<RedisPool>) -> serde_json::Value {
let cache_key = format!("cache:classes:{}", world_id);
match con.get::<String, String>(cache_key.clone()).await {
Ok(cached) => {
return serde_json::from_str(&cached).unwrap();
}
Err(_) => {}
}
let classes = fetch_classes(world_id.clone(), &mut con).await;
// I hate this but it's fast???
// The type only allows 12 at a time.
let response = ClassCounts { world_id, classes };
let out = json!(response);
con.set_ex::<String, String, ()>(cache_key, out.to_string(), 5)
.await
.unwrap();
out
}
pub async fn fetch_classes(world_id: String, con: &mut Connection<RedisPool>) -> Classes {
let filter_timestamp = SystemTime::now()
.sub(time::Duration::from_secs(60 * 15))
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs();
let (light_assault, engineer, combat_medic, heavy_assault, infiltrator, max): (
i32,
i32,
i32,
i32,
i32,
i32,
) = pipe()
.zcount(
format!("c:{}/{}", world_id, "light_assault"),
filter_timestamp,
"+inf",
)
.zcount(
format!("c:{}/{}", world_id, "engineer"),
filter_timestamp,
"+inf",
)
.zcount(
format!("c:{}/{}", world_id, "combat_medic"),
filter_timestamp,
"+inf",
)
.zcount(
format!("c:{}/{}", world_id, "heavy_assault"),
filter_timestamp,
"+inf",
)
.zcount(
format!("c:{}/{}", world_id, "infiltrator"),
filter_timestamp,
"+inf",
)
.zcount(
format!("c:{}/{}", world_id, "max"),
filter_timestamp,
"+inf",
)
.query_async(&mut **con)
.await
.unwrap();
Classes {
light_assault,
engineer,
combat_medic,
heavy_assault,
infiltrator,
max,
}
}

View file

@ -84,6 +84,11 @@ pub fn playground() -> RawHtml<String> {
juniper_rocket::playground_source("/graphql", None) juniper_rocket::playground_source("/graphql", None)
} }
#[get("/playground")]
pub fn playground2() -> RawHtml<String> {
juniper_rocket::playground_source("/graphql", None)
}
#[post("/", data = "<query>")] #[post("/", data = "<query>")]
pub async fn post_graphql( pub async fn post_graphql(
query: juniper_rocket::GraphQLRequest, query: juniper_rocket::GraphQLRequest,

View file

@ -0,0 +1,55 @@
<!DOCTYPE html>
<title>Saerro Listening Post</title>
<meta charset="utf-8" />
<style>
body {
font-family: monospace;
background-color: #010101;
color: #e0e0e0;
font-size: 1.25rem;
line-height: 1.6;
}
a {
color: #cead42;
text-decoration: none;
}
</style>
<h1>Saerro Listening Post</h1>
<h2>Live Population Stats API for PlanetSide 2</h2>
<ul>
<li><a href="/graphql/playground">Check out the GraphQL Playground</a></li>
<li>
<a
href="/graphql?query=%7B%20health%20%7B%20pc%20redis%20ps4us%20ps4eu%20%7D%7D"
>Current system status</a
>
(<a
href="/graphql/playground?query=%7B%20health%20%7B%20pc%20redis%20ps4us%20ps4eu%20%7D%7D"
>or in playground</a
>)
</li>
<li>
<a
href="/graphql?query=%7B%0A%20%20allWorlds%20%7B%0A%20%20%20%20name%0A%20%20%20%20population%0A%20%20%7D%0A%7D%0A"
>
Current population of all worlds
</a>
(<a
href="/graphql/playground?query=%7B%0A%20%20allWorlds%20%7B%0A%20%20%20%20name%0A%20%20%20%20population%0A%20%20%7D%0A%7D%0A"
>or in playground</a
>)
</li>
</ul>
<p>
All data is an aggregate of the last 15 minutes of Death and VehicleDestroy
events, including both attacker and victim.
</p>
<p>
This API is provided by Genudine Dynamics.<br />As always, we take no
responsibility for your use of this data... or our weapons. :)
</p>
<p>For help, please contact us in #api-dev on the PlanetSide 2 Discord.</p>
<p>[<a href="https://github.com/genudine/saerro">github</a>]</p>

View file

@ -1,12 +1,11 @@
pub mod classes;
pub mod cors; pub mod cors;
pub mod graphql; pub mod graphql;
pub mod population;
pub mod redispool; pub mod redispool;
pub mod vehicles;
use redispool::RedisPool; use redispool::RedisPool;
use rocket::fairing::AdHoc; use rocket::fairing::AdHoc;
use rocket::response::content::RawHtml;
use rocket::response::status;
use rocket::{error, Build, Rocket}; use rocket::{error, Build, Rocket};
use rocket_db_pools::deadpool_redis::redis::{cmd, pipe}; use rocket_db_pools::deadpool_redis::redis::{cmd, pipe};
use rocket_db_pools::{Connection, Database}; use rocket_db_pools::{Connection, Database};
@ -16,42 +15,15 @@ extern crate rocket;
#[macro_use] #[macro_use]
extern crate serde_json; extern crate serde_json;
fn hello_world(host: String, world_id: &str) -> serde_json::Value {
json!({
"population": format!("https://{}/w/{}", host, world_id),
"vehicles": format!("https://{}/w/{}/vehicles", host, world_id),
"classes": format!("https://{}/w/{}/classes", host, world_id),
})
}
fn hello(host: String) -> serde_json::Value {
json!({
"@": "Saerro Listening Post - PlanetSide 2 Live Population API",
"@GitHub": "https://github.com/genudine/saerro",
"@Disclaimer": "Genudine Dynamics is not responsible for any damages caused by this software. Use at your own risk.",
"@Support": "#api-dev in https://discord.com/servers/planetside-2-community-251073753759481856",
"GraphQL": format!("https://{}/graphql", host),
"Worlds": {
"Connery": hello_world(host.clone(), "1"),
"Miller": hello_world(host.clone(), "10"),
"Cobalt": hello_world(host.clone(), "13"),
"Emerald": hello_world(host.clone(), "17"),
"Jaeger": hello_world(host.clone(), "19"),
"SolTech": hello_world(host.clone(), "40"),
"Genudine": hello_world(host.clone(), "1000"),
"Ceres": hello_world(host.clone(), "2000"),
},
// "All World Population": format!("https://{}/m/?ids=1,10,13,17,19,40,1000,2000", host),
})
}
#[get("/")] #[get("/")]
fn index() -> serde_json::Value { async fn index() -> RawHtml<String> {
hello("saerro.harasse.rs".to_string()) RawHtml(include_str!("html/index.html").to_string())
} }
#[get("/health")] #[get("/health")]
async fn health(mut con: Connection<RedisPool>) -> serde_json::Value { async fn health(
mut con: Connection<RedisPool>,
) -> Result<serde_json::Value, status::Custom<serde_json::Value>> {
let (ping, pc, ps4us, ps4eu): (String, bool, bool, bool) = pipe() let (ping, pc, ps4us, ps4eu): (String, bool, bool, bool) = pipe()
.cmd("PING") .cmd("PING")
.get("heartbeat:pc") .get("heartbeat:pc")
@ -61,13 +33,23 @@ async fn health(mut con: Connection<RedisPool>) -> serde_json::Value {
.await .await
.unwrap_or_default(); .unwrap_or_default();
if ping != "PONG" {
return Err(status::Custom(
rocket::http::Status::ServiceUnavailable,
json!({ json!({
"status": "error",
"message": "Redis is not responding",
}),
));
}
Ok(json!({
"status": if ping == "PONG" && pc && ps4us && ps4eu { "ok" } else { "degraded" }, "status": if ping == "PONG" && pc && ps4us && ps4eu { "ok" } else { "degraded" },
"redis": ping == "PONG", "redis": ping == "PONG",
"pc": if pc { "primary" } else { "backup/down" }, "pc": if pc { "primary" } else { "backup/down" },
"ps4us": if ps4us { "primary" } else { "backup/down" }, "ps4us": if ps4us { "primary" } else { "backup/down" },
"ps4eu": if ps4eu { "primary" } else { "backup/down" }, "ps4eu": if ps4eu { "primary" } else { "backup/down" },
}) }))
} }
#[launch] #[launch]
@ -95,21 +77,13 @@ fn rocket() -> Rocket<Build> {
rocket rocket
})) }))
.manage(graphql::schema()) .manage(graphql::schema())
.mount( .mount("/", routes![index, health,])
"/",
routes![
index,
health,
population::get_world_pop,
vehicles::get_vehicles,
classes::get_classes,
],
)
.mount( .mount(
"/graphql", "/graphql",
routes![ routes![
graphql::graphiql, graphql::graphiql,
graphql::playground, graphql::playground,
graphql::playground2,
graphql::get_graphql, graphql::get_graphql,
graphql::post_graphql graphql::post_graphql
], ],

View file

@ -1,57 +0,0 @@
use core::time;
use std::{ops::Sub, time::SystemTime};
use rocket_db_pools::{deadpool_redis::redis::pipe, Connection};
use serde::{Deserialize, Serialize};
use crate::redispool::RedisPool;
#[derive(Serialize, Deserialize, Debug)]
pub struct Factions {
pub tr: i32,
pub nc: i32,
pub vs: i32,
pub ns: i32,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct WorldPopulation {
world_id: i32,
pub total: i32,
pub factions: Factions,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct MultipleWorldPopulation {
worlds: Vec<WorldPopulation>,
}
#[get("/w/<world_id>")]
pub async fn get_world_pop(world_id: String, mut con: Connection<RedisPool>) -> serde_json::Value {
json!(fetch_world_pop(world_id, &mut con).await)
}
pub async fn fetch_world_pop(world_id: String, con: &mut Connection<RedisPool>) -> WorldPopulation {
let filter_timestamp = SystemTime::now()
.sub(time::Duration::from_secs(60 * 15))
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs();
let (vs, nc, tr, ns): (i32, i32, i32, i32) = pipe()
.zcount(format!("wp:{}/{}", world_id, 1), filter_timestamp, "+inf")
.zcount(format!("wp:{}/{}", world_id, 2), filter_timestamp, "+inf")
.zcount(format!("wp:{}/{}", world_id, 3), filter_timestamp, "+inf")
.zcount(format!("wp:{}/{}", world_id, 4), filter_timestamp, "+inf")
.query_async(&mut **con)
.await
.unwrap();
let total = tr + vs + nc;
WorldPopulation {
world_id: world_id.parse().unwrap(),
total,
factions: Factions { tr, nc, vs, ns },
}
}

View file

@ -1,222 +0,0 @@
use core::time;
use rocket_db_pools::deadpool_redis::redis::{pipe, AsyncCommands};
use rocket_db_pools::Connection;
use serde::{Deserialize, Serialize};
use std::ops::Sub;
use std::time::SystemTime;
use crate::redispool::RedisPool;
#[derive(Serialize, Deserialize, Debug)]
struct VehiclesCounts {
world_id: String,
total: u32,
vehicles: Vehicles,
}
#[derive(Serialize, Deserialize, Debug)]
struct Vehicles {
flash: u32,
sunderer: u32,
lightning: u32,
scythe: u32,
vanguard: u32,
prowler: u32,
reaver: u32,
mosquito: u32,
galaxy: u32,
valkyrie: u32,
liberator: u32,
ant: u32,
harasser: u32,
dervish: u32,
chimera: u32,
javelin: u32,
corsair: u32,
magrider: u32,
}
#[get("/w/<world_id>/vehicles")]
pub async fn get_vehicles(world_id: String, mut con: Connection<RedisPool>) -> serde_json::Value {
let cache_key = format!("cache:vehicles:{}", world_id);
match con.get::<String, String>(cache_key.clone()).await {
Ok(cached) => {
return serde_json::from_str(&cached).unwrap();
}
Err(_) => {}
}
let filter_timestamp = SystemTime::now()
.sub(time::Duration::from_secs(60 * 15))
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs();
// I hate this but it's fast???
// The type only allows 12 at a time.
let (
flash,
sunderer,
lightning,
scythe,
vanguard,
prowler,
reaver,
mosquito,
galaxy,
valkyrie,
liberator,
ant
): (u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32) = pipe()
.zcount(
format!("v:{}/{}", world_id, "flash"),
filter_timestamp,
"+inf",
)
.zcount(
format!("v:{}/{}", world_id, "sunderer"),
filter_timestamp,
"+inf",
)
.zcount(
format!("v:{}/{}", world_id, "lightning"),
filter_timestamp,
"+inf",
)
.zcount(
format!("v:{}/{}", world_id, "scythe"),
filter_timestamp,
"+inf",
)
.zcount(
format!("v:{}/{}", world_id, "vanguard"),
filter_timestamp,
"+inf",
)
.zcount(
format!("v:{}/{}", world_id, "prowler"),
filter_timestamp,
"+inf",
)
.zcount(
format!("v:{}/{}", world_id, "reaver"),
filter_timestamp,
"+inf",
)
.zcount(
format!("v:{}/{}", world_id, "mosquito"),
filter_timestamp,
"+inf",
)
.zcount(
format!("v:{}/{}", world_id, "galaxy"),
filter_timestamp,
"+inf",
)
.zcount(
format!("v:{}/{}", world_id, "valkyrie"),
filter_timestamp,
"+inf",
)
.zcount(
format!("v:{}/{}", world_id, "liberator"),
filter_timestamp,
"+inf",
)
.zcount(
format!("v:{}/{}", world_id, "ant"),
filter_timestamp,
"+inf",
)
.query_async(&mut *con)
.await
.unwrap();
let (harasser, dervish, chimera, javelin, corsair, magrider): (u32, u32, u32, u32, u32, u32) = pipe()
.zcount(
format!("v:{}/{}", world_id, "harasser"),
filter_timestamp,
"+inf",
)
.zcount(
format!("v:{}/{}", world_id, "dervish"),
filter_timestamp,
"+inf",
)
.zcount(
format!("v:{}/{}", world_id, "chimera"),
filter_timestamp,
"+inf",
)
.zcount(
format!("v:{}/{}", world_id, "javelin"),
filter_timestamp,
"+inf",
)
.zcount(
format!("v:{}/{}", world_id, "corsair"),
filter_timestamp,
"+inf",
)
.zcount(
format!("v:{}/{}", world_id, "magrider"),
filter_timestamp,
"+inf",
)
.query_async(&mut *con)
.await
.unwrap();
let total = flash
+ sunderer
+ lightning
+ scythe
+ vanguard
+ prowler
+ reaver
+ mosquito
+ galaxy
+ valkyrie
+ liberator
+ ant
+ harasser
+ dervish
+ chimera
+ javelin
+ corsair
+ magrider;
let response = VehiclesCounts {
world_id,
total,
vehicles: Vehicles {
flash,
sunderer,
lightning,
scythe,
vanguard,
prowler,
reaver,
mosquito,
galaxy,
valkyrie,
liberator,
ant,
harasser,
dervish,
chimera,
javelin,
corsair,
magrider
},
};
let out = json!(response);
con.set_ex::<String, String, ()>(cache_key, out.to_string(), 5)
.await
.unwrap();
out
}