Skip to main content

syncweb_core/net/
network_log.rs

1use std::sync::Arc;
2
3use crate::{
4    Result,
5    storage::stats_db::{NetworkEventRecord, StatsDatabase, SyncSessionRecord},
6};
7
8/// Events that can be recorded in the network event log.
9#[derive(Clone, Copy, Debug)]
10#[non_exhaustive]
11pub enum NetworkEventType {
12    PeerJoined,
13    PeerLeft,
14    SyncStarted,
15    SyncFinished,
16    RelayConnected,
17    RelayDisconnected,
18    RelayFailed,
19    TopicSubscribed,
20    TopicUnsubscribed,
21    MemberAdded,
22    MemberRemoved,
23    FolderAdded,
24    FolderRemoved,
25    TicketCreated,
26    TicketAccepted,
27    Kicked,
28}
29
30impl NetworkEventType {
31    #[must_use]
32    pub const fn as_str(&self) -> &'static str {
33        match self {
34            Self::PeerJoined => "peer_joined",
35            Self::PeerLeft => "peer_left",
36            Self::SyncStarted => "sync_started",
37            Self::SyncFinished => "sync_finished",
38            Self::RelayConnected => "relay_connected",
39            Self::RelayDisconnected => "relay_disconnected",
40            Self::RelayFailed => "relay_failed",
41            Self::TopicSubscribed => "topic_subscribed",
42            Self::TopicUnsubscribed => "topic_unsubscribed",
43            Self::MemberAdded => "member_added",
44            Self::MemberRemoved => "member_removed",
45            Self::FolderAdded => "folder_added",
46            Self::FolderRemoved => "folder_removed",
47            Self::TicketCreated => "ticket_created",
48            Self::TicketAccepted => "ticket_accepted",
49            Self::Kicked => "kicked",
50        }
51    }
52}
53
54/// Logger for network-level events, sync sessions, and relay health.
55///
56/// All records are written to `stats.db`.
57#[derive(Clone, Debug)]
58pub struct NetworkLogger {
59    database: Arc<StatsDatabase>,
60}
61
62impl NetworkLogger {
63    /// Create a new `NetworkLogger` backed by the given stats database.
64    #[must_use]
65    pub fn new(database: StatsDatabase) -> Self {
66        Self {
67            database: Arc::new(database),
68        }
69    }
70
71    /// Create a new `NetworkLogger` from an already-`Arc`-wrapped database.
72    #[must_use]
73    pub const fn from_arc(database: Arc<StatsDatabase>) -> Self {
74        Self { database }
75    }
76
77    /// Record a network event.
78    ///
79    /// # Errors
80    ///
81    /// Returns an error if the database write fails.
82    pub fn record_event(
83        &self,
84        network_id: &str,
85        event: NetworkEventType,
86        peer: Option<&str>,
87        details: Option<&str>,
88    ) -> Result<()> {
89        self.database
90            .record_network_event(network_id, event.as_str(), peer, details, None)
91    }
92
93    /// Record a network event with metadata JSON.
94    ///
95    /// # Errors
96    ///
97    /// Returns an error if the database write fails.
98    pub fn record_event_with_metadata(
99        &self,
100        network_id: &str,
101        event: NetworkEventType,
102        peer: Option<&str>,
103        details: Option<&str>,
104        metadata_json: Option<&str>,
105    ) -> Result<()> {
106        self.database
107            .record_network_event(network_id, event.as_str(), peer, details, metadata_json)
108    }
109
110    /// Record the start of a sync session. Returns the session ID.
111    ///
112    /// # Errors
113    ///
114    /// Returns an error if the database write fails.
115    pub fn record_sync_start(&self, network_id: &str, folder_namespace: &str) -> Result<i64> {
116        self.database.record_sync_session_start(network_id, folder_namespace)
117    }
118
119    /// Record the finish of a sync session.
120    ///
121    /// # Errors
122    ///
123    /// Returns an error if the database write fails.
124    pub fn record_sync_finish(&self, session_id: i64, files: u64, bytes: u64, errors: u64, status: &str) -> Result<()> {
125        self.database
126            .record_sync_session_finish(session_id, files, bytes, errors, status)
127    }
128
129    /// Record a relay health check result.
130    ///
131    /// # Errors
132    ///
133    /// Returns an error if the database write fails.
134    pub fn record_relay_check(
135        &self,
136        relay_url: &str,
137        connected: bool,
138        latency_ms: Option<i64>,
139        error_message: Option<&str>,
140    ) -> Result<()> {
141        self.database
142            .record_relay_check(relay_url, connected, latency_ms, error_message)
143    }
144
145    /// Query recent events for a network.
146    ///
147    /// # Errors
148    ///
149    /// Returns an error if the database query fails.
150    pub fn recent_events(&self, network_id: &str, limit: usize) -> Result<Vec<NetworkEventRecord>> {
151        self.database.recent_network_events(network_id, limit)
152    }
153
154    /// Query recent sync sessions for a network.
155    ///
156    /// # Errors
157    ///
158    /// Returns an error if the database query fails.
159    pub fn recent_sessions(&self, network_id: &str, limit: usize) -> Result<Vec<SyncSessionRecord>> {
160        self.database.recent_sync_sessions(network_id, limit)
161    }
162
163    /// Calculate relay uptime as a ratio (0.0-1.0) over the given time window.
164    ///
165    /// # Errors
166    ///
167    /// Returns an error if the database query fails.
168    pub fn relay_uptime(&self, relay_url: &str, window_secs: i64) -> Result<f64> {
169        self.database.relay_uptime(relay_url, window_secs)
170    }
171}