Skip to main content

assay_workflow/api/
mod.rs

1pub mod activities;
2pub mod auth;
3pub mod dashboard;
4pub mod events;
5pub mod namespaces;
6pub mod openapi;
7pub mod queues;
8pub mod schedules;
9pub mod tasks;
10pub mod workers;
11pub mod workflow_tasks;
12pub mod workflows;
13
14use std::sync::Arc;
15
16use axum::middleware;
17use axum::Router;
18use tokio::sync::broadcast;
19use tracing::info;
20
21use crate::api::auth::AuthMode;
22use crate::engine::Engine;
23use crate::store::WorkflowStore;
24
25/// Shared state for all API handlers.
26pub struct AppState<S: WorkflowStore> {
27    pub engine: Arc<Engine<S>>,
28    pub event_tx: broadcast::Sender<events::BroadcastEvent>,
29    pub auth_mode: AuthMode,
30}
31
32/// Build the full API router.
33pub fn router<S: WorkflowStore + 'static>(state: Arc<AppState<S>>) -> Router {
34    let needs_auth = !matches!(state.auth_mode, AuthMode::NoAuth);
35
36    let api = Router::new()
37        .nest("/api/v1", api_v1_router())
38        .nest("/api/v1", events::router());
39
40    let api = if needs_auth {
41        api.layer(middleware::from_fn_with_state(
42            Arc::clone(&state),
43            auth::auth_middleware,
44        ))
45    } else {
46        api
47    };
48
49    // Dashboard + OpenAPI docs (no auth)
50    let app = api
51        .merge(dashboard::router())
52        .merge(openapi::router());
53
54    app.with_state(state)
55}
56
57fn api_v1_router<S: WorkflowStore + 'static>() -> Router<Arc<AppState<S>>> {
58    Router::new()
59        .merge(workflows::router())
60        .merge(activities::router())
61        .merge(workflow_tasks::router())
62        .merge(tasks::router())
63        .merge(schedules::router())
64        .merge(workers::router())
65        .merge(namespaces::router())
66        .merge(queues::router())
67}
68
69/// Start the HTTP server on the given port.
70pub async fn serve<S: WorkflowStore + 'static>(
71    engine: Engine<S>,
72    port: u16,
73    auth_mode: AuthMode,
74) -> anyhow::Result<()> {
75    let (event_tx, _) = broadcast::channel(1024);
76
77    let mode_desc = match &auth_mode {
78        AuthMode::NoAuth => "no-auth (open access)".to_string(),
79        AuthMode::ApiKey => "api-key".to_string(),
80        AuthMode::Jwt { issuer, .. } => format!("jwt (issuer: {issuer})"),
81    };
82
83    let state = Arc::new(AppState {
84        engine: Arc::new(engine),
85        event_tx,
86        auth_mode,
87    });
88
89    let app = router(state);
90    let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{port}")).await?;
91    info!("Workflow API listening on 0.0.0.0:{port} (auth: {mode_desc})");
92
93    axum::serve(listener, app).await?;
94    Ok(())
95}