better transit times
This commit is contained in:
parent
974f005549
commit
205302dbb8
22 changed files with 247 additions and 69 deletions
|
|
@ -95,6 +95,9 @@ async fn validate_token(
|
|||
.ok()?;
|
||||
|
||||
if !res.status().is_success() {
|
||||
let status = res.status();
|
||||
let body = res.text().await.unwrap_or_default();
|
||||
warn!("PocketBase auth-refresh returned {status}: {body}");
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ pub const POSTCODE_SEARCH_OFFSET: f64 = 0.02;
|
|||
pub const AI_FILTERS_MAX_TOKENS: usize = 2000;
|
||||
pub const AI_FILTERS_TEMPERATURE: f32 = 0.0;
|
||||
|
||||
/// Timeout for outbound HTTP service calls (seconds).
|
||||
pub const SERVICE_CALL_TIMEOUT: u64 = 120;
|
||||
|
||||
/// Inner London free zone bounds (south, west, north, east) — roughly zones 1–2.
|
||||
/// Users without a license can only query data within these bounds.
|
||||
pub const FREE_ZONE_BOUNDS: (f64, f64, f64, f64) = (51.42, -0.34, 51.60, 0.14);
|
||||
|
|
@ -24,5 +27,5 @@ pub const FREE_ZONE_BOUNDS: (f64, f64, f64, f64) = (51.42, -0.34, 51.60, 0.14);
|
|||
/// Homepage demo center (lat, lng). Unlicensed hexagon requests are allowed
|
||||
/// when the center of the requested bounds is within DEMO_CENTER_TOLERANCE of this point.
|
||||
/// Must match DEMO_VIEW_START in ScrollStory.tsx.
|
||||
pub const DEMO_CENTER: (f64, f64) = (52.2, -1.9);
|
||||
pub const DEMO_CENTER: (f64, f64) = (51.51, -0.12);
|
||||
pub const DEMO_CENTER_TOLERANCE: f64 = 1.0;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use std::sync::Arc;
|
|||
use std::time::Duration;
|
||||
|
||||
use anyhow::{bail, Context};
|
||||
use consts::SERVICE_CALL_TIMEOUT;
|
||||
use axum::middleware;
|
||||
use axum::routing::{any, get, patch, post};
|
||||
use axum::Router;
|
||||
|
|
@ -286,7 +287,7 @@ async fn main() -> anyhow::Result<()> {
|
|||
};
|
||||
|
||||
let http_client = reqwest::Client::builder()
|
||||
.timeout(Duration::from_secs(30))
|
||||
.timeout(Duration::from_secs(SERVICE_CALL_TIMEOUT))
|
||||
.connect_timeout(Duration::from_secs(5))
|
||||
.build()
|
||||
.context("Failed to build HTTP client")?;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ pub struct ParsedEnumFilter {
|
|||
pub allowed: FxHashSet<u32>,
|
||||
}
|
||||
|
||||
/// Parse comma-separated filter string into numeric and enum filters.
|
||||
/// Parse `;;`-separated filter string into numeric and enum filters.
|
||||
/// Numeric format: `name:min:max`
|
||||
/// Enum format: `name:val1|val2|val3` (pipe-separated string values)
|
||||
///
|
||||
|
|
@ -35,7 +35,7 @@ pub fn parse_filters(
|
|||
None => return Ok((numeric, enums)),
|
||||
};
|
||||
|
||||
for entry in input.split(',') {
|
||||
for entry in input.split(";;") {
|
||||
let parts: Vec<&str> = entry.splitn(2, ':').collect();
|
||||
if parts.len() != 2 {
|
||||
return Err(format!("Malformed filter entry (missing ':'): '{entry}'"));
|
||||
|
|
@ -234,7 +234,7 @@ mod tests {
|
|||
#[test]
|
||||
fn parse_multiple_numeric_filters() {
|
||||
let (numeric, _enums) = parse_filters(
|
||||
Some("Price:100000:500000,Area:50:200"),
|
||||
Some("Price:100000:500000;;Area:50:200"),
|
||||
&extended_feature_map(),
|
||||
&extended_enum_values(),
|
||||
)
|
||||
|
|
@ -248,7 +248,7 @@ mod tests {
|
|||
#[test]
|
||||
fn parse_mixed_filters() {
|
||||
let (numeric, enums) = parse_filters(
|
||||
Some("Price:100000:500000,Type:Semi|Terraced"),
|
||||
Some("Price:100000:500000;;Type:Semi|Terraced"),
|
||||
&extended_feature_map(),
|
||||
&extended_enum_values(),
|
||||
)
|
||||
|
|
@ -288,7 +288,7 @@ mod tests {
|
|||
#[test]
|
||||
fn parse_filter_with_whitespace() {
|
||||
let (numeric, enums) = parse_filters(
|
||||
Some("Price : 100000 : 500000 , Type : Detached | Flats/Maisonettes"),
|
||||
Some("Price : 100000 : 500000 ;; Type : Detached | Flats/Maisonettes"),
|
||||
&extended_feature_map(),
|
||||
&extended_enum_values(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ fn extract_filter_feature_names(filters_str: Option<&str>) -> Vec<String> {
|
|||
None => return Vec::new(),
|
||||
};
|
||||
let mut names = Vec::new();
|
||||
for entry in input.split(',') {
|
||||
for entry in input.split(";;") {
|
||||
let parts: Vec<&str> = entry.splitn(2, ':').collect();
|
||||
if parts.len() == 2 {
|
||||
let name = parts[0].trim().to_string();
|
||||
|
|
@ -110,7 +110,7 @@ fn build_frontend_params(
|
|||
];
|
||||
if let Some(fs) = filters_str {
|
||||
if !fs.is_empty() {
|
||||
for entry in fs.split(',') {
|
||||
for entry in fs.split(";;") {
|
||||
if !entry.is_empty() {
|
||||
parts.push(format!("filter={}", urlencoding::encode(entry.trim())));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ pub struct HexagonsResponse {
|
|||
pub struct HexagonParams {
|
||||
resolution: u8,
|
||||
bounds: Option<String>,
|
||||
/// Comma-separated filters: `name:min:max,...`
|
||||
/// `;;`-separated filters: `name:min:max;;...`
|
||||
filters: Option<String>,
|
||||
/// Comma-separated feature names to include in min/max aggregation.
|
||||
fields: Option<String>,
|
||||
|
|
@ -191,7 +191,7 @@ pub async fn get_hexagons(
|
|||
require_bounds(params.bounds).map_err(IntoResponse::into_response)?;
|
||||
|
||||
// Allow the homepage demo: check if the center of the requested bounds
|
||||
// is near the demo view center (52.2, -1.9).
|
||||
// is near the demo view center (51.51, -0.12).
|
||||
let center_lat = (south + north) / 2.0;
|
||||
let center_lng = (west + east) / 2.0;
|
||||
let is_demo_view = (center_lat - DEMO_CENTER.0).abs() <= DEMO_CENTER_TOLERANCE
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ pub struct PostcodesResponse {
|
|||
#[derive(Deserialize)]
|
||||
pub struct PostcodeParams {
|
||||
bounds: Option<String>,
|
||||
/// Comma-separated filters: `name:min:max,...`
|
||||
/// `;;`-separated filters: `name:min:max;;...`
|
||||
filters: Option<String>,
|
||||
/// Comma-separated feature names to include in min/max aggregation.
|
||||
fields: Option<String>,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use crate::state::AppState;
|
|||
|
||||
/// Pricing tiers: (cumulative user cap, price in pence).
|
||||
const TIERS: &[(u64, u64)] = &[
|
||||
(10, 0), // First 10 users: free
|
||||
(1, 0), // First 10 users: free
|
||||
(20, 1000), // Next 10: £10
|
||||
(45, 2500), // Next 25: £25
|
||||
(95, 5000), // Next 50: £50
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue