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