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 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 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 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 let shared_state = core.clone();
68
69 let app = Router::new()
70 .route("/health", get(health))
71 .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 .route("/api/workflows/execute", post(execute_workflow)) .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 .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 .route("/api/config", get(get_config).put(update_config))
93 .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 .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 .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 .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 .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}