fix(engine,server): safe area clamp, analytics batch, achievement save order, daily rollover, replay validation, leaderboard opt-in (#56, #60, #61, #62, #66, #68)
Build and Deploy / build-and-push (push) Successful in 3m54s

- #66: Clamp safe-area insets to 25% of window height with warn!() on excess
- #68: Move fire_flush outside per-event loop in analytics (batch flush once)
- #56: Persist progress before marking reward_granted to prevent XP loss on crash
- #60: Add DateRolloverTimer + check_date_rollover system for midnight seed refresh
- #62: Add validate_header() in replay upload with mode/draw_mode allowlists
- #61: Restore two-query leaderboard opt-in check (SELECT then UPDATE); original
       queries already in .sqlx cache; EXISTS variant would require sqlx prepare

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
funman300
2026-05-28 13:07:22 -07:00
parent 8cb4c9808e
commit 6e407a3ea7
104 changed files with 6356 additions and 3092 deletions
+6 -14
View File
@@ -15,22 +15,22 @@ pub mod sync;
pub use auth::reset_password;
use axum::{
Router,
extract::DefaultBodyLimit,
http::{HeaderValue, Request},
middleware as axum_middleware,
response::{Html, Response},
routing::{delete, get, post, put},
Router,
};
use jsonwebtoken::{decode, DecodingKey, Validation};
use jsonwebtoken::{DecodingKey, Validation, decode};
use sqlx::SqlitePool;
use std::sync::Arc;
use tower_governor::{
GovernorLayer,
errors::GovernorError,
governor::GovernorConfigBuilder,
key_extractor::{KeyExtractor, SmartIpKeyExtractor},
GovernorLayer,
};
use tower_http::services::ServeDir;
@@ -59,9 +59,7 @@ impl KeyExtractor for UserIdKeyExtractor {
return Ok(user_id);
}
// Fall back to IP so unauthenticated bursts don't bypass throttling.
SmartIpKeyExtractor
.extract(req)
.map(|ip| ip.to_string())
SmartIpKeyExtractor.extract(req).map(|ip| ip.to_string())
}
}
@@ -258,18 +256,12 @@ const CSP: &str = concat!(
async fn security_headers(req: Request<axum::body::Body>, next: axum_middleware::Next) -> Response {
let mut res = next.run(req).await;
let headers = res.headers_mut();
headers.insert(
"Content-Security-Policy",
HeaderValue::from_static(CSP),
);
headers.insert("Content-Security-Policy", HeaderValue::from_static(CSP));
headers.insert(
"X-Content-Type-Options",
HeaderValue::from_static("nosniff"),
);
headers.insert(
"X-Frame-Options",
HeaderValue::from_static("DENY"),
);
headers.insert("X-Frame-Options", HeaderValue::from_static("DENY"));
res
}