This repository has been archived on 2026-05-05. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
engram-retired/crates/engram-server/src/routes/tx.rs
T

110 lines
3.4 KiB
Rust

/// Transaction API routes.
///
/// POST /tx/apply — apply a command
/// POST /tx/rollback/{id} — roll back a command
/// GET /tx/history?since={ms} — command history
/// GET /tx/chain/{id} — causal chain for a command
use axum::{
extract::{Path, Query, State},
http::StatusCode,
Json,
};
use engram_tx::{Command, CommandResult};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use uuid::Uuid;
use crate::state::AppState;
// ── POST /tx/apply ────────────────────────────────────────────────────────────
pub async fn tx_apply(
State(state): State<Arc<AppState>>,
Json(cmd): Json<Command>,
) -> Result<Json<CommandResult>, StatusCode> {
let mut engine = state
.tx_engine
.lock()
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let result = engine
.apply(cmd)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(Json(result))
}
// ── POST /tx/rollback/{command_id} ───────────────────────────────────────────
#[derive(Serialize)]
pub struct RollbackResponse {
pub rollback_command_id: Uuid,
pub status: String,
}
pub async fn tx_rollback(
State(state): State<Arc<AppState>>,
Path(command_id): Path<Uuid>,
) -> Result<Json<RollbackResponse>, StatusCode> {
let mut engine = state
.tx_engine
.lock()
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let rb = engine
.rollback(command_id)
.map_err(|e| {
tracing::warn!("rollback failed: {}", e);
StatusCode::BAD_REQUEST
})?;
Ok(Json(RollbackResponse {
rollback_command_id: rb.id,
status: format!("{:?}", rb.status),
}))
}
// ── GET /tx/history ───────────────────────────────────────────────────────────
#[derive(Deserialize)]
pub struct HistoryParams {
pub since: Option<i64>,
}
#[derive(Serialize)]
pub struct HistoryResponse {
pub commands: Vec<Command>,
}
pub async fn tx_history(
State(state): State<Arc<AppState>>,
Query(params): Query<HistoryParams>,
) -> Result<Json<HistoryResponse>, StatusCode> {
let engine = state
.tx_engine
.lock()
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let since = params.since.unwrap_or(0);
let commands = engine
.history(since)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(Json(HistoryResponse { commands }))
}
// ── GET /tx/chain/{command_id} ────────────────────────────────────────────────
#[derive(Serialize)]
pub struct CausalChainResponse {
pub chain: Vec<Command>,
}
pub async fn tx_causal_chain(
State(state): State<Arc<AppState>>,
Path(command_id): Path<Uuid>,
) -> Result<Json<CausalChainResponse>, StatusCode> {
let engine = state
.tx_engine
.lock()
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let chain = engine
.causal_chain(command_id)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(Json(CausalChainResponse { chain }))
}