feat(server): templating for cluster map is almost done (cluster hardcoded)

This commit is contained in:
Maieul BOYER 2026-02-20 18:42:45 +01:00
parent ac98af1428
commit 5e0a9ded04
Signed by: maix
SSH key fingerprint: SHA256:iqCzqFFF5KjRixmDExqbAltCIj9ndlBWIGJf3t9Ln9g
6 changed files with 173 additions and 5 deletions

3
Cargo.lock generated
View file

@ -573,10 +573,12 @@ dependencies = [
"color-eyre",
"froxy-scraper",
"froxy-templates",
"indexmap",
"log",
"minijinja",
"rustc-hash",
"serde",
"serde_json",
"smol_str",
"tokio",
"tower-http",
@ -1774,6 +1776,7 @@ version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
dependencies = [
"indexmap",
"itoa",
"memchr",
"serde",

42
mapping.json Normal file
View file

@ -0,0 +1,42 @@
{
"f4": {
"rows": [
{ "range": "", "col": ["d", "x", "x", "x", "x", "x", "x", "x", "x","x", "x", "x", "x", "x", "x", "x", "d"] },
{ "range": "R14", "col": ["f4r14s1", "f4r14s2", "f4r14s3", "f4r14s4", "f4r14s5", "f4r14s6", "f4r14s7", "f4r14s8", "", "f4r14s9", "f4r14s10", "f4r14s11", "f4r14s12", "f4r14s13", "f4r14s14", "f4r14s15", "f4r14s16"] },
{ "range": "R13", "col": ["f4r13s1", "f4r13s2", "f4r13s3", "f4r13s4", "f4r13s5", "f4r13s6", "f4r13s7", "f4r13s8", "", "f4r13s9", "f4r13s10", "f4r13s11", "f4r13s12", "f4r13s13", "f4r13s14", "f4r13s15", "f4r13s16"] },
{ "range": "R12", "col": ["f4r12s1", "f4r12s2", "f4r12s3", "f4r12s4", "f4r12s5", "f4r12s6", "f4r12s7", "f4r12s8", "", "f4r12s9", "f4r12s10", "f4r12s11", "f4r12s12", "f4r12s13", "f4r12s14", "f4r12s15", "f4r12s16"] },
{ "range": "R11", "col": ["f4r11s1", "f4r11s2", "f4r11s3", "f4r11s4", "f4r11s5", "f4r11s6", "f4r11s7", "f4r11s8", "", "f4r11s9", "f4r11s10", "f4r11s11", "f4r11s12", "f4r11s13", "f4r11s14", "f4r11s15", "f4r11s16"] },
{ "range": "R10", "col": ["f4r10s1", "f4r10s2", "f4r10s3", "f4r10s4", "f4r10s5", "f4r10s6", "f4r10s7", "f4r10s8", "", "f4r10s9", "f4r10s10", "f4r10s11", "f4r10s12", "f4r10s13", "f4r10s14", "f4r10s15", "f4r10s16"] },
{ "range": "R9", "col": ["f", "f", "f", "f", "f", "f", "f", "f", "", "f", "f", "f", "f", "f", "f", "f", "f"] },
{ "range": "R8", "col": ["f", "f", "f", "f", "f", "f", "f", "f", "", "f", "f", "f", "f", "f", "f", "f", "f"] },
{ "range": "", "col": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""] },
{ "range": "R7", "col": ["f4r7s1", "f4r7s2", "f4r7s3", "f4r7s4", "f4r7s5", "f4r7s6", "f4r7s7", "f4r7s8", "", "f4r7s9", "f4r7s10", "f4r7s11", "f4r7s12", "f4r7s13", "f4r7s14", "f4r7s15", "f4r7s16"] },
{ "range": "R6", "col": ["f4r6s1", "f4r6s2", "f4r6s3", "f4r6s4", "f4r6s5", "f4r6s6", "f4r6s7", "f4r6s8", "", "f4r6s9", "f4r6s10", "f4r6s11", "f4r6s12", "f4r6s13", "f4r6s14", "f4r6s15", "f4r6s16"] },
{ "range": "R5", "col": ["f4r5s1", "f4r5s2", "f4r5s3", "f4r5s4", "f4r5s5", "f4r5s6", "f4r5s7", "f4r5s8", "", "f4r5s9", "f4r5s10", "f4r5s11", "f4r5s12", "f4r5s13", "f4r5s14", "f4r5s15", "f4r5s16"] },
{ "range": "R4", "col": ["f4r4s1", "f4r4s2", "f4r4s3", "f4r4s4", "f4r4s5", "f4r4s6", "f4r4s7", "f4r4s8", "", "f4r4s9", "f4r4s10", "f4r4s11", "f4r4s12", "f4r4s13", "f4r4s14", "f4r4s15", "f4r4s16"] },
{ "range": "R4", "col": ["f4r4s1", "f4r4s2", "f4r4s3", "f4r4s4", "f4r4s5", "f4r4s6", "f4r4s7", "f4r4s8", "", "f4r4s9", "f4r4s10", "f4r4s11", "f4r4s12", "f4r4s13", "f4r4s14", "f4r4s15", "f4r4s16"] },
{ "range": "R3", "col": ["f4r3s1", "f4r3s2", "f4r3s3", "f4r3s4", "f4r3s5", "f4r3s6", "f4r3s7", "f4r3s8", "", "f4r3s9", "f4r3s10", "f4r3s11", "f4r3s12", "f4r3s13", "f4r3s14", "f4r3s15", "f4r3s16"] },
{ "range": "R1", "col": ["f4r1s1", "f4r1s2", "f4r1s3", "f4r1s4", "f4r1s5", "f4r1s6", "f4r1s7", "f4r1s8", "", "f4r1s9", "f4r1s10", "f4r1s11", "f4r1s12", "f4r1s13", "f4r1s14", "f4r1s15", "f4r1s16"] },
{ "range": "", "col": ["d", "x", "x", "x", "x", "x", "x", "x", "x","x", "x", "x", "x", "x", "x", "x", "d"] }
]
},
"f6": {
"rows": [
{ "range": "R14", "col": ["f", "f", "f", "f", "f", "f", "f", "f", "", "f", "f", "f", "f", "f", "f", "f", "f"] },
{ "range": "R13", "col": ["f6r13s1", "f6r13s2", "f6r13s3", "f6r13s4", "f6r13s5", "f6r13s6", "f6r13s7", "f6r13s8", "", "f6r13s9", "f6r13s10", "f6r13s11", "f6r13s12", "f6r13s13", "f6r13s14", "f6r13s15", "f6r13s16"] },
{ "range": "R12", "col": ["f6r12s1", "f6r12s2", "f6r12s3", "f6r12s4", "f6r12s5", "f6r12s6", "f6r12s7", "f6r12s8", "", "f6r12s9", "f6r12s10", "f6r12s11", "f6r12s12", "f6r12s13", "f6r12s14", "f6r12s15", "f6r12s16"] },
{ "range": "R11", "col": ["f6r11s1", "f6r11s2", "f6r11s3", "f6r11s4", "f6r11s5", "f6r11s6", "f6r11s7", "f6r11s8", "", "f6r11s9", "f6r11s10", "f6r11s11", "f6r11s12", "f6r11s13", "f6r11s14", "f6r11s15", "f6r11s16"] },
{ "range": "R10", "col": ["f6r10s1", "f6r10s2", "f6r10s3", "f6r10s4", "f6r10s5", "f6r10s6", "f6r10s7", "f6r10s8", "", "f6r10s9", "f6r10s10", "f6r10s11", "f6r10s12", "f6r10s13", "f6r10s14", "f6r10s15", "f6r10s16"] },
{ "range": "R9", "col": ["f6r9s1", "f6r9s2", "f6r9s3", "f6r9s4", "f6r9s5", "f6r9s6", "f6r9s7", "f6r9s8", "", "f6r9s9", "f6r9s10", "f6r9s11", "f6r9s12", "f6r9s13", "f6r9s14", "f6r9s15", "f6r9s16"] },
{ "range": "R8", "col": ["f6r8s1", "f6r8s2", "f6r8s3", "f6r8s4", "f6r8s5", "f6r8s6", "f6r8s7", "f6r8s8", "", "f6r8s9", "f6r8s10", "f6r8s11", "f6r8s12", "f6r8s13", "f6r8s14", "f6r8s15", "f6r8s16"] },
{ "range": "", "col": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""] },
{ "range": "R7", "col": ["f6r7s1", "f6r7s2", "f6r7s3", "f6r7s4", "f6r7s5", "f6r7s6", "f6r7s7", "f6r7s8", "", "f6r7s9", "f6r7s10", "f6r7s11", "f6r7s12", "f6r7s13", "f6r7s14", "f6r7s15", "f6r7s16"] },
{ "range": "R6", "col": ["f6r6s1", "f6r6s2", "f6r6s3", "f6r6s4", "f6r6s5", "f6r6s6", "f6r6s7", "f6r6s8", "", "f6r6s9", "f6r6s10", "f6r6s11", "f6r6s12", "f6r6s13", "f6r6s14", "f6r6s15", "f6r6s16"] },
{ "range": "R5", "col": ["f6r5s1", "f6r5s2", "f6r5s3", "f6r5s4", "f6r5s5", "f6r5s6", "f6r5s7", "f6r5s8", "", "f6r5s9", "f6r5s10", "f6r5s11", "f6r5s12", "f6r5s13", "f6r5s14", "f6r5s15", "f6r5s16"] },
{ "range": "R4", "col": ["f6r4s1", "f6r4s2", "f6r4s3", "f6r4s4", "f6r4s5", "f6r4s6", "f6r4s7", "f6r4s8", "", "f6r4s9", "f6r4s10", "f6r4s11", "f6r4s12", "f6r4s13", "f6r4s14", "f6r4s15", "f6r4s16"] },
{ "range": "R3", "col": ["f6r3s1", "f6r3s2", "f6r3s3", "f6r3s4", "f6r3s5", "f6r3s6", "f6r3s7", "f6r3s8", "", "f6r3s9", "f6r3s10", "f6r3s11", "f6r3s12", "f6r3s13", "f6r3s14", "f6r3s15", "f6r3s16"] },
{ "range": "R2", "col": ["f6r2s1", "f6r2s2", "f6r2s3", "f6r2s4", "f6r2s5", "f6r2s6", "f6r2s7", "f6r2s8", "", "f6r2s9", "f6r2s10", "f6r2s11", "f6r2s12", "f6r2s13", "f6r2s14", "f6r2s15", "f6r2s16"] },
{ "range": "R1", "col": ["f", "f", "f", "f", "f", "f", "f", "f", "", "f", "f", "f", "f", "f", "f", "f", "f"] }
]
}
}

View file

@ -1,4 +1,4 @@
use froxy_scrapper::{state::State, types::ClusterLocation};
use froxy_scraper::{state::State, types::ClusterLocation};
#[tokio::main(flavor = "current_thread")]
async fn main() {
@ -10,12 +10,12 @@ async fn main() {
let text = res.text().await.unwrap();
let location = froxy_scrapper::parsing::get_cluster_location("F4", &text);
let location = froxy_scraper::parsing::get_cluster_location("F4", &text);
for l in &location.locations {
match &l.data {
froxy_scrapper::types::ClusterLocationData::Empty => {}
froxy_scrapper::types::ClusterLocationData::User {
froxy_scraper::types::ClusterLocationData::Empty => {}
froxy_scraper::types::ClusterLocationData::User {
login,
image,
relation,
@ -29,7 +29,7 @@ async fn main() {
.unwrap_or_else(|| "NO IMAGE".to_string())
)
}
froxy_scrapper::types::ClusterLocationData::Normal { status } => {
froxy_scraper::types::ClusterLocationData::Normal { status } => {
// eprintln!("[{:<10}] {status:?}", l.location)
}
}

View file

@ -21,3 +21,5 @@ froxy-scraper = { workspace = true }
color-eyre = "0.6.5"
cached = { version = "0.56.0", features = ["async", "async_tokio_rt_multi_thread"] }
rustc-hash = "2.1.1"
serde_json = { version = "1.0.149", features = ["preserve_order"] }
indexmap = { version = "2.13.0", features = ["serde"] }

119
server/src/cluster.rs Normal file
View file

@ -0,0 +1,119 @@
use std::collections::HashMap;
use axum::{
Router,
extract::{Query, State},
http::StatusCode,
response::Html,
};
use froxy_scraper::types::{ClusterLocationData, ClusterLocationStatus};
use froxy_templates::index::{ClusterData, LocationData, PcIssue};
use indexmap::IndexMap;
use log::trace;
use serde::{Deserialize, Serialize};
use smol_str::SmolStr;
pub fn router() -> Router<crate::GlobalState> {
Router::new().route("/", axum::routing::get(handle_cluster))
}
#[derive(Serialize, Deserialize, Debug, Clone)]
struct ClusterQuery {
cluster: Option<SmolStr>,
p: Option<SmolStr>,
}
#[cfg_attr(debug_assertions, axum::debug_handler)]
async fn handle_cluster(
State(state): State<crate::GlobalState>,
Query(ClusterQuery { cluster, p }): Query<ClusterQuery>,
) -> Result<Html<String>, StatusCode> {
let current_cluster = cluster.unwrap_or(SmolStr::new_static("f6"));
let map: HashMap<SmolStr, froxy_templates::index::Map> =
serde_json::from_str(include_str!("../../mapping.json")).unwrap();
let res = state.scraper.get("/", Some("cluster=f6")).await.unwrap();
let text = res.text().await.unwrap();
let location = froxy_scraper::parsing::get_cluster_location("f6", &text);
let issues: IndexMap<_, _> = location
.locations
.iter()
.filter(|s| matches!(s.data, ClusterLocationData::Normal { .. }))
.cloned()
.filter_map(|s| {
Some((
s.location.clone(),
match s.data {
ClusterLocationData::Normal {
status: ClusterLocationStatus::Dead,
} => PcIssue::Dead,
ClusterLocationData::Normal {
status: ClusterLocationStatus::Damaged,
} => PcIssue::Attention,
_ => return None,
},
))
})
.collect::<_>();
let loc: IndexMap<_, _> = location
.locations
.iter()
.filter(|s| matches!(s.data, ClusterLocationData::User { .. }))
.cloned()
.map(|s| {
let ClusterLocationData::User {
login,
image,
relation,
} = s.data
else {
unreachable!()
};
(
s.location.clone(),
LocationData {
login,
image: image.map(|u| u.to_string()).unwrap_or_default(),
me: matches!(relation, froxy_scraper::types::Relation::Me),
friend: matches!(relation, froxy_scraper::types::Relation::Friend),
close_friend: matches!(relation, froxy_scraper::types::Relation::CloseFriend),
pooled: matches!(relation, froxy_scraper::types::Relation::Pooled),
},
)
})
.collect::<_>();
trace!("location: {loc:?}");
state
.render_index(froxy_templates::index::IndexData {
locations: loc,
clusters: [(
SmolStr::new_static("f6"),
ClusterData {
users: 10,
max_pc: map[&SmolStr::new_static("f6")]
.rows
.iter()
.flat_map(|s| s.col.iter())
.filter(|s| matches!(s, froxy_templates::index::MapPos::Pc(_)))
.count() as u32,
silent: false,
piscine: false,
dead_pc: issues
.values()
.filter(|v| matches!(v, PcIssue::Dead))
.count() as u32,
map: map[&SmolStr::new_static("f6")].clone(),
issues,
},
)]
.into_iter()
.collect(),
current_cluster,
})
.map_err(crate::report_error)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
}

View file

@ -36,6 +36,7 @@ use log::info;
use tower_http::trace::TraceLayer;
use tracing_subscriber::EnvFilter;
mod cluster;
mod friends;
mod state;
mod static_;
@ -60,6 +61,7 @@ async fn main() {
let global_state = GlobalState::new();
let app = Router::new()
.merge(cluster::router())
.merge(friends::router())
.merge(user::router())
.merge(static_::router(&global_state))