feat(database): Add database compression function (#1027)

* feat(database): Add database compression function

Add database compression functionality to the connection statistics page to reduce the size of the database file

Add multi-language support and related UI interactions

* fix(database): Update the description of database compression operations and fix the statistics cleanup logic

Update the description of database compression operations in the multilingual files to explicitly state that data will not be lost

Fix the connection statistics cleanup logic to ensure correct matching of server IDs

Add error handling for the compression operation to prevent the UI from freezing

* Update lib/l10n/app_en.arb

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
GT610
2026-01-26 13:40:42 +08:00
committed by GitHub
parent 9ac866644c
commit a0a62acdbc
29 changed files with 253 additions and 1 deletions

View File

@@ -225,6 +225,9 @@ class ServersNotifier extends _$ServersNotifier {
Stores.setting.serverOrder.put(newOrder);
Stores.server.delete(id);
// Remove connection stats when server is deleted
Stores.connectionStats.clearServerStats(id);
// Remove SSH session when server is deleted
final sessionId = 'ssh_$id';
TermSessionManager.remove(sessionId);
@@ -243,6 +246,7 @@ class ServersNotifier extends _$ServersNotifier {
Stores.setting.serverOrder.put([]);
Stores.server.clear();
Stores.connectionStats.clearAll();
bakSync.sync(milliDelay: 1000);
}

View File

@@ -182,9 +182,23 @@ class ConnectionStatsStore extends HiveStore {
// Clear stats for a specific server
void clearServerStats(String serverId) {
final keysToDelete = keys().where((key) => key.startsWith(serverId)).toList();
final keysToDelete = keys().where((key) {
if (key == serverId) return true;
return key.startsWith('${serverId}_');
}).toList();
for (final key in keysToDelete) {
remove(key);
}
}
Future<void> compact() async {
Loggers.app.info('Start compacting connection_stats database...');
try {
await box.compact();
Loggers.app.info('Finished compacting connection_stats database');
} catch (e, st) {
Loggers.app.warning('Failed compacting connection_stats database', e, st);
rethrow;
}
}
}

View File

@@ -395,6 +395,24 @@ abstract class AppLocalizations {
/// **'Clear This Server Statistics'**
String get clearThisServerStats;
/// No description provided for @compactDatabase.
///
/// In en, this message translates to:
/// **'Compact Database'**
String get compactDatabase;
/// No description provided for @compactDatabaseContent.
///
/// In en, this message translates to:
/// **'Database size: {size}\n\nThis will reorganize the database to reduce file size. No data will be deleted.'**
String compactDatabaseContent(Object size);
/// No description provided for @confirm.
///
/// In en, this message translates to:
/// **'Confirm'**
String get confirm;
/// No description provided for @closeAfterSave.
///
/// In en, this message translates to:

View File

@@ -162,6 +162,17 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get clearThisServerStats => 'Statistiken dieses Servers löschen';
@override
String get compactDatabase => 'Datenbank komprimieren';
@override
String compactDatabaseContent(Object size) {
return 'Datenbankgröße: $size\n\nDies wird die Datenbank neu organisieren, um die Dateigröße zu reduzieren. Es werden keine Daten gelöscht.';
}
@override
String get confirm => 'Bestätigen';
@override
String get closeAfterSave => 'Speichern und schließen';

View File

@@ -161,6 +161,17 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get clearThisServerStats => 'Clear This Server Statistics';
@override
String get compactDatabase => 'Compact Database';
@override
String compactDatabaseContent(Object size) {
return 'Database size: $size\n\nThis will reorganize the database to reduce file size. No data will be deleted.';
}
@override
String get confirm => 'Confirm';
@override
String get closeAfterSave => 'Save and close';

View File

@@ -162,6 +162,17 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get clearThisServerStats => 'Limpiar estadísticas de este servidor';
@override
String get compactDatabase => 'Compactar base de datos';
@override
String compactDatabaseContent(Object size) {
return 'Tamaño de la base de datos: $size\n\nEsto reorganizará la base de datos para reducir el tamaño del archivo. No se eliminará ningún dato.';
}
@override
String get confirm => 'Confirm';
@override
String get closeAfterSave => 'Guardar y cerrar';

View File

@@ -162,6 +162,17 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get clearThisServerStats => 'Effacer les statistiques de ce serveur';
@override
String get compactDatabase => 'Compacter la base de données';
@override
String compactDatabaseContent(Object size) {
return 'Taille de la base de données : $size\n\nCela réorganisera la base de données pour réduire la taille du fichier. Aucune donnée ne sera supprimée.';
}
@override
String get confirm => 'Confirm';
@override
String get closeAfterSave => 'Enregistrer et fermer';

View File

@@ -160,6 +160,17 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get clearThisServerStats => 'Hapus Statistik Server Ini';
@override
String get compactDatabase => 'Kompres Database';
@override
String compactDatabaseContent(Object size) {
return 'Ukuran database: $size\n\nIni akan mengatur ulang database untuk mengurangi ukuran file. Tidak ada data yang akan dihapus.';
}
@override
String get confirm => 'Confirm';
@override
String get closeAfterSave => 'Simpan dan tutup';

View File

@@ -155,6 +155,17 @@ class AppLocalizationsJa extends AppLocalizations {
@override
String get clearThisServerStats => 'このサーバーの統計をクリア';
@override
String get compactDatabase => 'データベースを圧縮';
@override
String compactDatabaseContent(Object size) {
return 'データベースサイズ: $size\n\nこれにより、ファイルサイズを小さくするためにデータベースが再編成されます。データは削除されません。';
}
@override
String get confirm => 'Confirm';
@override
String get closeAfterSave => '保存して閉じる';

View File

@@ -161,6 +161,17 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get clearThisServerStats => 'Statistieken van deze server wissen';
@override
String get compactDatabase => 'Database comprimeren';
@override
String compactDatabaseContent(Object size) {
return 'Databasgrootte: $size\n\nDit zal de database opnieuw organiseren om de bestandsgrootte te verkleinen. Geen gegevens worden verwijderd.';
}
@override
String get confirm => 'Confirm';
@override
String get closeAfterSave => 'Opslaan en sluiten';

View File

@@ -161,6 +161,17 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get clearThisServerStats => 'Limpar estatísticas deste servidor';
@override
String get compactDatabase => 'Compactar banco de dados';
@override
String compactDatabaseContent(Object size) {
return 'Tamanho do banco de dados: $size\n\nIsso reorganizará o banco de dados para reduzir o tamanho do arquivo. Nenhum dado será excluído.';
}
@override
String get confirm => 'Confirm';
@override
String get closeAfterSave => 'Salvar e fechar';

View File

@@ -162,6 +162,17 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get clearThisServerStats => 'Очистить статистику этого сервера';
@override
String get compactDatabase => 'Сжать базу данных';
@override
String compactDatabaseContent(Object size) {
return 'Размер базы данных: $size\n\nЭто перестроит базу данных для уменьшения размера файла. Данные не будут удалены.';
}
@override
String get confirm => 'Подтвердить';
@override
String get closeAfterSave => 'Сохранить и закрыть';

View File

@@ -160,6 +160,17 @@ class AppLocalizationsTr extends AppLocalizations {
@override
String get clearThisServerStats => 'Bu Sunucu İstatistiklerini Temizle';
@override
String get compactDatabase => 'Veritabanını Sıkıştır';
@override
String compactDatabaseContent(Object size) {
return 'Veritabanı boyutu: $size\n\nBu, dosya boyutunu küçültmek için veritabanını yeniden düzenleyecektir. Veriler silinmeyecek.';
}
@override
String get confirm => 'Confirm';
@override
String get closeAfterSave => 'Kaydet ve kapat';

View File

@@ -161,6 +161,17 @@ class AppLocalizationsUk extends AppLocalizations {
@override
String get clearThisServerStats => 'Очистити статистику цього сервера';
@override
String get compactDatabase => 'Стиснути базу даних';
@override
String compactDatabaseContent(Object size) {
return 'Розмір бази даних: $size\n\nЦе перебудує базу даних, щоб зменшити розмір файлу. Дані не будуть видалені.';
}
@override
String get confirm => 'Confirm';
@override
String get closeAfterSave => 'Зберегти та закрити';

View File

@@ -153,6 +153,17 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get clearThisServerStats => '清空此服务器统计';
@override
String get compactDatabase => '压缩数据库';
@override
String compactDatabaseContent(Object size) {
return '数据库大小:$size\n\n此操作将重新组织数据库以减少体积,数据不会丢失。';
}
@override
String get confirm => '确认';
@override
String get closeAfterSave => '保存后关闭';
@@ -1135,6 +1146,14 @@ class AppLocalizationsZhTw extends AppLocalizationsZh {
@override
String get clearThisServerStats => '清空此伺服器統計';
@override
String get compactDatabase => '壓縮資料庫';
@override
String compactDatabaseContent(Object size) {
return '資料庫大小:$size\n\n此操作將重新組織資料庫以減少體積,資料不會遺失。';
}
@override
String get closeAfterSave => '儲存後關閉';

View File

@@ -46,6 +46,9 @@
"clearServerStatsContent": "Sind Sie sicher, dass Sie die Verbindungsstatistiken für Server \"{serverName}\" löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.",
"clearServerStatsTitle": "{serverName} Statistiken löschen",
"clearThisServerStats": "Statistiken dieses Servers löschen",
"compactDatabase": "Datenbank komprimieren",
"compactDatabaseContent": "Datenbankgröße: {size}\n\nDies wird die Datenbank neu organisieren, um die Dateigröße zu reduzieren. Es werden keine Daten gelöscht.",
"confirm": "Bestätigen",
"closeAfterSave": "Speichern und schließen",
"cmd": "Command",
"collapseUITip": "Ob lange Listen in der Benutzeroberfläche standardmäßig eingeklappt werden sollen oder nicht",

View File

@@ -46,6 +46,9 @@
"clearServerStatsContent": "Are you sure you want to clear connection statistics for server \"{serverName}\"? This action cannot be undone.",
"clearServerStatsTitle": "Clear {serverName} Statistics",
"clearThisServerStats": "Clear This Server Statistics",
"compactDatabase": "Compact Database",
"compactDatabaseContent": "Database size: {size}\n\nThis will rebuild the whole database to reduce file size.",
"confirm": "Confirm",
"closeAfterSave": "Save and close",
"cmd": "Command",
"collapseUITip": "Whether to collapse long lists present in the UI by default",

View File

@@ -46,6 +46,8 @@
"clearServerStatsContent": "¿Estás seguro de que quieres limpiar las estadísticas de conexión del servidor \"{serverName}\"? Esta acción no se puede deshacer.",
"clearServerStatsTitle": "Limpiar estadísticas de {serverName}",
"clearThisServerStats": "Limpiar estadísticas de este servidor",
"compactDatabase": "Compactar base de datos",
"compactDatabaseContent": "Tamaño de la base de datos: {size}\n\nEsto reorganizará la base de datos para reducir el tamaño del archivo. No se eliminará ningún dato.",
"closeAfterSave": "Guardar y cerrar",
"cmd": "Comando",
"collapseUITip": "¿Colapsar por defecto las listas largas en la UI?",

View File

@@ -46,6 +46,8 @@
"clearServerStatsContent": "Êtes-vous sûr de vouloir effacer les statistiques de connexion du serveur \"{serverName}\" ? Cette action ne peut pas être annulée.",
"clearServerStatsTitle": "Effacer les statistiques de {serverName}",
"clearThisServerStats": "Effacer les statistiques de ce serveur",
"compactDatabase": "Compacter la base de données",
"compactDatabaseContent": "Taille de la base de données : {size}\n\nCela réorganisera la base de données pour réduire la taille du fichier. Aucune donnée ne sera supprimée.",
"closeAfterSave": "Enregistrer et fermer",
"cmd": "Commande",
"collapseUITip": "Indique si les longues listes présentées dans l'interface utilisateur doivent être réduites par défaut.",

View File

@@ -46,6 +46,8 @@
"clearServerStatsContent": "Apakah Anda yakin ingin menghapus statistik koneksi untuk server \"{serverName}\"? Tindakan ini tidak dapat dibatalkan.",
"clearServerStatsTitle": "Hapus Statistik {serverName}",
"clearThisServerStats": "Hapus Statistik Server Ini",
"compactDatabase": "Kompres Database",
"compactDatabaseContent": "Ukuran database: {size}\n\nIni akan mengatur ulang database untuk mengurangi ukuran file. Tidak ada data yang akan dihapus.",
"closeAfterSave": "Simpan dan tutup",
"cmd": "Memerintah",
"collapseUITip": "Apakah akan menciutkan daftar panjang yang ada di UI secara default atau tidak",

View File

@@ -46,6 +46,8 @@
"clearServerStatsContent": "サーバー\"{serverName}\"の接続統計を削除してもよろしいですか?この操作は元に戻せません。",
"clearServerStatsTitle": "{serverName}の統計をクリア",
"clearThisServerStats": "このサーバーの統計をクリア",
"compactDatabase": "データベースを圧縮",
"compactDatabaseContent": "データベースサイズ: {size}\n\nこれにより、ファイルサイズを小さくするためにデータベースが再編成されます。データは削除されません。",
"closeAfterSave": "保存して閉じる",
"cmd": "コマンド",
"collapseUITip": "UIの長いリストをデフォルトで折りたたむかどうか",

View File

@@ -46,6 +46,8 @@
"clearServerStatsContent": "Weet u zeker dat u de verbindingsstatistieken voor server \"{serverName}\" wilt wissen? Deze actie kan niet ongedaan worden gemaakt.",
"clearServerStatsTitle": "Statistieken van {serverName} wissen",
"clearThisServerStats": "Statistieken van deze server wissen",
"compactDatabase": "Database comprimeren",
"compactDatabaseContent": "Databasgrootte: {size}\n\nDit zal de database opnieuw organiseren om de bestandsgrootte te verkleinen. Geen gegevens worden verwijderd.",
"closeAfterSave": "Opslaan en sluiten",
"cmd": "Opdracht",
"collapseUITip": "Of lange lijsten in de UI standaard moeten worden ingeklapt",

View File

@@ -46,6 +46,8 @@
"clearServerStatsContent": "Tem certeza de que deseja limpar as estatísticas de conexão para o servidor \"{serverName}\"? Esta ação não pode ser desfeita.",
"clearServerStatsTitle": "Limpar estatísticas de {serverName}",
"clearThisServerStats": "Limpar estatísticas deste servidor",
"compactDatabase": "Compactar banco de dados",
"compactDatabaseContent": "Tamanho do banco de dados: {size}\n\nIsso reorganizará o banco de dados para reduzir o tamanho do arquivo. Nenhum dado será excluído.",
"closeAfterSave": "Salvar e fechar",
"cmd": "Comando",
"collapseUITip": "Deve colapsar listas longas na UI por padrão?",

View File

@@ -46,6 +46,9 @@
"clearServerStatsContent": "Вы уверены, что хотите очистить статистику соединений для сервера \"{serverName}\"? Это действие не может быть отменено.",
"clearServerStatsTitle": "Очистить статистику {serverName}",
"clearThisServerStats": "Очистить статистику этого сервера",
"compactDatabase": "Сжать базу данных",
"compactDatabaseContent": "Размер базы данных: {size}\n\nЭто перестроит базу данных для уменьшения размера файла. Данные не будут удалены.",
"confirm": "Подтвердить",
"closeAfterSave": "Сохранить и закрыть",
"cmd": "Команда",
"collapseUITip": "Свернуть длинные списки в UI по умолчанию",

View File

@@ -46,6 +46,8 @@
"clearServerStatsContent": "\"{serverName}\" sunucusu için bağlantı istatistiklerini temizlemek istediğinizden emin misiniz? Bu işlem geri alınamaz.",
"clearServerStatsTitle": "{serverName} İstatistiklerini Temizle",
"clearThisServerStats": "Bu Sunucu İstatistiklerini Temizle",
"compactDatabase": "Veritabanını Sıkıştır",
"compactDatabaseContent": "Veritabanı boyutu: {size}\n\nBu, dosya boyutunu küçültmek için veritabanını yeniden düzenleyecektir. Veriler silinmeyecek.",
"closeAfterSave": "Kaydet ve kapat",
"cmd": "Komut",
"collapseUITip": "Arayüzde uzun listelerin varsayılan olarak daraltılıp daraltılmayacağı",

View File

@@ -46,6 +46,8 @@
"clearServerStatsContent": "Ви впевнені, що хочете очистити статистику з'єднань для сервера \"{serverName}\"? Цю дію не можна скасувати.",
"clearServerStatsTitle": "Очистити статистику {serverName}",
"clearThisServerStats": "Очистити статистику цього сервера",
"compactDatabase": "Стиснути базу даних",
"compactDatabaseContent": "Розмір бази даних: {size}\n\nЦе перебудує базу даних, щоб зменшити розмір файлу. Дані не будуть видалені.",
"closeAfterSave": "Зберегти та закрити",
"cmd": "Команда",
"collapseUITip": "Сховати довгі списки, що є у UI за замовчуванням",

View File

@@ -46,6 +46,9 @@
"clearServerStatsContent": "确定要清空服务器 \"{serverName}\" 的连接统计数据吗?此操作无法撤销。",
"clearServerStatsTitle": "清空 {serverName} 统计",
"clearThisServerStats": "清空此服务器统计",
"compactDatabase": "压缩数据库",
"compactDatabaseContent": "数据库大小:{size}\n\n此操作将重新组织数据库以减少体积数据不会丢失。",
"confirm": "确认",
"closeAfterSave": "保存后关闭",
"cmd": "命令",
"collapseUITip": "是否默认折叠 UI 中的长列表",

View File

@@ -46,6 +46,8 @@
"clearServerStatsContent": "確定要清空伺服器 \"{serverName}\" 的連線統計資料嗎?此操作無法撤銷。",
"clearServerStatsTitle": "清空 {serverName} 統計",
"clearThisServerStats": "清空此伺服器統計",
"compactDatabase": "壓縮資料庫",
"compactDatabaseContent": "資料庫大小:{size}\n\n此操作將重新組織資料庫以減少體積資料不會遺失。",
"closeAfterSave": "儲存後關閉",
"cmd": "指令",
"collapseUITip": "是否預設折疊 UI 中存在的長列表",

View File

@@ -1,3 +1,5 @@
import 'dart:io';
import 'package:fl_lib/fl_lib.dart';
import 'package:flutter/material.dart';
import 'package:server_box/core/extension/context/locale.dart';
@@ -16,6 +18,7 @@ class ConnectionStatsPage extends StatefulWidget {
class _ConnectionStatsPageState extends State<ConnectionStatsPage> {
List<ServerConnectionStats> _serverStats = [];
bool _isLoading = true;
bool _isCompacting = false;
@override
void initState() {
@@ -47,6 +50,13 @@ class _ConnectionStatsPageState extends State<ConnectionStatsPage> {
icon: const Icon(Icons.clear_all, color: Colors.red),
tooltip: libL10n.clear,
),
IconButton(
onPressed: _isCompacting ? null : _showCompactDialog,
icon: _isCompacting
? SizedLoading.small
: const Icon(Icons.compress),
tooltip: l10n.compactDatabase,
),
],
),
body: _buildBody,
@@ -196,6 +206,42 @@ class _ConnectionStatsPageState extends State<ConnectionStatsPage> {
),
);
}
void _showCompactDialog() {
final path = '${Paths.doc}${Pfs.seperator}connection_stats_enc.hive';
final file = File(path);
final oldSize = file.existsSync() ? file.lengthSync() : 0;
final sizeStr = oldSize < 1000 ? '$oldSize B' : oldSize < 1000 * 1000 ? '${(oldSize / 1000).toStringAsFixed(1)} KB' : '${(oldSize / (1000 * 1000)).toStringAsFixed(1)} MB';
context.showRoundDialog(
title: l10n.compactDatabase,
child: Text(l10n.compactDatabaseContent(sizeStr)),
actions: [
TextButton(onPressed: context.pop, child: Text(libL10n.cancel)),
TextButton(
onPressed: () async {
context.pop();
setState(() => _isCompacting = true);
try {
await Stores.connectionStats.compact();
final newSize = file.existsSync() ? file.lengthSync() : 0;
final newSizeStr = newSize < 1000 ? '$newSize B' : newSize < 1000 * 1000 ? '${(newSize / 1000).toStringAsFixed(1)} KB' : '${(newSize / (1000 * 1000)).toStringAsFixed(1)} MB';
if (mounted) {
setState(() => _isCompacting = false);
context.showSnackBar('${libL10n.success}: $sizeStr -> $newSizeStr');
}
} catch (e) {
if (mounted) {
setState(() => _isCompacting = false);
context.showSnackBar('${libL10n.error}: $e');
}
}
},
child: Text(l10n.confirm),
),
],
);
}
}
extension on _ConnectionStatsPageState {