Skip to main content

bhtune_server/routes/demo/
mod.rs

1//! Restricted simulator-only public Demo API.
2
3use 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}