feat: add run deletion, session reopen, and feed merging (v0.6.0)

- DELETE /api/sessions/{id}/runs/{run_id} removes stopped runs
- POST /api/sessions/{id}/reopen transitions Completed → Active
- Optional merge_feeds field on POST /api/sessions/{id}/runs combines
  frames from multiple feeds with priority-based deduplication
- In-place merge_frames() avoids 2× peak memory
- All new fields use #[serde(default)] for backward compatibility
- Updated README.md with new endpoints and feed merging docs
This commit is contained in:
Jan-Peter von Hunnius 2026-06-06 09:38:42 +02:00
parent e3aa511ac7
commit 9fae8ba9d8
5 changed files with 208 additions and 11 deletions

View file

@ -7,7 +7,7 @@ use axum::{
extract::{Path, State},
http::StatusCode,
response::{IntoResponse, Response},
routing::{get, post},
routing::{delete, get, post},
};
use serde::{Deserialize, Serialize};
use std::sync::{Arc, Mutex, MutexGuard};
@ -85,6 +85,8 @@ struct CreateSessionRequest {
struct StartRunRequest {
feed_name: String,
label: Option<String>,
#[serde(default)]
merge_feeds: Vec<String>,
}
async fn create_session(
@ -132,7 +134,7 @@ async fn start_run(
Json(req): Json<StartRunRequest>,
) -> Response {
let mut mgr = lock_sessions(&state);
match mgr.start_run(&id, &req.feed_name, req.label) {
match mgr.start_run(&id, &req.feed_name, req.label, req.merge_feeds) {
Ok(run) => (StatusCode::CREATED, Json(run)).into_response(),
Err(msg) => err(StatusCode::BAD_REQUEST, &msg),
}
@ -149,6 +151,28 @@ async fn stop_run(
}
}
async fn delete_run(
State(state): State<Arc<AppState>>,
Path((id, run_id)): Path<(String, String)>,
) -> Response {
let mut mgr = lock_sessions(&state);
match mgr.delete_run(&id, &run_id) {
Ok(()) => StatusCode::NO_CONTENT.into_response(),
Err(msg) => err(StatusCode::BAD_REQUEST, &msg),
}
}
async fn reopen_session(
State(state): State<Arc<AppState>>,
Path(id): Path<String>,
) -> Response {
let mut mgr = lock_sessions(&state);
match mgr.reopen_session(&id) {
Ok(session) => ok_json(session),
Err(msg) => err(StatusCode::BAD_REQUEST, &msg),
}
}
async fn enrich_with_recording_meta(
state: &Arc<AppState>,
result: &mut serde_json::Value,
@ -521,6 +545,8 @@ pub fn build_router(state: Arc<AppState>) -> Router {
.route("/api/sessions/{id}", get(get_session).delete(delete_session))
.route("/api/sessions/{id}/runs", post(start_run))
.route("/api/sessions/{id}/runs/{run_id}/stop", post(stop_run))
.route("/api/sessions/{id}/runs/{run_id}", delete(delete_run))
.route("/api/sessions/{id}/reopen", post(reopen_session))
.route("/api/sessions/{id}/complete", post(complete_session))
.route("/api/sessions/{id}/analysis", get(get_analysis))
.route("/api/sessions/{id}/analyze", post(run_analysis))