refactor, add vehicles and classes
add gitignore for node_modules
This commit is contained in:
parent
6bedb26037
commit
f5df061b42
19 changed files with 1213 additions and 108 deletions
106
services/api/src/classes.rs
Normal file
106
services/api/src/classes.rs
Normal file
|
@ -0,0 +1,106 @@
|
|||
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 ClassCounts {
|
||||
world_id: String,
|
||||
classes: Classes,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct Classes {
|
||||
light_assault: u32,
|
||||
engineer: u32,
|
||||
combat_medic: u32,
|
||||
heavy_assault: u32,
|
||||
infiltrator: u32,
|
||||
max: u32,
|
||||
}
|
||||
|
||||
#[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 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 (light_assault, engineer, combat_medic, heavy_assault, infiltrator, max): (
|
||||
u32,
|
||||
u32,
|
||||
u32,
|
||||
u32,
|
||||
u32,
|
||||
u32,
|
||||
) = 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();
|
||||
|
||||
let response = ClassCounts {
|
||||
world_id,
|
||||
classes: Classes {
|
||||
light_assault,
|
||||
engineer,
|
||||
combat_medic,
|
||||
heavy_assault,
|
||||
infiltrator,
|
||||
max,
|
||||
},
|
||||
};
|
||||
|
||||
let out = json!(response);
|
||||
|
||||
con.set_ex::<String, String, ()>(cache_key, out.to_string(), 5)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
out
|
||||
}
|
|
@ -1,10 +1,16 @@
|
|||
pub mod classes;
|
||||
pub mod cors;
|
||||
pub mod population;
|
||||
pub mod redispool;
|
||||
pub mod vehicles;
|
||||
|
||||
use redispool::RedisPool;
|
||||
use rocket::fairing::AdHoc;
|
||||
use rocket::{error, Build, Rocket};
|
||||
use rocket_db_pools::deadpool_redis::redis::{cmd, pipe};
|
||||
use rocket_db_pools::{Connection, Database};
|
||||
|
||||
use core::time;
|
||||
use once_cell::sync::Lazy;
|
||||
use rocket::{Build, Rocket};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{ops::Sub, time::SystemTime};
|
||||
|
||||
#[macro_use]
|
||||
extern crate rocket;
|
||||
|
@ -16,31 +22,14 @@ struct IncomingHeaders {
|
|||
host: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct Factions {
|
||||
tr: u32,
|
||||
nc: u32,
|
||||
vs: u32,
|
||||
ns: u32,
|
||||
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),
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct WorldPopulation {
|
||||
world_id: u32,
|
||||
total: u32,
|
||||
factions: Factions,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct MultipleWorldPopulation {
|
||||
worlds: Vec<WorldPopulation>,
|
||||
}
|
||||
|
||||
pub static REDIS_CLIENT: Lazy<redis::Client> = Lazy::new(|| {
|
||||
redis::Client::open(std::env::var("REDIS_ADDR").unwrap_or("redis://localhost:6379".to_string()))
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
fn hello(host: String) -> serde_json::Value {
|
||||
json!({
|
||||
"@": "Saerro Listening Post - PlanetSide 2 Live Population API",
|
||||
|
@ -48,73 +37,76 @@ fn hello(host: String) -> serde_json::Value {
|
|||
"@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",
|
||||
"Worlds": {
|
||||
"Connery": format!("https://{}/w/1", host),
|
||||
"Miller": format!("https://{}/w/10", host),
|
||||
"Cobalt": format!("https://{}/w/13", host),
|
||||
"Emerald": format!("https://{}/w/17", host),
|
||||
"Jaeger": format!("https://{}/w/19", host),
|
||||
"SolTech": format!("https://{}/w/40", host),
|
||||
"Genudine": format!("https://{}/w/1000", host),
|
||||
"Ceres": format!("https://{}/w/2000", host),
|
||||
"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 Worlds": format!("https://{}/m/?ids=1,10,13,17,19,40,1000,2000", host),
|
||||
// "All World Population": format!("https://{}/m/?ids=1,10,13,17,19,40,1000,2000", host),
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_world_pop(world_id: String) -> WorldPopulation {
|
||||
let mut con = REDIS_CLIENT.get_connection().unwrap();
|
||||
|
||||
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): (u32, u32, u32, u32) = redis::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(&mut con)
|
||||
.unwrap();
|
||||
|
||||
let total = tr + vs + nc;
|
||||
|
||||
let response = WorldPopulation {
|
||||
world_id: world_id.parse().unwrap(),
|
||||
total,
|
||||
factions: Factions { tr, nc, vs, ns },
|
||||
};
|
||||
|
||||
response
|
||||
}
|
||||
|
||||
#[get("/w/<world_id>")]
|
||||
async fn world_pop(world_id: String) -> serde_json::Value {
|
||||
let response = get_world_pop(world_id).await;
|
||||
|
||||
json!(response)
|
||||
}
|
||||
|
||||
#[get("/m?<ids>")]
|
||||
async fn multiple_world_pop(ids: String) -> serde_json::Value {
|
||||
let mut response = MultipleWorldPopulation { worlds: vec![] };
|
||||
|
||||
for id in ids.split(",") {
|
||||
response.worlds.push(get_world_pop(id.to_string()).await);
|
||||
}
|
||||
|
||||
json!(response)
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
async fn index() -> serde_json::Value {
|
||||
fn index() -> serde_json::Value {
|
||||
hello("saerro.harasse.rs".to_string())
|
||||
}
|
||||
|
||||
#[get("/health")]
|
||||
async fn health(mut con: Connection<RedisPool>) -> serde_json::Value {
|
||||
let (ping, pc, ps4us, ps4eu): (String, bool, bool, bool) = pipe()
|
||||
.cmd("PING")
|
||||
.get("heartbeat:pc")
|
||||
.get("heartbeat:ps4us")
|
||||
.get("heartbeat:ps4eu")
|
||||
.query_async(&mut *con)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
|
||||
json!({
|
||||
"status": "ok",
|
||||
"redis": ping == "PONG",
|
||||
"pc": if pc { "primary" } else { "backup/down" },
|
||||
"ps4us": if ps4us { "primary" } else { "backup/down" },
|
||||
"ps4eu": if ps4eu { "primary" } else { "backup/down" },
|
||||
})
|
||||
}
|
||||
|
||||
#[launch]
|
||||
fn rocket() -> Rocket<Build> {
|
||||
let figment = rocket::Config::figment().merge((
|
||||
"databases.redis.url",
|
||||
format!(
|
||||
"redis://{}:{}",
|
||||
std::env::var("REDIS_HOST").unwrap_or("localhost".to_string()),
|
||||
std::env::var("REDIS_PORT").unwrap_or("6379".to_string()),
|
||||
),
|
||||
));
|
||||
|
||||
rocket::build()
|
||||
.configure(figment)
|
||||
.attach(cors::CORS)
|
||||
.mount("/", routes![index, world_pop, multiple_world_pop])
|
||||
.attach(RedisPool::init())
|
||||
.attach(AdHoc::on_ignite("Redis Check", |rocket| async move {
|
||||
if let Some(pool) = RedisPool::fetch(&rocket) {
|
||||
let mut con = pool.get().await.unwrap();
|
||||
let _: () = cmd("PING").query_async(&mut con).await.unwrap();
|
||||
} else {
|
||||
error!("Redis connection failed");
|
||||
}
|
||||
rocket
|
||||
}))
|
||||
.mount(
|
||||
"/",
|
||||
routes![
|
||||
index,
|
||||
health,
|
||||
population::get_world_pop,
|
||||
vehicles::get_vehicles,
|
||||
classes::get_classes,
|
||||
],
|
||||
)
|
||||
}
|
||||
|
|
55
services/api/src/population.rs
Normal file
55
services/api/src/population.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
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 {
|
||||
tr: u32,
|
||||
nc: u32,
|
||||
vs: u32,
|
||||
ns: u32,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct WorldPopulation {
|
||||
world_id: u32,
|
||||
total: u32,
|
||||
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 {
|
||||
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): (u32, u32, u32, u32) = 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;
|
||||
|
||||
let response = WorldPopulation {
|
||||
world_id: world_id.parse().unwrap(),
|
||||
total,
|
||||
factions: Factions { tr, nc, vs, ns },
|
||||
};
|
||||
|
||||
json!(response)
|
||||
}
|
5
services/api/src/redispool.rs
Normal file
5
services/api/src/redispool.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
use rocket_db_pools::{deadpool_redis, Database};
|
||||
|
||||
#[derive(Database)]
|
||||
#[database("redis")]
|
||||
pub struct RedisPool(deadpool_redis::Pool);
|
214
services/api/src/vehicles.rs
Normal file
214
services/api/src/vehicles.rs
Normal file
|
@ -0,0 +1,214 @@
|
|||
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,
|
||||
}
|
||||
|
||||
#[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): (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",
|
||||
)
|
||||
.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;
|
||||
|
||||
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,
|
||||
},
|
||||
};
|
||||
|
||||
let out = json!(response);
|
||||
|
||||
con.set_ex::<String, String, ()>(cache_key, out.to_string(), 5)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
out
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue