mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
server: server struct
This commit is contained in:
1
crates/brk_cli/src/config.rs
Normal file
1
crates/brk_cli/src/config.rs
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
@@ -10,7 +10,7 @@ use brk_core::path_dot_brk;
|
||||
use brk_exit::Exit;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_parser::rpc::{self, Auth, Client, RpcApi};
|
||||
use brk_server::{Frontend, tokio};
|
||||
use brk_server::{Frontend, Server, tokio};
|
||||
use clap::{Parser, ValueEnum};
|
||||
use color_eyre::eyre::eyre;
|
||||
use log::info;
|
||||
@@ -37,15 +37,14 @@ pub fn run(config: RunConfig) -> color_eyre::Result<()> {
|
||||
.enable_all()
|
||||
.build()?
|
||||
.block_on(async {
|
||||
let served_indexer = indexer.clone();
|
||||
let served_computer = computer.clone();
|
||||
let frontend = config.frontend();
|
||||
|
||||
let server = if config.serve() {
|
||||
let served_indexer = indexer.clone();
|
||||
let served_computer = computer.clone();
|
||||
|
||||
let server = Server::new(served_indexer, served_computer, config.frontend())?;
|
||||
|
||||
Some(tokio::spawn(async move {
|
||||
brk_server::main(served_indexer, served_computer, frontend)
|
||||
.await
|
||||
.unwrap();
|
||||
server.serve().await.unwrap();
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
@@ -84,11 +83,11 @@ pub fn run(config: RunConfig) -> color_eyre::Result<()> {
|
||||
#[derive(Parser, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
|
||||
pub struct RunConfig {
|
||||
/// Bitcoin data directory path, saved
|
||||
#[arg(short, long, value_name = "PATH")]
|
||||
#[arg(long, value_name = "PATH")]
|
||||
bitcoindir: Option<String>,
|
||||
|
||||
/// Bitcoin Research Kit outputs directory path, saved
|
||||
#[arg(short, long, value_name = "PATH")]
|
||||
#[arg(long, value_name = "PATH")]
|
||||
brkdir: Option<String>,
|
||||
|
||||
/// Executed by the runner, default: all, saved
|
||||
|
||||
@@ -58,6 +58,7 @@ impl Computer {
|
||||
let txinindexes_count = indexer.vecs().txinindex_to_txoutindex.len();
|
||||
let txoutindexes_count = indexer.vecs().txoutindex_to_addressindex.len();
|
||||
|
||||
// self.vecs.txindex_to_last_txinindex.compute_last_index_from_first(
|
||||
// starting_indexes.txindex,
|
||||
// &mut indexer.vecs().txindex_to_first_txinindex,
|
||||
// txinindexes_count,
|
||||
|
||||
@@ -24,7 +24,7 @@ pub struct Params {
|
||||
#[serde(default, alias = "t")]
|
||||
/// Inclusive ending index, if negative will be from the end
|
||||
pub to: Option<i64>,
|
||||
#[clap(long)]
|
||||
#[clap(short = 'F', long)]
|
||||
/// Format of the output
|
||||
pub format: Option<Format>,
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use brk_parser::{
|
||||
Parser,
|
||||
rpc::{self, RpcApi},
|
||||
};
|
||||
use brk_server::Frontend;
|
||||
use brk_server::{Frontend, Server};
|
||||
use log::info;
|
||||
|
||||
pub fn main() -> color_eyre::Result<()> {
|
||||
@@ -43,10 +43,10 @@ pub fn main() -> color_eyre::Result<()> {
|
||||
let served_indexer = indexer.clone();
|
||||
let served_computer = computer.clone();
|
||||
|
||||
let server = Server::new(served_indexer, served_computer, Frontend::KiboMoney)?;
|
||||
|
||||
let server = tokio::spawn(async move {
|
||||
brk_server::main(served_indexer, served_computer, Frontend::KiboMoney)
|
||||
.await
|
||||
.unwrap();
|
||||
server.serve().await.unwrap();
|
||||
});
|
||||
|
||||
if process {
|
||||
|
||||
@@ -3,9 +3,9 @@ use axum::{Router, routing::get};
|
||||
use super::AppState;
|
||||
|
||||
mod explorer;
|
||||
mod vecs;
|
||||
mod query;
|
||||
|
||||
pub use vecs::DTS;
|
||||
pub use query::DTS;
|
||||
|
||||
pub trait ApiRoutes {
|
||||
fn add_api_routes(self) -> Self;
|
||||
@@ -13,6 +13,6 @@ pub trait ApiRoutes {
|
||||
|
||||
impl ApiRoutes for Router<AppState> {
|
||||
fn add_api_routes(self) -> Self {
|
||||
self.route("/api/vecs", get(vecs::handler))
|
||||
self.route("/api/query", get(query::handler))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ use axum::{
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use brk_query::{Format, Index, Output, Params};
|
||||
use color_eyre::eyre::eyre;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{log_result, traits::HeaderMapExtended};
|
||||
|
||||
@@ -26,17 +24,15 @@ pub async fn handler(
|
||||
) -> Response {
|
||||
let instant = Instant::now();
|
||||
|
||||
let path = uri.path();
|
||||
|
||||
match req_to_response_res(headers, query, app_state) {
|
||||
Ok(response) => {
|
||||
log_result(response.status(), path, instant);
|
||||
log_result(response.status(), &uri, instant);
|
||||
response
|
||||
}
|
||||
Err(error) => {
|
||||
let mut response =
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, error.to_string()).into_response();
|
||||
log_result(response.status(), path, instant);
|
||||
log_result(response.status(), &uri, instant);
|
||||
response.headers_mut().insert_cors();
|
||||
response
|
||||
}
|
||||
@@ -7,7 +7,7 @@ use std::{
|
||||
use axum::{
|
||||
body::Body,
|
||||
extract::{self, State},
|
||||
http::{HeaderMap, StatusCode},
|
||||
http::{HeaderMap, StatusCode, Uri},
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use log::{error, info};
|
||||
@@ -22,18 +22,24 @@ use super::minify::minify_js;
|
||||
pub async fn file_handler(
|
||||
headers: HeaderMap,
|
||||
State(app_state): State<AppState>,
|
||||
uri: Uri,
|
||||
path: extract::Path<String>,
|
||||
) -> Response {
|
||||
any_handler(headers, app_state, Some(path))
|
||||
any_handler(headers, app_state, uri, 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(app_state): State<AppState>,
|
||||
uri: Uri,
|
||||
) -> Response {
|
||||
any_handler(headers, app_state, uri, None)
|
||||
}
|
||||
|
||||
fn any_handler(
|
||||
headers: HeaderMap,
|
||||
app_state: AppState,
|
||||
uri: Uri,
|
||||
path: Option<extract::Path<String>>,
|
||||
) -> Response {
|
||||
let website_path = app_state
|
||||
@@ -70,11 +76,7 @@ fn any_handler(
|
||||
path_to_response(&headers, &website_path.join("index.html"))
|
||||
};
|
||||
|
||||
log_result(
|
||||
response.status(),
|
||||
&format!("/{}", path.map_or("".to_owned(), |p| p.0)),
|
||||
instant,
|
||||
);
|
||||
log_result(response.status(), &uri, instant);
|
||||
|
||||
response
|
||||
}
|
||||
|
||||
@@ -11,7 +11,12 @@ use std::{
|
||||
};
|
||||
|
||||
use api::{ApiRoutes, DTS};
|
||||
use axum::{Json, Router, http::StatusCode, routing::get, serve};
|
||||
use axum::{
|
||||
Json, Router,
|
||||
http::{StatusCode, Uri},
|
||||
routing::get,
|
||||
serve,
|
||||
};
|
||||
use brk_computer::Computer;
|
||||
use brk_core::path_dot_brk;
|
||||
use brk_indexer::Indexer;
|
||||
@@ -31,6 +36,8 @@ pub use files::Frontend;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
// indexer: &'static Indexer,
|
||||
// computer: &'static Computer,
|
||||
query: &'static Query<'static>,
|
||||
frontend: Frontend,
|
||||
websites_path: Option<PathBuf>,
|
||||
@@ -41,99 +48,106 @@ const DOWNLOADS: &str = "downloads";
|
||||
const WEBSITES: &str = "websites";
|
||||
|
||||
// TODO
|
||||
pub struct Server;
|
||||
pub struct Server(AppState);
|
||||
|
||||
pub async fn main(
|
||||
indexer: Indexer,
|
||||
computer: Computer,
|
||||
frontend: Frontend,
|
||||
) -> color_eyre::Result<()> {
|
||||
let indexer = Box::leak(Box::new(indexer));
|
||||
let computer = Box::leak(Box::new(computer));
|
||||
let query = Box::leak(Box::new(Query::build(indexer, computer)));
|
||||
impl Server {
|
||||
pub fn new(
|
||||
indexer: Indexer,
|
||||
computer: Computer,
|
||||
frontend: Frontend,
|
||||
) -> color_eyre::Result<Self> {
|
||||
let indexer = Box::leak(Box::new(indexer));
|
||||
let computer = Box::leak(Box::new(computer));
|
||||
let query = Box::leak(Box::new(Query::build(indexer, computer)));
|
||||
|
||||
let websites_path = if frontend.is_some() {
|
||||
let websites_dev_path = Path::new(DEV_PATH).join(WEBSITES);
|
||||
let websites_path = if frontend.is_some() {
|
||||
let websites_dev_path = Path::new(DEV_PATH).join(WEBSITES);
|
||||
|
||||
let websites_path = if fs::exists(&websites_dev_path)? {
|
||||
websites_dev_path
|
||||
let websites_path = if fs::exists(&websites_dev_path)? {
|
||||
websites_dev_path
|
||||
} else {
|
||||
let downloads_path = path_dot_brk().join(DOWNLOADS);
|
||||
|
||||
let downloaded_websites_path = downloads_path.join("brk-main").join(WEBSITES);
|
||||
|
||||
if !fs::exists(&downloaded_websites_path)? {
|
||||
info!("Downloading websites from Github...");
|
||||
|
||||
// TODO
|
||||
// Need to download versioned, this is only for testing !
|
||||
let url =
|
||||
"https://github.com/bitcoinresearchkit/brk/archive/refs/heads/main.zip";
|
||||
|
||||
let response = minreq::get(url).send()?;
|
||||
let bytes = response.as_bytes();
|
||||
let cursor = Cursor::new(bytes);
|
||||
|
||||
let mut zip = zip::ZipArchive::new(cursor)?;
|
||||
|
||||
zip.extract(&downloads_path)?;
|
||||
}
|
||||
|
||||
downloaded_websites_path
|
||||
};
|
||||
|
||||
query.generate_dts_file(frontend, websites_path.as_path())?;
|
||||
|
||||
Some(websites_path)
|
||||
} else {
|
||||
let downloads_path = path_dot_brk().join(DOWNLOADS);
|
||||
|
||||
let downloaded_websites_path = downloads_path.join("brk-main").join(WEBSITES);
|
||||
|
||||
if !fs::exists(&downloaded_websites_path)? {
|
||||
info!("Downloading websites from Github...");
|
||||
|
||||
// TODO
|
||||
// Need to download versioned, this is only for testing !
|
||||
let url = "https://github.com/bitcoinresearchkit/brk/archive/refs/heads/main.zip";
|
||||
|
||||
let response = minreq::get(url).send()?;
|
||||
let bytes = response.as_bytes();
|
||||
let cursor = Cursor::new(bytes);
|
||||
|
||||
let mut zip = zip::ZipArchive::new(cursor)?;
|
||||
|
||||
zip.extract(&downloads_path)?;
|
||||
}
|
||||
|
||||
downloaded_websites_path
|
||||
None
|
||||
};
|
||||
|
||||
query.generate_dts_file(frontend, websites_path.as_path())?;
|
||||
|
||||
Some(websites_path)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let state = AppState {
|
||||
query,
|
||||
frontend,
|
||||
websites_path,
|
||||
};
|
||||
|
||||
let compression_layer = CompressionLayer::new()
|
||||
.br(true)
|
||||
.deflate(true)
|
||||
.gzip(true)
|
||||
.zstd(true);
|
||||
|
||||
let router = Router::new()
|
||||
.add_api_routes()
|
||||
.add_website_routes(frontend)
|
||||
.route("/version", get(Json(env!("CARGO_PKG_VERSION"))))
|
||||
.with_state(state)
|
||||
.layer(compression_layer);
|
||||
|
||||
let mut port = 3110;
|
||||
|
||||
let mut listener;
|
||||
loop {
|
||||
listener = TcpListener::bind(format!("0.0.0.0:{port}")).await;
|
||||
if listener.is_ok() {
|
||||
break;
|
||||
}
|
||||
port += 1;
|
||||
Ok(Self(AppState {
|
||||
query,
|
||||
frontend,
|
||||
websites_path,
|
||||
}))
|
||||
}
|
||||
|
||||
info!("Starting server on port {port}...");
|
||||
pub async fn serve(self) -> color_eyre::Result<()> {
|
||||
let state = self.0;
|
||||
|
||||
let listener = listener.unwrap();
|
||||
let compression_layer = CompressionLayer::new()
|
||||
.br(true)
|
||||
.deflate(true)
|
||||
.gzip(true)
|
||||
.zstd(true);
|
||||
|
||||
serve(listener, router).await?;
|
||||
let router = Router::new()
|
||||
.add_api_routes()
|
||||
.add_website_routes(state.frontend)
|
||||
.route("/version", get(Json(env!("CARGO_PKG_VERSION"))))
|
||||
.with_state(state)
|
||||
.layer(compression_layer);
|
||||
|
||||
Ok(())
|
||||
let mut port = 3110;
|
||||
|
||||
let mut listener;
|
||||
loop {
|
||||
listener = TcpListener::bind(format!("0.0.0.0:{port}")).await;
|
||||
if listener.is_ok() {
|
||||
break;
|
||||
}
|
||||
port += 1;
|
||||
}
|
||||
|
||||
info!("Starting server on port {port}...");
|
||||
|
||||
let listener = listener.unwrap();
|
||||
|
||||
serve(listener, router).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn log_result(code: StatusCode, path: &str, instant: Instant) {
|
||||
pub fn log_result(code: StatusCode, uri: &Uri, instant: Instant) {
|
||||
let time = format!("{}µs", instant.elapsed().as_micros());
|
||||
let time = time.bright_black();
|
||||
match code {
|
||||
StatusCode::INTERNAL_SERVER_ERROR => error!("{} {} {}", code.as_u16().red(), path, time),
|
||||
StatusCode::NOT_MODIFIED => info!("{} {} {}", code.as_u16().bright_black(), path, time),
|
||||
StatusCode::OK => info!("{} {} {}", code.as_u16().green(), path, time),
|
||||
_ => error!("{} {} {}", code.as_u16().red(), path, time),
|
||||
StatusCode::INTERNAL_SERVER_ERROR => error!("{} {} {}", code.as_u16().red(), uri, time),
|
||||
StatusCode::NOT_MODIFIED => info!("{} {} {}", code.as_u16().bright_black(), uri, time),
|
||||
StatusCode::OK => info!("{} {} {}", code.as_u16().green(), uri, time),
|
||||
_ => error!("{} {} {}", code.as_u16().red(), uri, time),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user