mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-18 14:38:10 -07:00
global: snapshot
This commit is contained in:
@@ -1,13 +1,15 @@
|
||||
use aide::axum::{ApiRouter, routing::get_with};
|
||||
use axum::{
|
||||
Json,
|
||||
extract::{Path, State},
|
||||
http::StatusCode,
|
||||
http::HeaderMap,
|
||||
response::Response,
|
||||
};
|
||||
use brk_structs::{AddressInfo, AddressPath};
|
||||
|
||||
use crate::extended::{ResponseExtended, ResultExtended, TransformResponseExtended};
|
||||
use crate::{
|
||||
VERSION,
|
||||
extended::{HeaderMapExtended, ResponseExtended, ResultExtended, TransformResponseExtended},
|
||||
};
|
||||
|
||||
use super::AppState;
|
||||
|
||||
@@ -19,14 +21,19 @@ impl AddressesRoutes for ApiRouter<AppState> {
|
||||
fn add_addresses_routes(self) -> Self {
|
||||
self.api_route(
|
||||
"/api/chain/address/{address}",
|
||||
get_with(async |Path(address): Path<AddressPath>,
|
||||
State(app_state): State<AppState>|
|
||||
-> Result<Response, (StatusCode, Json<String>)> {
|
||||
let address_info = app_state.interface.get_address_info(address).to_server_result()?;
|
||||
|
||||
let bytes = sonic_rs::to_vec(&address_info).unwrap();
|
||||
|
||||
Ok(Response::new_json_from_bytes(bytes))
|
||||
get_with(async |
|
||||
headers: HeaderMap,
|
||||
Path(address): Path<AddressPath>,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
let etag = format!("{VERSION}-{}", state.get_height());
|
||||
if headers.has_etag(&etag) {
|
||||
return Response::new_not_modified();
|
||||
}
|
||||
match state.get_address_info(address).with_status() {
|
||||
Ok(value) => Response::new_json(&value, &etag),
|
||||
Err((status, message)) => Response::new_json_with(status, &message, &etag)
|
||||
}
|
||||
}, |op| op
|
||||
.tag("Chain")
|
||||
.summary("Address information")
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
use aide::axum::{ApiRouter, routing::get_with};
|
||||
use axum::{
|
||||
Json,
|
||||
extract::{Path, State},
|
||||
http::StatusCode,
|
||||
http::HeaderMap,
|
||||
response::Response,
|
||||
};
|
||||
use brk_structs::{TransactionInfo, TxidPath};
|
||||
|
||||
use crate::extended::{ResponseExtended, ResultExtended, TransformResponseExtended};
|
||||
use crate::{
|
||||
VERSION,
|
||||
extended::{HeaderMapExtended, ResponseExtended, ResultExtended, TransformResponseExtended},
|
||||
};
|
||||
|
||||
use super::AppState;
|
||||
|
||||
@@ -20,14 +22,19 @@ impl TransactionsRoutes for ApiRouter<AppState> {
|
||||
self.api_route(
|
||||
"/api/chain/tx/{txid}",
|
||||
get_with(
|
||||
async |Path(txid): Path<TxidPath>,
|
||||
State(app_state): State<AppState>|
|
||||
-> Result<Response, (StatusCode, Json<String>)> {
|
||||
let tx_info = app_state.interface.get_transaction_info(txid).to_server_result()?;
|
||||
|
||||
let bytes = sonic_rs::to_vec(&tx_info).unwrap();
|
||||
|
||||
Ok(Response::new_json_from_bytes(bytes))
|
||||
async |
|
||||
headers: HeaderMap,
|
||||
Path(txid): Path<TxidPath>,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
let etag = format!("{VERSION}-{}", state.get_height());
|
||||
if headers.has_etag(&etag) {
|
||||
return Response::new_not_modified();
|
||||
}
|
||||
match state.get_transaction_info(txid).with_status() {
|
||||
Ok(value) => Response::new_json(&value, &etag),
|
||||
Err((status, message)) => Response::new_json_with(status, &message, &etag)
|
||||
}
|
||||
},
|
||||
|op| op
|
||||
.tag("Chain")
|
||||
|
||||
@@ -23,9 +23,9 @@ pub async fn handler(
|
||||
uri: Uri,
|
||||
headers: HeaderMap,
|
||||
query: Query<Params>,
|
||||
State(app_state): State<AppState>,
|
||||
State(state): State<AppState>,
|
||||
) -> Response {
|
||||
match req_to_response_res(uri, headers, query, app_state) {
|
||||
match req_to_response_res(uri, headers, query, state) {
|
||||
Ok(response) => response,
|
||||
Err(error) => {
|
||||
let mut response =
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
use aide::axum::{ApiRouter, routing::get_with};
|
||||
use axum::{
|
||||
Json,
|
||||
extract::{Path, Query, State},
|
||||
http::{HeaderMap, StatusCode, Uri},
|
||||
response::{IntoResponse, Redirect, Response},
|
||||
routing::get,
|
||||
};
|
||||
use brk_interface::{PaginatedMetrics, PaginationParam, Params, ParamsDeprec, ParamsOpt};
|
||||
use brk_structs::{Index, IndexInfo, MetricCount, MetricPath};
|
||||
use brk_structs::{Index, IndexInfo, MetricCount, MetricPath, MetricSearchQuery};
|
||||
use brk_traversable::TreeNode;
|
||||
|
||||
use crate::{
|
||||
@@ -23,8 +22,6 @@ pub trait ApiMetricsRoutes {
|
||||
fn add_metrics_routes(self) -> Self;
|
||||
}
|
||||
|
||||
const TO_SEPARATOR: &str = "_to_";
|
||||
|
||||
impl ApiMetricsRoutes for ApiRouter<AppState> {
|
||||
fn add_metrics_routes(self) -> Self {
|
||||
self
|
||||
@@ -32,149 +29,145 @@ impl ApiMetricsRoutes for ApiRouter<AppState> {
|
||||
.api_route(
|
||||
"/api/metrics/count",
|
||||
get_with(
|
||||
async |State(app_state): State<AppState>| {
|
||||
Json(app_state.interface.metric_count())
|
||||
},
|
||||
|op| {
|
||||
op.tag("Metrics")
|
||||
.summary("Metric count")
|
||||
.description("Current metric count")
|
||||
.with_ok_response::<Vec<MetricCount>, _>(|res| res)
|
||||
.with_not_modified()
|
||||
async |
|
||||
headers: HeaderMap,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
let etag = VERSION;
|
||||
if headers.has_etag(etag) {
|
||||
return Response::new_not_modified();
|
||||
}
|
||||
Response::new_json(state.metric_count(), etag)
|
||||
},
|
||||
|op| op.tag("Metrics")
|
||||
.summary("Metric count")
|
||||
.description("Current metric count")
|
||||
.with_ok_response::<Vec<MetricCount>, _>(|res| res)
|
||||
.with_not_modified(),
|
||||
),
|
||||
)
|
||||
.api_route(
|
||||
"/api/metrics/indexes",
|
||||
get_with(
|
||||
async |State(app_state): State<AppState>| {
|
||||
Json(app_state.interface.get_indexes())
|
||||
},
|
||||
|op| {
|
||||
op.tag("Metrics")
|
||||
.summary("List available indexes")
|
||||
.description(
|
||||
"Returns all available indexes with their accepted query aliases. Use any alias when querying metrics."
|
||||
)
|
||||
.with_ok_response::<Vec<IndexInfo>, _>(|res| res)
|
||||
.with_not_modified()
|
||||
async |
|
||||
headers: HeaderMap,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
let etag = VERSION;
|
||||
if headers.has_etag(etag) {
|
||||
return Response::new_not_modified();
|
||||
}
|
||||
Response::new_json(state.get_indexes(), etag)
|
||||
},
|
||||
|op| op.tag("Metrics")
|
||||
.summary("List available indexes")
|
||||
.description(
|
||||
"Returns all available indexes with their accepted query aliases. Use any alias when querying metrics."
|
||||
)
|
||||
.with_ok_response::<Vec<IndexInfo>, _>(|res| res)
|
||||
.with_not_modified(),
|
||||
),
|
||||
)
|
||||
.api_route(
|
||||
"/api/metrics/list",
|
||||
get_with(
|
||||
async |State(app_state): State<AppState>,
|
||||
Query(pagination): Query<PaginationParam>| {
|
||||
Json(app_state.interface.get_metrics(pagination))
|
||||
},
|
||||
|op| {
|
||||
op.tag("Metrics")
|
||||
.summary("Metrics list")
|
||||
.description("Paginated list of available metrics")
|
||||
.with_ok_response::<PaginatedMetrics, _>(|res| res)
|
||||
.with_not_modified()
|
||||
async |
|
||||
headers: HeaderMap,
|
||||
State(state): State<AppState>,
|
||||
Query(pagination): Query<PaginationParam>
|
||||
| {
|
||||
let etag = VERSION;
|
||||
if headers.has_etag(etag) {
|
||||
return Response::new_not_modified();
|
||||
}
|
||||
Response::new_json(state.get_metrics(pagination), etag)
|
||||
},
|
||||
|op| op.tag("Metrics")
|
||||
.summary("Metrics list")
|
||||
.description("Paginated list of available metrics")
|
||||
.with_ok_response::<PaginatedMetrics, _>(|res| res)
|
||||
.with_not_modified(),
|
||||
),
|
||||
)
|
||||
.api_route(
|
||||
"/api/metrics/catalog",
|
||||
get_with(
|
||||
async |headers: HeaderMap, State(app_state): State<AppState>| -> Response {
|
||||
async |headers: HeaderMap, State(state): State<AppState>| -> Response {
|
||||
let etag = VERSION;
|
||||
|
||||
if headers
|
||||
.get_if_none_match()
|
||||
.is_some_and(|prev_etag| etag == prev_etag)
|
||||
{
|
||||
if headers.has_etag(etag) {
|
||||
return Response::new_not_modified();
|
||||
}
|
||||
|
||||
let bytes = sonic_rs::to_vec(&app_state.interface.get_metrics_catalog()).unwrap();
|
||||
|
||||
let mut response = Response::new_json_from_bytes(bytes);
|
||||
|
||||
let headers = response.headers_mut();
|
||||
headers.insert_cors();
|
||||
headers.insert_etag(etag);
|
||||
|
||||
response
|
||||
Response::new_json(state.get_metrics_catalog(), etag)
|
||||
},
|
||||
|op| {
|
||||
op.tag("Metrics")
|
||||
|op| op.tag("Metrics")
|
||||
.summary("Metrics catalog")
|
||||
.description(
|
||||
"Returns the complete hierarchical catalog of available metrics organized as a tree structure. Metrics are grouped by categories and subcategories. Best viewed in an interactive JSON viewer (e.g., Firefox's built-in JSON viewer) for easy navigation of the nested structure."
|
||||
)
|
||||
.with_ok_response::<TreeNode, _>(|res| res)
|
||||
.with_not_modified()
|
||||
},
|
||||
.with_not_modified(),
|
||||
),
|
||||
)
|
||||
.api_route(
|
||||
"/api/search/{metric}",
|
||||
"/api/metrics/search",
|
||||
get_with(
|
||||
async |
|
||||
headers: HeaderMap,
|
||||
State(app_state): State<AppState>,
|
||||
Path(MetricPath { metric }): Path<MetricPath>
|
||||
State(state): State<AppState>,
|
||||
Query(query): Query<MetricSearchQuery>
|
||||
| {
|
||||
let etag = VERSION;
|
||||
|
||||
if headers
|
||||
.get_if_none_match()
|
||||
.is_some_and(|prev_etag| etag == prev_etag)
|
||||
{
|
||||
if headers.has_etag(etag) {
|
||||
return Response::new_not_modified();
|
||||
}
|
||||
|
||||
let bytes = sonic_rs::to_vec(&app_state.interface.search_metric(&metric, usize::MAX)).unwrap();
|
||||
|
||||
let mut response = Response::new_json_from_bytes(bytes);
|
||||
|
||||
let headers = response.headers_mut();
|
||||
headers.insert_cors();
|
||||
headers.insert_etag(etag);
|
||||
|
||||
response
|
||||
},
|
||||
|op| {
|
||||
op.tag("Metrics")
|
||||
.summary("Metric search")
|
||||
.description(
|
||||
"Search metrics based on a query"
|
||||
)
|
||||
.with_ok_response::<Vec<String>, _>(|res| res)
|
||||
.with_not_modified()
|
||||
Response::new_json(state.match_metric(query), etag)
|
||||
},
|
||||
|op| op.tag("Metrics")
|
||||
.summary("Search metrics")
|
||||
.description("Fuzzy search for metrics by name. Supports partial matches and typos.")
|
||||
.with_ok_response::<Vec<String>, _>(|res| res)
|
||||
.with_not_modified(),
|
||||
),
|
||||
)
|
||||
.api_route(
|
||||
"/api/metrics/{metric}",
|
||||
get_with(
|
||||
async |
|
||||
State(app_state): State<AppState>,
|
||||
headers: HeaderMap,
|
||||
State(state): State<AppState>,
|
||||
Path(MetricPath { metric }): Path<MetricPath>
|
||||
| {
|
||||
match app_state.interface.metric_to_indexes(metric) {
|
||||
Some(indexes) => Json(indexes).into_response(),
|
||||
None => StatusCode::NOT_FOUND.into_response()
|
||||
let etag = VERSION;
|
||||
if headers.has_etag(etag) {
|
||||
return Response::new_not_modified();
|
||||
}
|
||||
if let Some(indexes) = state.metric_to_indexes(metric.clone()) {
|
||||
return Response::new_json(indexes, etag)
|
||||
}
|
||||
let value = if let Some(first) = state.match_metric(MetricSearchQuery {
|
||||
q: metric.clone(),
|
||||
limit: 1,
|
||||
}).first() {
|
||||
format!("Could not find '{metric}', did you mean '{first}' ?")
|
||||
} else {
|
||||
format!("Could not find '{metric}'.")
|
||||
};
|
||||
Response::new_json_with(StatusCode::NOT_FOUND, value, etag)
|
||||
},
|
||||
|op| {
|
||||
op.tag("Metrics")
|
||||
.summary("Get supported indexes for a metric")
|
||||
.description(
|
||||
"Returns the list of indexes are supported by the specified metric. \
|
||||
For example, `realized_price` might be available on dateindex, weekindex, and monthindex."
|
||||
)
|
||||
.with_ok_response::<Vec<Index>, _>(|res| res)
|
||||
.with_not_modified()
|
||||
.with_not_found()
|
||||
},
|
||||
|op| op.tag("Metrics")
|
||||
.summary("Get supported indexes for a metric")
|
||||
.description(
|
||||
"Returns the list of indexes are supported by the specified metric. \
|
||||
For example, `realized_price` might be available on dateindex, weekindex, and monthindex."
|
||||
)
|
||||
.with_ok_response::<Vec<Index>, _>(|res| res)
|
||||
.with_not_modified()
|
||||
.with_not_found(),
|
||||
),
|
||||
)
|
||||
// WIP
|
||||
.route("/api/metrics/bulk", get(data::handler))
|
||||
// WIP
|
||||
.route(
|
||||
"/api/metrics/{metric}/{index}",
|
||||
get(
|
||||
@@ -221,8 +214,9 @@ impl ApiMetricsRoutes for ApiRouter<AppState> {
|
||||
Query(params_opt): Query<ParamsOpt>,
|
||||
state: State<AppState>|
|
||||
-> Response {
|
||||
let separator = "_to_";
|
||||
let variant = variant.replace("-", "_");
|
||||
let mut split = variant.split(TO_SEPARATOR);
|
||||
let mut split = variant.split(separator);
|
||||
|
||||
let ser_index = split.next().unwrap();
|
||||
let Ok(index) = Index::try_from(ser_index) else {
|
||||
@@ -230,7 +224,7 @@ impl ApiMetricsRoutes for ApiRouter<AppState> {
|
||||
};
|
||||
|
||||
let params = Params::from((
|
||||
(index, split.collect::<Vec<_>>().join(TO_SEPARATOR)),
|
||||
(index, split.collect::<Vec<_>>().join(separator)),
|
||||
params_opt,
|
||||
));
|
||||
data::handler(uri, headers, Query(params), state).await
|
||||
|
||||
@@ -52,8 +52,8 @@ impl ApiRoutes for ApiRouter<AppState> {
|
||||
get_with(
|
||||
async || -> Json<Health> {
|
||||
Json(Health {
|
||||
status: "healthy".to_string(),
|
||||
service: "brk".to_string(),
|
||||
status: "healthy",
|
||||
service: "brk",
|
||||
timestamp: jiff::Timestamp::now().to_string(),
|
||||
})
|
||||
},
|
||||
@@ -73,21 +73,11 @@ impl ApiRoutes for ApiRouter<AppState> {
|
||||
-> Response {
|
||||
let etag = VERSION;
|
||||
|
||||
if headers
|
||||
.get_if_none_match()
|
||||
.is_some_and(|prev_etag| etag == prev_etag)
|
||||
{
|
||||
if headers.has_etag(etag) {
|
||||
return Response::new_not_modified();
|
||||
}
|
||||
|
||||
let mut response =
|
||||
Response::new_json_from_bytes(sonic_rs::to_vec(&api).unwrap());
|
||||
|
||||
let headers = response.headers_mut();
|
||||
headers.insert_cors();
|
||||
headers.insert_etag(etag);
|
||||
|
||||
response
|
||||
Response::new_json(&api, etag)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
@@ -21,7 +21,7 @@ pub enum ModifiedState {
|
||||
pub trait HeaderMapExtended {
|
||||
fn insert_cors(&mut self);
|
||||
|
||||
fn get_if_none_match(&self) -> Option<&str>;
|
||||
fn has_etag(&self, etag: &str) -> bool;
|
||||
|
||||
fn get_if_modified_since(&self) -> Option<DateTime>;
|
||||
fn check_if_modified_since(&self, path: &Path) -> Result<(ModifiedState, DateTime)>;
|
||||
@@ -123,8 +123,9 @@ impl HeaderMapExtended for HeaderMap {
|
||||
None
|
||||
}
|
||||
|
||||
fn get_if_none_match(&self) -> Option<&str> {
|
||||
self.get(IF_NONE_MATCH).and_then(|v| v.to_str().ok())
|
||||
fn has_etag(&self, etag: &str) -> bool {
|
||||
self.get(IF_NONE_MATCH)
|
||||
.is_some_and(|prev_etag| etag == prev_etag)
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
|
||||
|
||||
@@ -11,7 +11,12 @@ where
|
||||
Self: Sized,
|
||||
{
|
||||
fn new_not_modified() -> Self;
|
||||
fn new_json_from_bytes(bytes: Vec<u8>) -> Self;
|
||||
fn new_json<T>(value: T, etag: &str) -> Self
|
||||
where
|
||||
T: sonic_rs::Serialize;
|
||||
fn new_json_with<T>(status: StatusCode, value: T, etag: &str) -> Self
|
||||
where
|
||||
T: sonic_rs::Serialize;
|
||||
}
|
||||
|
||||
impl ResponseExtended for Response<Body> {
|
||||
@@ -22,10 +27,25 @@ impl ResponseExtended for Response<Body> {
|
||||
response
|
||||
}
|
||||
|
||||
fn new_json_from_bytes(bytes: Vec<u8>) -> Self {
|
||||
Response::builder()
|
||||
.header("content-type", "application/json")
|
||||
.body(bytes.into())
|
||||
.unwrap()
|
||||
fn new_json<T>(value: T, etag: &str) -> Self
|
||||
where
|
||||
T: sonic_rs::Serialize,
|
||||
{
|
||||
Self::new_json_with(StatusCode::default(), value, etag)
|
||||
}
|
||||
|
||||
fn new_json_with<T>(status: StatusCode, value: T, etag: &str) -> Self
|
||||
where
|
||||
T: sonic_rs::Serialize,
|
||||
{
|
||||
let bytes = sonic_rs::to_vec(&value).unwrap();
|
||||
let mut response = Response::builder().body(bytes.into()).unwrap();
|
||||
*response.status_mut() = status;
|
||||
let headers = response.headers_mut();
|
||||
headers.insert_cors();
|
||||
headers.insert_content_type_application_json();
|
||||
headers.insert_cache_control_must_revalidate();
|
||||
headers.insert_etag(etag);
|
||||
response
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use axum::{Json, http::StatusCode};
|
||||
use axum::http::StatusCode;
|
||||
use brk_error::{Error, Result};
|
||||
|
||||
pub trait ResultExtended<T> {
|
||||
fn to_server_result(self) -> Result<T, (StatusCode, Json<String>)>;
|
||||
fn with_status(self) -> Result<T, (StatusCode, String)>;
|
||||
}
|
||||
|
||||
impl<T> ResultExtended<T> for Result<T> {
|
||||
fn to_server_result(self) -> Result<T, (StatusCode, Json<String>)> {
|
||||
fn with_status(self) -> Result<T, (StatusCode, String)> {
|
||||
self.map_err(|e| {
|
||||
(
|
||||
match e {
|
||||
@@ -17,7 +17,7 @@ impl<T> ResultExtended<T> for Result<T> {
|
||||
Error::UnknownAddress | Error::UnknownTxid => StatusCode::NOT_FOUND,
|
||||
_ => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
},
|
||||
Json(e.to_string()),
|
||||
e.to_string(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,22 +14,22 @@ use crate::{AppState, HeaderMapExtended, ModifiedState, ResponseExtended};
|
||||
|
||||
pub async fn file_handler(
|
||||
headers: HeaderMap,
|
||||
State(app_state): State<AppState>,
|
||||
State(state): State<AppState>,
|
||||
path: extract::Path<String>,
|
||||
) -> Response {
|
||||
any_handler(headers, app_state, Some(path))
|
||||
any_handler(headers, state, Some(path))
|
||||
}
|
||||
|
||||
pub async fn index_handler(headers: HeaderMap, State(app_state): State<AppState>) -> Response {
|
||||
any_handler(headers, app_state, None)
|
||||
pub async fn index_handler(headers: HeaderMap, State(state): State<AppState>) -> Response {
|
||||
any_handler(headers, state, None)
|
||||
}
|
||||
|
||||
fn any_handler(
|
||||
headers: HeaderMap,
|
||||
app_state: AppState,
|
||||
state: AppState,
|
||||
path: Option<extract::Path<String>>,
|
||||
) -> Response {
|
||||
let files_path = app_state.path.as_ref().unwrap();
|
||||
let files_path = state.path.as_ref().unwrap();
|
||||
|
||||
if let Some(path) = path.as_ref() {
|
||||
let path = path.0.replace("..", "").replace("\\", "");
|
||||
@@ -52,14 +52,14 @@ fn any_handler(
|
||||
}
|
||||
}
|
||||
|
||||
path_to_response(&headers, &app_state, &path)
|
||||
path_to_response(&headers, &state, &path)
|
||||
} else {
|
||||
path_to_response(&headers, &app_state, &files_path.join("index.html"))
|
||||
path_to_response(&headers, &state, &files_path.join("index.html"))
|
||||
}
|
||||
}
|
||||
|
||||
fn path_to_response(headers: &HeaderMap, app_state: &AppState, path: &Path) -> Response {
|
||||
match path_to_response_(headers, app_state, path) {
|
||||
fn path_to_response(headers: &HeaderMap, state: &AppState, path: &Path) -> Response {
|
||||
match path_to_response_(headers, state, path) {
|
||||
Ok(response) => response,
|
||||
Err(error) => {
|
||||
let mut response =
|
||||
@@ -72,7 +72,7 @@ fn path_to_response(headers: &HeaderMap, app_state: &AppState, path: &Path) -> R
|
||||
}
|
||||
}
|
||||
|
||||
fn path_to_response_(headers: &HeaderMap, app_state: &AppState, path: &Path) -> Result<Response> {
|
||||
fn path_to_response_(headers: &HeaderMap, state: &AppState, path: &Path) -> Result<Response> {
|
||||
let (modified, date) = headers.check_if_modified_since(path)?;
|
||||
if modified == ModifiedState::NotModifiedSince {
|
||||
return Ok(Response::new_not_modified());
|
||||
@@ -86,7 +86,7 @@ fn path_to_response_(headers: &HeaderMap, app_state: &AppState, path: &Path) ->
|
||||
|| serialized_path.ends_with("service-worker.js");
|
||||
|
||||
let guard_res = if !must_revalidate {
|
||||
Some(app_state.cache.get_value_or_guard(
|
||||
Some(state.cache.get_value_or_guard(
|
||||
&path.to_str().unwrap().to_owned(),
|
||||
Some(Duration::from_millis(50)),
|
||||
))
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#![doc = include_str!("../examples/main.rs")]
|
||||
#![doc = "```"]
|
||||
|
||||
use std::{path::PathBuf, sync::Arc, time::Duration};
|
||||
use std::{ops::Deref, path::PathBuf, sync::Arc, time::Duration};
|
||||
|
||||
use aide::axum::ApiRouter;
|
||||
use api::ApiRoutes;
|
||||
@@ -42,6 +42,13 @@ pub struct AppState {
|
||||
cache: Arc<Cache<String, Bytes>>,
|
||||
}
|
||||
|
||||
impl Deref for AppState {
|
||||
type Target = &'static Interface<'static>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.interface
|
||||
}
|
||||
}
|
||||
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
pub struct Server(AppState);
|
||||
|
||||
Reference in New Issue
Block a user