Include api docs as compressed book in build
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -8,3 +8,5 @@
|
||||
/docs
|
||||
/docs1
|
||||
/docs2
|
||||
/apidoc/book
|
||||
/apidoc/book.cbor
|
||||
|
||||
6
apidoc/book.toml
Normal file
6
apidoc/book.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[book]
|
||||
authors = ["Dominik Werder <dominik.werder@gmail.com>"]
|
||||
language = "en"
|
||||
multilingual = false
|
||||
src = "src"
|
||||
title = "Daqbuf API"
|
||||
6
apidoc/src/SUMMARY.md
Normal file
6
apidoc/src/SUMMARY.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Summary
|
||||
|
||||
[Introduction](intro.md)
|
||||
- [Search Channels](search.md)
|
||||
- [Binned Data](bins.md)
|
||||
- [Event Data](events.md)
|
||||
1
apidoc/src/bins.md
Normal file
1
apidoc/src/bins.md
Normal file
@@ -0,0 +1 @@
|
||||
# Binned Data
|
||||
1
apidoc/src/events.md
Normal file
1
apidoc/src/events.md
Normal file
@@ -0,0 +1 @@
|
||||
# Event Data
|
||||
1
apidoc/src/intro.md
Normal file
1
apidoc/src/intro.md
Normal file
@@ -0,0 +1 @@
|
||||
# Introduction
|
||||
1
apidoc/src/search.md
Normal file
1
apidoc/src/search.md
Normal file
@@ -0,0 +1 @@
|
||||
# Search Channels
|
||||
@@ -25,6 +25,9 @@ chrono = "0.4.23"
|
||||
md-5 = "0.10.6"
|
||||
regex = "1.10.2"
|
||||
rand = "0.8.5"
|
||||
ciborium = "0.2.1"
|
||||
flate2 = "1"
|
||||
brotli = "2.4"
|
||||
err = { path = "../err" }
|
||||
netpod = { path = "../netpod" }
|
||||
query = { path = "../query" }
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod binned;
|
||||
pub mod databuffer_tools;
|
||||
pub mod docs;
|
||||
pub mod eventdata;
|
||||
pub mod events;
|
||||
pub mod maintenance;
|
||||
|
||||
121
crates/httpret/src/api4/docs.rs
Normal file
121
crates/httpret/src/api4/docs.rs
Normal file
@@ -0,0 +1,121 @@
|
||||
use crate::bodystream::response;
|
||||
use crate::err::Error;
|
||||
use bytes::Bytes;
|
||||
use http::StatusCode;
|
||||
use httpclient::body_bytes;
|
||||
use httpclient::body_empty;
|
||||
use httpclient::Requ;
|
||||
use httpclient::StreamResponse;
|
||||
use netpod::log::*;
|
||||
use netpod::ReqCtx;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::VecDeque;
|
||||
use std::io::Cursor;
|
||||
use std::io::Write;
|
||||
use std::sync::OnceLock;
|
||||
use std::time::Instant;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct Contents {
|
||||
files: HashMap<String, Vec<u8>>,
|
||||
dirs: HashMap<String, Contents>,
|
||||
}
|
||||
|
||||
impl Contents {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
files: HashMap::new(),
|
||||
dirs: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn all_files() -> &'static Contents {
|
||||
static CELL: OnceLock<Contents> = OnceLock::new();
|
||||
CELL.get_or_init(extract_all_files)
|
||||
}
|
||||
|
||||
fn extract_all_files() -> Contents {
|
||||
let ts1 = Instant::now();
|
||||
let buf1 = Vec::with_capacity(1024 * 1024 * 12);
|
||||
let mut dec = brotli::DecompressorWriter::new(buf1, 1024 * 12);
|
||||
// let mut dec = flate2::write::ZlibDecoder::new(buf1);
|
||||
if let Err(e) = dec.write_all(blob()) {
|
||||
error!("can not decode {e}");
|
||||
return Contents::new();
|
||||
}
|
||||
if let Err(e) = dec.flush() {
|
||||
error!("can not decode {e}");
|
||||
return Contents::new();
|
||||
}
|
||||
let buf1 = dec.get_ref();
|
||||
// let buf1 = match dec.finish() {
|
||||
// Ok(x) => x,
|
||||
// Err(e) => {
|
||||
// error!("can not decode {e}");
|
||||
// return Contents::new();
|
||||
// }
|
||||
// };
|
||||
match ciborium::from_reader::<Contents, _>(Cursor::new(buf1)) {
|
||||
Ok(x) => {
|
||||
let ts2 = Instant::now();
|
||||
let dt = ts2.saturating_duration_since(ts1);
|
||||
debug!("content br blob loaded in {:.0} ms", 1e3 * dt.as_secs_f32());
|
||||
x
|
||||
}
|
||||
Err(_) => {
|
||||
error!("can not read apidoc");
|
||||
Contents::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn blob() -> &'static [u8] {
|
||||
include_bytes!(concat!("../../../../apidoc/book.cbor"))
|
||||
}
|
||||
|
||||
pub struct DocsHandler {}
|
||||
|
||||
impl DocsHandler {
|
||||
pub fn path_prefix() -> &'static str {
|
||||
"/api/4/docs/"
|
||||
}
|
||||
|
||||
pub fn handler(req: &Requ) -> Option<Self> {
|
||||
if req.uri().path().starts_with(Self::path_prefix()) {
|
||||
Some(Self {})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn handle(&self, req: Requ, _ctx: &ReqCtx) -> Result<StreamResponse, Error> {
|
||||
let path = req.uri().path();
|
||||
let mut segs: VecDeque<_> = path.split("/").collect();
|
||||
for _ in 0..4 {
|
||||
segs.pop_front();
|
||||
}
|
||||
let mut data = all_files();
|
||||
loop {
|
||||
if let Some(seg) = segs.pop_front() {
|
||||
if segs.len() == 0 {
|
||||
if let Some(x) = data.files.get(seg) {
|
||||
return Ok(response(StatusCode::OK).body(body_bytes(Bytes::from(x.as_slice())))?);
|
||||
} else {
|
||||
return Ok(response(StatusCode::NOT_FOUND).body(body_empty())?);
|
||||
}
|
||||
} else {
|
||||
if let Some(x) = data.dirs.get(seg) {
|
||||
data = x;
|
||||
} else {
|
||||
return Ok(response(StatusCode::NOT_FOUND).body(body_empty())?);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Ok(response(StatusCode::NOT_FOUND).body(body_empty())?);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -241,48 +241,6 @@ where
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
macro_rules! static_http {
|
||||
($path:expr, $tgt:expr, $tgtex:expr, $ctype:expr) => {
|
||||
if $path == concat!("/api/4/documentation/", $tgt) {
|
||||
let c = include_bytes!(concat!("../static/documentation/", $tgtex));
|
||||
let ret = response(StatusCode::OK)
|
||||
.header("content-type", $ctype)
|
||||
.body(body_bytes(c.to_vec()))?;
|
||||
return Ok(ret);
|
||||
}
|
||||
};
|
||||
($path:expr, $tgt:expr, $ctype:expr) => {
|
||||
if $path == concat!("/api/4/documentation/", $tgt) {
|
||||
let c = include_bytes!(concat!("../static/documentation/", $tgt));
|
||||
let ret = response(StatusCode::OK)
|
||||
.header("content-type", $ctype)
|
||||
.body(body_bytes(c.to_vec()))?;
|
||||
return Ok(ret);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! static_http_api1 {
|
||||
($path:expr, $tgt:expr, $tgtex:expr, $ctype:expr) => {
|
||||
if $path == concat!("/api/1/documentation/", $tgt) {
|
||||
let c = include_bytes!(concat!("../static/documentation/", $tgtex));
|
||||
let ret = response(StatusCode::OK)
|
||||
.header("content-type", $ctype)
|
||||
.body(body_bytes(c.to_vec()))?;
|
||||
return Ok(ret);
|
||||
}
|
||||
};
|
||||
($path:expr, $tgt:expr, $ctype:expr) => {
|
||||
if $path == concat!("/api/1/documentation/", $tgt) {
|
||||
let c = include_bytes!(concat!("../static/documentation/", $tgt));
|
||||
let ret = response(StatusCode::OK)
|
||||
.header("content-type", $ctype)
|
||||
.body(body_bytes(c.to_vec()))?;
|
||||
return Ok(ret);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async fn http_service_try(
|
||||
req: Requ,
|
||||
ctx: ReqCtx,
|
||||
@@ -449,18 +407,8 @@ async fn http_service_inner(
|
||||
Ok(h.handle(req, &node_config).await?)
|
||||
} else if let Some(h) = api1::RequestStatusHandler::handler(&req) {
|
||||
Ok(h.handle(req, &node_config).await?)
|
||||
} else if path.starts_with("/api/1/documentation/") {
|
||||
if req.method() == Method::GET {
|
||||
api_1_docs(path)
|
||||
} else {
|
||||
Ok(response(StatusCode::METHOD_NOT_ALLOWED).body(body_empty())?)
|
||||
}
|
||||
} else if path.starts_with("/api/4/documentation/") {
|
||||
if req.method() == Method::GET {
|
||||
api_4_docs(path)
|
||||
} else {
|
||||
Ok(response(StatusCode::METHOD_NOT_ALLOWED).body(body_empty())?)
|
||||
}
|
||||
} else if let Some(h) = api4::docs::DocsHandler::handler(&req) {
|
||||
Ok(h.handle(req, ctx).await?)
|
||||
} else {
|
||||
use std::fmt::Write;
|
||||
let mut body = String::new();
|
||||
@@ -480,21 +428,6 @@ async fn http_service_inner(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn api_4_docs(path: &str) -> Result<StreamResponse, RetrievalError> {
|
||||
static_http!(path, "", "api4.html", "text/html");
|
||||
static_http!(path, "style.css", "text/css");
|
||||
static_http!(path, "script.js", "text/javascript");
|
||||
static_http!(path, "status-main.html", "text/html");
|
||||
Ok(response(StatusCode::NOT_FOUND).body(body_empty())?)
|
||||
}
|
||||
|
||||
pub fn api_1_docs(path: &str) -> Result<StreamResponse, RetrievalError> {
|
||||
static_http_api1!(path, "", "api1.html", "text/html");
|
||||
static_http_api1!(path, "style.css", "text/css");
|
||||
static_http_api1!(path, "script.js", "text/javascript");
|
||||
Ok(response(StatusCode::NOT_FOUND).body(body_empty())?)
|
||||
}
|
||||
|
||||
pub struct StatusBoardAllHandler {}
|
||||
|
||||
impl StatusBoardAllHandler {
|
||||
|
||||
@@ -4,8 +4,6 @@ pub mod api4;
|
||||
use crate::api1::channel_search_configs_v1;
|
||||
use crate::api1::channel_search_list_v1;
|
||||
use crate::api1::gather_json_2_v1;
|
||||
use crate::api_1_docs;
|
||||
use crate::api_4_docs;
|
||||
use crate::err::Error;
|
||||
use crate::gather::gather_get_json_generic;
|
||||
use crate::gather::SubRes;
|
||||
@@ -48,7 +46,6 @@ use netpod::ACCEPT_ALL;
|
||||
use netpod::APP_JSON;
|
||||
use netpod::PSI_DAQBUFFER_SERVICE_MARK;
|
||||
use query::api4::binned::BinnedQuery;
|
||||
use query::api4::events::PlainEventsQuery;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use serde_json::Value as JsonValue;
|
||||
@@ -204,18 +201,6 @@ async fn proxy_http_service_inner(
|
||||
Ok(proxy_single_backend_query::<BinnedQuery>(req, ctx, proxy_config).await?)
|
||||
} else if path == "/api/4/channel/config" {
|
||||
Ok(proxy_single_backend_query::<ChannelConfigQuery>(req, ctx, proxy_config).await?)
|
||||
} else if path.starts_with("/api/1/documentation/") {
|
||||
if req.method() == Method::GET {
|
||||
Ok(api_1_docs(path)?)
|
||||
} else {
|
||||
Ok(response(StatusCode::METHOD_NOT_ALLOWED).body(body_empty())?)
|
||||
}
|
||||
} else if path.starts_with("/api/4/documentation/") {
|
||||
if req.method() == Method::GET {
|
||||
Ok(api_4_docs(path)?)
|
||||
} else {
|
||||
Ok(response(StatusCode::METHOD_NOT_ALLOWED).body(body_empty())?)
|
||||
}
|
||||
} else if path.starts_with("/api/4/test/http/204") {
|
||||
Ok(response(StatusCode::NO_CONTENT).body(body_string("No Content"))?)
|
||||
} else if path.starts_with("/api/4/test/http/400") {
|
||||
@@ -242,6 +227,8 @@ async fn proxy_http_service_inner(
|
||||
h.handle(req, proxy_config).await
|
||||
} else if path.starts_with(DISTRI_PRE) {
|
||||
proxy_distribute_v2(req).await
|
||||
} else if let Some(h) = super::api4::docs::DocsHandler::handler(&req) {
|
||||
Ok(h.handle(req, ctx).await?)
|
||||
} else {
|
||||
use std::fmt::Write;
|
||||
let mut body = String::new();
|
||||
|
||||
Reference in New Issue
Block a user