Skip to content

Conversation

@lollipopkit
Copy link
Owner

@lollipopkit lollipopkit commented Sep 2, 2025

Fixes #748

Summary by Sourcery

Introduce comprehensive server connection statistics by modeling and persisting per-connection data, instrumenting SSH flows to record attempts, and adding a UI page for viewing and managing stats.

New Features:

  • Add ConnectionStat and ServerConnectionStats data models with Hive persistence and type adapters
  • Implement ConnectionStatsStore to record, purge, and query connection attempts over time
  • Instrument SSH connection logic to log both successful and categorized failed attempts with timestamps and durations
  • Add a Connection Statistics entry in settings and a dedicated page displaying per-server success rates, recent attempts, and detailed history with refresh and clear actions

Enhancements:

  • Categorize connection failures into timeout, authentication, network, or unknown types
  • Automatically remove connection records older than 30 days

Documentation:

  • Update CLAUDE.md with guidance on using hive_ce, fl_lib utilities, and code generation commands

@sourcery-ai
Copy link

sourcery-ai bot commented Sep 2, 2025

Reviewer's Guide

Implements server connection statistics by introducing freezed data models and Hive adapters, logging connection outcomes in the server provider, persisting and cleaning records via a new ConnectionStatsStore, and adding a settings entry with a dedicated UI for viewing and managing connection history.

File-Level Changes

Change Details Files
Introduce connection statistics models and Hive adapters
  • Define ConnectionStat and ServerConnectionStats with Freezed annotations
  • Implement ConnectionResult enum with display extension
  • Generate JSON and Hive TypeAdapters for both models
  • Update Hive registrar to register new adapters
lib/data/model/server/connection_stat.dart
lib/data/model/server/connection_stat.freezed.dart
lib/data/model/server/connection_stat.g.dart
lib/hive/hive_registrar.g.dart
Implement ConnectionStatsStore and integrate into DI
  • Create ConnectionStatsStore with record, cleanup, query, and clear methods
  • Register ConnectionStatsStore in Stores and include in backup list
lib/data/store/connection_stats.dart
lib/data/res/store.dart
Log connection attempts in ServerNotifier
  • Record successful connections with timestamp and duration
  • Catch exceptions, categorize failure reasons, and record failed attempts
lib/data/provider/server/single.dart
Add UI entry and ConnectionStatsPage
  • Add connection stats ListTile to settings page
  • Implement ConnectionStatsPage with server cards, dialogs for details, and actions to clear stats
lib/view/page/setting/entries/server.dart
lib/view/page/setting/entry.dart
lib/view/page/server/connection_stats.dart
Update generated code and documentation
  • Bump Riverpod hashes in generated container and server files
  • Adjust CLAUDE.md to reflect new conventions (hive_ce, fl_lib, l10n guidance)
lib/data/provider/container.g.dart
lib/data/provider/server/single.g.dart
CLAUDE.md

Assessment against linked issues

Issue Objective Addressed Explanation
#748 Add statistics tracking for normal devices (successful connections), abnormal devices (failed connections), and all devices.
#748 Provide a user interface to view these statistics, including success rate, total attempts, recent connection history, and details for each device.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes - here's some feedback:

  • The exception-based failure result detection is brittle—consider catching specific exception types (e.g. TimeoutException, AuthException) or encoding error metadata instead of string‐matching on exception messages.
  • Relying on string‐splitting of the Hive keys for serverId and timestamp may break if your IDs contain underscores—consider using a structured key or separate indexing fields in the store for safer lookup.
  • Scanning all keys every time you fetch stats (especially in getAllServerStats) could become slow at scale—consider grouping records by server or caching aggregated stats to avoid full key iteration on each refresh.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The exception-based failure result detection is brittle—consider catching specific exception types (e.g. TimeoutException, AuthException) or encoding error metadata instead of string‐matching on exception messages.
- Relying on string‐splitting of the Hive keys for serverId and timestamp may break if your IDs contain underscores—consider using a structured key or separate indexing fields in the store for safer lookup.
- Scanning all keys every time you fetch stats (especially in getAllServerStats) could become slow at scale—consider grouping records by server or caching aggregated stats to avoid full key iteration on each refresh.

## Individual Comments

### Comment 1
<location> `lib/data/store/connection_stats.dart:11` </location>
<code_context>
+  
+  // Record a connection attempt
+  void recordConnection(ConnectionStat stat) {
+    final key = '${stat.serverId}_${stat.timestamp.millisecondsSinceEpoch}';
+    set(key, stat);
+    _cleanOldRecords(stat.serverId);
+  }
</code_context>

<issue_to_address>
Key generation may lead to collisions for rapid consecutive connections.

Appending only millisecondsSinceEpoch to the key may not ensure uniqueness for rapid consecutive connections. Add a random or incrementing component to the key to prevent collisions.
</issue_to_address>

### Comment 2
<location> `lib/data/store/connection_stats.dart:86` </location>
<code_context>
+    }
+    
+    // Get recent connections (last 20)
+    final recentConnections = allStats.take(20).toList();
+    
+    return ServerConnectionStats(
</code_context>

<issue_to_address>
Recent connections may not be the most recent if allStats is not sorted.

Sort 'allStats' by timestamp in descending order before selecting the first 20 items to ensure accuracy. Note that this relies on getConnectionHistory's current behavior; future changes to that method could introduce bugs if sorting is not guaranteed.
</issue_to_address>

### Comment 3
<location> `lib/view/page/server/connection_stats.dart:77` </location>
<code_context>
+  }
+
+  Widget _buildServerStatsCard(ServerConnectionStats stats) {
+    final successRate = (stats.successRate * 100).toStringAsFixed(1);
+    final lastSuccessTime = stats.lastSuccessTime;
+    final lastFailureTime = stats.lastFailureTime;
</code_context>

<issue_to_address>
Success rate calculation may display misleading percentages for zero attempts.

Consider displaying 'N/A' or a placeholder instead of '0.0%' when totalAttempts is zero to prevent misinterpretation.

Suggested implementation:

```
  Widget _buildServerStatsCard(ServerConnectionStats stats) {
    final successRate = stats.totalAttempts == 0
        ? 'N/A'
        : '${(stats.successRate * 100).toStringAsFixed(1)}%';
    final lastSuccessTime = stats.lastSuccessTime;
    final lastFailureTime = stats.lastFailureTime;

```

If you are displaying `successRate` elsewhere in the card (e.g., in a `Text` widget), ensure you remove any hardcoded '%' sign, as the replacement above already includes it when appropriate.
</issue_to_address>

### Comment 4
<location> `CLAUDE.md:95` </location>
<code_context>
+  - `libL10n` is from `fl_lib` package, and `l10n` is from this project.
+  - Before adding new strings, check if it already exists in `libL10n`.
+  - Prioritize using strings from `libL10n` to avoid duplication, even if the meaning is not 100% exact, just use the substitution of `libL10n`.
+- Split UI into Widget build, Actions, Utils. use `extension on` to archive this
</code_context>

<issue_to_address>
Typo: 'archive' should be 'achieve'.

Change 'archive this' to 'achieve this' to correct the wording.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 11 to 12
final key = '${stat.serverId}_${stat.timestamp.millisecondsSinceEpoch}';
set(key, stat);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Key generation may lead to collisions for rapid consecutive connections.

Appending only millisecondsSinceEpoch to the key may not ensure uniqueness for rapid consecutive connections. Add a random or incrementing component to the key to prevent collisions.

}

// Get recent connections (last 20)
final recentConnections = allStats.take(20).toList();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (bug_risk): Recent connections may not be the most recent if allStats is not sorted.

Sort 'allStats' by timestamp in descending order before selecting the first 20 items to ensure accuracy. Note that this relies on getConnectionHistory's current behavior; future changes to that method could introduce bugs if sorting is not guaranteed.

}

Widget _buildServerStatsCard(ServerConnectionStats stats) {
final successRate = (stats.successRate * 100).toStringAsFixed(1);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Success rate calculation may display misleading percentages for zero attempts.

Consider displaying 'N/A' or a placeholder instead of '0.0%' when totalAttempts is zero to prevent misinterpretation.

Suggested implementation:

  Widget _buildServerStatsCard(ServerConnectionStats stats) {
    final successRate = stats.totalAttempts == 0
        ? 'N/A'
        : '${(stats.successRate * 100).toStringAsFixed(1)}%';
    final lastSuccessTime = stats.lastSuccessTime;
    final lastFailureTime = stats.lastFailureTime;

If you are displaying successRate elsewhere in the card (e.g., in a Text widget), ensure you remove any hardcoded '%' sign, as the replacement above already includes it when appropriate.

CLAUDE.md Outdated
- `libL10n` is from `fl_lib` package, and `l10n` is from this project.
- Before adding new strings, check if it already exists in `libL10n`.
- Prioritize using strings from `libL10n` to avoid duplication, even if the meaning is not 100% exact, just use the substitution of `libL10n`.
- Split UI into Widget build, Actions, Utils. use `extension on` to archive this
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (typo): Typo: 'archive' should be 'achieve'.

Change 'archive this' to 'achieve this' to correct the wording.

@lollipopkit lollipopkit merged commit 2466341 into main Sep 2, 2025
1 check passed
@lollipopkit lollipopkit deleted the lollipopkit/issue748 branch September 2, 2025 11:42
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.

Can developers add statistics for normal devices, abnormal devices, and all devices?

2 participants