1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3use axum::http::StatusCode;
4use axum::response::IntoResponse;
5use axum::routing::{head, put};
6use axum::{Router, routing::get};
7use lru::LruCache;
8use nix_compat::nix_http;
9use parking_lot::RwLock;
10use snix_castore::Node;
11use snix_castore::blobservice::BlobService;
12use snix_castore::directoryservice::DirectoryService;
13use snix_store::pathinfoservice::PathInfoService;
14use std::num::NonZeroUsize;
15use std::sync::Arc;
16
17mod nar;
18mod outhash;
19
20#[derive(Clone)]
21pub struct AppState {
22 blob_service: Arc<dyn BlobService>,
23 directory_service: Arc<dyn DirectoryService>,
24 path_info_service: Arc<dyn PathInfoService>,
25
26 root_nodes: Arc<RwLock<LruCache<[u8; 32], Node>>>,
29}
30
31impl AppState {
32 pub fn new(
33 blob_service: Arc<dyn BlobService>,
34 directory_service: Arc<dyn DirectoryService>,
35 path_info_service: Arc<dyn PathInfoService>,
36 root_nodes_cache_capacity: NonZeroUsize,
37 ) -> Self {
38 Self {
39 blob_service,
40 directory_service,
41 path_info_service,
42 root_nodes: Arc::new(RwLock::new(LruCache::new(root_nodes_cache_capacity))),
43 }
44 }
45}
46
47pub fn gen_router(priority: u64) -> Router<AppState> {
48 #[cfg(feature = "otlp")]
49 let metrics_meter = opentelemetry::global::meter("nar-bridge");
50 #[cfg(feature = "otlp")]
51 let metrics_layer = opentelemetry_instrumentation_tower::HTTPMetricsLayerBuilder::builder()
52 .with_meter(metrics_meter)
53 .build()
54 .unwrap();
55
56 let router = Router::new()
57 .route("/", get(root))
58 .route("/nar/{nar_str}", get(StatusCode::NOT_FOUND))
59 .route("/nar/{nar_str}", head(nar::head_root_nodes))
60 .route("/nar/{nar_str}", put(nar::put))
61 .route("/nar/snix-castore/{root_node_enc}", get(nar::get_head))
62 .route("/nar/snix-castore/{root_node_enc}", head(nar::get_head))
63 .route("/{outhash_str}", get(outhash::get))
64 .route("/{outhash_str}", head(outhash::head))
65 .route("/{outhash_str}", put(outhash::put))
66 .route("/nix-cache-info", get(move || nix_cache_info(priority)));
67
68 let router = router.layer(tower_http::compression::CompressionLayer::new());
69
70 #[cfg(feature = "otlp")]
71 return router.layer(metrics_layer);
72 #[cfg(not(feature = "otlp"))]
73 return router;
74}
75
76async fn root() -> &'static str {
77 "Hello from nar-bridge"
78}
79
80async fn nix_cache_info(priority: u64) -> impl IntoResponse {
81 (
82 [("Content-Type", nix_http::MIME_TYPE_CACHE_INFO)],
83 format!("StoreDir: /nix/store\nWantMassQuery: 1\nPriority: {priority}\n"),
84 )
85}