global: fixes

This commit is contained in:
nym21
2026-04-27 12:52:02 +02:00
parent b24bfdc15c
commit 76869ed2b6
114 changed files with 6623 additions and 1981 deletions
+5 -1
View File
@@ -10,7 +10,7 @@ use brk_types::{AddrStats, AddrValidation, Transaction, Txid, Utxo, Version};
use crate::{
AppState, CacheStrategy,
extended::TransformResponseExtended,
params::{AddrParam, AddrTxidsParam, ValidateAddrParam},
params::{AddrParam, AddrTxidsParam, Empty, ValidateAddrParam},
};
pub trait AddrRoutes {
@@ -28,6 +28,7 @@ impl AddrRoutes for ApiRouter<AppState> {
uri: Uri,
headers: HeaderMap,
Path(path): Path<AddrParam>,
_: Empty,
State(state): State<AppState>
| {
let strategy = state.addr_cache(Version::ONE, &path.addr, false);
@@ -96,6 +97,7 @@ impl AddrRoutes for ApiRouter<AppState> {
uri: Uri,
headers: HeaderMap,
Path(path): Path<AddrParam>,
_: Empty,
State(state): State<AppState>
| {
let hash = state.sync(|q| q.addr_mempool_hash(&path.addr));
@@ -118,6 +120,7 @@ impl AddrRoutes for ApiRouter<AppState> {
uri: Uri,
headers: HeaderMap,
Path(path): Path<AddrParam>,
_: Empty,
State(state): State<AppState>
| {
let strategy = state.addr_cache(Version::ONE, &path.addr, false);
@@ -140,6 +143,7 @@ impl AddrRoutes for ApiRouter<AppState> {
uri: Uri,
headers: HeaderMap,
Path(path): Path<ValidateAddrParam>,
_: Empty,
State(state): State<AppState>
| {
state.cached_json(&headers, CacheStrategy::Deploy, &uri, move |_q| Ok(AddrValidation::from_addr(&path.addr))).await
+20 -18
View File
@@ -11,7 +11,9 @@ use brk_types::{
use crate::{
AppState, CacheStrategy,
extended::TransformResponseExtended,
params::{BlockHashParam, BlockHashStartIndex, BlockHashTxIndex, HeightParam, TimestampParam},
params::{
BlockHashParam, BlockHashStartIndex, BlockHashTxIndex, Empty, HeightParam, TimestampParam,
},
};
pub trait BlockRoutes {
@@ -26,7 +28,7 @@ impl BlockRoutes for ApiRouter<AppState> {
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<BlockHashParam>,
State(state): State<AppState>| {
_: Empty, State(state): State<AppState>| {
let strategy = state.block_cache(Version::ONE, &path.hash);
state.cached_json(&headers, strategy, &uri, move |q| q.block(&path.hash)).await
},
@@ -48,7 +50,7 @@ impl BlockRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/block/{hash}",
get_with(
async |uri: Uri, headers: HeaderMap, Path(path): Path<BlockHashParam>, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, Path(path): Path<BlockHashParam>, _: Empty, State(state): State<AppState>| {
let strategy = state.block_cache(Version::ONE, &path.hash);
state.cached_json(&headers, strategy, &uri, move |q| {
let height = q.height_by_hash(&path.hash)?;
@@ -71,7 +73,7 @@ impl BlockRoutes for ApiRouter<AppState> {
.api_route(
"/api/block/{hash}/header",
get_with(
async |uri: Uri, headers: HeaderMap, Path(path): Path<BlockHashParam>, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, Path(path): Path<BlockHashParam>, _: Empty, State(state): State<AppState>| {
let strategy = state.block_cache(Version::ONE, &path.hash);
state.cached_text(&headers, strategy, &uri, move |q| q.block_header_hex(&path.hash)).await
},
@@ -94,7 +96,7 @@ impl BlockRoutes for ApiRouter<AppState> {
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<HeightParam>,
State(state): State<AppState>| {
_: Empty, State(state): State<AppState>| {
state.cached_text(&headers, state.height_cache(Version::ONE, path.height), &uri, move |q| q.block_hash_by_height(path.height).map(|h| h.to_string())).await
},
|op| {
@@ -118,7 +120,7 @@ impl BlockRoutes for ApiRouter<AppState> {
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<TimestampParam>,
State(state): State<AppState>| {
_: Empty, State(state): State<AppState>| {
state.cached_json(&headers, state.timestamp_cache(Version::ONE, path.timestamp), &uri, move |q| q.block_by_timestamp(path.timestamp)).await
},
|op| {
@@ -140,7 +142,7 @@ impl BlockRoutes for ApiRouter<AppState> {
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<BlockHashParam>,
State(state): State<AppState>| {
_: Empty, State(state): State<AppState>| {
let strategy = state.block_cache(Version::ONE, &path.hash);
state.cached_bytes(&headers, strategy, &uri, move |q| q.block_raw(&path.hash)).await
},
@@ -165,7 +167,7 @@ impl BlockRoutes for ApiRouter<AppState> {
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<BlockHashParam>,
State(state): State<AppState>| {
_: Empty, State(state): State<AppState>| {
state.cached_json(&headers, state.block_status_cache(Version::ONE, &path.hash), &uri, move |q| q.block_status(&path.hash)).await
},
|op| {
@@ -186,7 +188,7 @@ impl BlockRoutes for ApiRouter<AppState> {
.api_route(
"/api/blocks/tip/height",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
state.cached_text(&headers, CacheStrategy::Tip, &uri, |q| Ok(q.indexed_height().to_string())).await
},
|op| {
@@ -203,7 +205,7 @@ impl BlockRoutes for ApiRouter<AppState> {
.api_route(
"/api/blocks/tip/hash",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
state.cached_text(&headers, CacheStrategy::Tip, &uri, |q| Ok(q.tip_blockhash().to_string())).await
},
|op| {
@@ -223,7 +225,7 @@ impl BlockRoutes for ApiRouter<AppState> {
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<BlockHashTxIndex>,
State(state): State<AppState>| {
_: Empty, State(state): State<AppState>| {
let strategy = state.block_cache(Version::ONE, &path.hash);
state.cached_text(&headers, strategy, &uri, move |q| q.block_txid_at_index(&path.hash, path.index).map(|t| t.to_string())).await
},
@@ -248,7 +250,7 @@ impl BlockRoutes for ApiRouter<AppState> {
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<BlockHashParam>,
State(state): State<AppState>| {
_: Empty, State(state): State<AppState>| {
let strategy = state.block_cache(Version::ONE, &path.hash);
state.cached_json(&headers, strategy, &uri, move |q| q.block_txids(&path.hash)).await
},
@@ -273,7 +275,7 @@ impl BlockRoutes for ApiRouter<AppState> {
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<BlockHashParam>,
State(state): State<AppState>| {
_: Empty, State(state): State<AppState>| {
let strategy = state.block_cache(Version::ONE, &path.hash);
state.cached_json(&headers, strategy, &uri, move |q| q.block_txs(&path.hash, TxIndex::default())).await
},
@@ -299,7 +301,7 @@ impl BlockRoutes for ApiRouter<AppState> {
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<BlockHashStartIndex>,
State(state): State<AppState>| {
_: Empty, State(state): State<AppState>| {
let strategy = state.block_cache(Version::ONE, &path.hash);
state.cached_json(&headers, strategy, &uri, move |q| q.block_txs(&path.hash, path.start_index)).await
},
@@ -322,7 +324,7 @@ impl BlockRoutes for ApiRouter<AppState> {
.api_route(
"/api/blocks",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
state
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.blocks(None))
.await
@@ -344,7 +346,7 @@ impl BlockRoutes for ApiRouter<AppState> {
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<HeightParam>,
State(state): State<AppState>| {
_: Empty, State(state): State<AppState>| {
state.cached_json(&headers, state.height_cache(Version::ONE, path.height), &uri, move |q| q.blocks(Some(path.height))).await
},
|op| {
@@ -364,7 +366,7 @@ impl BlockRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/blocks",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
state
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.blocks_v1(None))
.await
@@ -386,7 +388,7 @@ impl BlockRoutes for ApiRouter<AppState> {
async |uri: Uri,
headers: HeaderMap,
Path(path): Path<HeightParam>,
State(state): State<AppState>| {
_: Empty, State(state): State<AppState>| {
state.cached_json(&headers, state.height_cache(Version::ONE, path.height), &uri, move |q| q.blocks_v1(Some(path.height))).await
},
|op| {
+4 -4
View File
@@ -5,7 +5,7 @@ use axum::{
};
use brk_types::{MempoolBlock, RecommendedFees};
use crate::{AppState, extended::TransformResponseExtended};
use crate::{AppState, extended::TransformResponseExtended, params::Empty};
pub trait FeesRoutes {
fn add_fees_routes(self) -> Self;
@@ -16,7 +16,7 @@ impl FeesRoutes for ApiRouter<AppState> {
self.api_route(
"/api/v1/fees/mempool-blocks",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
state
.cached_json(&headers, state.mempool_cache(), &uri, |q| {
q.mempool_blocks()
@@ -37,7 +37,7 @@ impl FeesRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/fees/recommended",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
state
.cached_json(&headers, state.mempool_cache(), &uri, |q| {
q.recommended_fees()
@@ -58,7 +58,7 @@ impl FeesRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/fees/precise",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
state
.cached_json(&headers, state.mempool_cache(), &uri, |q| {
q.recommended_fees()
+5 -3
View File
@@ -6,7 +6,9 @@ use axum::{
use brk_types::{DifficultyAdjustment, HistoricalPrice, Prices, Timestamp, Version};
use crate::{
AppState, CacheStrategy, extended::TransformResponseExtended, params::OptionalTimestampParam,
AppState, CacheStrategy,
extended::TransformResponseExtended,
params::{Empty, OptionalTimestampParam},
};
pub trait GeneralRoutes {
@@ -18,7 +20,7 @@ impl GeneralRoutes for ApiRouter<AppState> {
self.api_route(
"/api/v1/difficulty-adjustment",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
state
.cached_json(&headers, CacheStrategy::Tip, &uri, |q| {
q.difficulty_adjustment()
@@ -39,7 +41,7 @@ impl GeneralRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/prices",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
state
.cached_json(&headers, state.mempool_cache(), &uri, |q| {
Ok(Prices {
+5 -5
View File
@@ -5,7 +5,7 @@ use axum::{
};
use brk_types::{Dollars, MempoolInfo, MempoolRecentTx, Txid};
use crate::{AppState, extended::TransformResponseExtended};
use crate::{AppState, extended::TransformResponseExtended, params::Empty};
pub trait MempoolRoutes {
fn add_mempool_routes(self) -> Self;
@@ -16,7 +16,7 @@ impl MempoolRoutes for ApiRouter<AppState> {
self.api_route(
"/api/mempool",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
state
.cached_json(&headers, state.mempool_cache(), &uri, |q| q.mempool_info())
.await
@@ -35,7 +35,7 @@ impl MempoolRoutes for ApiRouter<AppState> {
.api_route(
"/api/mempool/txids",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
state
.cached_json(&headers, state.mempool_cache(), &uri, |q| q.mempool_txids())
.await
@@ -54,7 +54,7 @@ impl MempoolRoutes for ApiRouter<AppState> {
.api_route(
"/api/mempool/recent",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
state
.cached_json(&headers, state.mempool_cache(), &uri, |q| q.mempool_recent())
.await
@@ -73,7 +73,7 @@ impl MempoolRoutes for ApiRouter<AppState> {
.api_route(
"/api/mempool/price",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
state
.cached_json(&headers, state.mempool_cache(), &uri, |q| q.live_price())
.await
+8 -2
View File
@@ -17,7 +17,7 @@ use brk_types::{
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::{CacheStrategy, Error, extended::TransformResponseExtended};
use crate::{CacheStrategy, Error, extended::TransformResponseExtended, params::Empty};
use super::AppState;
use super::series_legacy;
@@ -46,7 +46,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter<AppState> {
.api_route(
"/api/metrics",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| Ok(q.series_catalog().clone())).await
},
|op| op
@@ -68,6 +68,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter<AppState> {
async |
uri: Uri,
headers: HeaderMap,
_: Empty,
State(state): State<AppState>
| {
state.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| Ok(q.series_count())).await
@@ -91,6 +92,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter<AppState> {
async |
uri: Uri,
headers: HeaderMap,
_: Empty,
State(state): State<AppState>
| {
state.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| Ok(q.indexes().to_vec())).await
@@ -186,6 +188,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter<AppState> {
async |
uri: Uri,
headers: HeaderMap,
_: Empty,
State(state): State<AppState>,
Path(path): Path<LegacySeriesParam>
| {
@@ -273,6 +276,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter<AppState> {
get_with(
async |uri: Uri,
headers: HeaderMap,
_: Empty,
State(state): State<AppState>,
Path(path): Path<LegacySeriesWithIndex>| {
state
@@ -299,6 +303,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter<AppState> {
get_with(
async |uri: Uri,
headers: HeaderMap,
_: Empty,
State(state): State<AppState>,
Path(path): Path<LegacySeriesWithIndex>| {
state
@@ -325,6 +330,7 @@ impl ApiMetricsLegacyRoutes for ApiRouter<AppState> {
get_with(
async |uri: Uri,
headers: HeaderMap,
_: Empty,
State(state): State<AppState>,
Path(path): Path<LegacySeriesWithIndex>| {
state
+18 -18
View File
@@ -14,7 +14,7 @@ use brk_types::{
use crate::{
AppState, CacheStrategy,
extended::TransformResponseExtended,
params::{BlockCountParam, PoolSlugAndHeightParam, PoolSlugParam, TimePeriodParam},
params::{BlockCountParam, Empty, PoolSlugAndHeightParam, PoolSlugParam, TimePeriodParam},
};
pub trait MiningRoutes {
@@ -30,7 +30,7 @@ impl MiningRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/mining/pools",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
// Pool list is compiled-in, only changes on deploy
state.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| Ok(q.all_pools())).await
},
@@ -48,7 +48,7 @@ impl MiningRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/mining/pools/{time_period}",
get_with(
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, _: Empty, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.mining_pools(path.time_period)).await
},
|op| {
@@ -66,7 +66,7 @@ impl MiningRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/mining/pool/{slug}",
get_with(
async |uri: Uri, headers: HeaderMap, Path(path): Path<PoolSlugParam>, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, Path(path): Path<PoolSlugParam>, _: Empty, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.pool_detail(path.slug)).await
},
|op| {
@@ -84,7 +84,7 @@ impl MiningRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/mining/hashrate/pools",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Tip, &uri, |q| q.pools_hashrate(None)).await
},
|op| {
@@ -101,7 +101,7 @@ impl MiningRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/mining/hashrate/pools/{time_period}",
get_with(
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, _: Empty, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.pools_hashrate(Some(path.time_period))).await
},
|op| {
@@ -119,7 +119,7 @@ impl MiningRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/mining/pool/{slug}/hashrate",
get_with(
async |uri: Uri, headers: HeaderMap, Path(path): Path<PoolSlugParam>, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, Path(path): Path<PoolSlugParam>, _: Empty, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.pool_hashrate(path.slug)).await
},
|op| {
@@ -137,7 +137,7 @@ impl MiningRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/mining/pool/{slug}/blocks",
get_with(
async |uri: Uri, headers: HeaderMap, Path(path): Path<PoolSlugParam>, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, Path(path): Path<PoolSlugParam>, _: Empty, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.pool_blocks(path.slug, None)).await
},
|op| {
@@ -155,7 +155,7 @@ impl MiningRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/mining/pool/{slug}/blocks/{height}",
get_with(
async |uri: Uri, headers: HeaderMap, Path(PoolSlugAndHeightParam {slug, height}): Path<PoolSlugAndHeightParam>, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, Path(PoolSlugAndHeightParam {slug, height}): Path<PoolSlugAndHeightParam>, _: Empty, State(state): State<AppState>| {
state.cached_json(&headers, state.height_cache(Version::ONE, height), &uri, move |q| q.pool_blocks(slug, Some(height))).await
},
|op| {
@@ -173,7 +173,7 @@ impl MiningRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/mining/hashrate",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Tip, &uri, |q| q.hashrate(None)).await
},
|op| {
@@ -190,7 +190,7 @@ impl MiningRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/mining/hashrate/{time_period}",
get_with(
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, _: Empty, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.hashrate(Some(path.time_period))).await
},
|op| {
@@ -208,7 +208,7 @@ impl MiningRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/mining/difficulty-adjustments",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Tip, &uri, |q| q.difficulty_adjustments(None)).await
},
|op| {
@@ -225,7 +225,7 @@ impl MiningRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/mining/difficulty-adjustments/{time_period}",
get_with(
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, _: Empty, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.difficulty_adjustments(Some(path.time_period))).await
},
|op| {
@@ -243,7 +243,7 @@ impl MiningRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/mining/reward-stats/{block_count}",
get_with(
async |uri: Uri, headers: HeaderMap, Path(path): Path<BlockCountParam>, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, Path(path): Path<BlockCountParam>, _: Empty, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.reward_stats(path.block_count)).await
},
|op| {
@@ -261,7 +261,7 @@ impl MiningRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/mining/blocks/fees/{time_period}",
get_with(
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, _: Empty, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.block_fees(path.time_period)).await
},
|op| {
@@ -279,7 +279,7 @@ impl MiningRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/mining/blocks/rewards/{time_period}",
get_with(
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, _: Empty, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.block_rewards(path.time_period)).await
},
|op| {
@@ -297,7 +297,7 @@ impl MiningRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/mining/blocks/fee-rates/{time_period}",
get_with(
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, _: Empty, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.block_fee_rates(path.time_period)).await
},
|op| {
@@ -315,7 +315,7 @@ impl MiningRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/mining/blocks/sizes-weights/{time_period}",
get_with(
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, Path(path): Path<TimePeriodParam>, _: Empty, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.block_sizes_weights(path.time_period)).await
},
|op| {
+9 -5
View File
@@ -25,7 +25,7 @@ use brk_types::{
use crate::{
AppState, CacheParams, CacheStrategy, Result,
extended::{HeaderMapExtended, TransformResponseExtended},
params::SeriesParam,
params::{Empty, SeriesParam},
};
/// Shared response pipeline for every series endpoint.
@@ -42,9 +42,7 @@ pub(super) async fn serve(
to_bytes: impl FnOnce(&BrkQuery, ResolvedQuery) -> BrkResult<Bytes> + Send + 'static,
) -> Result<Response> {
let max_weight = state.max_weight_for(&addr);
let resolved = state
.run(move |q| q.resolve(params, max_weight))
.await?;
let resolved = state.run(move |q| q.resolve(params, max_weight)).await?;
let format = resolved.format();
let csv_filename = resolved.csv_filename();
@@ -114,7 +112,7 @@ impl ApiSeriesRoutes for ApiRouter<AppState> {
self.api_route(
"/api/series",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
state.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| Ok(q.series_catalog().clone())).await
},
|op| op
@@ -135,6 +133,7 @@ impl ApiSeriesRoutes for ApiRouter<AppState> {
async |
uri: Uri,
headers: HeaderMap,
_: Empty,
State(state): State<AppState>
| {
state.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| Ok(q.series_count())).await
@@ -154,6 +153,7 @@ impl ApiSeriesRoutes for ApiRouter<AppState> {
async |
uri: Uri,
headers: HeaderMap,
_: Empty,
State(state): State<AppState>
| {
state.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| Ok(q.indexes().to_vec())).await
@@ -216,6 +216,7 @@ impl ApiSeriesRoutes for ApiRouter<AppState> {
async |
uri: Uri,
headers: HeaderMap,
_: Empty,
State(state): State<AppState>,
Path(path): Path<SeriesParam>
| {
@@ -309,6 +310,7 @@ impl ApiSeriesRoutes for ApiRouter<AppState> {
get_with(
async |uri: Uri,
headers: HeaderMap,
_: Empty,
State(state): State<AppState>,
Path(path): Path<SeriesNameWithIndex>| {
state
@@ -334,6 +336,7 @@ impl ApiSeriesRoutes for ApiRouter<AppState> {
get_with(
async |uri: Uri,
headers: HeaderMap,
_: Empty,
State(state): State<AppState>,
Path(path): Path<SeriesNameWithIndex>| {
state
@@ -357,6 +360,7 @@ impl ApiSeriesRoutes for ApiRouter<AppState> {
get_with(
async |uri: Uri,
headers: HeaderMap,
_: Empty,
State(state): State<AppState>,
Path(path): Path<SeriesNameWithIndex>| {
state
+5 -2
View File
@@ -29,6 +29,7 @@ use vecdb::ReadableOptionVec;
use crate::{
AppState, CacheStrategy, Result,
extended::{HeaderMapExtended, TransformResponseExtended},
params::Empty,
};
pub const SUNSET: &str = "2027-01-01T00:00:00Z";
@@ -43,7 +44,8 @@ pub async fn handler(
Query(params): Query<SeriesSelection>,
State(state): State<AppState>,
) -> Result<Response> {
let mut response = super::series::serve(state, uri, headers, addr, params, legacy_bytes).await?;
let mut response =
super::series::serve(state, uri, headers, addr, params, legacy_bytes).await?;
if response.status() == StatusCode::OK {
response.headers_mut().insert_deprecation(SUNSET);
}
@@ -151,7 +153,7 @@ impl ApiSeriesLegacyRoutes for ApiRouter<AppState> {
self.api_route(
"/api/series/cost-basis",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
state
.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| q.urpd_cohorts())
.await
@@ -177,6 +179,7 @@ impl ApiSeriesLegacyRoutes for ApiRouter<AppState> {
async |uri: Uri,
headers: HeaderMap,
Path(params): Path<CostBasisCohortParam>,
_: Empty,
State(state): State<AppState>| {
state
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| {
+5 -5
View File
@@ -7,7 +7,7 @@ use axum::{
};
use brk_types::{DiskUsage, Health, SyncStatus};
use crate::{CacheStrategy, VERSION, extended::TransformResponseExtended};
use crate::{CacheStrategy, VERSION, extended::TransformResponseExtended, params::Empty};
use super::AppState;
@@ -20,7 +20,7 @@ impl ServerRoutes for ApiRouter<AppState> {
self.api_route(
"/health",
get_with(
async |State(state): State<AppState>| -> axum::Json<Health> {
async |_: Empty, State(state): State<AppState>| -> axum::Json<Health> {
let uptime = state.started_instant.elapsed();
let started_at = state.started_at.to_string();
let sync = state
@@ -55,7 +55,7 @@ impl ServerRoutes for ApiRouter<AppState> {
.api_route(
"/version",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
state
.cached_json(&headers, CacheStrategy::Deploy, &uri, |_| {
Ok(env!("CARGO_PKG_VERSION"))
@@ -75,7 +75,7 @@ impl ServerRoutes for ApiRouter<AppState> {
.api_route(
"/api/server/sync",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
state
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| {
let tip_height = q.client().get_last_height()?;
@@ -99,7 +99,7 @@ impl ServerRoutes for ApiRouter<AppState> {
.api_route(
"/api/server/disk",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
let brk_path = state.data_path.clone();
state
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| {
+20 -13
View File
@@ -5,15 +5,16 @@ use aide::axum::{
use axum::{
extract::{Path, State},
http::{HeaderMap, Uri},
response::Response,
};
use brk_types::{
CpfpInfo, MerkleProof, RbfResponse, Transaction, TxOutspend, TxStatus, Txid, Version,
};
use crate::{
AppState, CacheStrategy,
AppState, CacheStrategy, Error, Result,
extended::TransformResponseExtended,
params::{TxIndexParam, TxidParam, TxidVout, TxidsParam},
params::{Empty, TxIndexParam, TxidParam, TxidVout, TxidsParam},
};
pub trait TxRoutes {
@@ -26,7 +27,7 @@ impl TxRoutes for ApiRouter<AppState> {
.api_route(
"/api/tx-index/{index}",
get_with(
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxIndexParam>, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxIndexParam>, _: Empty, State(state): State<AppState>| {
state.cached_text(&headers, CacheStrategy::Immutable(Version::ONE), &uri, move |q| q.txid_by_index(param.index).map(|t| t.to_string())).await
},
|op| op
@@ -44,7 +45,7 @@ impl TxRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/cpfp/{txid}",
get_with(
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxidParam>, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxidParam>, _: Empty, State(state): State<AppState>| {
state.cached_json(&headers, state.tx_cache(Version::ONE, &param.txid), &uri, move |q| q.cpfp(&param.txid)).await
},
|op| op
@@ -62,7 +63,7 @@ impl TxRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/tx/{txid}/rbf",
get_with(
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxidParam>, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxidParam>, _: Empty, State(state): State<AppState>| {
state.cached_json(&headers, state.mempool_cache(), &uri, move |q| q.tx_rbf(&param.txid)).await
},
|op| op
@@ -84,6 +85,7 @@ impl TxRoutes for ApiRouter<AppState> {
uri: Uri,
headers: HeaderMap,
Path(param): Path<TxidParam>,
_: Empty,
State(state): State<AppState>
| {
state.cached_json(&headers, state.tx_cache(Version::ONE, &param.txid), &uri, move |q| q.transaction(&param.txid)).await
@@ -109,6 +111,7 @@ impl TxRoutes for ApiRouter<AppState> {
uri: Uri,
headers: HeaderMap,
Path(param): Path<TxidParam>,
_: Empty,
State(state): State<AppState>
| {
state.cached_text(&headers, state.tx_cache(Version::ONE, &param.txid), &uri, move |q| q.transaction_hex(&param.txid)).await
@@ -130,7 +133,7 @@ impl TxRoutes for ApiRouter<AppState> {
.api_route(
"/api/tx/{txid}/merkleblock-proof",
get_with(
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxidParam>, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxidParam>, _: Empty, State(state): State<AppState>| {
state.cached_text(&headers, state.tx_cache(Version::ONE, &param.txid), &uri, move |q| q.merkleblock_proof(&param.txid)).await
},
|op| op
@@ -148,7 +151,7 @@ impl TxRoutes for ApiRouter<AppState> {
.api_route(
"/api/tx/{txid}/merkle-proof",
get_with(
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxidParam>, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxidParam>, _: Empty, State(state): State<AppState>| {
state.cached_json(&headers, state.tx_cache(Version::ONE, &param.txid), &uri, move |q| q.merkle_proof(&param.txid)).await
},
|op| op
@@ -170,6 +173,7 @@ impl TxRoutes for ApiRouter<AppState> {
uri: Uri,
headers: HeaderMap,
Path(path): Path<TxidVout>,
_: Empty,
State(state): State<AppState>
| {
let v = Version::ONE;
@@ -204,6 +208,7 @@ impl TxRoutes for ApiRouter<AppState> {
uri: Uri,
headers: HeaderMap,
Path(param): Path<TxidParam>,
_: Empty,
State(state): State<AppState>
| {
let v = Version::ONE;
@@ -232,7 +237,7 @@ impl TxRoutes for ApiRouter<AppState> {
.api_route(
"/api/tx/{txid}/raw",
get_with(
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxidParam>, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, Path(param): Path<TxidParam>, _: Empty, State(state): State<AppState>| {
state.cached_bytes(&headers, state.tx_cache(Version::ONE, &param.txid), &uri, move |q| q.transaction_raw(&param.txid)).await
},
|op| op
@@ -254,6 +259,7 @@ impl TxRoutes for ApiRouter<AppState> {
uri: Uri,
headers: HeaderMap,
Path(param): Path<TxidParam>,
_: Empty,
State(state): State<AppState>
| {
state.cached_json(&headers, state.tx_cache(Version::ONE, &param.txid), &uri, move |q| q.transaction_status(&param.txid)).await
@@ -275,9 +281,10 @@ impl TxRoutes for ApiRouter<AppState> {
.api_route(
"/api/v1/transaction-times",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
let params = TxidsParam::from_query(uri.query().unwrap_or(""));
state.cached_json(&headers, state.mempool_cache(), &uri, move |q| q.transaction_times(&params.txids)).await
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| -> Result<Response> {
let params = TxidsParam::from_query(uri.query().unwrap_or(""))
.map_err(Error::bad_request)?;
Ok(state.cached_json(&headers, state.mempool_cache(), &uri, move |q| q.transaction_times(&params.txids)).await)
},
|op| op
.id("get_transaction_times")
@@ -292,12 +299,12 @@ impl TxRoutes for ApiRouter<AppState> {
.api_route(
"/api/tx",
post_with(
async |State(state): State<AppState>, body: String| {
async |_: Empty, State(state): State<AppState>, body: String| {
let hex = body.trim().to_string();
state.run(move |q| q.broadcast_transaction(&hex))
.await
.map(|txid| txid.to_string())
.map_err(crate::Error::from)
.map_err(Error::from)
},
|op| {
op.id("post_tx")
+3 -2
View File
@@ -8,7 +8,7 @@ use brk_types::{Cohort, Date, Urpd, Version};
use crate::{
CacheStrategy,
extended::TransformResponseExtended,
params::{UrpdCohortParam, UrpdParams, UrpdQuery},
params::{Empty, UrpdCohortParam, UrpdParams, UrpdQuery},
};
use super::AppState;
@@ -22,7 +22,7 @@ impl ApiUrpdRoutes for ApiRouter<AppState> {
self.api_route(
"/api/urpd",
get_with(
async |uri: Uri, headers: HeaderMap, State(state): State<AppState>| {
async |uri: Uri, headers: HeaderMap, _: Empty, State(state): State<AppState>| {
state
.cached_json(&headers, CacheStrategy::Deploy, &uri, |q| q.urpd_cohorts())
.await
@@ -47,6 +47,7 @@ impl ApiUrpdRoutes for ApiRouter<AppState> {
async |uri: Uri,
headers: HeaderMap,
Path(params): Path<UrpdCohortParam>,
_: Empty,
State(state): State<AppState>| {
state
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| {
+1 -5
View File
@@ -52,9 +52,5 @@ pub(crate) fn init(mode: CdnCacheMode) {
/// Cached-tier directive for stable responses. Defaults to `Live` if [`init`]
/// was never called (tests, library use without a `Server`).
pub(super) fn cdn_cached() -> &'static str {
CDN_CACHE_MODE
.get()
.copied()
.unwrap_or_default()
.as_str()
CDN_CACHE_MODE.get().copied().unwrap_or_default().as_str()
}
+27
View File
@@ -0,0 +1,27 @@
use aide::OperationInput;
use axum::{extract::FromRequestParts, http::request::Parts};
use crate::Error;
/// Extractor that rejects requests carrying any query string.
/// Used on path-only endpoints to prevent cache-busting via injected
/// query params (the cache key includes the URI).
pub struct Empty;
impl<S> FromRequestParts<S> for Empty
where
S: Send + Sync,
{
type Rejection = Error;
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
match parts.uri.query() {
Some(q) if !q.is_empty() => Err(Error::bad_request(format!(
"this endpoint does not accept query parameters (got `?{q}`)"
))),
_ => Ok(Empty),
}
}
}
impl OperationInput for Empty {}
+2
View File
@@ -4,6 +4,7 @@ mod block_count_param;
mod blockhash_param;
mod blockhash_start_index;
mod blockhash_tx_index;
mod empty;
mod height_param;
mod pool_slug_param;
mod series_param;
@@ -22,6 +23,7 @@ pub use block_count_param::*;
pub use blockhash_param::*;
pub use blockhash_start_index::*;
pub use blockhash_tx_index::*;
pub use empty::*;
pub use height_param::*;
pub use pool_slug_param::*;
pub use series_param::*;
+19 -13
View File
@@ -13,19 +13,25 @@ pub struct TxidsParam {
impl TxidsParam {
/// Parsed manually from URI since serde_urlencoded doesn't support repeated keys.
pub fn from_query(query: &str) -> Self {
Self {
txids: query
.split('&')
.filter_map(|pair| {
let (key, val) = pair.split_once('=')?;
if key == "txId[]" || key == "txId%5B%5D" {
Txid::from_str(val).ok()
} else {
None
}
})
.collect(),
/// Rejects unknown keys to prevent cache-busting via injected query params.
pub fn from_query(query: &str) -> Result<Self, String> {
if query.is_empty() {
return Ok(Self { txids: Vec::new() });
}
let mut txids = Vec::new();
for pair in query.split('&') {
let (key, val) = pair.split_once('=').ok_or_else(|| {
format!("malformed query parameter `{pair}`, expected `txId[]=<txid>`")
})?;
if key == "txId[]" || key == "txId%5B%5D" {
let txid = Txid::from_str(val).map_err(|e| format!("invalid txid `{val}`: {e}"))?;
txids.push(txid);
} else {
return Err(format!(
"unknown query parameter `{key}`, expected `txId[]`"
));
}
}
Ok(Self { txids })
}
}
+7 -5
View File
@@ -197,10 +197,9 @@ impl AppState {
let encoding = ContentEncoding::negotiate(headers);
let cache_key = format!("{}-{}-{}", uri, params.etag, encoding.as_str());
let result = self
.get_or_insert(
&cache_key,
async move { self.run(move |q| f(q, encoding)).await },
)
.get_or_insert(&cache_key, async move {
self.run(move |q| f(q, encoding)).await
})
.await;
match result {
@@ -296,7 +295,10 @@ impl AppState {
uri,
params,
|h| {
h.insert(header::CONTENT_TYPE, HeaderValue::from_static("application/json"));
h.insert(
header::CONTENT_TYPE,
HeaderValue::from_static("application/json"),
);
},
move |_q, enc| {
let value = value_result?;