assay_workflow/api/
mod.rs1pub mod activities;
2pub mod api_keys;
3pub mod auth;
4pub mod events;
5pub mod namespaces;
6pub mod openapi;
7pub mod public;
8pub mod queues;
9pub mod schedules;
10pub mod tasks;
11pub mod workers;
12pub mod workflow_tasks;
13pub mod workflows;
14
15use std::sync::Arc;
16
17use axum::middleware;
18use axum::Router;
19use tokio::sync::broadcast;
20use tracing::info;
21
22use crate::auth_mode::AuthMode;
23use crate::ctx::{BroadcastEvent, EngineEvent, WorkflowCtx};
24use crate::store::WorkflowStore;
25
26pub fn router<S: WorkflowStore>(state: Arc<WorkflowCtx<S>>) -> Router {
37 let needs_auth = state.auth_mode.is_enabled();
38
39 let authed_api = Router::new()
40 .nest("/api/v1", api_v1_router::<S>())
41 .nest("/api/v1", events::router::<S>());
42
43 let authed_api = if needs_auth {
44 authed_api.layer(middleware::from_fn_with_state(
45 Arc::clone(&state),
46 auth::auth_middleware::<S>,
47 ))
48 } else {
49 authed_api
50 };
51
52 let public_api = Router::new().nest("/api/v1", public::router::<S>());
54
55 let app = authed_api
56 .merge(public_api)
57 .merge(openapi::router::<S>());
58
59 app.with_state(state)
60}
61
62fn api_v1_router<S: WorkflowStore>() -> Router<Arc<WorkflowCtx<S>>> {
63 Router::new()
64 .merge(workflows::router::<S>())
65 .merge(activities::router::<S>())
66 .merge(workflow_tasks::router::<S>())
67 .merge(tasks::router::<S>())
68 .merge(schedules::router::<S>())
69 .merge(workers::router::<S>())
70 .merge(namespaces::router::<S>())
71 .merge(queues::router::<S>())
72 .merge(api_keys::router::<S>())
73}
74
75pub async fn serve(
77 store: impl WorkflowStore + 'static,
78 port: u16,
79 auth_mode: AuthMode,
80) -> anyhow::Result<()> {
81 serve_with_version(store, port, auth_mode, None).await
82}
83
84pub async fn serve_with_version(
90 store: impl WorkflowStore + 'static,
91 port: u16,
92 auth_mode: AuthMode,
93 binary_version: Option<&'static str>,
94) -> anyhow::Result<()> {
95 let (sse_tx, _) = broadcast::channel::<BroadcastEvent>(1024);
100 let (engine_tx, mut engine_rx) = broadcast::channel::<EngineEvent>(1024);
101
102 let store = Arc::new(store);
103 let mut ctx = WorkflowCtx::start(store)
104 .with_event_broadcaster(engine_tx)
105 .with_auth_mode(auth_mode.clone())
106 .with_sse_tx(sse_tx.clone());
107
108 if let Some(v) = binary_version {
109 ctx = ctx.with_binary_version(v);
110 }
111
112 {
113 let sse_tx = sse_tx.clone();
114 tokio::spawn(async move {
115 while let Ok(evt) = engine_rx.recv().await {
116 let _ = sse_tx.send(BroadcastEvent {
117 event_type: evt.event_type,
118 workflow_id: evt.workflow_id,
119 payload: Some(serde_json::json!({
120 "namespace": evt.namespace,
121 }).to_string()),
122 });
123 }
124 });
125 }
126
127 let mode_desc = auth_mode.describe();
128 let state = Arc::new(ctx);
129
130 let app = router(state);
131 let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{port}")).await?;
132 info!("Workflow API listening on 0.0.0.0:{port} (auth: {mode_desc})");
133
134 axum::serve(listener, app).await?;
135 Ok(())
136}