restflow_server/
main.rs

1mod static_assets;
2
3#[global_allocator]
4static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
5
6mod api;
7
8use api::{agents::*, config::*, python::*, secrets::*, tasks::*, triggers::*, workflows::*};
9use axum::{
10    Router,
11    http::{Method, header},
12    routing::{get, post, put},
13};
14use restflow_core::{AppCore, paths};
15use std::sync::Arc;
16use tower_http::cors::CorsLayer;
17
18#[derive(serde::Serialize)]
19struct Health {
20    status: String,
21}
22
23async fn health() -> axum::Json<Health> {
24    axum::Json(Health {
25        status: "restflow is working!".to_string(),
26    })
27}
28
29#[tokio::main]
30async fn main() {
31    // Initialize tracing logger
32    tracing_subscriber::fmt()
33        .with_env_filter(
34            tracing_subscriber::EnvFilter::try_from_default_env()
35                .unwrap_or_else(|_| "info,restflow_server=debug".into()),
36        )
37        .with_target(false)
38        .with_thread_ids(true)
39        .with_line_number(true)
40        .init();
41
42    tracing::info!("Starting RestFlow backend server");
43
44    // Use AppCore for unified initialization
45    let db_path =
46        paths::ensure_database_path_string().expect("Failed to determine RestFlow database path");
47    let core = Arc::new(
48        AppCore::new(&db_path)
49            .await
50            .expect("Failed to initialize app core"),
51    );
52
53    // Configure CORS
54    let cors = CorsLayer::new()
55        .allow_origin(tower_http::cors::Any)
56        .allow_methods([
57            Method::GET,
58            Method::POST,
59            Method::PUT,
60            Method::DELETE,
61            Method::OPTIONS,
62            Method::PATCH,
63        ])
64        .allow_headers([header::CONTENT_TYPE, header::AUTHORIZATION]);
65
66    // AppState is now just an alias for Arc<AppCore>
67    let shared_state = core.clone();
68
69    let app = Router::new()
70        .route("/health", get(health))
71        // Workflow CRUD
72        .route("/api/workflows", get(list_workflows).post(create_workflow))
73        .route(
74            "/api/workflows/{id}",
75            get(get_workflow)
76                .put(update_workflow)
77                .delete(delete_workflow),
78        )
79        // Workflow execution
80        .route("/api/workflows/execute", post(execute_workflow)) // Inline workflow execution (awaits completion)
81        // Full workflow execution uses async: POST /api/workflows/{id}/executions
82        .route(
83            "/api/workflows/{workflow_id}/executions",
84            get(list_workflow_executions).post(submit_workflow),
85        )
86        .route("/api/executions/{execution_id}", get(get_execution_status))
87        // Task management
88        .route("/api/tasks", get(list_tasks))
89        .route("/api/tasks/{task_id}", get(get_task_status))
90        .route("/api/nodes/execute", post(execute_node))
91        // System configuration
92        .route("/api/config", get(get_config).put(update_config))
93        // Trigger activation
94        .route("/api/workflows/{id}/activate", put(activate_workflow))
95        .route("/api/workflows/{id}/deactivate", put(deactivate_workflow))
96        .route(
97            "/api/workflows/{id}/trigger-status",
98            get(get_workflow_trigger_status),
99        )
100        .route("/api/workflows/{id}/test", post(test_workflow_trigger))
101        // Webhook endpoints - accepts GET/POST/PUT/DELETE/PATCH for maximum flexibility
102        .route(
103            "/api/triggers/webhook/{webhook_id}",
104            get(handle_webhook_trigger)
105                .post(handle_webhook_trigger)
106                .put(handle_webhook_trigger)
107                .delete(handle_webhook_trigger)
108                .patch(handle_webhook_trigger),
109        )
110        // Python integration
111        .route("/api/python/execute", post(execute_script))
112        .route("/api/python/scripts", get(list_scripts))
113        .route("/api/python/templates", get(list_templates))
114        .route("/api/python/templates/{template_id}", get(get_template))
115        // Agent management
116        .route("/api/agents", get(list_agents).post(create_agent))
117        .route(
118            "/api/agents/{id}",
119            get(get_agent).put(update_agent).delete(delete_agent),
120        )
121        .route("/api/agents/{id}/execute", post(execute_agent))
122        .route("/api/agents/execute-inline", post(execute_agent_inline))
123        // Secret management
124        .route("/api/secrets", get(list_secrets).post(create_secret))
125        .route(
126            "/api/secrets/{key}",
127            put(update_secret).delete(delete_secret),
128        )
129        .fallback(static_assets::static_handler)
130        .layer(cors)
131        .with_state(shared_state);
132
133    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
134        .await
135        .expect("Failed to bind to port 3000");
136
137    tracing::info!("RestFlow running on http://localhost:3000");
138
139    axum::serve(listener, app)
140        .await
141        .expect("Failed to start server");
142}