vibes
This commit is contained in:
parent
80c093b7ba
commit
f72c43a9fa
101 changed files with 2168 additions and 1177 deletions
|
|
@ -1,3 +1,5 @@
|
|||
#![allow(clippy::min_ident_chars)]
|
||||
|
||||
mod aggregation;
|
||||
mod auth;
|
||||
mod consts;
|
||||
|
|
@ -17,11 +19,11 @@ 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;
|
||||
use clap::Parser;
|
||||
use consts::SERVICE_CALL_TIMEOUT;
|
||||
use tower::limit::ConcurrencyLimitLayer;
|
||||
use tower_http::compression::CompressionLayer;
|
||||
|
||||
|
|
@ -36,7 +38,10 @@ use tracing_subscriber::EnvFilter;
|
|||
use state::AppState;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "perfect-postcode", about = "Perfect Postcode property map server")]
|
||||
#[command(
|
||||
name = "perfect-postcode",
|
||||
about = "Perfect Postcode property map server"
|
||||
)]
|
||||
struct Cli {
|
||||
/// Path to properties.parquet (one row per historical property)
|
||||
#[arg(long)]
|
||||
|
|
@ -129,7 +134,6 @@ struct Cli {
|
|||
/// Google OAuth client secret for PocketBase SSO
|
||||
#[arg(long, env = "GOOGLE_OAUTH_CLIENT_SECRET")]
|
||||
google_oauth_client_secret: String,
|
||||
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
|
|
@ -137,8 +141,7 @@ async fn main() -> anyhow::Result<()> {
|
|||
let file_appender = tracing_appender::rolling::daily("logs", "server.log");
|
||||
let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
|
||||
|
||||
let env_filter =
|
||||
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
|
||||
let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
|
||||
|
||||
tracing_subscriber::registry()
|
||||
.with(env_filter)
|
||||
|
|
@ -332,10 +335,7 @@ async fn main() -> anyhow::Result<()> {
|
|||
info!("Gemini configured (model: {})", cli.gemini_model);
|
||||
let tt_path = &cli.travel_times;
|
||||
if !tt_path.exists() {
|
||||
bail!(
|
||||
"Travel times directory not found: {}",
|
||||
tt_path.display()
|
||||
);
|
||||
bail!("Travel times directory not found: {}", tt_path.display());
|
||||
}
|
||||
info!("Loading travel time data from {}", tt_path.display());
|
||||
let travel_time_store = {
|
||||
|
|
@ -476,7 +476,9 @@ async fn main() -> anyhow::Result<()> {
|
|||
)
|
||||
.route(
|
||||
"/api/travel-destinations",
|
||||
get(move |query| routes::get_travel_destinations(state_travel_destinations.clone(), query)),
|
||||
get(move |query| {
|
||||
routes::get_travel_destinations(state_travel_destinations.clone(), query)
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/api/journey",
|
||||
|
|
@ -490,24 +492,34 @@ async fn main() -> anyhow::Result<()> {
|
|||
)
|
||||
.route(
|
||||
"/api/hexagon-stats",
|
||||
get(move |ext, query| routes::get_hexagon_stats(state_hexagon_stats.clone(), ext, query)),
|
||||
get(move |ext, query| {
|
||||
routes::get_hexagon_stats(state_hexagon_stats.clone(), ext, query)
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/api/postcode-stats",
|
||||
get(move |ext, query| routes::get_postcode_stats(state_postcode_stats.clone(), ext, query)),
|
||||
get(move |ext, query| {
|
||||
routes::get_postcode_stats(state_postcode_stats.clone(), ext, query)
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/api/postcode-properties",
|
||||
get(move |ext, query| routes::get_postcode_properties(state_postcode_properties.clone(), ext, query)),
|
||||
get(move |ext, query| {
|
||||
routes::get_postcode_properties(state_postcode_properties.clone(), ext, query)
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/api/screenshot",
|
||||
get(move |headers, query| routes::get_screenshot(state_screenshot.clone(), headers, query)),
|
||||
get(move |headers, query| {
|
||||
routes::get_screenshot(state_screenshot.clone(), headers, query)
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/api/export",
|
||||
get(move |headers, ext, query| routes::get_export(state_export.clone(), headers, ext, query))
|
||||
.layer(ConcurrencyLimitLayer::new(3)),
|
||||
get(move |headers, ext, query| {
|
||||
routes::get_export(state_export.clone(), headers, ext, query)
|
||||
})
|
||||
.layer(ConcurrencyLimitLayer::new(3)),
|
||||
)
|
||||
.route("/api/me", get(routes::get_me))
|
||||
.route(
|
||||
|
|
@ -525,9 +537,7 @@ async fn main() -> anyhow::Result<()> {
|
|||
)
|
||||
.route(
|
||||
"/api/newsletter",
|
||||
patch(move |ext, body| {
|
||||
routes::patch_newsletter(state_newsletter.clone(), ext, body)
|
||||
}),
|
||||
patch(move |ext, body| routes::patch_newsletter(state_newsletter.clone(), ext, body)),
|
||||
)
|
||||
.route(
|
||||
"/api/pricing",
|
||||
|
|
@ -546,8 +556,9 @@ async fn main() -> anyhow::Result<()> {
|
|||
)
|
||||
.route(
|
||||
"/api/invites",
|
||||
get(move |ext| routes::get_invites(state_invites_list.clone(), ext))
|
||||
.post(move |ext, body| routes::post_invites(state_invites_create.clone(), ext, body)),
|
||||
get(move |ext| routes::get_invites(state_invites_list.clone(), ext)).post(
|
||||
move |ext, body| routes::post_invites(state_invites_create.clone(), ext, body),
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/api/invite/{code}",
|
||||
|
|
@ -591,35 +602,35 @@ async fn main() -> anyhow::Result<()> {
|
|||
);
|
||||
|
||||
let app = if let Some(ref dist) = cli.dist {
|
||||
api.fallback_service(
|
||||
ServeDir::new(dist).fallback(ServeFile::new(dist.join("index.html"))),
|
||||
)
|
||||
api.fallback_service(ServeDir::new(dist).fallback(ServeFile::new(dist.join("index.html"))))
|
||||
} else {
|
||||
api
|
||||
}
|
||||
.layer(middleware::from_fn(metrics::track_metrics))
|
||||
.layer(middleware::from_fn(auth::auth_middleware))
|
||||
.layer(middleware::from_fn(
|
||||
move |req: axum::extract::Request, next: middleware::Next| {
|
||||
let st = state_crawler.clone();
|
||||
async move {
|
||||
// Inject state into request extensions for auth + OG middleware
|
||||
let (mut parts, body) = req.into_parts();
|
||||
parts.extensions.insert(st);
|
||||
let req = axum::extract::Request::from_parts(parts, body);
|
||||
og_middleware::og_middleware(req, next).await
|
||||
}
|
||||
},
|
||||
))
|
||||
.layer(cors)
|
||||
.layer(CompressionLayer::new().zstd(true).gzip(true))
|
||||
.layer(TraceLayer::new_for_http());
|
||||
.layer(middleware::from_fn(metrics::track_metrics))
|
||||
.layer(middleware::from_fn(auth::auth_middleware))
|
||||
.layer(middleware::from_fn(
|
||||
move |req: axum::extract::Request, next: middleware::Next| {
|
||||
let st = state_crawler.clone();
|
||||
async move {
|
||||
// Inject state into request extensions for auth + OG middleware
|
||||
let (mut parts, body) = req.into_parts();
|
||||
parts.extensions.insert(st);
|
||||
let req = axum::extract::Request::from_parts(parts, body);
|
||||
og_middleware::og_middleware(req, next).await
|
||||
}
|
||||
},
|
||||
))
|
||||
.layer(cors)
|
||||
.layer(CompressionLayer::new().zstd(true).gzip(true))
|
||||
.layer(TraceLayer::new_for_http());
|
||||
|
||||
// Lock all current and future memory pages to prevent swapping
|
||||
unsafe {
|
||||
if libc::mlockall(libc::MCL_CURRENT | libc::MCL_FUTURE) != 0 {
|
||||
let err = std::io::Error::last_os_error();
|
||||
tracing::warn!("mlockall failed (need CAP_IPC_LOCK or sufficient RLIMIT_MEMLOCK): {err}");
|
||||
tracing::warn!(
|
||||
"mlockall failed (need CAP_IPC_LOCK or sufficient RLIMIT_MEMLOCK): {err}"
|
||||
);
|
||||
} else {
|
||||
info!("All memory pages locked (mlockall)");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue