Rewrite server in rust

This commit is contained in:
Andras Schmelczer 2026-01-31 10:18:54 +00:00
parent 0cea9b873c
commit bf2d5de156
13 changed files with 3875 additions and 547 deletions

109
server-rs/src/main.rs Normal file
View file

@ -0,0 +1,109 @@
mod consts;
mod data;
mod index;
mod routes;
use std::path::PathBuf;
use std::sync::Arc;
use axum::routing::get;
use axum::Router;
use tower_http::compression::CompressionLayer;
use tower_http::cors::{Any, CorsLayer};
use tower_http::services::ServeDir;
use routes::AppState;
#[tokio::main]
async fn main() {
let parquet_path = PathBuf::from(
std::env::args()
.nth(1)
.unwrap_or_else(|| "data_sources/processed/wide.parquet".to_string()),
);
if !parquet_path.exists() {
eprintln!("Error: {} not found.", parquet_path.display());
std::process::exit(1);
}
// Load property data and build indices
let property_data = data::PropertyData::load(&parquet_path);
let grid = index::GridIndex::build(&property_data.lat, &property_data.lon, 0.01);
let h3_cells = data::precompute_h3(&property_data.lat, &property_data.lon);
// Load POI data and build spatial index
// Derive POI path from the data parquet path (same directory)
let poi_path = parquet_path
.parent()
.and_then(|p| p.parent())
.map(|p| p.join("filtered_uk_pois.parquet"))
.unwrap_or_else(|| PathBuf::from("data_sources/filtered_uk_pois.parquet"));
let poi_data = if poi_path.exists() {
data::POIData::load(&poi_path)
} else {
eprintln!("Warning: {} not found. POI endpoints will be unavailable.", poi_path.display());
data::POIData {
id: Vec::new(),
name: Vec::new(),
category: Vec::new(),
lat: Vec::new(),
lng: Vec::new(),
emoji: Vec::new(),
}
};
let poi_grid = index::GridIndex::build(&poi_data.lat, &poi_data.lng, 0.01);
let state = Arc::new(AppState {
data: property_data,
grid,
h3_cells,
poi_data,
poi_grid,
});
let cors = CorsLayer::new()
.allow_origin(Any)
.allow_methods(Any)
.allow_headers(Any);
// API routes
let state_features = state.clone();
let state_hexagons = state.clone();
let state_pois = state.clone();
let state_poi_categories = state.clone();
let api = Router::new()
.route(
"/api/features",
get(move || routes::get_features(state_features.clone())),
)
.route(
"/api/hexagons",
get(move |query| routes::get_hexagons(state_hexagons.clone(), query)),
)
.route(
"/api/pois",
get(move |query| routes::get_pois(state_pois.clone(), query)),
)
.route(
"/api/poi-categories",
get(move || routes::get_poi_categories(state_poi_categories.clone())),
);
// Static file serving for frontend
let frontend_dist = PathBuf::from("frontend/dist");
let app = if frontend_dist.exists() {
api.fallback_service(ServeDir::new(frontend_dist))
} else {
api
};
let app = app.layer(cors).layer(CompressionLayer::new().gzip(true));
let addr = "0.0.0.0:8001";
eprintln!("Server listening on {}", addr);
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app).await.unwrap();
}