Skip to main content

systemprompt_database/repository/service/
listing.rs

1//! Read-side listings over the `services` registry for this instance.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use systemprompt_identifiers::{InstanceId, ServiceName};
7use systemprompt_manifest::services::{ServiceModule, ServiceStatus};
8
9use super::model::{ServiceConfig, ServiceRow, into_configs};
10use super::repo::ServiceRepository;
11use crate::error::DatabaseResult;
12
13impl ServiceRepository {
14    pub async fn list_all_agent_service_names(&self) -> DatabaseResult<Vec<ServiceName>> {
15        let rows = sqlx::query!(
16            r#"SELECT name FROM services WHERE instance_id = $1 AND module_name = 'agent'"#,
17            self.instance_id.as_str()
18        )
19        .fetch_all(&*self.pool)
20        .await?;
21        Ok(rows.into_iter().map(|r| ServiceName::new(r.name)).collect())
22    }
23
24    pub async fn list_mcp_services(&self) -> DatabaseResult<Vec<ServiceConfig>> {
25        self.list_services_by_type(ServiceModule::Mcp).await
26    }
27
28    pub async fn list_all_running_services(&self) -> DatabaseResult<Vec<ServiceConfig>> {
29        let rows = sqlx::query_as!(
30            ServiceRow,
31            r#"
32            SELECT instance_id as "instance_id: InstanceId", name, module_name, status, pid, port, binary_mtime,
33                   heartbeat_at::text as "heartbeat_at!",
34                   created_at::text as "created_at!", updated_at::text as "updated_at!"
35            FROM services
36            WHERE instance_id = $1 AND status = $2
37            ORDER BY name
38            "#,
39            self.instance_id.as_str(),
40            ServiceStatus::Running.as_str()
41        )
42        .fetch_all(&*self.pool)
43        .await?;
44        into_configs(rows)
45    }
46
47    pub async fn list_running_services_by_module(
48        &self,
49        module_name: ServiceModule,
50    ) -> DatabaseResult<Vec<ServiceConfig>> {
51        let rows = sqlx::query_as!(
52            ServiceRow,
53            r#"
54            SELECT instance_id as "instance_id: InstanceId", name, module_name, status, pid, port, binary_mtime,
55                   heartbeat_at::text as "heartbeat_at!",
56                   created_at::text as "created_at!", updated_at::text as "updated_at!"
57            FROM services
58            WHERE instance_id = $1 AND module_name = $2 AND status = $3
59            ORDER BY name
60            "#,
61            self.instance_id.as_str(),
62            module_name.as_str(),
63            ServiceStatus::Running.as_str()
64        )
65        .fetch_all(&*self.pool)
66        .await?;
67        into_configs(rows)
68    }
69
70    pub async fn count_running_services(
71        &self,
72        module_name: ServiceModule,
73    ) -> DatabaseResult<usize> {
74        let row = sqlx::query!(
75            r#"SELECT COUNT(*) as "count!" FROM services
76               WHERE instance_id = $1 AND module_name = $2 AND status = $3"#,
77            self.instance_id.as_str(),
78            module_name.as_str(),
79            ServiceStatus::Running.as_str()
80        )
81        .fetch_one(&*self.pool)
82        .await?;
83        Ok(usize::try_from(row.count).unwrap_or(0))
84    }
85
86    pub async fn list_running_services_with_pid(&self) -> DatabaseResult<Vec<ServiceConfig>> {
87        self.list_all_running_services().await
88    }
89
90    pub async fn list_services_by_type(
91        &self,
92        module_name: ServiceModule,
93    ) -> DatabaseResult<Vec<ServiceConfig>> {
94        let rows = sqlx::query_as!(
95            ServiceRow,
96            r#"
97            SELECT instance_id as "instance_id: InstanceId", name, module_name, status, pid, port, binary_mtime,
98                   heartbeat_at::text as "heartbeat_at!",
99                   created_at::text as "created_at!", updated_at::text as "updated_at!"
100            FROM services
101            WHERE instance_id = $1 AND module_name = $2
102            ORDER BY name
103            "#,
104            self.instance_id.as_str(),
105            module_name.as_str()
106        )
107        .fetch_all(&*self.pool)
108        .await?;
109        into_configs(rows)
110    }
111}