1use std::sync::Arc;
2
3use hyphae::{LeftJoinExt, MapExt};
4
5use crate::{
6 entities::client::{ClientServerIdRelation, GetAllClients},
7 prelude::*,
8 report::{ReportContext, ReportHandler},
9};
10
11#[myko_item]
12#[derive(Eq)]
13pub struct Server {
14 pub version: String,
15 #[searchable]
16 pub address: String,
17 pub port: u16,
18 pub started_at: String, }
20
21#[myko_query(Server)]
22pub struct GetConnectedServer {}
23
24impl QueryHandler for GetConnectedServer {
25 fn test_entity(ctx: QueryTestContext<Self>) -> bool {
26 let item_id = ctx.item.id.to_string();
27 let host_id = ctx.query_context.req.host_id.to_string();
28 item_id == host_id
29 }
30
31 #[cfg(not(target_arch = "wasm32"))]
32 fn build_view(
33 ctx: QueryBuildArgs<Self>,
34 ) -> Option<impl MapQuery<Key = Arc<str>, Value = Arc<dyn AnyItem>>>
35 where
36 Self: Send + Sync + 'static,
37 {
38 let host_id: Arc<str> = ctx
39 .query_context
40 .query_context
41 .req
42 .host_id
43 .to_string()
44 .into();
45 let store = ctx
46 .query_context
47 .registry()
48 .get_or_create(Server::ENTITY_NAME_STATIC);
49 Some(crate::query::build_ids_source_map(&store, &[host_id]))
50 }
51}
52
53#[myko_query(Server)]
54pub struct GetPeerServers {}
55
56impl QueryHandler for GetPeerServers {
57 fn test_entity(ctx: QueryTestContext<Self>) -> bool {
58 let item_id = ctx.item.id.to_string();
59 let host_id = ctx.query_context.req.host_id.to_string();
60 item_id != host_id
61 }
62
63 #[cfg(not(target_arch = "wasm32"))]
64 fn build_view(
65 ctx: QueryBuildArgs<Self>,
66 ) -> Option<impl MapQuery<Key = Arc<str>, Value = Arc<dyn AnyItem>>>
67 where
68 Self: Send + Sync + 'static,
69 {
70 let host_id = ctx.query_context.query_context.req.host_id.to_string();
71 let store = ctx
72 .query_context
73 .registry()
74 .get_or_create(Server::ENTITY_NAME_STATIC)
75 .as_ref()
76 .clone()
77 .lock();
78 Some(store.select_by(move |id, _server| id.as_ref() != host_id))
79 }
80}
81
82#[myko_macros::myko_report_output]
89#[derive(Eq)]
90pub struct ServerStatsOutput {
91 pub server: Option<Arc<Server>>,
93 pub client_count: usize,
95 pub uptime_seconds: Option<i64>,
97}
98
99#[myko_macros::myko_report(ServerStatsOutput)]
122pub struct ServerStats {}
123
124impl ReportHandler for ServerStats {
125 type Output = ServerStatsOutput;
126
127 fn compute(&self, ctx: ReportContext) -> impl Materialize<Arc<Self::Output>, Definite> {
128 let host_id: Arc<str> = ctx.host_id().to_string().into();
129 let stats_by_server = ctx
133 .query_map_by_str(GetConnectedServer {})
134 .left_join_fk::<ClientServerIdRelation, _>(ctx.query_map_by_str(GetAllClients {}))
135 .map_joined_values(|_server_id, server, clients| (server.clone(), clients.len()))
136 .materialize();
137
138 stats_by_server.get(&host_id).map(|stats| {
139 let Some((server, client_count)) = stats else {
140 return Arc::new(ServerStatsOutput {
141 server: None,
142 client_count: 0,
143 uptime_seconds: None,
144 });
145 };
146 let uptime_seconds = chrono::DateTime::parse_from_rfc3339(&server.started_at)
147 .ok()
148 .map(|started| {
149 let now = chrono::Utc::now();
150 now.signed_duration_since(started.with_timezone(&chrono::Utc))
151 .num_seconds()
152 });
153 Arc::new(ServerStatsOutput {
154 server: Some(server.clone()),
155 client_count: *client_count,
156 uptime_seconds,
157 })
158 })
159 }
160}