Skip to content

ref(store): migrate stores to drift/pref async architecture#1061

Open
lollipopkit wants to merge 29 commits intomainfrom
fix/sqlite-migration-safety
Open

ref(store): migrate stores to drift/pref async architecture#1061
lollipopkit wants to merge 29 commits intomainfrom
fix/sqlite-migration-safety

Conversation

@lollipopkit
Copy link
Owner

@lollipopkit lollipopkit commented Feb 28, 2026

Summary

  • migrate core stores to async architecture (Drift ORM + PrefStore)
  • add unified app database schema and use app.db
  • update Hive->SQLite migrator to support per-box retry and safe completion flag
  • adapt backup/restore and providers/views to new async store APIs
  • include fl_lib submodule update required by PrefStore behavior

Verification

  • flutter analyze lib test
  • flutter test

Closes #1037

Summary by CodeRabbit

  • New Features
    • Encrypted SQLite local DB (SQLCipher) and a one‑time migration from legacy storage at startup.
  • Improvements
    • Core data moved to DB-backed, async persistence with optimistic UI, improved import/restore, retention/cleanup, compaction and backup sync; container and connection stats are now DB‑backed.
  • Documentation
    • Multilingual docs updated to describe the new AppDb/SQLCipher + PrefStore storage abstractions.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 28, 2026

📝 Walkthrough

Walkthrough

This PR migrates local persistence from Hive to an AppDb/Drift + SQLCipher stack and PrefStore: adds a Drift-based AppDb schema and SQLCipher bootstrap, a HiveToSqliteMigrator, and replaces many Hive-backed stores (servers, snippets, private keys, container, history, settings, connection_stats, etc.) with asynchronous AppDb/PrefStore implementations. It updates providers, call sites to async Futures, adds platform plugin registrations for sqlcipher, and updates pubspec/dev deps.

Possibly related PRs

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/sqlite-migration-safety

devin-ai-integration[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
lib/data/model/app/bak/backup.dart (2)

146-161: Consider logging when container type parsing fails.

When both byName and the toString fallback fail to parse the container type, the entry is silently skipped. Adding a log statement would help diagnose backup compatibility issues.

♻️ Suggested improvement
         type ??= ContainerType.values.firstWhereOrNull(
           (e) => e.toString() == raw,
         );
         if (type != null) {
           await Stores.container.setType(type, id);
+        } else {
+          _logger.warning('Unknown container type: $raw for id: $id');
         }
         continue;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/data/model/app/bak/backup.dart` around lines 146 - 161, The code that
parses ContainerType inside the block that handles keys starting with
'providerConfig' silently skips entries when both
ContainerType.values.byName(raw) and the toString() fallback fail; update this
block (the parsing around ContainerType, the variable raw and id, and the
subsequent Stores.container.setType call) to emit a warning or error log when
type remains null so failed parses are visible — include the key/id and raw
value in the log for debugging and use your project’s logger (e.g.,
processLogger or the existing logger utility) rather than printing to stdout.

82-89: Consider wrapping restore operations in error handling to prevent partial state.

If any restore operation fails (e.g., _restoreSnippets throws after _restoreServers succeeds), the database is left in an inconsistent partial state with some stores cleared/restored and others untouched. Consider wrapping the operations in a try-catch to either rollback or provide meaningful error feedback to the user.

♻️ Suggested approach
+    try {
       await _restoreServers(spis);
       await _restoreSnippets(snippets);
       await _restoreKeys(keys);
       await _restoreHistory(history.cast<String, Object?>());
       await _restoreContainer(container.cast<String, Object?>());
       await _restoreSettings(
         settings?.cast<String, Object?>() ?? <String, Object?>{},
       );
+    } catch (e, trace) {
+      _logger.severe('Restore failed, data may be in partial state', e, trace);
+      rethrow;
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/data/model/app/bak/backup.dart` around lines 82 - 89, The sequence of
restore calls (_restoreServers, _restoreSnippets, _restoreKeys, _restoreHistory,
_restoreContainer, _restoreSettings) can leave the DB in a partially restored
state if one throws, so wrap the entire restore sequence in error handling:
start a transaction or capture snapshots of affected stores before calling the
restores, run all restores inside a try block, and on any catch rollback to the
saved snapshots (or roll back the transaction) and rethrow or surface a clear
error; ensure cleanup and an informative error message so callers/users know the
restore failed and the DB was reverted.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@lib/data/model/app/bak/backup.dart`:
- Around line 146-161: The code that parses ContainerType inside the block that
handles keys starting with 'providerConfig' silently skips entries when both
ContainerType.values.byName(raw) and the toString() fallback fail; update this
block (the parsing around ContainerType, the variable raw and id, and the
subsequent Stores.container.setType call) to emit a warning or error log when
type remains null so failed parses are visible — include the key/id and raw
value in the log for debugging and use your project’s logger (e.g.,
processLogger or the existing logger utility) rather than printing to stdout.
- Around line 82-89: The sequence of restore calls (_restoreServers,
_restoreSnippets, _restoreKeys, _restoreHistory, _restoreContainer,
_restoreSettings) can leave the DB in a partially restored state if one throws,
so wrap the entire restore sequence in error handling: start a transaction or
capture snapshots of affected stores before calling the restores, run all
restores inside a try block, and on any catch rollback to the saved snapshots
(or roll back the transaction) and rethrow or surface a clear error; ensure
cleanup and an informative error message so callers/users know the restore
failed and the DB was reverted.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1311da5 and 9060a26.

📒 Files selected for processing (1)
  • lib/data/model/app/bak/backup.dart

devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/data/model/app/bak/backup2.dart (1)

22-23: ⚠️ Potential issue | 🟡 Minor

Regenerate and commit the Freezed/JSON generated files after these model changes.

The generated files backup2.freezed.dart and backup2.g.dart must be regenerated via dart run build_runner build --delete-conflicting-outputs and committed alongside these annotated model modifications, as required by the coding guidelines for lib/data/model/**/*.dart.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/data/model/app/bak/backup2.dart` around lines 22 - 23, The Freezed model
BackupV2 was modified but the generated artifacts are missing; run dart run
build_runner build --delete-conflicting-outputs to regenerate
backup2.freezed.dart and backup2.g.dart, verify there are no build errors, and
commit those generated files alongside the BackupV2 changes so
lib/data/model/**/*.dart rules are satisfied.
🧹 Nitpick comments (2)
lib/data/store/connection_stats.dart (2)

172-186: Performance: Fetches all history rows then filters in memory.

The query fetches all connectionStatsRecords for all servers, then filters to the top 20 per server in Dart code. For large datasets, this could consume significant memory and time.

Consider using SQL window functions (ROW_NUMBER() OVER (PARTITION BY server_id ORDER BY timestamp_ms DESC)) to limit at the database level, or batch the history queries per server with limits.

For typical connection stats volumes, this may be acceptable. Flag for future optimization if data grows.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/data/store/connection_stats.dart` around lines 172 - 186, The current
implementation in connection_stats.dart reads all rows from
connectionStatsRecords then keeps only the first 20 per server in memory (the
loop that populates recentConnectionsByServer using _fromRow), which is
inefficient for large datasets; change the data access to push the limit into
the DB by either: (a) using a SQL window function (ROW_NUMBER() OVER (PARTITION
BY server_id ORDER BY timestamp_ms DESC)) to select only rows where row_number
<= 20, or (b) looping serverIds and issuing a parameterized query per server
ordering by timestampMs DESC with LIMIT 20, then convert rows with _fromRow into
recentConnectionsByServer — update the code that builds historyRows and the
subsequent loop to use the new query approach.

68-85: Hardcoded table name in raw SQL.

The table name connection_stats_records is hardcoded in the SQL string. If the Drift table definition changes, this query will fail at runtime without compile-time detection. Consider extracting the table name as a constant or adding a comment referencing the Drift table definition.

This is a common trade-off when using customSelect for complex aggregations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/data/store/connection_stats.dart` around lines 68 - 85, The SQL in the
customSelect call hardcodes the table name "connection_stats_records", which
will break if the Drift table is renamed; update the query to use a single
constant for the table name (e.g., define a const like kConnectionStatsTable =
'connection_stats_records' and interpolate that constant into the customSelect
SQL) or add a clear comment referencing the Drift table/class (e.g.,
ConnectionStatsRecords) so future renames are caught; keep the rest of the
customSelect call and variables (successName, serverId) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/data/model/app/bak/backup2.dart`:
- Around line 137-139: Replace the unsafe logging that interpolates the raw
backup payload (the "$val" in the _loggerV2.warning call) so sensitive backup
contents are never written to logs; update the catch block around the decode
logic to log a safe message via _loggerV2.warning including the error (e) and
stack (s) plus non-sensitive metadata about val (e.g., its runtimeType,
length/size, or a truncated hash/fingerprint) and keep the existing descriptive
text ("Decode backup object map failed") instead of the raw payload.

In `@lib/data/model/app/bak/container_restore.dart`:
- Around line 17-23: When handling keys that start with
_containerProviderConfigKey in the loop (the block using
key.startsWith(_containerProviderConfigKey)), guard against an empty id produced
by key.substring(_containerProviderConfigKey.length) by checking that
id.isNotEmpty before calling _resolveContainerType(raw) and
Stores.container.setType(type, id); if id is empty simply skip processing that
entry so you don't persist an invalid mapping.

In `@lib/data/store/connection_stats.dart`:
- Around line 36-51: The cleanup guard can race because `_cleanupRunning` is
read and set across an `await`; fix by making the check-and-set synchronous
before any await: evaluate `shouldCleanupByCount`/`shouldCleanupByInterval`,
then immediately if not `_cleanupRunning` set `_cleanupRunning = true` in the
same synchronous block and only then perform `await _cleanOldRecords()`;
alternatively implement a simple mutual-exclusion Future/Completer lock around
`_cleanOldRecords()` so callers await the same in-flight cleanup instead of both
entering (use `_cleanupRunning` -> a `_cleanupFuture` that is set to the cleanup
Future and cleared when done). Ensure you update places that reset
`_cleanupRunning` (`finally` block) to instead complete/clear the
`_cleanupFuture` or reset `_cleanupRunning` consistently.

---

Outside diff comments:
In `@lib/data/model/app/bak/backup2.dart`:
- Around line 22-23: The Freezed model BackupV2 was modified but the generated
artifacts are missing; run dart run build_runner build
--delete-conflicting-outputs to regenerate backup2.freezed.dart and
backup2.g.dart, verify there are no build errors, and commit those generated
files alongside the BackupV2 changes so lib/data/model/**/*.dart rules are
satisfied.

---

Nitpick comments:
In `@lib/data/store/connection_stats.dart`:
- Around line 172-186: The current implementation in connection_stats.dart reads
all rows from connectionStatsRecords then keeps only the first 20 per server in
memory (the loop that populates recentConnectionsByServer using _fromRow), which
is inefficient for large datasets; change the data access to push the limit into
the DB by either: (a) using a SQL window function (ROW_NUMBER() OVER (PARTITION
BY server_id ORDER BY timestamp_ms DESC)) to select only rows where row_number
<= 20, or (b) looping serverIds and issuing a parameterized query per server
ordering by timestampMs DESC with LIMIT 20, then convert rows with _fromRow into
recentConnectionsByServer — update the code that builds historyRows and the
subsequent loop to use the new query approach.
- Around line 68-85: The SQL in the customSelect call hardcodes the table name
"connection_stats_records", which will break if the Drift table is renamed;
update the query to use a single constant for the table name (e.g., define a
const like kConnectionStatsTable = 'connection_stats_records' and interpolate
that constant into the customSelect SQL) or add a clear comment referencing the
Drift table/class (e.g., ConnectionStatsRecords) so future renames are caught;
keep the rest of the customSelect call and variables (successName, serverId)
unchanged.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9060a26 and 4896bef.

📒 Files selected for processing (8)
  • lib/data/model/app/bak/backup.dart
  • lib/data/model/app/bak/backup2.dart
  • lib/data/model/app/bak/container_restore.dart
  • lib/data/provider/container.dart
  • lib/data/provider/private_key.dart
  • lib/data/provider/snippet.dart
  • lib/data/res/store.dart
  • lib/data/store/connection_stats.dart

Comment on lines +137 to +139
} catch (e, s) {
_loggerV2.warning('Decode backup object map failed: $val', e, s);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Do not log raw backup payload values in decode failures.

At Line 138, interpolating $val can leak sensitive backup contents (including private-key material) to logs.

Suggested fix
-        _loggerV2.warning('Decode backup object map failed: $val', e, s);
+        _loggerV2.warning(
+          'Decode backup object map failed (valueType=${val.runtimeType})',
+          e,
+          s,
+        );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/data/model/app/bak/backup2.dart` around lines 137 - 139, Replace the
unsafe logging that interpolates the raw backup payload (the "$val" in the
_loggerV2.warning call) so sensitive backup contents are never written to logs;
update the catch block around the decode logic to log a safe message via
_loggerV2.warning including the error (e) and stack (s) plus non-sensitive
metadata about val (e.g., its runtimeType, length/size, or a truncated
hash/fingerprint) and keep the existing descriptive text ("Decode backup object
map failed") instead of the raw payload.

coderabbitai[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
lib/data/model/app/bak/backup2.dart (1)

17-18: Reminder: Run build_runner after modifications.

This file uses @freezed with generated part files. After finalizing changes, ensure code generation is up to date.

dart run build_runner build --delete-conflicting-outputs

As per coding guidelines: "After modifying annotated model files, run: dart run build_runner build --delete-conflicting-outputs"

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/data/model/app/bak/backup2.dart` around lines 17 - 18, This file declares
generated parts (part 'backup2.freezed.dart' and part 'backup2.g.dart') for a
`@freezed` model—after you finish edits to the model (e.g., backup2.dart) run code
generation to update those parts by executing the build_runner command; ensure
you run: dart run build_runner build --delete-conflicting-outputs and verify
that backup2.freezed.dart and backup2.g.dart are regenerated and checked into
the branch so the `@freezed` annotations and JSON (de)serialization remain in
sync.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@lib/data/model/app/bak/backup2.dart`:
- Around line 17-18: This file declares generated parts (part
'backup2.freezed.dart' and part 'backup2.g.dart') for a `@freezed` model—after you
finish edits to the model (e.g., backup2.dart) run code generation to update
those parts by executing the build_runner command; ensure you run: dart run
build_runner build --delete-conflicting-outputs and verify that
backup2.freezed.dart and backup2.g.dart are regenerated and checked into the
branch so the `@freezed` annotations and JSON (de)serialization remain in sync.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7088a95 and ca745c2.

📒 Files selected for processing (4)
  • lib/data/model/app/bak/backup2.dart
  • lib/data/model/app/bak/container_restore.dart
  • lib/data/store/connection_stats.dart
  • lib/data/store/server.dart

devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/data/store/server.dart`:
- Around line 14-20: ServerStore currently uses Drift via adb.AppDb (see
ServerStore class and the _db getter) but the repo policy requires Hive-backed
persistence under lib/data/store; either convert ServerStore to a Hive-backed
implementation (replace adb.AppDb usage, persist state using Hive boxes and the
Hive API within ServerStore) so it complies with the lib/data/store/**/*.dart
guideline, or include an accompanying PR change to the repository rule to allow
Drift; ensure you update or replace references to adb.AppDb and the _db getter
accordingly and implement Hive box open/close, read, write, and migration logic
inside ServerStore to match existing store semantics.
- Around line 444-445: Replace the sensitive raw payload in the error log inside
the server custom map decode path: do not log the variable raw; instead log only
safe metadata (e.g., length/size and runtime type) and the error/stack. Locate
the dprint call that currently uses 'Failed to decode server custom map: $raw'
and change it to include metadata like raw length and type along with e and st,
or omit raw entirely, so no full payload or tokens are written to logs.
- Around line 122-126: The migration block currently removes the old mapping
before setting the new one which can lose data if setting fails; change the
sequence in the migration logic that references containerHostsToMigrate,
container.set, and container.remove so you perform await container.set(newId,
dockerHost) first and only after it succeeds call await container.remove(oldId);
ensure you keep the null check for dockerHost and consider wrapping the
set/remove pair in a try/catch (or rethrow) so failures during set do not leave
the mapping removed.
- Around line 44-47: The code currently deletes the record with delete(old.id)
before writing the new record, which can cause permanent data loss if
put(newInfo) fails; change the flow in the function handling old.id/newInfo.id
to first attempt to write newInfo (call put(newInfo)) while guarding against ID
collisions (e.g., check exists/get for newInfo.id or use an upsert/transaction
if available), only after a successful put then delete(old.id); ensure you
handle and propagate errors so that if put fails the old record remains intact
and if newInfo.id equals old.id simply perform an update instead of delete/put.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ca745c2 and 0e3e0be.

📒 Files selected for processing (1)
  • lib/data/store/server.dart

Comment on lines +14 to +20
class ServerStore {
ServerStore._();

static final instance = ServerStore._();

void put(Spi info) {
set(info.id, info);
adb.AppDb get _db => adb.AppDb.instance;

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Store implementation conflicts with the current repository persistence rule.

ServerStore now persists through Drift in lib/data/store/, but the active repository rule for this path still requires Hive-backed stores. Please either align implementation with the rule or update the rule in the same PR to prevent policy drift.

As per coding guidelines lib/data/store/**/*.dart: Persist state using Hive stores placed under lib/data/store/.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/data/store/server.dart` around lines 14 - 20, ServerStore currently uses
Drift via adb.AppDb (see ServerStore class and the _db getter) but the repo
policy requires Hive-backed persistence under lib/data/store; either convert
ServerStore to a Hive-backed implementation (replace adb.AppDb usage, persist
state using Hive boxes and the Hive API within ServerStore) so it complies with
the lib/data/store/**/*.dart guideline, or include an accompanying PR change to
the repository rule to allow Drift; ensure you update or replace references to
adb.AppDb and the _db getter accordingly and implement Hive box open/close,
read, write, and migration logic inside ServerStore to match existing store
semantics.

devin-ai-integration[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

@lollipopkit
Copy link
Owner Author

@coderabbitai Why still is blocking?

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 2, 2026

Tip

For best results, initiate chat on the files or code changes.

@lollipopkit I'd be happy to help identify any blocking operations! However, I need a bit more context to give you a precise answer. Could you please clarify:

  1. Which file or operation are you referring to?
  2. What behavior are you observing that seems blocking?
  3. Is this about:
    • A specific method that's still synchronous?
    • UI freezing during an operation?
    • Startup/initialization blocking?
    • Something else?

From the PR changes, I can see that most operations have been converted to async (store fetches, puts, provider loads, etc.). If you can point me to a specific file or line, I can analyze whether there's a blocking operation that should be async.


🧠 Learnings used
Learnt from: CR
Repo: lollipopkit/flutter_server_box PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-08T09:36:41.682Z
Learning: Applies to lib/data/store/**/*.dart : Persist state using Hive stores placed under lib/data/store/

@lollipopkit
Copy link
Owner Author

@coderabbitai Why still is blocking this PR?

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 2, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the current status of PR `#1061`
gh pr view 1061 --json state,isDraft,mergeable,reviewDecision,statusCheckRollup,reviews,comments --repo lollipopkit/flutter_server_box

Length of output: 202731


🏁 Script executed:

#!/bin/bash
# Check if there are any blocking reviews or check failures
gh pr checks 1061 --repo lollipopkit/flutter_server_box

Length of output: 108


🏁 Script executed:

#!/bin/bash
# Get detailed PR information including merge blockers
gh pr view 1061 --repo lollipopkit/flutter_server_box

Length of output: 1523


@lollipopkit The PR is currently blocked by my "Changes requested" review status. Here's what's happening:

Current Status

  • Mergeable: No conflicts
  • CI Checks: Passing (flutter analysis succeeded)
  • Review Decision: Changes requested by CodeRabbit

What's Blocking

The review system shows I requested changes on your last commit (168917c). Looking at my most recent reviews, the main concerns were:

  1. Store backend policy - Using Drift in lib/data/store/ when the repo guideline mentions Hive
  2. Migration safety - Delete-before-write patterns that could lose data
  3. Error handling - Missing try-catch in some async flows

How to Unblock

Since you've been actively addressing issues, you have a few options:

  1. If you've addressed the concerns: Comment @coderabbitai review and I'll re-review the current state
  2. If you want to discuss specific issues: Point me to the files/changes you'd like me to focus on
  3. If you disagree with the feedback: You can dismiss my review or explain your approach

Looking at your commit history, you've made many fixes. The review status just needs to be updated. Would you like me to do a fresh review of the current state?


🧠 Learnings used
Learnt from: CR
Repo: lollipopkit/flutter_server_box PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-08T09:36:41.682Z
Learning: Applies to lib/data/store/**/*.dart : Persist state using Hive stores placed under lib/data/store/

@GT-610
Copy link
Collaborator

GT-610 commented Mar 3, 2026

@coderabbitai Why still is blocking this PR?

Looking at the whole timeline, it seems that 3 issues are still not resolved.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 3, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Fetch all review comments on PR `#1061` to identify unresolved threads
gh api repos/lollipopkit/flutter_server_box/pulls/1061/comments --paginate | jq -r '.[] | select(.user.login == "coderabbitai[bot]") | {id: .id, path: .path, line: .line, body: .body, created_at: .created_at, in_reply_to_id: .in_reply_to_id}' | jq -s '.'

Length of output: 80042


🏁 Script executed:

#!/bin/bash
# Get PR review threads to see which are unresolved
gh api repos/lollipopkit/flutter_server_box/pulls/1061/reviews --paginate | jq -r '.[] | select(.user.login == "coderabbitai[bot]") | {id: .id, state: .state, submitted_at: .submitted_at, body: .body}' | jq -s 'sort_by(.submitted_at) | reverse | .[0:5]'

Length of output: 29739


🏁 Script executed:

#!/bin/bash
# Check conversation threads on the PR to find unresolved items
gh pr view 1061 --repo lollipopkit/flutter_server_box --json reviewThreads --jq '.reviewThreads[] | select(.isResolved == false) | {path: .path, isResolved: .isResolved, comments: [.comments[] | {author: .author.login, body: .body}]}'

Length of output: 674


@GT-610 You're correct—there are 3 unresolved issues blocking this PR. Here's what still needs to be addressed:


🚫 Blocking Issues

1. ServerStore policy mismatch (lib/data/store/server.dart, line 20)

Severity: 🟠 Major
Issue: ServerStore is using Drift/AppDb for persistence, but the repository guideline requires Hive-backed stores under lib/data/store/.

What needs to happen:

  • Either convert ServerStore to use Hive instead of Drift
  • OR update the repository guidelines in this PR to explicitly allow Drift for stores

Relevant code:

adb.AppDb get _db => adb.AppDb.instance;  // Uses Drift, violates Hive policy

2. ConnectionStatsStore policy mismatch (lib/data/store/connection_stats.dart, line 17)

Severity: 🟠 Major
Issue: Same as above—ConnectionStatsStore uses Drift but should use Hive per the coding guidelines.

What needs to happen:

  • Convert to Hive OR update the repository policy to allow Drift for this store

Relevant code:

adb.AppDb get _db => adb.AppDb.instance;  // Uses Drift, violates Hive policy

3. Sensitive data leak in backup logging (lib/data/model/app/bak/backup2.dart, line 138)

Severity: 🟠 Major
Issue: The code logs the raw backup payload (which can contain private keys, passwords, etc.) when decoding fails:

_loggerV2.warning('Decode backup object map failed: $val', e, s);  // Logs sensitive data!

What needs to happen:

  • Replace $val with safe metadata (e.g., valueType=${val.runtimeType} or length)
  • Never log the actual backup content

Suggested fix:

_loggerV2.warning(
  'Decode backup object map failed (valueType=${val.runtimeType})',
  e,
  s,
);

Next Steps

You have two options:

  1. Fix the implementation: Convert stores to Hive and sanitize the logging
  2. Update the policy: If Drift is the intended direction, update the repository guidelines in this PR to reflect that decision

The first two issues are about architectural consistency (Drift vs. Hive), and the third is a security concern about logging sensitive data.

Would you like me to help with either approach? I can provide specific code changes or help draft the policy update.


🧠 Learnings used
Learnt from: CR
Repo: lollipopkit/flutter_server_box PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-08T09:36:41.682Z
Learning: Applies to lib/data/store/**/*.dart : Persist state using Hive stores placed under lib/data/store/

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (1)
lib/data/store/connection_stats.dart (1)

1-18: ⚠️ Potential issue | 🟠 Major

Inline exception comment is not enough to satisfy the current repository policy.

This file is still Drift-backed under lib/data/store/, but the repository rule requires Hive-backed persistence in that path. Please codify the exception in the project policy (or move this store outside the scoped path) so implementation and policy are consistent.

As per coding guidelines: lib/data/store/**/*.dart: Persist state using Hive stores placed under lib/data/store/.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/data/store/connection_stats.dart` around lines 1 - 18, The code comment
notes a policy exception but the repository policy and file placement are
inconsistent: ConnectionStatsStore (class ConnectionStatsStore in this file)
remains Drift-backed under lib/data/store/, which violates the rule that files
under lib/data/store/** must be Hive-backed; either codify this Drift exception
in the project policy/config (e.g., add an explicit exception entry for
ConnectionStatsStore/connection_stats.dart in the policy doc or CI rule) or
move/rename the Drift-backed store out of the lib/data/store/ scope (e.g., to
lib/data/sql_store/ or lib/data/legacy/) and update imports/usages accordingly;
ensure the chosen action updates the repository policy file (or CI
configuration) and the inline comment to reference the formal policy exception
identifier so code placement and policy are consistent.
🧹 Nitpick comments (4)
docs/src/content/docs/fr/principles/ssh.md (1)

88-88: Clarify the storage backend to match the migration terminology.

“local storage” is a bit ambiguous here. Consider naming the concrete backend used in this PR (e.g., AppDb/SQLite with SQLCipher via PrefStore) so the security model is explicit.

✏️ Suggested wording
-- Mot de passe stocké chiffré dans le stockage local
+- Mot de passe stocké chiffré dans AppDb (SQLite/SQLCipher), via PrefStore
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/src/content/docs/fr/principles/ssh.md` at line 88, Replace the ambiguous
phrase "Mot de passe stocké chiffré dans le stockage local" with a concrete
backend description referencing the migration terminology used in this PR, e.g.,
state that the password is stored encrypted in AppDb (SQLite with SQLCipher) via
PrefStore; update the sentence to read something like "Mot de passe stocké
chiffré dans AppDb (SQLite + SQLCipher) via PrefStore" so the security model and
backend are explicit.
docs/src/content/docs/zh/principles/architecture.md (1)

84-88: Consider adding historical context about the Hive → Drift migration.

For users familiar with the previous architecture, it would be helpful to briefly mention that this Store layer replaces the previous Hive-based persistence, and reference the migration strategy if applicable to understanding how the system works.

📝 Suggested addition
 **为何采用这套 Store 层?**
+- 此架构从 Hive 迁移而来,提供更强的数据一致性和安全性
 - `AppDb` + Drift 负责关系型和查询密集型数据
 - `PrefStore` 负责轻量级键值持久化
 - `SQLCipher` 保护本地数据库中的敏感记录
 - Store API 将持久化细节隔离在 UI 和业务逻辑之外

(Translation: "This architecture migrated from Hive to provide stronger data consistency and security")

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/src/content/docs/zh/principles/architecture.md` around lines 84 - 88,
Add one short sentence after the Store layer bullet list noting that this Store
design replaced the previous Hive-based persistence; reference the migration
path and rationale by name (mention "Hive → Drift migration", and the key
components "AppDb", "PrefStore", "SQLCipher", and "Store API") and point readers
to the migration guide or docs page if available so readers familiar with the
old architecture can find migration details and rationale.
docs/src/content/docs/fr/principles/architecture.md (1)

88-88: Minor terminology consistency tweak in French text.

Consider replacing “Store APIs” with “API de Store” (or fully French wording) to keep style consistent within the paragraph.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/src/content/docs/fr/principles/architecture.md` at line 88, Replace the
English phrase "Store APIs" in the sentence "Les Store APIs gardent les détails
de persistance hors de l'UI et de la logique métier" with a consistent French
wording such as "API de Store" (or fully French alternative like "APIs de
stockage") so the sentence reads e.g. "Les API de Store gardent les détails de
persistance hors de l'UI et de la logique métier"; update the exact phrase
"Store APIs" wherever it appears in this paragraph to maintain terminology
consistency.
docs/src/content/docs/fr/development/structure.md (1)

55-55: Consider a more idiomatic French phrasing for the storage sentence.

“Stockage local basé sur …” is understandable, but “Stockage local avec …” (or “reposant sur …”) reads more naturally in this context.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/src/content/docs/fr/development/structure.md` at line 55, Remplace la
formulation actuelle "Stockage local basé sur AppDb/SQLCipher et PrefStore :"
par une tournure plus idiomatique — par exemple "Stockage local avec
AppDb/SQLCipher et PrefStore :" ou "Stockage local reposant sur AppDb/SQLCipher
et PrefStore :" — en conservant le sens et la ponctuation; cherche l'occurrence
exacte "Stockage local basé sur AppDb/SQLCipher et PrefStore" dans le fichier et
remplace-la par l'une des alternatives proposées.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/src/content/docs/es/principles/architecture.md`:
- Line 82: Replace inconsistent capitalization of the term "stores" in this
section by choosing a single form (either "Capa de Stores" or "Capa de
stores"/"capa de stores") and applying it consistently throughout; update the
heading "Capa de Stores" and the inline occurrence "capa de stores" so both use
the same casing and terminology, and ensure subsequent references in the section
follow that chosen form.

In `@docs/src/content/docs/es/principles/ssh.md`:
- Line 88: Replace the awkward Spanish phrase "Contraseña almacenada cifrada en
el almacenamiento local" with the clearer wording "Contraseña almacenada de
forma cifrada en el almacenamiento local" to improve readability; locate the
exact line containing the original sentence and update it accordingly.

In `@docs/src/content/docs/zh/development/architecture.md`:
- Around line 47-52: The docs introduce AppDb/Drift/SQLCipher storage (AppDb,
Drift, SQLCipher, PrefStore, Store API) that conflicts with the current
repository guideline requiring Hive-backed stores under lib/data/store/; before
merging, either revert or update the documentation to match the repo policy:
confirm whether the repository policy has been updated to permit Drift/SQLCipher
stores, and if it has not, change this doc to reflect the existing Hive
requirement and coordinate a separate policy PR; if the policy is updated, amend
the documentation to reference the policy change, add a clear migration note
describing Hive→Drift steps for existing contributors, and update the quoted
guideline ("Persist state using Hive stores placed under lib/data/store/") to
the new policy text.

In `@docs/src/content/docs/zh/principles/state.md`:
- Around line 267-283: The documentation example (ServersNotifier class) shows
persistence via Stores.server (Drift/Store) which conflicts with the repo
guideline requiring Hive-backed stores under lib/data/store/; update the example
to use the Hive-backed store implementation (the same API surface used by
Stores.server.fetch/put/delete) or explicitly document this example as an
alternative architecture and request a policy change; also make the async state
updates use AsyncValue.guard in addServer and removeServer (replace direct
AsyncData(...) assignments with await AsyncValue.guard(() =>
Stores.server.fetch())) to match earlier patterns and add basic error
propagation.

---

Duplicate comments:
In `@lib/data/store/connection_stats.dart`:
- Around line 1-18: The code comment notes a policy exception but the repository
policy and file placement are inconsistent: ConnectionStatsStore (class
ConnectionStatsStore in this file) remains Drift-backed under lib/data/store/,
which violates the rule that files under lib/data/store/** must be Hive-backed;
either codify this Drift exception in the project policy/config (e.g., add an
explicit exception entry for ConnectionStatsStore/connection_stats.dart in the
policy doc or CI rule) or move/rename the Drift-backed store out of the
lib/data/store/ scope (e.g., to lib/data/sql_store/ or lib/data/legacy/) and
update imports/usages accordingly; ensure the chosen action updates the
repository policy file (or CI configuration) and the inline comment to reference
the formal policy exception identifier so code placement and policy are
consistent.

---

Nitpick comments:
In `@docs/src/content/docs/fr/development/structure.md`:
- Line 55: Remplace la formulation actuelle "Stockage local basé sur
AppDb/SQLCipher et PrefStore :" par une tournure plus idiomatique — par exemple
"Stockage local avec AppDb/SQLCipher et PrefStore :" ou "Stockage local reposant
sur AppDb/SQLCipher et PrefStore :" — en conservant le sens et la ponctuation;
cherche l'occurrence exacte "Stockage local basé sur AppDb/SQLCipher et
PrefStore" dans le fichier et remplace-la par l'une des alternatives proposées.

In `@docs/src/content/docs/fr/principles/architecture.md`:
- Line 88: Replace the English phrase "Store APIs" in the sentence "Les Store
APIs gardent les détails de persistance hors de l'UI et de la logique métier"
with a consistent French wording such as "API de Store" (or fully French
alternative like "APIs de stockage") so the sentence reads e.g. "Les API de
Store gardent les détails de persistance hors de l'UI et de la logique métier";
update the exact phrase "Store APIs" wherever it appears in this paragraph to
maintain terminology consistency.

In `@docs/src/content/docs/fr/principles/ssh.md`:
- Line 88: Replace the ambiguous phrase "Mot de passe stocké chiffré dans le
stockage local" with a concrete backend description referencing the migration
terminology used in this PR, e.g., state that the password is stored encrypted
in AppDb (SQLite with SQLCipher) via PrefStore; update the sentence to read
something like "Mot de passe stocké chiffré dans AppDb (SQLite + SQLCipher) via
PrefStore" so the security model and backend are explicit.

In `@docs/src/content/docs/zh/principles/architecture.md`:
- Around line 84-88: Add one short sentence after the Store layer bullet list
noting that this Store design replaced the previous Hive-based persistence;
reference the migration path and rationale by name (mention "Hive → Drift
migration", and the key components "AppDb", "PrefStore", "SQLCipher", and "Store
API") and point readers to the migration guide or docs page if available so
readers familiar with the old architecture can find migration details and
rationale.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 36e5436e-1a02-4a6b-8651-528f96e33f9d

📥 Commits

Reviewing files that changed from the base of the PR and between 2dfa714 and 1a07950.

📒 Files selected for processing (27)
  • docs/src/content/docs/de/development/architecture.md
  • docs/src/content/docs/de/development/structure.md
  • docs/src/content/docs/de/principles/architecture.md
  • docs/src/content/docs/de/principles/ssh.md
  • docs/src/content/docs/development/architecture.md
  • docs/src/content/docs/development/structure.md
  • docs/src/content/docs/es/development/architecture.md
  • docs/src/content/docs/es/development/structure.md
  • docs/src/content/docs/es/principles/architecture.md
  • docs/src/content/docs/es/principles/ssh.md
  • docs/src/content/docs/fr/development/architecture.md
  • docs/src/content/docs/fr/development/structure.md
  • docs/src/content/docs/fr/principles/architecture.md
  • docs/src/content/docs/fr/principles/ssh.md
  • docs/src/content/docs/ja/development/architecture.md
  • docs/src/content/docs/ja/development/structure.md
  • docs/src/content/docs/ja/principles/architecture.md
  • docs/src/content/docs/ja/principles/ssh.md
  • docs/src/content/docs/principles/architecture.md
  • docs/src/content/docs/principles/ssh.md
  • docs/src/content/docs/principles/state.md
  • docs/src/content/docs/zh/development/architecture.md
  • docs/src/content/docs/zh/development/structure.md
  • docs/src/content/docs/zh/principles/architecture.md
  • docs/src/content/docs/zh/principles/ssh.md
  • docs/src/content/docs/zh/principles/state.md
  • lib/data/store/connection_stats.dart
✅ Files skipped from review due to trivial changes (1)
  • docs/src/content/docs/de/principles/ssh.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • docs/src/content/docs/development/structure.md
  • docs/src/content/docs/zh/development/structure.md

Comment on lines +267 to 283
class ServersNotifier extends _$ServersNotifier {
@override
List<Server> build() {
// 从 Hive 加载
return Hive.box<Server>('servers').values.toList();
Future<List<Spi>> build() async {
// 从 Store 层加载
return Stores.server.fetch();
}

void addServer(Server server) {
state = [...state, server];
// 持久化到 Hive
Hive.box<Server>('servers').put(server.id, server);
Future<void> addServer(Spi server) async {
await Stores.server.put(server);
state = AsyncData(await Stores.server.fetch());
}

void removeServer(String id) {
state = state.where((s) => s.id != id).toList();
// 从 Hive 中删除
Hive.box<Server>('servers').delete(id);
Future<void> removeServer(String id) async {
await Stores.server.delete(id);
state = AsyncData(await Stores.server.fetch());
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Documentation example conflicts with repository persistence guidelines.

This code example accurately reflects the Store-based async architecture, but it conflicts with the repository guideline requiring Hive-backed stores under lib/data/store/. Based on learnings: "Persist state using Hive stores placed under lib/data/store/".

This architectural conflict has already been flagged as a blocking issue in the PR review. The resolution requires either:

  1. Reverting to Hive-based persistence, or
  2. Updating the repository policy to officially support Drift/Store architecture

Optional enhancement: Consider adding error handling to the example for consistency with the AsyncValue.guard pattern shown in earlier sections (lines 86-88). This would make the example more complete:

Future<void> addServer(Spi server) async {
  await Stores.server.put(server);
  state = await AsyncValue.guard(() => Stores.server.fetch());
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/src/content/docs/zh/principles/state.md` around lines 267 - 283, The
documentation example (ServersNotifier class) shows persistence via
Stores.server (Drift/Store) which conflicts with the repo guideline requiring
Hive-backed stores under lib/data/store/; update the example to use the
Hive-backed store implementation (the same API surface used by
Stores.server.fetch/put/delete) or explicitly document this example as an
alternative architecture and request a policy change; also make the async state
updates use AsyncValue.guard in addServer and removeServer (replace direct
AsyncData(...) assignments with await AsyncValue.guard(() =>
Stores.server.fetch())) to match earlier patterns and add basic error
propagation.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Mar 6, 2026

Deploying serverbox with  Cloudflare Pages  Cloudflare Pages

Latest commit: fdcdce3
Status: ✅  Deploy successful!
Preview URL: https://18d2ad34.serverbox.pages.dev
Branch Preview URL: https://fix-sqlite-migration-safety.serverbox.pages.dev

View logs

Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 35 additional findings in Devin Review.

Open in Devin Review

Comment on lines +415 to +421
throw SSHErr(
type: SSHErrType.connect,
message:
'Jump server `$jumpId` not found for ${spi.name} (${_hostIdentifier(spi)}) '
'while resolving in $context',
);
}

Choose a reason for hiding this comment

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

🔴 _resolveJumpSpi throws exception for missing jump servers instead of falling back to direct connection

The new _resolveJumpSpi helper (lib/core/utils/server.dart:399-421) throws an SSHErr when a server's jumpId references a server that no longer exists. The old code in genClient returned null in this case, causing the connection to fall back to a direct socket connection. Now, any server with a stale jumpId (e.g., pointing to a deleted jump server) will fail to connect entirely with an error. This also affects ensureKnownHostKey at lib/core/utils/server.dart:367-371 which calls the same helper. Users who previously deleted a jump server but kept the reference in another server's config could connect fine; after this change they cannot connect at all.

Old vs new behavior

Old code (returned null, fell through to direct connection):

final jumpSpi_ = () {
  if (jumpSpi != null) return jumpSpi;
  final jumpId = spi.jumpId;
  if (jumpId != null) {
    return jumpSpisById?[jumpId] ?? Stores.server.box.get(jumpId);
  }
}();
if (jumpSpi_ != null) { /* use jump */ } else { /* direct */ }

New code (throws):

final resolved = await Stores.server.fetchOne(jumpId);
if (resolved != null) return resolved;
throw SSHErr(...);
Suggested change
throw SSHErr(
type: SSHErrType.connect,
message:
'Jump server `$jumpId` not found for ${spi.name} (${_hostIdentifier(spi)}) '
'while resolving in $context',
);
}
final resolved = await Stores.server.fetchOne(jumpId);
return resolved;
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
lib/data/store/connection_stats.dart (1)

1-20: ⚠️ Potential issue | 🟠 Major

Store backend in this path still conflicts with repository storage policy.

ConnectionStatsStore (Line 8) uses AppDb/Drift (Line 19) under lib/data/store/**, which does not match the current Hive-only rule for this directory. Please align by either migrating this store back to Hive or updating the repository policy/exception in the same PR.

As per coding guidelines: lib/data/store/**/*.dart: Persist state using Hive stores placed under lib/data/store/

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/data/store/connection_stats.dart` around lines 1 - 20,
ConnectionStatsStore currently depends on drift/adb.AppDb (imports:
package:drift/drift.dart and adb.AppDb) which violates the lib/data/store
Hive-only policy; either convert this store to a Hive-backed implementation or
add a repository policy exception in this PR. To fix, replace
ConnectionStatsStore (and any references) with a Hive-backed store (e.g.,
ConnectionStatsHiveStore) using Hive type adapters and Hive box names instead of
adb.AppDb and drift types, remove the drift and adb imports from
connection_stats.dart, and update callers to use the new Hive store;
alternatively, if database-backed storage is intended, add/update the repository
policy/exception documentation and tests in this PR to permit adb.AppDb usage
for connection stats.
docs/src/content/docs/zh/development/architecture.md (1)

47-55: ⚠️ Potential issue | 🟠 Major

Documentation states a storage policy change that is not reflected in current repo guidelines.

Line 54 says lib/data/store/ is no longer Hive-only, but the active repository guideline still requires Hive-backed stores there. Please either update the formal guideline in this PR or adjust this statement to avoid policy/doc drift.

As per coding guidelines: lib/data/store/**/*.dart: Persist state using Hive stores placed under lib/data/store/

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/src/content/docs/zh/development/architecture.md` around lines 47 - 55,
The doc line claiming lib/data/store/ is no longer Hive-only conflicts with the
active guideline that requires Hive-backed stores; either (A) update the
repository guideline entry that matches the pattern `lib/data/store/**/*.dart`
to reflect migration to AppDb/SQLCipher and remove the Hive-only requirement,
and adjust any enforcement rules/tests, or (B) revert the sentence in the
architecture doc to state that lib/data/store/ remains Hive-backed and mention
that HiveToSqliteMigrator migrates legacy Hive data to AppDb/SQLCipher; modify
the architecture.md text around "AppDb/SQLCipher + PrefStore" and the
`lib/data/store/` sentence accordingly so docs and policy align.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/src/content/docs/fr/development/structure.md`:
- Line 16: The docs line under the store section references "AppDb/SQLCipher +
PrefStore" which conflicts with the repo policy requiring Hive-backed stores
under lib/data/store/**/*.dart; update the documentation to reflect the
repository canonical policy or mark this doc as transitional/pending: either
replace "AppDb/SQLCipher + PrefStore" with the current canonical "Hive-backed
stores (lib/data/store/)" text or add a clear note next to the store/ entry that
the repository currently enforces Hive persists for lib/data/store/**/*.dart and
that AppDb/SQLCipher + PrefStore is a proposed/transition option until the
policy is changed.

In `@docs/src/content/docs/principles/state.md`:
- Around line 263-282: The docs example conflicts with the repository
persistence policy: replace the Drift/PrefStore usage in the ServersNotifier
example (methods build, addServer, removeServer referencing Stores.server) with
the canonical Hive-backed store calls used by our lib/data/store/* convention
(e.g., use the Hive-backed ServerStore.put/delete/fetch methods and import the
Hive store singleton instead of Stores.server), or alternatively update the
repository policy to explicitly allow Drift/PrefStore and note that change in
the docs; ensure the ServersNotifier example and its use of
build/addServer/removeServer reference the approved store API consistently.

---

Duplicate comments:
In `@docs/src/content/docs/zh/development/architecture.md`:
- Around line 47-55: The doc line claiming lib/data/store/ is no longer
Hive-only conflicts with the active guideline that requires Hive-backed stores;
either (A) update the repository guideline entry that matches the pattern
`lib/data/store/**/*.dart` to reflect migration to AppDb/SQLCipher and remove
the Hive-only requirement, and adjust any enforcement rules/tests, or (B) revert
the sentence in the architecture doc to state that lib/data/store/ remains
Hive-backed and mention that HiveToSqliteMigrator migrates legacy Hive data to
AppDb/SQLCipher; modify the architecture.md text around "AppDb/SQLCipher +
PrefStore" and the `lib/data/store/` sentence accordingly so docs and policy
align.

In `@lib/data/store/connection_stats.dart`:
- Around line 1-20: ConnectionStatsStore currently depends on drift/adb.AppDb
(imports: package:drift/drift.dart and adb.AppDb) which violates the
lib/data/store Hive-only policy; either convert this store to a Hive-backed
implementation or add a repository policy exception in this PR. To fix, replace
ConnectionStatsStore (and any references) with a Hive-backed store (e.g.,
ConnectionStatsHiveStore) using Hive type adapters and Hive box names instead of
adb.AppDb and drift types, remove the drift and adb imports from
connection_stats.dart, and update callers to use the new Hive store;
alternatively, if database-backed storage is intended, add/update the repository
policy/exception documentation and tests in this PR to permit adb.AppDb usage
for connection stats.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 722f445d-1929-4916-93ab-12c6ce738aea

📥 Commits

Reviewing files that changed from the base of the PR and between 1a07950 and 3a77f48.

⛔ Files ignored due to path filters (1)
  • pubspec.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • docs/src/content/docs/es/principles/architecture.md
  • docs/src/content/docs/es/principles/ssh.md
  • docs/src/content/docs/fr/development/structure.md
  • docs/src/content/docs/fr/principles/architecture.md
  • docs/src/content/docs/fr/principles/ssh.md
  • docs/src/content/docs/principles/state.md
  • docs/src/content/docs/zh/development/architecture.md
  • docs/src/content/docs/zh/principles/architecture.md
  • docs/src/content/docs/zh/principles/state.md
  • ios/Flutter/AppFrameworkInfo.plist
  • lib/data/store/connection_stats.dart
  • pubspec.yaml
💤 Files with no reviewable changes (1)
  • ios/Flutter/AppFrameworkInfo.plist
🚧 Files skipped from review as they are similar to previous changes (1)
  • pubspec.yaml

│ ├── model/ # Modèles de données par fonctionnalité
│ ├── provider/ # Providers Riverpod
│ └── store/ # Stockage local (Hive)
│ └── store/ # Abstractions de stockage local (AppDb/SQLCipher + PrefStore)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Resolve policy/docs mismatch for storage architecture before merging.

These lines document AppDb/SQLCipher + PrefStore as the store standard, but the current repository policy still requires Hive-backed stores under lib/data/store/. Please either update the canonical policy source in this PR or adjust this doc to clearly mark it as a pending/transition state.

Based on learnings: "Applies to lib/data/store/**/*.dart : Persist state using Hive stores placed under lib/data/store/".

Also applies to: 55-55

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/src/content/docs/fr/development/structure.md` at line 16, The docs line
under the store section references "AppDb/SQLCipher + PrefStore" which conflicts
with the repo policy requiring Hive-backed stores under
lib/data/store/**/*.dart; update the documentation to reflect the repository
canonical policy or mark this doc as transitional/pending: either replace
"AppDb/SQLCipher + PrefStore" with the current canonical "Hive-backed stores
(lib/data/store/)" text or add a clear note next to the store/ entry that the
repository currently enforces Hive persists for lib/data/store/**/*.dart and
that AppDb/SQLCipher + PrefStore is a proposed/transition option until the
policy is changed.

Comment on lines +263 to 282
### Store Integration

```dart
@riverpod
class ServerStoreNotifier extends _$ServerStoreNotifier {
class ServersNotifier extends _$ServersNotifier {
@override
List<Server> build() {
// Load from Hive
return Hive.box<Server>('servers').values.toList();
Future<List<Spi>> build() async {
// Load from the store layer
return Stores.server.fetch();
}

void addServer(Server server) {
state = [...state, server];
// Persist to Hive
Hive.box<Server>('servers').put(server.id, server);
Future<void> addServer(Spi server) async {
await Stores.server.put(server);
state = await AsyncValue.guard(() => Stores.server.fetch());
}

void removeServer(String id) {
state = state.where((s) => s.id != id).toList();
// Remove from Hive
Hive.box<Server>('servers').delete(id);
Future<void> removeServer(String id) async {
await Stores.server.delete(id);
state = await AsyncValue.guard(() => Stores.server.fetch());
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Resolve the persistence-policy mismatch before documenting this as canonical.

This section codifies async Stores.server usage aligned with Drift/AppDb migration, but it conflicts with the repository persistence rule that stores under lib/data/store/ must be Hive-backed. Please either update the repository policy to permit Drift/PrefStore or keep the docs aligned with the current policy until that change is approved.

Based on learnings: Applies to lib/data/store/**/*.dart : Persist state using Hive stores placed under lib/data/store/.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/src/content/docs/principles/state.md` around lines 263 - 282, The docs
example conflicts with the repository persistence policy: replace the
Drift/PrefStore usage in the ServersNotifier example (methods build, addServer,
removeServer referencing Stores.server) with the canonical Hive-backed store
calls used by our lib/data/store/* convention (e.g., use the Hive-backed
ServerStore.put/delete/fetch methods and import the Hive store singleton instead
of Stores.server), or alternatively update the repository policy to explicitly
allow Drift/PrefStore and note that change in the docs; ensure the
ServersNotifier example and its use of build/addServer/removeServer reference
the approved store API consistently.

Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 39 additional findings in Devin Review.

Open in Devin Review

Comment on lines +145 to +151
Stores.connectionStats.recordConnection(ConnectionStat(
serverId: spi.id,
serverName: spi.name,
timestamp: time1,
result: ConnectionResult.success,
durationMs: spentTime,
));

Choose a reason for hiding this comment

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

🟡 Unawaited recordConnection calls can produce unhandled async errors

Stores.connectionStats.recordConnection(...) is an async method performing a database insert, but it's called without await or unawaited() at lines 145 and 178 in lib/data/provider/server/single.dart. If the database write fails (e.g., disk full, DB locked), the resulting unhandled Future error will propagate to the zone error handler. While the app's runZonedGuarded will catch it, it bypasses any local error handling and may mask persistent database issues.

Suggested change
Stores.connectionStats.recordConnection(ConnectionStat(
serverId: spi.id,
serverName: spi.name,
timestamp: time1,
result: ConnectionResult.success,
durationMs: spentTime,
));
// Record successful connection
unawaited(Stores.connectionStats.recordConnection(ConnectionStat(
serverId: spi.id,
serverName: spi.name,
timestamp: time1,
result: ConnectionResult.success,
durationMs: spentTime,
)).catchError((e, s) {
Loggers.app.warning('Record connection stat failed', e, s);
}));
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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.

proposal: hive -> sqlite migration

2 participants