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 downloaded bytes for a folder/network as a bandwidth event.
130    ///
131    /// # Errors
132    ///
133    /// Returns an error if the database write fails.
134    pub fn record_bandwidth_download(
135        &self,
136        bytes: u64,
137        files: u64,
138        folder_namespace: Option<&str>,
139        peer: Option<&str>,
140        network_id: Option<&str>,
141    ) -> Result<()> {
142        if bytes == 0 {
143            return Ok(());
144        }
145        self.database
146            .record_download(bytes, files, folder_namespace, peer, network_id)
147    }
148
149    /// Record uploaded bytes for a folder/network as a bandwidth event.
150    ///
151    /// # Errors
152    ///
153    /// Returns an error if the database write fails.
154    pub fn record_bandwidth_upload(
155        &self,
156        bytes: u64,
157        files: u64,
158        folder_namespace: Option<&str>,
159        peer: Option<&str>,
160        network_id: Option<&str>,
161    ) -> Result<()> {
162        if bytes == 0 {
163            return Ok(());
164        }
165        self.database
166            .record_upload(bytes, files, folder_namespace, peer, network_id)
167    }
168
169    /// Record a relay health check result.
170    ///
171    /// # Errors
172    ///
173    /// Returns an error if the database write fails.
174    pub fn record_relay_check(
175        &self,
176        relay_url: &str,
177        connected: bool,
178        latency_ms: Option<i64>,
179        error_message: Option<&str>,
180    ) -> Result<()> {
181        self.database
182            .record_relay_check(relay_url, connected, latency_ms, error_message)
183    }
184
185    /// Query recent events for a network.
186    ///
187    /// # Errors
188    ///
189    /// Returns an error if the database query fails.
190    pub fn recent_events(&self, network_id: &str, limit: usize) -> Result<Vec<NetworkEventRecord>> {
191        self.database.recent_network_events(network_id, limit)
192    }
193
194    /// Query recent sync sessions for a network.
195    ///
196    /// # Errors
197    ///
198    /// Returns an error if the database query fails.
199    pub fn recent_sessions(&self, network_id: &str, limit: usize) -> Result<Vec<SyncSessionRecord>> {
200        self.database.recent_sync_sessions(network_id, limit)
201    }
202
203    /// Calculate relay uptime as a ratio (0.0-1.0) over the given time window.
204    ///
205    /// # Errors
206    ///
207    /// Returns an error if the database query fails.
208    pub fn relay_uptime(&self, relay_url: &str, window_secs: i64) -> Result<f64> {
209        self.database.relay_uptime(relay_url, window_secs)
210    }
211}