Skip to content

Conversation

@dest4590
Copy link
Owner

No description provided.

Copilot AI review requested due to automatic review settings December 19, 2025 18:29
@dest4590 dest4590 merged commit 1229a64 into main Dec 19, 2025
2 of 5 checks passed
Copy link

Copilot AI left a 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.

Comment on lines 105 to 135
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;
}
Copy link

Copilot AI Dec 19, 2025

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.

Copilot uses AI. Check for mistakes.

Sentry.init({
app,
dsn: "https://2220bc70de22a1841c3792c4bf314731@o4510521933889536.ingest.de.sentry.io/4510521935528016",
Copy link

Copilot AI Dec 19, 2025

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.

Copilot uses AI. Check for mistakes.
Comment on lines +55 to 56
this.client.interceptors.request.use((config) => {
const baseUrl = getAuthUrl();
Copy link

Copilot AI Dec 19, 2025

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.

Copilot uses AI. Check for mistakes.
Comment on lines +85 to +87
pub async fn get_server_connectivity_status() -> ServerConnectivityStatus {
let servers = &SERVERS;
servers.wait_for_initial_check().await;
Copy link

Copilot AI Dec 19, 2025

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.

Suggested change
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;

Copilot uses AI. Check for mistakes.
native-dialog = "0.9.4"
sysinfo = "0.37.2"
backoff = { version = "0.4.0", features = ["tokio"] }
socket2 = "0.6.1"
Copy link

Copilot AI Dec 19, 2025

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.

Copilot uses AI. Check for mistakes.
Sentry.init({
app,
dsn: "https://2220bc70de22a1841c3792c4bf314731@o4510521933889536.ingest.de.sentry.io/4510521935528016",
sendDefaultPii: true,
Copy link

Copilot AI Dec 19, 2025

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.

Copilot uses AI. Check for mistakes.
if (this.shouldCache(url, method)) {
if (cached && Date.now() - cached.timestamp < cached.ttl) {
this.metrics.cacheHits++;
console.log(`Cache hit for ${url}`);
Copy link

Copilot AI Dec 19, 2025

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.

Copilot uses AI. Check for mistakes.
Comment on lines +71 to +92
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;
}
Copy link

Copilot AI Dec 19, 2025

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.

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +33
#[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
}
Copy link

Copilot AI Dec 19, 2025

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.

Copilot uses AI. Check for mistakes.
Comment on lines +75 to +97
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;
}
}
});
Copy link

Copilot AI Dec 19, 2025

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.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants