1use axum::{
4 routing::{delete, get, post},
5 Router,
6};
7use tower_http::{compression::CompressionLayer, cors::CorsLayer};
8
9use crate::handlers::analytics::AnalyticsState;
10use crate::handlers::AdminState;
11use crate::handlers::*;
12use crate::time_travel_handlers;
13use mockforge_core::{get_global_logger, init_global_logger};
14
15pub fn create_admin_router(
17 http_server_addr: Option<std::net::SocketAddr>,
18 ws_server_addr: Option<std::net::SocketAddr>,
19 grpc_server_addr: Option<std::net::SocketAddr>,
20 graphql_server_addr: Option<std::net::SocketAddr>,
21 api_enabled: bool,
22 admin_port: u16,
23 prometheus_url: String,
24) -> Router {
25 let _logger = get_global_logger().unwrap_or_else(|| init_global_logger(1000));
27
28 let state = AdminState::new(
29 http_server_addr,
30 ws_server_addr,
31 grpc_server_addr,
32 graphql_server_addr,
33 api_enabled,
34 admin_port,
35 );
36
37 let state_clone = state.clone();
39 tokio::spawn(async move {
40 state_clone.start_system_monitoring().await;
41 });
42 let mut router = Router::new()
43 .route("/", get(serve_admin_html))
44 .route("/assets/index.css", get(serve_admin_css))
45 .route("/assets/index.js", get(serve_admin_js))
46 .route("/api-docs", get(serve_api_docs))
47 .route("/mockforge-icon.png", get(serve_icon))
48 .route("/mockforge-icon-32.png", get(serve_icon_32))
49 .route("/mockforge-icon-48.png", get(serve_icon_48))
50 .route("/mockforge-logo.png", get(serve_logo))
51 .route("/mockforge-logo-40.png", get(serve_logo_40))
52 .route("/mockforge-logo-80.png", get(serve_logo_80));
53
54 router = router
55 .route("/__mockforge/dashboard", get(get_dashboard))
56 .route("/_mf", get(get_dashboard)) .route("/__mockforge/health", get(get_health))
58 .route("/admin/server-info", get(get_server_info))
59 .route("/__mockforge/server-info", get(get_server_info))
60 .route("/__mockforge/routes", get(get_routes))
61 .route("/__mockforge/logs", get(get_logs))
62 .route("/__mockforge/logs/sse", get(logs_sse))
63 .route("/__mockforge/metrics", get(get_metrics))
64 .route("/__mockforge/config", get(get_config))
65 .route("/__mockforge/config/latency", post(update_latency))
66 .route("/__mockforge/config/faults", post(update_faults))
67 .route("/__mockforge/config/proxy", post(update_proxy))
68 .route("/__mockforge/config/traffic-shaping", post(update_traffic_shaping))
69 .route("/__mockforge/logs", delete(clear_logs))
70 .route("/__mockforge/restart", post(restart_servers))
71 .route("/__mockforge/restart/status", get(get_restart_status))
72 .route("/__mockforge/fixtures", get(get_fixtures))
73 .route("/__mockforge/fixtures/{id}", delete(delete_fixture))
74 .route("/__mockforge/fixtures/bulk", delete(delete_fixtures_bulk))
75 .route("/__mockforge/fixtures/{id}/download", get(download_fixture))
76 .route("/__mockforge/fixtures/{id}/rename", post(rename_fixture))
77 .route("/__mockforge/fixtures/{id}/move", post(move_fixture))
78 .route("/__mockforge/import/insomnia", post(import_insomnia))
79 .route("/__mockforge/plugins", get(get_plugins))
81 .route("/__mockforge/plugins/status", get(get_plugin_status))
82 .route("/__mockforge/plugins/{id}", get(get_plugin_details))
83 .route("/__mockforge/plugins/{id}", delete(delete_plugin))
84 .route("/__mockforge/plugins/reload", post(reload_plugin))
85 .route("/__mockforge/workspaces", get(get_workspaces))
87 .route("/__mockforge/workspaces", post(create_workspace))
88 .route("/__mockforge/workspaces/{workspace_id}", get(get_workspace))
89 .route("/__mockforge/workspaces/{workspace_id}", delete(delete_workspace))
90 .route("/__mockforge/workspaces/{workspace_id}/activate", post(set_active_workspace))
91 .route("/__mockforge/workspaces/{workspace_id}/environments", get(get_environments))
93 .route("/__mockforge/workspaces/{workspace_id}/environments", post(create_environment))
94 .route("/__mockforge/workspaces/{workspace_id}/environments/order", axum::routing::put(update_environments_order))
95 .route("/__mockforge/workspaces/{workspace_id}/environments/{environment_id}", axum::routing::put(update_environment))
96 .route("/__mockforge/workspaces/{workspace_id}/environments/{environment_id}", delete(delete_environment))
97 .route("/__mockforge/workspaces/{workspace_id}/environments/{environment_id}/activate", post(set_active_environment))
98 .route("/__mockforge/workspaces/{workspace_id}/environments/{environment_id}/variables", get(get_environment_variables))
99 .route("/__mockforge/workspaces/{workspace_id}/environments/{environment_id}/variables", post(set_environment_variable))
100 .route("/__mockforge/workspaces/{workspace_id}/environments/{environment_id}/variables/{variable_name}", delete(remove_environment_variable))
101 .route("/__mockforge/chains", get(proxy_chains_list))
103 .route("/__mockforge/chains", post(proxy_chains_create))
104 .route("/__mockforge/chains/{id}", get(proxy_chain_get))
105 .route("/__mockforge/chains/{id}", axum::routing::put(proxy_chain_update))
106 .route("/__mockforge/chains/{id}", delete(proxy_chain_delete))
107 .route("/__mockforge/chains/{id}/execute", post(proxy_chain_execute))
108 .route("/__mockforge/chains/{id}/validate", post(proxy_chain_validate))
109 .route("/__mockforge/chains/{id}/history", get(proxy_chain_history))
110 .route("/__mockforge/validation", get(get_validation))
112 .route("/__mockforge/validation", post(update_validation))
113 .route("/__mockforge/env", get(get_env_vars))
115 .route("/__mockforge/env", post(update_env_var))
116 .route("/__mockforge/files/content", post(get_file_content))
118 .route("/__mockforge/files/save", post(save_file_content))
119 .route("/__mockforge/smoke", get(get_smoke_tests))
121 .route("/__mockforge/smoke/run", get(run_smoke_tests_endpoint))
122 .route("/__mockforge/time-travel/status", get(time_travel_handlers::get_time_travel_status))
124 .route("/__mockforge/time-travel/enable", post(time_travel_handlers::enable_time_travel))
125 .route("/__mockforge/time-travel/disable", post(time_travel_handlers::disable_time_travel))
126 .route("/__mockforge/time-travel/advance", post(time_travel_handlers::advance_time))
127 .route("/__mockforge/time-travel/scale", post(time_travel_handlers::set_time_scale))
128 .route("/__mockforge/time-travel/reset", post(time_travel_handlers::reset_time_travel))
129 .route("/__mockforge/time-travel/schedule", post(time_travel_handlers::schedule_response))
130 .route("/__mockforge/time-travel/scheduled", get(time_travel_handlers::list_scheduled_responses))
131 .route("/__mockforge/time-travel/scheduled/{id}", delete(time_travel_handlers::cancel_scheduled_response))
132 .route("/__mockforge/time-travel/scheduled/clear", post(time_travel_handlers::clear_scheduled_responses))
133 .route("/health/live", get(health::liveness_probe))
135 .route("/health/ready", get(health::readiness_probe))
136 .route("/health/startup", get(health::startup_probe))
137 .route("/health", get(health::deep_health_check))
138 .route("/healthz", get(health::deep_health_check))
140 .route("/readyz", get(health::readiness_probe))
141 .route("/livez", get(health::liveness_probe))
142 .route("/startupz", get(health::startup_probe));
143
144 let analytics_state = AnalyticsState::new(prometheus_url);
146
147 let analytics_router = Router::new()
148 .route("/__mockforge/analytics/summary", get(analytics::get_summary))
149 .route("/__mockforge/analytics/requests", get(analytics::get_requests))
150 .route("/__mockforge/analytics/endpoints", get(analytics::get_endpoints))
151 .route("/__mockforge/analytics/websocket", get(analytics::get_websocket))
152 .route("/__mockforge/analytics/smtp", get(analytics::get_smtp))
153 .route("/__mockforge/analytics/system", get(analytics::get_system))
154 .with_state(analytics_state);
155
156 router = router.merge(analytics_router);
157
158 {
161 use mockforge_http::{create_ui_builder_router, UIBuilderState};
162
163 let server_config = mockforge_core::config::ServerConfig::default();
166 let ui_builder_state = UIBuilderState::new(server_config);
167 let ui_builder_router = create_ui_builder_router(ui_builder_state);
168
169 router = router.nest_service("/__mockforge/ui-builder", ui_builder_router);
171 tracing::info!("UI Builder mounted at /__mockforge/ui-builder");
172 }
173
174 router = router.route("/{*path}", get(serve_admin_html));
177
178 router
179 .layer(CompressionLayer::new())
180 .layer(CorsLayer::permissive())
181 .with_state(state)
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187
188 #[tokio::test]
189 async fn test_create_admin_router() {
190 let http_addr: std::net::SocketAddr = "127.0.0.1:3000".parse().unwrap();
191 let router = create_admin_router(
192 Some(http_addr),
193 None,
194 None,
195 None,
196 true,
197 8080,
198 "http://localhost:9090".to_string(),
199 );
200
201 let _ = router;
203 }
204
205 #[tokio::test]
206 async fn test_create_admin_router_no_servers() {
207 let router = create_admin_router(
208 None,
209 None,
210 None,
211 None,
212 false,
213 8080,
214 "http://localhost:9090".to_string(),
215 );
216
217 let _ = router;
219 }
220}