-
Notifications
You must be signed in to change notification settings - Fork 12
0.2.5 winter #89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
0.2.5 winter #89
Conversation
updated because fedora doesn't uses webkit2gtk <4.1 anymore
skip-ci
skip-ci
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR bumps the version to 0.2.5 with the "Winter" codename and introduces several significant changes including a winter theme, Sentry error tracking integration, IRC improvements, and refactoring of server connectivity logic.
Key Changes:
- Addition of Sentry SDK for error tracking and session replay
- Implementation of winter event theme system
- Removal of Halloween event code and greetings
- Refactoring of server connectivity to remove circuit breaker and backoff retry logic
- IRC enhancements including ping mechanism, user profile integration, and room state tracking
- API client improvements with metrics tracking
Reviewed changes
Copilot reviewed 36 out of 38 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| src/main.ts | Adds Sentry integration with PII collection enabled |
| src/utils/events.ts | Adds winter event detection and theme application, removes Halloween greeting function |
| src/services/apiClient.ts | Removes ensureAuthUrl, adds request metrics tracking and cache statistics |
| src/services/syncService.ts | Optimizes favorites sync to check for changes before updating |
| src/composables/useUser.ts | Simplifies race condition handling (removes promise-based approach) |
| src/composables/useIrcChat.ts | Adds online users/guests tracking and sender information parsing |
| src/components/features/social/InlineIRCChat.vue | Major refactoring with user profile integration and enhanced message parsing |
| src-tauri/src/core/network/servers.rs | Removes circuit breaker and exponential backoff, adds watch channel for initial check |
| src-tauri/src/commands/irc.rs | Adds 60-second ping interval task for IRC connection |
| src-tauri/src/core/clients/client.rs | Adds stdin piping to client process |
| package.json | Updates dependencies including Sentry, daisyui, gsap, lucide, and dev dependencies |
| src-tauri/Cargo.toml | Removes backoff dependency, updates version to 0.2.5 |
| SECURITY.md | Adds comprehensive Warden security documentation |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| for server in servers { | ||
| if server.circuit_breaker.is_open() { | ||
| log_warn!("Skipping {} Server {}", name, server.url); | ||
| continue; | ||
| } | ||
|
|
||
| let op = || async { | ||
| let resp = client.head(&server.url).send().await.map_err(|e| { | ||
| backoff::Error::<Box<dyn std::error::Error + Send + Sync>>::transient(Box::new( | ||
| e, | ||
| )) | ||
| })?; | ||
| if !resp.status().is_success() { | ||
| return Err( | ||
| backoff::Error::<Box<dyn std::error::Error + Send + Sync>>::transient( | ||
| format!("Status not success: {}", resp.status()).into(), | ||
| ), | ||
| ); | ||
| } | ||
| Ok(resp) | ||
| }; | ||
|
|
||
| let backoff = ExponentialBackoff { | ||
| max_elapsed_time: Some(BACKOFF_MAX_ELAPSED), | ||
| ..Default::default() | ||
| }; | ||
|
|
||
| match retry(backoff, op).await { | ||
| Ok(response) => { | ||
| log_info!( | ||
| "{} Server {} responded with: {}", | ||
| name, | ||
| server.url, | ||
| response.status() | ||
| ); | ||
| server.circuit_breaker.record_success(); | ||
| let mut lock = selected.write().unwrap(); | ||
| *lock = Some(server.clone()); | ||
| return; | ||
| match client.head(&server.url).send().await { | ||
| Ok(resp) => { | ||
| if resp.status().is_success() { | ||
| log_info!( | ||
| "{} Server {} responded with: {}", | ||
| name, | ||
| server.url, | ||
| resp.status() | ||
| ); | ||
| let mut lock = selected.write().unwrap(); | ||
| *lock = Some(server.clone()); | ||
| return; | ||
| } else { | ||
| log_warn!( | ||
| "{} Server {} returned status: {}", | ||
| name, | ||
| server.url, | ||
| resp.status() | ||
| ); | ||
| } | ||
| } | ||
| Err(e) => { | ||
| log_warn!("Failed to connect to {} Server {}: {}", name, server.url, e); | ||
| server.circuit_breaker.record_failure(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| let mut lock = selected.write().unwrap(); | ||
| *lock = None; | ||
| } |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The removal of the circuit breaker and backoff retry logic simplifies the code but removes resilience features. The original implementation with exponential backoff and circuit breaker patterns helped prevent cascading failures and gave failing servers time to recover. The new implementation makes a single HEAD request with no retry logic, which could lead to incorrectly marking servers as offline due to transient network issues. This significantly reduces the reliability of server selection.
|
|
||
| Sentry.init({ | ||
| app, | ||
| dsn: "https://2220bc70de22a1841c3792c4bf314731@o4510521933889536.ingest.de.sentry.io/4510521935528016", |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Sentry DSN (Data Source Name) containing the project key is hardcoded and publicly exposed in the source code. This is a security concern as it can be abused by malicious actors to send fake error reports, potentially exhausting your Sentry quota or polluting your error tracking data. Consider moving this to an environment variable or a configuration file that is not committed to version control.
| this.client.interceptors.request.use((config) => { | ||
| const baseUrl = getAuthUrl(); |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The removal of ensureAuthUrl() from the interceptor means that the auth URL will not be validated before each request. This could lead to requests being sent to an uninitialized or incorrect URL if initializeAuthUrl() hasn't completed yet. Since initializeAuthUrl() is now called asynchronously in main.ts, there's a race condition risk where API requests might be made before the auth URL is properly initialized.
| pub async fn get_server_connectivity_status() -> ServerConnectivityStatus { | ||
| let servers = &SERVERS; | ||
| servers.wait_for_initial_check().await; |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function signature is changed from synchronous to async, which is a breaking change for any code calling this function. While the change appears to add the wait_for_initial_check call to ensure the server status is up-to-date before returning, this could introduce delays in the UI. Consider whether this is the intended behavior and if there are any callers that might timeout or need adjustment.
| pub async fn get_server_connectivity_status() -> ServerConnectivityStatus { | |
| let servers = &SERVERS; | |
| servers.wait_for_initial_check().await; | |
| pub fn get_server_connectivity_status() -> ServerConnectivityStatus { | |
| let servers = &SERVERS; |
| native-dialog = "0.9.4" | ||
| sysinfo = "0.37.2" | ||
| backoff = { version = "0.4.0", features = ["tokio"] } | ||
| socket2 = "0.6.1" |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The removal of the backoff dependency from Cargo.toml but the continued use of circuit breaker-related logic removal suggests this was an intentional simplification. However, removing retry logic and circuit breakers from production code that handles network operations is generally considered a step backward in terms of reliability and resilience. Consider re-evaluating this decision or documenting why it was necessary.
| Sentry.init({ | ||
| app, | ||
| dsn: "https://2220bc70de22a1841c3792c4bf314731@o4510521933889536.ingest.de.sentry.io/4510521935528016", | ||
| sendDefaultPii: true, |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The sendDefaultPii option is set to true, which means Sentry will collect Personally Identifiable Information (PII) such as user IP addresses, usernames, and email addresses. This should only be enabled if you have explicit user consent and a privacy policy that covers this data collection. Consider setting this to false unless PII collection is absolutely necessary and properly disclosed.
| if (this.shouldCache(url, method)) { | ||
| if (cached && Date.now() - cached.timestamp < cached.ttl) { | ||
| this.metrics.cacheHits++; | ||
| console.log(`Cache hit for ${url}`); |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A console.log statement is being added to production code for cache hits. This can clutter the console and expose internal implementation details to users. Consider removing this debug logging or wrapping it in a development-only check.
| if (globalUserState.isLoading) { | ||
| return; | ||
| } | ||
|
|
||
| globalUserState.isLoading = true; | ||
|
|
||
| loadPromise = (async () => { | ||
| try { | ||
| const initData = await userService.initializeUser(); | ||
| try { | ||
| const initData = await userService.initializeUser(); | ||
|
|
||
| globalUserState.profile = initData.profile; | ||
| globalUserState.info = initData.user_info; | ||
| globalUserState.adminStatus = initData.admin_status; | ||
| globalUserState.syncStatus = initData.sync_status; | ||
| globalUserState.lastUpdated = new Date().toISOString(); | ||
| globalUserState.isLoaded = true; | ||
|
|
||
| console.log('Global user data loaded successfully'); | ||
| } catch (error) { | ||
| console.error('Failed to load global user data:', error); | ||
| } finally { | ||
| globalUserState.isLoading = false; | ||
| loadPromise = null; | ||
| } | ||
| })(); | ||
| globalUserState.profile = initData.profile; | ||
| globalUserState.info = initData.user_info; | ||
| globalUserState.adminStatus = initData.admin_status; | ||
| globalUserState.syncStatus = initData.sync_status; | ||
| globalUserState.lastUpdated = new Date().toISOString(); | ||
| globalUserState.isLoaded = true; | ||
|
|
||
| return loadPromise; | ||
| console.log('Global user data loaded successfully'); | ||
| } catch (error) { | ||
| console.error('Failed to load global user data:', error); | ||
| } finally { | ||
| globalUserState.isLoading = false; | ||
| } |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The race condition fix removes the loadPromise variable but replaces it with a simple isLoading check. However, this doesn't actually prevent race conditions - if two calls to loadUserData() happen simultaneously, the second call will return early while the first is still loading, but the second caller won't wait for the first to complete. The original implementation with loadPromise was correct as it allowed subsequent calls to wait for the ongoing load operation. Consider reverting to the promise-based approach or implementing a proper deferred promise pattern.
| #[serde(skip)] | ||
| pub flags_path: PathBuf, | ||
| } | ||
|
|
||
| impl Flags { | ||
| pub fn load_from_disk(path: PathBuf) -> Self { | ||
| let mut loaded = <Self as JsonStorage>::load_from_disk(path.clone()); | ||
| loaded.flags_path = path; | ||
| loaded | ||
| } |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The #[serde(skip)] attribute is added to path fields, but the custom load_from_disk methods manually set these paths after deserialization. This pattern is inconsistent across the codebase and could lead to issues if the trait's load_from_disk method is called directly instead of the custom implementation. Consider documenting why this pattern is necessary or implementing a more robust solution using serde's #[serde(default)] with a custom deserializer.
| tokio::spawn(async move { | ||
| let mut interval = time::interval(Duration::from_secs(60)); | ||
|
|
||
| interval.tick().await; | ||
|
|
||
| loop { | ||
| interval.tick().await; | ||
|
|
||
| let mut writer_guard = writer_for_pinger.lock().await; | ||
|
|
||
| if let Some(writer) = writer_guard.as_mut() { | ||
| let ping_packet = json!({ "op": "ping" }); | ||
| let ping_str = format!("{}\n", ping_packet.to_string()); | ||
|
|
||
| if let Err(e) = writer.write_all(ping_str.as_bytes()).await { | ||
| log_error!("IRC Pinger failed to write: {}", e); | ||
| break; | ||
| } | ||
| } else { | ||
| break; | ||
| } | ||
| } | ||
| }); |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The IRC ping mechanism spawns a tokio task that runs indefinitely with a 60-second interval, but there's no mechanism to stop this task when the IRC connection is closed or the application shuts down. This could lead to resource leaks if connections are frequently opened and closed. Consider storing the task handle and cancelling it appropriately, or using tokio's cancellation tokens.
No description provided.