feat(server): handle compressed requests and cors
This commit is contained in:
parent
fe151e6905
commit
28746242d3
|
@ -236,7 +236,10 @@ checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
|
|||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
|
@ -1040,6 +1043,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
"chrono",
|
||||
"serde",
|
||||
"sqlx",
|
||||
"tokio",
|
||||
|
|
|
@ -8,8 +8,9 @@ edition = "2021"
|
|||
[dependencies]
|
||||
anyhow = "1"
|
||||
axum = "0.6"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
sqlx = { version = "0.7", features = ["runtime-tokio", "sqlite", "chrono"] }
|
||||
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
|
||||
tower = "0.4"
|
||||
tower-http = { version = "0.4", features = ["decompression-gzip"] }
|
||||
tower-http = { version = "0.4", features = ["compression-gzip", "decompression-gzip", "cors"] }
|
||||
|
|
|
@ -3,13 +3,15 @@ use std::sync::Arc;
|
|||
use anyhow::Result;
|
||||
use axum::{BoxError, Json, Router, Server};
|
||||
use axum::error_handling::HandleErrorLayer;
|
||||
use axum::extract::State;
|
||||
use axum::http::StatusCode;
|
||||
use axum::extract::{Path, State};
|
||||
use axum::http::{Method, StatusCode};
|
||||
use axum::response::{IntoResponse, Response};
|
||||
use axum::routing::{get, post};
|
||||
use serde::Deserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::SqlitePool;
|
||||
use tower::ServiceBuilder;
|
||||
use tower_http::compression::CompressionLayer;
|
||||
use tower_http::cors::CorsLayer;
|
||||
use tower_http::decompression::RequestDecompressionLayer;
|
||||
|
||||
static MIGRATOR: sqlx::migrate::Migrator = sqlx::migrate!();
|
||||
|
@ -19,10 +21,20 @@ async fn main() -> Result<()> {
|
|||
let pool = SqlitePool::connect("./database.sqlite").await?;
|
||||
MIGRATOR.run(&pool).await?;
|
||||
|
||||
let cors = CorsLayer::new()
|
||||
.allow_origin([
|
||||
"https://map.anna.lgbt".parse()?,
|
||||
])
|
||||
.allow_methods([Method::GET]);
|
||||
|
||||
let app = Router::new()
|
||||
.route("/", get(index))
|
||||
.route("/:territory", get(territory))
|
||||
.route("/:territory/:world", get(territory_world))
|
||||
.route("/upload", post(upload))
|
||||
.with_state(Arc::new(pool))
|
||||
.layer(cors)
|
||||
.layer(CompressionLayer::new())
|
||||
.layer(
|
||||
ServiceBuilder::new()
|
||||
.layer(HandleErrorLayer::new(|_: BoxError| async move {
|
||||
|
@ -41,6 +53,72 @@ async fn index() -> &'static str {
|
|||
"hi"
|
||||
}
|
||||
|
||||
async fn territory(
|
||||
State(pool): State<Arc<SqlitePool>>,
|
||||
Path(territory): Path<u32>,
|
||||
) -> Result<Json<Vec<AnonymousPlayerInfo>>, AppError>
|
||||
{
|
||||
let info = sqlx::query_as!(
|
||||
AnonymousPlayerInfo,
|
||||
// language=sqlite
|
||||
r#"
|
||||
select world,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
w,
|
||||
customize,
|
||||
level,
|
||||
job,
|
||||
free_company,
|
||||
current_hp,
|
||||
max_hp
|
||||
from players
|
||||
where territory = ?
|
||||
and unixepoch('now') - unixepoch(timestamp) < 30
|
||||
"#,
|
||||
territory,
|
||||
)
|
||||
.fetch_all(&*pool)
|
||||
.await?;
|
||||
|
||||
Ok(Json(info))
|
||||
}
|
||||
|
||||
async fn territory_world(
|
||||
State(pool): State<Arc<SqlitePool>>,
|
||||
Path((territory, world)): Path<(u32, u32)>,
|
||||
) -> Result<Json<Vec<AnonymousPlayerInfo>>, AppError>
|
||||
{
|
||||
let info = sqlx::query_as!(
|
||||
AnonymousPlayerInfo,
|
||||
// language=sqlite
|
||||
r#"
|
||||
select world,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
w,
|
||||
customize,
|
||||
level,
|
||||
job,
|
||||
free_company,
|
||||
current_hp,
|
||||
max_hp
|
||||
from players
|
||||
where territory = ?
|
||||
and current_world = ?
|
||||
and unixepoch('now') - unixepoch(timestamp) < 30
|
||||
"#,
|
||||
territory,
|
||||
world,
|
||||
)
|
||||
.fetch_all(&*pool)
|
||||
.await?;
|
||||
|
||||
Ok(Json(info))
|
||||
}
|
||||
|
||||
async fn upload(
|
||||
pool: State<Arc<SqlitePool>>,
|
||||
data: Json<Update>,
|
||||
|
@ -48,9 +126,16 @@ async fn upload(
|
|||
let mut t = pool.begin().await?;
|
||||
|
||||
for player in &data.players {
|
||||
let fc = match player.free_company.trim() {
|
||||
"" => None,
|
||||
x => Some(x),
|
||||
if !player.is_sane() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let name = player.name.trim();
|
||||
|
||||
let fc = match &player.free_company {
|
||||
Some(x) if x.trim() == "" => None,
|
||||
Some(x) => Some(x),
|
||||
None => None,
|
||||
};
|
||||
|
||||
sqlx::query!(
|
||||
|
@ -73,8 +158,9 @@ async fn upload(
|
|||
current_hp = ?,
|
||||
max_hp = ?
|
||||
",
|
||||
player.name,
|
||||
name,
|
||||
player.world,
|
||||
|
||||
data.territory,
|
||||
data.world,
|
||||
player.x,
|
||||
|
@ -120,18 +206,48 @@ struct Update {
|
|||
struct PlayerInfo {
|
||||
name: String,
|
||||
world: u32,
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32,
|
||||
w: f32,
|
||||
x: f64,
|
||||
y: f64,
|
||||
z: f64,
|
||||
w: f64,
|
||||
customize: Vec<u8>,
|
||||
level: u8,
|
||||
job: u32,
|
||||
free_company: String,
|
||||
free_company: Option<String>,
|
||||
current_hp: u32,
|
||||
max_hp: u32,
|
||||
}
|
||||
|
||||
impl PlayerInfo {
|
||||
fn is_sane(&self) -> bool {
|
||||
if self.name.trim().is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.world == 65535 || self.level == 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct AnonymousPlayerInfo {
|
||||
world: i64,
|
||||
// timestamp: DateTime<Utc>,
|
||||
x: f64,
|
||||
y: f64,
|
||||
z: f64,
|
||||
w: f64,
|
||||
customize: Vec<u8>,
|
||||
level: i64,
|
||||
job: i64,
|
||||
free_company: Option<String>,
|
||||
current_hp: i64,
|
||||
max_hp: i64,
|
||||
}
|
||||
|
||||
// Make our own error that wraps `anyhow::Error`.
|
||||
struct AppError(anyhow::Error);
|
||||
|
||||
|
|
Loading…
Reference in New Issue