semantic bufo search
find-bufo.com
bufo
1mod config;
2mod embedding;
3mod search;
4mod turbopuffer;
5
6use actix_cors::Cors;
7use actix_files as fs;
8use actix_governor::{Governor, GovernorConfigBuilder};
9use actix_web::{middleware, web, App, HttpResponse, HttpServer};
10use anyhow::Result;
11use config::Config;
12use opentelemetry_instrumentation_actix_web::{RequestMetrics, RequestTracing};
13use tracing::level_filters::LevelFilter;
14
15async fn index() -> HttpResponse {
16 HttpResponse::Ok()
17 .content_type("text/html; charset=utf-8")
18 .body(include_str!("../static/index.html"))
19}
20
21#[actix_web::main]
22async fn main() -> Result<()> {
23 dotenv::dotenv().ok();
24
25 // initialize logfire with info level filter to exclude trace/debug spans
26 let logfire = logfire::configure()
27 .with_default_level_filter(LevelFilter::INFO)
28 .finish()
29 .map_err(|e| anyhow::anyhow!("failed to initialize logfire: {}", e))?;
30
31 let _guard = logfire.shutdown_guard();
32
33 let config = Config::from_env()?;
34 let host = config.host.clone();
35 let port = config.port;
36
37 logfire::info!("starting bufo search server",
38 host = &host,
39 port = port as i64
40 );
41
42 // rate limiter: 10 requests per minute per IP
43 let governor_conf = GovernorConfigBuilder::default()
44 .milliseconds_per_request(6000) // 1 request per 6 seconds = 10 per minute
45 .burst_size(10)
46 .finish()
47 .unwrap();
48
49 HttpServer::new(move || {
50 let cors = Cors::permissive();
51
52 App::new()
53 // opentelemetry tracing and metrics FIRST
54 .wrap(RequestTracing::new())
55 .wrap(RequestMetrics::default())
56 // existing middleware
57 .wrap(middleware::Logger::default())
58 .wrap(cors)
59 .app_data(web::Data::new(config.clone()))
60 .route("/", web::get().to(index))
61 .service(
62 web::scope("/api")
63 .wrap(Governor::new(&governor_conf))
64 .route("/search", web::post().to(search::search))
65 .route("/search", web::get().to(search::search_get))
66 .route("/health", web::get().to(|| async { HttpResponse::Ok().body("ok") }))
67 )
68 .service(fs::Files::new("/static", "./static").show_files_listing())
69 })
70 .bind((host.as_str(), port))?
71 .run()
72 .await?;
73
74 Ok(())
75}