bhtune_server/routes/demo/
mod.rs1use std::convert::Infallible;
4use std::future::Future;
5use std::net::{IpAddr, Ipv6Addr, SocketAddr};
6use std::time::Duration as StdDuration;
7
8use async_stream::stream;
9use axum::extract::{FromRequestParts, Path, Query, State};
10use axum::http::{HeaderMap, HeaderValue, StatusCode, header, request::Parts};
11use axum::response::sse::{Event, KeepAlive, Sse};
12use axum::response::{IntoResponse, Response};
13use axum::routing::{any, get};
14use axum::{Json, Router};
15use bhtune_core::{ControllerDirection, template::built_in_templates};
16use bhtune_db::models::{
17 DemoSessionRow, Pagination, TemplateOrigin, TuneDriver, TuneMvActuationRow, TuneOutcome,
18 TuneResultRow, TuneRunRow, TuneSampleRow, TuneWriteRow,
19};
20use bhtune_runtime::config::{
21 DEMO_COOKIE_NAME, DEMO_CYCLES_COUNT_DEFAULT, DEMO_CYCLES_COUNT_MAX, DEMO_CYCLES_COUNT_MIN,
22 DEMO_CYCLES_SKIP_DEFAULT, DEMO_CYCLES_SKIP_MAX, DEMO_CYCLES_SKIP_MIN,
23 DEMO_NOISE_PROTECTION_SECS_DEFAULT, DEMO_NOISE_PROTECTION_SECS_MAX,
24 DEMO_NOISE_PROTECTION_SECS_MIN, DEMO_RANGE_ENDPOINT_MAX, DEMO_RANGE_ENDPOINT_MIN,
25 DEMO_RANGE_HIGH, DEMO_RANGE_LOW, DEMO_RANGE_SPAN_MAX, DEMO_RANGE_SPAN_MIN,
26 DEMO_RELAY_AMP_DEFAULT, DEMO_RELAY_AMP_MAX, DEMO_RELAY_AMP_MIN, DEMO_SIM_DEAD_TIME_DEFAULT,
27 DEMO_SIM_DEAD_TIME_MAX, DEMO_SIM_DEAD_TIME_MIN, DEMO_SIM_GAIN_DEFAULT, DEMO_SIM_GAIN_MAX,
28 DEMO_SIM_GAIN_MIN, DEMO_SIM_INITIAL_VALUE_DEFAULT, DEMO_SIM_NOISE_DEFAULT,
29 DEMO_SIM_NOISE_MAX_PV_SPAN_FRACTION, DEMO_SIM_SEED_DEFAULT, DEMO_SIM_SEED_MAX,
30 DEMO_SIM_TAU_DEFAULT, DEMO_SIM_TAU_MAX, DEMO_SIM_TAU_MIN, DEMO_TAG_NAME, DemoPolicy,
31 ServerMode,
32};
33use bhtune_runtime::tune::{drive, prepare_owned};
34use chrono::{Duration, Utc};
35use rand::random;
36use sha2::{Digest, Sha256};
37use tower_http::limit::RequestBodyLimitLayer;
38use tower_http::timeout::TimeoutLayer;
39
40use crate::AppState;
41use crate::error::{ApiError, require_present};
42use crate::routes::history::{
43 InitialReadingsResponse, MvActuationResponse, PidConstantTagsResponse,
44 PidParameterLabelsResponse, ResultResponse, RunDetailResponse, RunExportFormat, RunExportQuery,
45 RunListQuery, RunListResponse, RunSummaryResponse, SampleResponse, WriteResponse,
46 filter_from_query, parse_stored_request,
47};
48use crate::routes::runs::StartRunRequest;
49use crate::routes::templates::TemplateResponse;
50use crate::state::DemoQuotaExceeded;
51
52const SSE_POLL_INTERVAL: StdDuration = StdDuration::from_millis(300);
53const FORWARDED_CLIENT_IP_HEADER: &str = "X-BHTune-Client-IP";
54
55mod dto;
56mod handlers;
57mod helpers;
58mod validation;
59
60pub(crate) use handlers::{
61 cancel_run, delete_run, export_run, get_run, get_template, last_request, list_runs,
62 list_templates, start_run, stream_run,
63};
64use helpers::api_not_found;
65pub(crate) use helpers::{PeerAddress, ordinary_request_permit, session_cookie_header};
66
67pub fn router(policy: DemoPolicy) -> Router<AppState> {
68 let ordinary = Router::new()
69 .route("/api/templates", get(list_templates))
70 .route("/api/templates/{name}", get(get_template))
71 .route("/api/runs", get(list_runs).post(start_run))
72 .route("/api/runs/last-request", get(last_request))
73 .route("/api/runs/{id}", get(get_run).delete(delete_run))
74 .route("/api/runs/{id}/cancel", axum::routing::post(cancel_run))
75 .route("/api/runs/{id}/export", get(export_run))
76 .route("/api", any(api_not_found))
77 .route("/api/{*path}", any(api_not_found))
78 .layer(TimeoutLayer::with_status_code(
79 StatusCode::REQUEST_TIMEOUT,
80 StdDuration::from_secs(policy.ordinary_request_timeout_secs),
81 ));
82 let streaming = Router::new().route("/api/runs/{id}/stream", get(stream_run));
83 ordinary.merge(streaming).layer(RequestBodyLimitLayer::new(
84 policy.max_json_body_bytes as usize,
85 ))
86}