mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-17 14:08:10 -07:00
server: snapshot
This commit is contained in:
@@ -27,11 +27,11 @@ pub fn create_openapi() -> OpenApi {
|
||||
|
||||
let tags = vec![
|
||||
Tag {
|
||||
name: "Addresses".to_string(),
|
||||
name: "Metrics".to_string(),
|
||||
description: Some(
|
||||
"Query Bitcoin address data including balances, transaction history, and UTXOs. \
|
||||
Supports all address types: P2PKH, P2SH, P2WPKH, P2WSH, and P2TR."
|
||||
.to_string()
|
||||
"Access Bitcoin network metrics and time-series data. Query historical statistics \
|
||||
across various indexes with JSON or CSV output."
|
||||
.to_string(),
|
||||
),
|
||||
..Default::default()
|
||||
},
|
||||
@@ -40,42 +40,7 @@ pub fn create_openapi() -> OpenApi {
|
||||
description: Some(
|
||||
"Retrieve block data by hash or height. Access block headers, transaction lists, \
|
||||
and raw block bytes."
|
||||
.to_string()
|
||||
),
|
||||
..Default::default()
|
||||
},
|
||||
Tag {
|
||||
name: "Mempool".to_string(),
|
||||
description: Some(
|
||||
"Monitor unconfirmed transactions and fee estimates. Get mempool statistics, \
|
||||
transaction IDs, and recommended fee rates for different confirmation targets."
|
||||
.to_string()
|
||||
),
|
||||
..Default::default()
|
||||
},
|
||||
Tag {
|
||||
name: "Metrics".to_string(),
|
||||
description: Some(
|
||||
"Access Bitcoin network metrics and time-series data. Query historical statistics \
|
||||
across various indexes (date, week, month, year, halving epoch) with JSON or CSV output."
|
||||
.to_string()
|
||||
),
|
||||
..Default::default()
|
||||
},
|
||||
Tag {
|
||||
name: "Mining".to_string(),
|
||||
description: Some(
|
||||
"Mining statistics including pool distribution, hashrate, difficulty adjustments, \
|
||||
block rewards, and fee rates across configurable time periods."
|
||||
.to_string()
|
||||
),
|
||||
..Default::default()
|
||||
},
|
||||
Tag {
|
||||
name: "Server".to_string(),
|
||||
description: Some(
|
||||
"API metadata and health monitoring. Version information and service status."
|
||||
.to_string()
|
||||
.to_string(),
|
||||
),
|
||||
..Default::default()
|
||||
},
|
||||
@@ -84,7 +49,42 @@ pub fn create_openapi() -> OpenApi {
|
||||
description: Some(
|
||||
"Retrieve transaction data by txid. Access full transaction details, confirmation \
|
||||
status, raw hex, and output spend information."
|
||||
.to_string()
|
||||
.to_string(),
|
||||
),
|
||||
..Default::default()
|
||||
},
|
||||
Tag {
|
||||
name: "Addresses".to_string(),
|
||||
description: Some(
|
||||
"Query Bitcoin address data including balances, transaction history, and UTXOs. \
|
||||
Supports all address types: P2PKH, P2SH, P2WPKH, P2WSH, and P2TR."
|
||||
.to_string(),
|
||||
),
|
||||
..Default::default()
|
||||
},
|
||||
Tag {
|
||||
name: "Mempool".to_string(),
|
||||
description: Some(
|
||||
"Monitor unconfirmed transactions and fee estimates. Get mempool statistics, \
|
||||
transaction IDs, and recommended fee rates for different confirmation targets."
|
||||
.to_string(),
|
||||
),
|
||||
..Default::default()
|
||||
},
|
||||
Tag {
|
||||
name: "Mining".to_string(),
|
||||
description: Some(
|
||||
"Mining statistics including pool distribution, hashrate, difficulty adjustments, \
|
||||
block rewards, and fee rates across configurable time periods."
|
||||
.to_string(),
|
||||
),
|
||||
..Default::default()
|
||||
},
|
||||
Tag {
|
||||
name: "Server".to_string(),
|
||||
description: Some(
|
||||
"API metadata and health monitoring. Version information and service status."
|
||||
.to_string(),
|
||||
),
|
||||
..Default::default()
|
||||
},
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![doc = "\n## Example\n\n```rust"]
|
||||
#![doc = include_str!("../examples/server.rs")]
|
||||
#![doc = "```"]
|
||||
|
||||
use std::{ops::Deref, path::PathBuf, sync::Arc, time::Duration};
|
||||
use std::{path::PathBuf, sync::Arc, time::Duration};
|
||||
|
||||
use aide::axum::ApiRouter;
|
||||
use api::ApiRoutes;
|
||||
use axum::{
|
||||
Extension,
|
||||
body::{Body, Bytes},
|
||||
body::Body,
|
||||
http::{Request, Response, StatusCode, Uri},
|
||||
middleware::Next,
|
||||
response::Redirect,
|
||||
@@ -20,7 +16,6 @@ use brk_error::Result;
|
||||
use brk_logger::OwoColorize;
|
||||
use brk_mcp::route::MCPRoutes;
|
||||
use brk_query::AsyncQuery;
|
||||
use files::FilesRoutes;
|
||||
use log::{error, info};
|
||||
use quick_cache::sync::Cache;
|
||||
use tokio::net::TcpListener;
|
||||
@@ -31,89 +26,13 @@ mod api;
|
||||
pub mod cache;
|
||||
mod extended;
|
||||
mod files;
|
||||
mod state;
|
||||
|
||||
use api::*;
|
||||
pub use cache::{CacheParams, CacheStrategy};
|
||||
use extended::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
query: AsyncQuery,
|
||||
path: Option<PathBuf>,
|
||||
cache: Arc<Cache<String, Bytes>>,
|
||||
}
|
||||
|
||||
impl Deref for AppState {
|
||||
type Target = AsyncQuery;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.query
|
||||
}
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
/// JSON response with caching
|
||||
pub async fn cached_json<T, F>(
|
||||
&self,
|
||||
headers: &axum::http::HeaderMap,
|
||||
strategy: CacheStrategy,
|
||||
f: F,
|
||||
) -> axum::http::Response<axum::body::Body>
|
||||
where
|
||||
T: serde::Serialize + Send + 'static,
|
||||
F: FnOnce(&brk_query::Query) -> brk_error::Result<T> + Send + 'static,
|
||||
{
|
||||
let params = CacheParams::resolve(&strategy, || self.sync(|q| q.height().into()));
|
||||
if params.matches_etag(headers) {
|
||||
return ResponseExtended::new_not_modified();
|
||||
}
|
||||
match self.run(f).await {
|
||||
Ok(value) => ResponseExtended::new_json_cached(&value, ¶ms),
|
||||
Err(e) => ResultExtended::<T>::to_json_response(Err(e), params.etag_str()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Text response with caching
|
||||
pub async fn cached_text<T, F>(
|
||||
&self,
|
||||
headers: &axum::http::HeaderMap,
|
||||
strategy: CacheStrategy,
|
||||
f: F,
|
||||
) -> axum::http::Response<axum::body::Body>
|
||||
where
|
||||
T: AsRef<str> + Send + 'static,
|
||||
F: FnOnce(&brk_query::Query) -> brk_error::Result<T> + Send + 'static,
|
||||
{
|
||||
let params = CacheParams::resolve(&strategy, || self.sync(|q| q.height().into()));
|
||||
if params.matches_etag(headers) {
|
||||
return ResponseExtended::new_not_modified();
|
||||
}
|
||||
match self.run(f).await {
|
||||
Ok(value) => ResponseExtended::new_text_cached(value.as_ref(), ¶ms),
|
||||
Err(e) => ResultExtended::<T>::to_text_response(Err(e), params.etag_str()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Binary response with caching
|
||||
pub async fn cached_bytes<T, F>(
|
||||
&self,
|
||||
headers: &axum::http::HeaderMap,
|
||||
strategy: CacheStrategy,
|
||||
f: F,
|
||||
) -> axum::http::Response<axum::body::Body>
|
||||
where
|
||||
T: Into<Vec<u8>> + Send + 'static,
|
||||
F: FnOnce(&brk_query::Query) -> brk_error::Result<T> + Send + 'static,
|
||||
{
|
||||
let params = CacheParams::resolve(&strategy, || self.sync(|q| q.height().into()));
|
||||
if params.matches_etag(headers) {
|
||||
return ResponseExtended::new_not_modified();
|
||||
}
|
||||
match self.run(f).await {
|
||||
Ok(value) => ResponseExtended::new_bytes_cached(value.into(), ¶ms),
|
||||
Err(e) => ResultExtended::<T>::to_bytes_response(Err(e), params.etag_str()),
|
||||
}
|
||||
}
|
||||
}
|
||||
use files::FilesRoutes;
|
||||
use state::*;
|
||||
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
use std::{ops::Deref, path::PathBuf, sync::Arc};
|
||||
|
||||
use axum::{
|
||||
body::{Body, Bytes},
|
||||
http::{HeaderMap, Response},
|
||||
};
|
||||
use brk_query::AsyncQuery;
|
||||
use quick_cache::sync::Cache;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
CacheParams, CacheStrategy,
|
||||
extended::{ResponseExtended, ResultExtended},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
pub query: AsyncQuery,
|
||||
pub path: Option<PathBuf>,
|
||||
pub cache: Arc<Cache<String, Bytes>>,
|
||||
}
|
||||
|
||||
impl Deref for AppState {
|
||||
type Target = AsyncQuery;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.query
|
||||
}
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
/// JSON response with caching
|
||||
pub async fn cached_json<T, F>(
|
||||
&self,
|
||||
headers: &HeaderMap,
|
||||
strategy: CacheStrategy,
|
||||
f: F,
|
||||
) -> Response<Body>
|
||||
where
|
||||
T: Serialize + Send + 'static,
|
||||
F: FnOnce(&brk_query::Query) -> brk_error::Result<T> + Send + 'static,
|
||||
{
|
||||
let params = CacheParams::resolve(&strategy, || self.sync(|q| q.height().into()));
|
||||
if params.matches_etag(headers) {
|
||||
return ResponseExtended::new_not_modified();
|
||||
}
|
||||
match self.run(f).await {
|
||||
Ok(value) => ResponseExtended::new_json_cached(&value, ¶ms),
|
||||
Err(e) => ResultExtended::<T>::to_json_response(Err(e), params.etag_str()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Text response with caching
|
||||
pub async fn cached_text<T, F>(
|
||||
&self,
|
||||
headers: &HeaderMap,
|
||||
strategy: CacheStrategy,
|
||||
f: F,
|
||||
) -> Response<Body>
|
||||
where
|
||||
T: AsRef<str> + Send + 'static,
|
||||
F: FnOnce(&brk_query::Query) -> brk_error::Result<T> + Send + 'static,
|
||||
{
|
||||
let params = CacheParams::resolve(&strategy, || self.sync(|q| q.height().into()));
|
||||
if params.matches_etag(headers) {
|
||||
return ResponseExtended::new_not_modified();
|
||||
}
|
||||
match self.run(f).await {
|
||||
Ok(value) => ResponseExtended::new_text_cached(value.as_ref(), ¶ms),
|
||||
Err(e) => ResultExtended::<T>::to_text_response(Err(e), params.etag_str()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Binary response with caching
|
||||
pub async fn cached_bytes<T, F>(
|
||||
&self,
|
||||
headers: &HeaderMap,
|
||||
strategy: CacheStrategy,
|
||||
f: F,
|
||||
) -> Response<Body>
|
||||
where
|
||||
T: Into<Vec<u8>> + Send + 'static,
|
||||
F: FnOnce(&brk_query::Query) -> brk_error::Result<T> + Send + 'static,
|
||||
{
|
||||
let params = CacheParams::resolve(&strategy, || self.sync(|q| q.height().into()));
|
||||
if params.matches_etag(headers) {
|
||||
return ResponseExtended::new_not_modified();
|
||||
}
|
||||
match self.run(f).await {
|
||||
Ok(value) => ResponseExtended::new_bytes_cached(value.into(), ¶ms),
|
||||
Err(e) => ResultExtended::<T>::to_bytes_response(Err(e), params.etag_str()),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user