systemprompt_api/services/proxy/
resolver.rs1use std::sync::Arc;
7
8use systemprompt_database::ServiceConfig;
9use systemprompt_mcp::services::McpOrchestrator;
10use systemprompt_runtime::AppContext;
11
12use super::backend::ProxyError;
13
14#[derive(Debug, Clone, Copy)]
15pub struct ServiceResolver;
16
17impl ServiceResolver {
18 pub async fn resolve(
19 service_name: &str,
20 ctx: &AppContext,
21 ) -> Result<ServiceConfig, ProxyError> {
22 let service_repo = ctx.service_repository();
23
24 let service = match service_repo.find_service_by_name(service_name).await {
25 Ok(svc) => svc,
26 Err(e) => {
27 tracing::error!(service = %service_name, error = %e, "Database error when looking up service");
28 return Err(ProxyError::DatabaseError {
29 service: service_name.to_owned(),
30 source: e,
31 });
32 },
33 };
34
35 let Some(service) = service else {
36 tracing::warn!(service = %service_name, "Service not found");
37 return Err(ProxyError::ServiceNotFound {
38 service: service_name.to_owned(),
39 });
40 };
41
42 if service.status != "running" {
43 if service.status == "crashed" {
44 tracing::info!(service = %service_name, "Service crashed, attempting restart");
45
46 if Self::attempt_restart(service_name, ctx).await.is_ok() {
47 let restarted = service_repo
48 .find_service_by_name(service_name)
49 .await
50 .map_err(|e| ProxyError::DatabaseError {
51 service: service_name.to_owned(),
52 source: e,
53 })?;
54
55 if let Some(restarted) = restarted
56 && restarted.status == "running"
57 {
58 tracing::info!(service = %service_name, "Service restarted, retrying proxy");
59 return Ok(restarted);
60 }
61
62 tracing::warn!(
63 service = %service_name,
64 "Restart reported success but the service is not running"
65 );
66 }
67 }
68
69 tracing::warn!(service = %service_name, status = %service.status, "Service not running");
70 return Err(ProxyError::ServiceNotRunning {
71 service: service_name.to_owned(),
72 status: service.status.clone(),
73 });
74 }
75
76 Ok(service)
77 }
78
79 async fn attempt_restart(service_name: &str, ctx: &AppContext) -> Result<(), ProxyError> {
80 let orchestrator = McpOrchestrator::new(
81 Arc::clone(ctx.db_pool()),
82 (**ctx.service_repository()).clone(),
83 Arc::clone(ctx.app_paths_arc()),
84 ctx.mcp_registry().clone(),
85 )
86 .map_err(|e| ProxyError::ServiceNotRunning {
87 service: service_name.to_owned(),
88 status: format!("Failed to create orchestrator: {e}"),
89 })?;
90
91 match orchestrator
92 .start_services(Some(service_name.to_owned()))
93 .await
94 {
95 Ok(()) => {},
96 Err(e) => {
97 tracing::error!(service = %service_name, error = %e, "Failed to restart service");
98 return Err(ProxyError::ServiceNotRunning {
99 service: service_name.to_owned(),
100 status: format!("Restart failed: {e}"),
101 });
102 },
103 }
104
105 tokio::time::sleep(std::time::Duration::from_millis(500)).await;
106 Ok(())
107 }
108}