feat: added live updates for checking in

This commit is contained in:
Andrew Rioux 2025-02-22 20:30:32 -05:00
parent f284cf47eb
commit e103fa9f28
Signed by: andrew.rioux
GPG Key ID: 9B8BAC47C17ABB94
6 changed files with 62 additions and 16 deletions

View File

@ -12,6 +12,7 @@ pub enum Error {
Rustls(rustls::Error), Rustls(rustls::Error),
Rcgen(rcgen::Error), Rcgen(rcgen::Error),
WebPki(rustls::client::VerifierBuilderError), WebPki(rustls::client::VerifierBuilderError),
TokioSend,
} }
impl std::fmt::Display for Error { impl std::fmt::Display for Error {
@ -38,6 +39,9 @@ impl std::fmt::Display for Error {
Error::WebPki(err) => { Error::WebPki(err) => {
write!(f, "webpki error: {err:?}") write!(f, "webpki error: {err:?}")
} }
Error::TokioSend => {
write!(f, "tokio broadcast error")
}
} }
} }
} }
@ -105,3 +109,9 @@ impl From<rustls::client::VerifierBuilderError> for Error {
Self::WebPki(err) Self::WebPki(err)
} }
} }
impl<T> From<tokio::sync::broadcast::error::SendError<T>> for Error {
fn from(_: tokio::sync::broadcast::error::SendError<T>) -> Self {
Self::TokioSend
}
}

View File

@ -107,9 +107,7 @@ pub async fn start_listener(
.fetch_one(&db) .fetch_one(&db)
.await?; .await?;
let sender = broadcast::Sender::new(128); let app = router::get_router(db, beacon_event_broadcast.clone());
let app = router::get_router(db, sender.clone());
let ca_cert = rustls::pki_types::CertificateDer::from(listener.certificate.clone()); let ca_cert = rustls::pki_types::CertificateDer::from(listener.certificate.clone());
@ -182,7 +180,7 @@ pub async fn start_listener(
)); ));
}; };
blm_handle.insert(listener_id, BeaconListenerHandle { join_handle, events_broadcast: sender }); blm_handle.insert(listener_id, BeaconListenerHandle { join_handle, events_broadcast: beacon_event_broadcast });
Ok(()) Ok(())
} }

View File

@ -76,6 +76,8 @@ pub async fn handle_checkin(
let current_beacon_reg = match current_beacon_reg { let current_beacon_reg = match current_beacon_reg {
Some(rec) => { Some(rec) => {
state.event_publisher.send(BeaconEvent::Checkin(reg.beacon_id.clone()))?;
parse_db_config(rec) parse_db_config(rec)
}, },
None => { None => {
@ -111,6 +113,8 @@ pub async fn handle_checkin(
.fetch_one(&state.db) .fetch_one(&state.db)
.await?; .await?;
state.event_publisher.send(BeaconEvent::NewBeacon(reg.beacon_id.clone()))?;
parse_db_config(rec) parse_db_config(rec)
} }
}; };

View File

@ -1,6 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use leptos::{either::Either, prelude::*}; use leptos::{either::Either, prelude::*};
use leptos_router::components::A;
#[cfg(feature = "hydrate")] #[cfg(feature = "hydrate")]
use leptos_use::use_websocket; use leptos_use::use_websocket;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -62,7 +63,7 @@ pub struct CurrentBeaconInstance {
unsafe impl Send for CurrentBeaconInstance {} unsafe impl Send for CurrentBeaconInstance {}
unsafe impl Send for SidebarEvents {} unsafe impl Send for SidebarEvents {}
#[derive(Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SidebarEvents { pub enum SidebarEvents {
BeaconList(Vec<CurrentBeaconInstance>), BeaconList(Vec<CurrentBeaconInstance>),
NewBeacon(CurrentBeaconInstance), NewBeacon(CurrentBeaconInstance),
@ -99,6 +100,8 @@ pub fn BeaconSidebar() -> impl IntoView {
return; return;
}; };
leptos::logging::log!("Received message: {m:?}");
match m { match m {
SidebarEvents::BeaconList(bs) => { SidebarEvents::BeaconList(bs) => {
let mut bs = bs.to_vec(); let mut bs = bs.to_vec();
@ -125,6 +128,7 @@ pub fn BeaconSidebar() -> impl IntoView {
return; return;
}; };
if let Some(ref mut b) = bs.iter_mut().find(|b| b.beacon_id == *bid) { if let Some(ref mut b) = bs.iter_mut().find(|b| b.beacon_id == *bid) {
leptos::logging::log!("Found beacon to check in");
b.last_checkin = chrono::Utc::now(); b.last_checkin = chrono::Utc::now();
} }
}), }),
@ -163,10 +167,6 @@ pub fn BeaconSidebar() -> impl IntoView {
let partitions = move || -> Vec<BeaconPartition> { let partitions = move || -> Vec<BeaconPartition> {
use regex::Regex; use regex::Regex;
leptos::logging::log!(
"There are {:?} beacons",
current_beacons.read().as_ref().map(Vec::len)
);
let sm = sort_method.read(); let sm = sort_method.read();
let search_regex = Regex::new(&*search_input.read()); let search_regex = Regex::new(&*search_input.read());
let str_match_search = { let str_match_search = {
@ -558,23 +558,26 @@ pub fn BeaconSidebar() -> impl IntoView {
<For <For
each={ each={
let beacons = Arc::clone(&partition.beacons); move || (partition.beacons)()
move || (beacons)()
} }
key=|b| b.beacon_id.clone() key=|b| (b.beacon_id.clone(), b.last_checkin)
let:beacon let:beacon
> >
<div class="beacon-instance"> <div class="beacon-instance">
<div class="beacon-instance-id"> <div class="beacon-instance-id">
{match &*beacon.nickname { {match &*beacon.nickname {
"" => Either::Left(view! { "" => Either::Left(view! {
<A href=format!("/beacons/beacon/{}", &beacon.beacon_id)>
<span>{beacon.beacon_id.clone()}</span> <span>{beacon.beacon_id.clone()}</span>
</A>
}), }),
nick => { nick => {
let nick = nick.to_string(); let nick = nick.to_string();
Either::Right(view! { Either::Right(view! {
<A href=format!("/beacons/beacon/{}", &beacon.beacon_id)>
{nick} {nick}
<span>"(" {beacon.beacon_id.clone()} ")"</span> <span>"(" {beacon.beacon_id.clone()} ")"</span>
</A>
}) })
} }
}} }}

View File

@ -430,8 +430,35 @@ async fn handle_listener_events(
let mut event_receiver = state.beacon_event_broadcast.subscribe(); let mut event_receiver = state.beacon_event_broadcast.subscribe();
loop { loop {
use sparse_handler::BeaconEvent;
let event = event_receiver.recv().await; let event = event_receiver.recv().await;
match event {
Ok(BeaconEvent::Checkin(bid)) => {
socket.send(ws::Message::Text(serde_json::to_string(&SidebarEvents::Checkin(bid.clone()))?)).await?;
}
Ok(BeaconEvent::NewBeacon(bid)) => {
let beacons = sqlx::query!(
"SELECT template_id, peer_ip, nickname, cwd, operating_system, beacon_userent, hostname, config_id FROM beacon_instance
WHERE beacon_id = ?",
bid
)
.fetch_one(&state.db)
.await?;
let category_ids = sqlx::query!(
"SELECT category_id FROM beacon_category_assignment
WHERE beacon_id = ?",
bid
)
.fetch_one(&state.db)
.await?;
}
Err(e) => {
tracing::warn!("Unable to handle general event: {e:?}");
}
}
} }
} }

View File

@ -66,7 +66,11 @@ aside.beacons {
color: yellow; color: yellow;
} }
.beacon-instance-id { .beacon-instance-id a {
text-decoration: none;
&:hover {
text-decoration: underline;
}
} }
} }