vibe_graph_api/routes/
mod.rs

1//! API route handlers.
2
3pub mod git;
4mod graph;
5mod health;
6pub mod ops;
7
8use std::path::PathBuf;
9use std::sync::Arc;
10
11use axum::{
12    routing::{delete, get, post},
13    Router,
14};
15use tower_http::cors::{Any, CorsLayer};
16use tower_http::trace::{DefaultMakeSpan, DefaultOnResponse, TraceLayer};
17use tracing::Level;
18use vibe_graph_ops::OpsContext;
19
20use crate::types::ApiState;
21use crate::ws::ws_handler;
22use git::GitOpsState;
23use ops::OpsState;
24
25/// Create the API router with all endpoints.
26pub fn create_api_router(state: Arc<ApiState>) -> Router {
27    let cors = CorsLayer::new()
28        .allow_origin(Any)
29        .allow_methods(Any)
30        .allow_headers(Any);
31
32    Router::new()
33        // Health
34        .route("/health", get(health::health_handler))
35        // Graph endpoints
36        .route("/graph", get(graph::graph_handler))
37        .route("/graph/nodes", get(graph::nodes_handler))
38        .route("/graph/edges", get(graph::edges_handler))
39        .route("/graph/metadata", get(graph::metadata_handler))
40        // Git endpoints
41        .route("/git/changes", get(git::changes_handler))
42        // WebSocket
43        .route("/ws", get(ws_handler))
44        // Request tracing (enable with RUST_LOG=tower_http=info or higher)
45        .layer(
46            TraceLayer::new_for_http()
47                .make_span_with(
48                    DefaultMakeSpan::new()
49                        .level(Level::INFO)
50                        .include_headers(false),
51                )
52                .on_response(DefaultOnResponse::new().level(Level::INFO)),
53        )
54        .layer(cors)
55        .with_state(state)
56}
57
58/// Create the operations router with all ops endpoints.
59///
60/// This router provides REST access to all vibe-graph operations.
61/// Mount at `/api/ops` for full API access.
62pub fn create_ops_router(ctx: OpsContext) -> Router {
63    let state = Arc::new(OpsState { ctx });
64
65    let cors = CorsLayer::new()
66        .allow_origin(Any)
67        .allow_methods(Any)
68        .allow_headers(Any);
69
70    Router::new()
71        // Sync operations
72        .route("/sync", post(ops::sync_handler))
73        .route("/sync", get(ops::sync_query_handler))
74        // Graph operations
75        .route("/graph", post(ops::graph_handler))
76        .route("/graph", get(ops::graph_query_handler))
77        // Status
78        .route("/status", get(ops::status_handler))
79        // Load
80        .route("/load", get(ops::load_handler))
81        // Clean
82        .route("/clean", delete(ops::clean_handler))
83        // Git changes
84        .route("/git-changes", get(ops::git_changes_handler))
85        // Request tracing
86        .layer(
87            TraceLayer::new_for_http()
88                .make_span_with(
89                    DefaultMakeSpan::new()
90                        .level(Level::INFO)
91                        .include_headers(false),
92                )
93                .on_response(DefaultOnResponse::new().level(Level::INFO)),
94        )
95        .layer(cors)
96        .with_state(state)
97}
98
99/// Create a git commands router for a single-repo workspace.
100///
101/// This router provides REST access to git commands:
102/// - GET /repos - List available repositories
103/// - POST /add - Stage files
104/// - POST /commit - Create commit
105/// - POST /reset - Unstage files
106/// - GET /branches - List branches
107/// - POST /checkout - Switch branch
108/// - GET /log - Commit history
109/// - GET /diff - Get diff
110pub fn create_git_commands_router(workspace_path: PathBuf) -> Router {
111    let state = Arc::new(GitOpsState::single_repo(workspace_path));
112    build_git_commands_router(state)
113}
114
115/// Create a git commands router for a multi-repo workspace.
116///
117/// # Arguments
118///
119/// * `workspace_path` - The root workspace path
120/// * `repos` - List of (name, path) tuples for each repository
121pub fn create_git_commands_router_multi(
122    workspace_path: PathBuf,
123    repos: Vec<(String, PathBuf)>,
124) -> Router {
125    let state = Arc::new(GitOpsState::multi_repo(workspace_path, repos));
126    build_git_commands_router(state)
127}
128
129/// Internal helper to build git commands router with given state.
130fn build_git_commands_router(state: Arc<GitOpsState>) -> Router {
131    let cors = CorsLayer::new()
132        .allow_origin(Any)
133        .allow_methods(Any)
134        .allow_headers(Any);
135
136    Router::new()
137        // List repos
138        .route("/repos", get(git::repos_handler))
139        // Stage files
140        .route("/add", post(git::add_handler))
141        // Commit
142        .route("/commit", post(git::commit_handler))
143        // Unstage files
144        .route("/reset", post(git::reset_handler))
145        // List branches
146        .route("/branches", get(git::branches_handler))
147        // Checkout branch
148        .route("/checkout", post(git::checkout_handler))
149        // Commit history
150        .route("/log", get(git::log_handler))
151        // Diff
152        .route("/diff", get(git::diff_handler))
153        .layer(
154            TraceLayer::new_for_http()
155                .make_span_with(
156                    DefaultMakeSpan::new()
157                        .level(Level::INFO)
158                        .include_headers(false),
159                )
160                .on_response(DefaultOnResponse::new().level(Level::INFO)),
161        )
162        .layer(cors)
163        .with_state(state)
164}
165
166/// Create a combined API router with both graph/ws and ops endpoints.
167///
168/// This creates a router that serves:
169/// - `/ops/*` - Operations API (sync, graph build, status, etc.)
170/// - `/health`, `/graph/*`, `/git/*`, `/ws` - Graph visualization API
171pub fn create_full_api_router(api_state: Arc<ApiState>, ops_ctx: OpsContext) -> Router {
172    let cors = CorsLayer::new()
173        .allow_origin(Any)
174        .allow_methods(Any)
175        .allow_headers(Any);
176
177    // Create the ops router (already has state applied, becomes Router<()>)
178    let ops_router = create_ops_router(ops_ctx);
179
180    // Create the visualization API router with its state
181    let viz_router = Router::new()
182        .route("/health", get(health::health_handler))
183        .route("/graph", get(graph::graph_handler))
184        .route("/graph/nodes", get(graph::nodes_handler))
185        .route("/graph/edges", get(graph::edges_handler))
186        .route("/graph/metadata", get(graph::metadata_handler))
187        .route("/git/changes", get(git::changes_handler))
188        .route("/ws", get(ws_handler))
189        .with_state(api_state);
190
191    // Merge both routers
192    Router::new()
193        .nest("/ops", ops_router)
194        .merge(viz_router)
195        .layer(
196            TraceLayer::new_for_http()
197                .make_span_with(
198                    DefaultMakeSpan::new()
199                        .level(Level::INFO)
200                        .include_headers(false),
201                )
202                .on_response(DefaultOnResponse::new().level(Level::INFO)),
203        )
204        .layer(cors)
205}
206
207/// Create a complete API router including git commands (single-repo).
208///
209/// This creates a router that serves:
210/// - `/ops/*` - Operations API (sync, graph build, status, etc.)
211/// - `/git/cmd/*` - Git command API (add, commit, reset, etc.)
212/// - `/health`, `/graph/*`, `/git/changes`, `/ws` - Graph visualization API
213pub fn create_full_api_router_with_git(
214    api_state: Arc<ApiState>,
215    ops_ctx: OpsContext,
216    workspace_path: PathBuf,
217) -> Router {
218    let git_cmd_router = create_git_commands_router(workspace_path);
219    build_full_api_router_with_git_router(api_state, ops_ctx, git_cmd_router)
220}
221
222/// Create a complete API router including git commands (multi-repo).
223///
224/// This creates a router that serves:
225/// - `/ops/*` - Operations API (sync, graph build, status, etc.)
226/// - `/git/cmd/*` - Git command API with multi-repo support
227/// - `/health`, `/graph/*`, `/git/changes`, `/ws` - Graph visualization API
228pub fn create_full_api_router_with_git_multi(
229    api_state: Arc<ApiState>,
230    ops_ctx: OpsContext,
231    workspace_path: PathBuf,
232    repos: Vec<(String, PathBuf)>,
233) -> Router {
234    let git_cmd_router = create_git_commands_router_multi(workspace_path, repos);
235    build_full_api_router_with_git_router(api_state, ops_ctx, git_cmd_router)
236}
237
238/// Internal helper to build full router with git commands.
239fn build_full_api_router_with_git_router(
240    api_state: Arc<ApiState>,
241    ops_ctx: OpsContext,
242    git_cmd_router: Router,
243) -> Router {
244    let cors = CorsLayer::new()
245        .allow_origin(Any)
246        .allow_methods(Any)
247        .allow_headers(Any);
248
249    // Create the ops router
250    let ops_router = create_ops_router(ops_ctx);
251
252    // Create the visualization API router with its state
253    let viz_router = Router::new()
254        .route("/health", get(health::health_handler))
255        .route("/graph", get(graph::graph_handler))
256        .route("/graph/nodes", get(graph::nodes_handler))
257        .route("/graph/edges", get(graph::edges_handler))
258        .route("/graph/metadata", get(graph::metadata_handler))
259        .route("/git/changes", get(git::changes_handler))
260        .route("/ws", get(ws_handler))
261        .with_state(api_state);
262
263    // Merge all routers
264    Router::new()
265        .nest("/ops", ops_router)
266        .nest("/git/cmd", git_cmd_router)
267        .merge(viz_router)
268        .layer(
269            TraceLayer::new_for_http()
270                .make_span_with(
271                    DefaultMakeSpan::new()
272                        .level(Level::INFO)
273                        .include_headers(false),
274                )
275                .on_response(DefaultOnResponse::new().level(Level::INFO)),
276        )
277        .layer(cors)
278}