diff --git a/README.md b/README.md index 87090a5d0..c71b41210 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Please only download pkgs from the source that **you trust**! ## 🔖 Feature -- `Status chart` (CPU, Sensors, GPU...), `SSH` Term, `SFTP`, `Docker & Process`... +- `Status chart` (CPU, Sensors, GPU...), `SSH` Term, `SFTP`, `Docker & Process & Systemd`... - Platform specific: `Bio auth`、`Msg push`、`Home widget`、`watchOS App`... - English, 简体中文; Deutsch [@its-tom](https://github.com/its-tom), 繁體中文 [@kalashnikov](https://github.com/kalashnikov), Indonesian [@azkadev](https://github.com/azkadev), Français [@FrancXPT](https://github.com/FrancXPT), Dutch [@QazCetelic](https://github.com/QazCetelic), Türkçe [@mikropsoft](https://github.com/mikropsoft); Español, Русский язык, Português, 日本語 (Generated by GPT) diff --git a/README_zh.md b/README_zh.md index cfb8b3ae1..2dc52a76e 100644 --- a/README_zh.md +++ b/README_zh.md @@ -38,7 +38,7 @@ Linux / Windows | [GitHub](https://github.com/lollipopkit/flutter_server_box/rel ## 🔖 特点 -- `状态图表`(CPU、传感器、GPU 等), `SSH` 终端, `SFTP`, `Docker & 进程` 管理... +- `状态图表`(CPU、传感器、GPU 等), `SSH` 终端, `SFTP`, `Docker & 进程 & Systemd` 管理... - 特殊支持:`生物认证`、`推送`、`桌面小部件`、`watchOS App`、`跟随系统颜色`... - 本地化 - English, 简体中文 diff --git a/lib/data/model/app/backup.dart b/lib/data/model/app/backup.dart index f4d2a0d45..b95c2a5d0 100644 --- a/lib/data/model/app/backup.dart +++ b/lib/data/model/app/backup.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:fl_lib/fl_lib.dart'; +import 'package:json_annotation/json_annotation.dart'; import 'package:logging/logging.dart'; import 'package:server_box/data/model/server/private_key_info.dart'; import 'package:server_box/data/model/server/server_private_info.dart'; @@ -11,10 +12,13 @@ import 'package:server_box/data/res/provider.dart'; import 'package:server_box/data/res/rebuild.dart'; import 'package:server_box/data/res/store.dart'; +part 'backup.g.dart'; + const backupFormatVersion = 1; final _logger = Logger('Backup'); +@JsonSerializable() class Backup { // backup format version final int version; @@ -37,31 +41,9 @@ class Backup { this.lastModTime, }); - Backup.fromJson(Map json) - : version = json['version'] as int, - date = json['date'], - spis = (json['spis'] as List) - .map((e) => ServerPrivateInfo.fromJson(e)) - .toList(), - snippets = - (json['snippets'] as List).map((e) => Snippet.fromJson(e)).toList(), - keys = (json['keys'] as List) - .map((e) => PrivateKeyInfo.fromJson(e)) - .toList(), - container = json['container'] ?? {}, - lastModTime = json['lastModTime'], - history = json['history'] ?? {}; - - Map toJson() => { - 'version': version, - 'date': date, - 'spis': spis, - 'snippets': snippets, - 'keys': keys, - 'container': container, - 'lastModTime': lastModTime, - 'history': history, - }; + factory Backup.fromJson(Map json) => _$BackupFromJson(json); + + Map toJson() => _$BackupToJson(this); Backup.loadFromStore() : version = backupFormatVersion, @@ -201,8 +183,8 @@ class Backup { _logger.info('Restore success'); } - Backup.fromJsonString(String raw) - : this.fromJson(json.decode(_diyDecrypt(raw))); + factory Backup.fromJsonString(String raw) => + Backup.fromJson(json.decode(_diyDecrypt(raw))); } String _diyEncrypt(String raw) => json.encode( diff --git a/lib/data/model/app/backup.g.dart b/lib/data/model/app/backup.g.dart new file mode 100644 index 000000000..29dcc24a4 --- /dev/null +++ b/lib/data/model/app/backup.g.dart @@ -0,0 +1,35 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'backup.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Backup _$BackupFromJson(Map json) => Backup( + version: (json['version'] as num).toInt(), + date: json['date'] as String, + spis: (json['spis'] as List) + .map((e) => ServerPrivateInfo.fromJson(e as Map)) + .toList(), + snippets: (json['snippets'] as List) + .map((e) => Snippet.fromJson(e as Map)) + .toList(), + keys: (json['keys'] as List) + .map((e) => PrivateKeyInfo.fromJson(e as Map)) + .toList(), + container: json['container'] as Map, + history: json['history'] as Map, + lastModTime: (json['lastModTime'] as num?)?.toInt(), + ); + +Map _$BackupToJson(Backup instance) => { + 'version': instance.version, + 'date': instance.date, + 'spis': instance.spis, + 'snippets': instance.snippets, + 'keys': instance.keys, + 'container': instance.container, + 'history': instance.history, + 'lastModTime': instance.lastModTime, + }; diff --git a/lib/data/model/server/custom.dart b/lib/data/model/server/custom.dart index 0720378ed..443b82b0d 100644 --- a/lib/data/model/server/custom.dart +++ b/lib/data/model/server/custom.dart @@ -1,7 +1,9 @@ import 'package:hive_flutter/adapters.dart'; +import 'package:json_annotation/json_annotation.dart'; part 'custom.g.dart'; +@JsonSerializable() @HiveType(typeId: 7) final class ServerCustom { // @HiveField(0) @@ -28,44 +30,11 @@ final class ServerCustom { this.logoUrl, }); - static ServerCustom fromJson(Map json) { - //final temperature = json["temperature"] as String?; - final pveAddr = json['pveAddr'] as String?; - final pveIgnoreCert = json['pveIgnoreCert'] as bool; - final cmds = json['cmds'] as Map?; - final preferTempDev = json['preferTempDev'] as String?; - final logoUrl = json['logoUrl'] as String?; - return ServerCustom( - //temperature: temperature, - pveAddr: pveAddr, - pveIgnoreCert: pveIgnoreCert, - cmds: cmds?.cast(), - preferTempDev: preferTempDev, - logoUrl: logoUrl, - ); - } + factory ServerCustom.fromJson(Map json) => + _$ServerCustomFromJson(json); - Map toJson() { - final json = {}; - // if (temperature != null) { - // json["temperature"] = temperature; - // } - if (pveAddr != null) { - json['pveAddr'] = pveAddr; - } - json['pveIgnoreCert'] = pveIgnoreCert; + Map toJson() => _$ServerCustomToJson(this); - if (cmds != null) { - json['cmds'] = cmds; - } - if (preferTempDev != null) { - json['preferTempDev'] = preferTempDev; - } - if (logoUrl != null) { - json['logoUrl'] = logoUrl; - } - return json; - } @override String toString() { diff --git a/lib/data/model/server/custom.g.dart b/lib/data/model/server/custom.g.dart index a8fe4cd7c..0ebf5142e 100644 --- a/lib/data/model/server/custom.g.dart +++ b/lib/data/model/server/custom.g.dart @@ -51,3 +51,26 @@ class ServerCustomAdapter extends TypeAdapter { runtimeType == other.runtimeType && typeId == other.typeId; } + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +ServerCustom _$ServerCustomFromJson(Map json) => ServerCustom( + pveAddr: json['pveAddr'] as String?, + pveIgnoreCert: json['pveIgnoreCert'] as bool? ?? false, + cmds: (json['cmds'] as Map?)?.map( + (k, e) => MapEntry(k, e as String), + ), + preferTempDev: json['preferTempDev'] as String?, + logoUrl: json['logoUrl'] as String?, + ); + +Map _$ServerCustomToJson(ServerCustom instance) => + { + 'pveAddr': instance.pveAddr, + 'pveIgnoreCert': instance.pveIgnoreCert, + 'cmds': instance.cmds, + 'preferTempDev': instance.preferTempDev, + 'logoUrl': instance.logoUrl, + }; diff --git a/lib/data/model/server/private_key_info.dart b/lib/data/model/server/private_key_info.dart index bb63d74d7..840e9ce8e 100644 --- a/lib/data/model/server/private_key_info.dart +++ b/lib/data/model/server/private_key_info.dart @@ -1,11 +1,14 @@ import 'package:hive_flutter/hive_flutter.dart'; +import 'package:json_annotation/json_annotation.dart'; part 'private_key_info.g.dart'; +@JsonSerializable() @HiveType(typeId: 1) class PrivateKeyInfo { @HiveField(0) final String id; + @JsonKey(name: 'private_key') @HiveField(1) final String key; @@ -14,6 +17,11 @@ class PrivateKeyInfo { required this.key, }); + factory PrivateKeyInfo.fromJson(Map json) => + _$PrivateKeyInfoFromJson(json); + + Map toJson() => _$PrivateKeyInfoToJson(this); + String? get type { final lines = key.split('\n'); if (lines.length < 2) { @@ -26,15 +34,4 @@ class PrivateKeyInfo { } return splited[1]; } - - PrivateKeyInfo.fromJson(Map json) - : id = json['id'].toString(), - key = json['private_key'].toString(); - - Map toJson() { - final data = {}; - data['id'] = id; - data['private_key'] = key; - return data; - } } diff --git a/lib/data/model/server/private_key_info.g.dart b/lib/data/model/server/private_key_info.g.dart index adceca72e..4b99aec82 100644 --- a/lib/data/model/server/private_key_info.g.dart +++ b/lib/data/model/server/private_key_info.g.dart @@ -42,3 +42,19 @@ class PrivateKeyInfoAdapter extends TypeAdapter { runtimeType == other.runtimeType && typeId == other.typeId; } + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +PrivateKeyInfo _$PrivateKeyInfoFromJson(Map json) => + PrivateKeyInfo( + id: json['id'] as String, + key: json['private_key'] as String, + ); + +Map _$PrivateKeyInfoToJson(PrivateKeyInfo instance) => + { + 'id': instance.id, + 'private_key': instance.key, + }; diff --git a/lib/data/model/server/server_private_info.dart b/lib/data/model/server/server_private_info.dart index aa69d20e3..842a8ab24 100644 --- a/lib/data/model/server/server_private_info.dart +++ b/lib/data/model/server/server_private_info.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:hive_flutter/hive_flutter.dart'; +import 'package:json_annotation/json_annotation.dart'; import 'package:server_box/data/model/server/custom.dart'; import 'package:server_box/data/model/server/server.dart'; import 'package:server_box/data/model/server/wol_cfg.dart'; @@ -11,6 +12,7 @@ import 'package:server_box/data/model/app/error.dart'; part 'server_private_info.g.dart'; /// In former version, it's called `ServerPrivateInfo`. +@JsonSerializable() @HiveType(typeId: 3) class ServerPrivateInfo { @HiveField(0) @@ -25,6 +27,7 @@ class ServerPrivateInfo { final String? pwd; /// [id] of private key + @JsonKey(name: 'pubKeyId') @HiveField(5) final String? keyId; @HiveField(6) @@ -66,83 +69,10 @@ class ServerPrivateInfo { this.envs, }) : id = '$user@$ip:$port'; - static ServerPrivateInfo fromJson(Map json) { - final ip = json['ip'] as String? ?? ''; - final port = json['port'] as int? ?? 22; - final user = json['user'] as String? ?? 'root'; - final name = json['name'] as String? ?? ''; - final pwd = json['pwd'] as String? ?? json['authorization'] as String?; - final keyId = json['pubKeyId'] as String?; - final tags = (json['tags'] as List?)?.cast(); - final alterUrl = json['alterUrl'] as String?; - final autoConnect = json['autoConnect'] as bool? ?? true; - final jumpId = json['jumpId'] as String?; - final custom = json['customCmd'] == null - ? null - : ServerCustom.fromJson(json['custom'].cast()); - final wolCfg = json['wolCfg'] == null - ? null - : WakeOnLanCfg.fromJson(json['wolCfg'].cast()); - final envs_ = json['envs'] as Map?; - final envs = {}; - if (envs_ != null) { - envs_.forEach((key, value) { - if (value is String) { - envs[key] = value; - } - }); - } - - return ServerPrivateInfo( - name: name, - ip: ip, - port: port, - user: user, - pwd: pwd, - keyId: keyId, - tags: tags, - alterUrl: alterUrl, - autoConnect: autoConnect, - jumpId: jumpId, - custom: custom, - wolCfg: wolCfg, - envs: envs.isEmpty ? null : envs, - ); - } - - Map toJson() { - final Map data = {}; - data['name'] = name; - data['ip'] = ip; - data['port'] = port; - data['user'] = user; - if (pwd != null) { - data['pwd'] = pwd; - } - if (keyId != null) { - data['pubKeyId'] = keyId; - } - if (tags != null) { - data['tags'] = tags; - } - if (alterUrl != null) { - data['alterUrl'] = alterUrl; - } - data['autoConnect'] = autoConnect; - if (jumpId != null) { - data['jumpId'] = jumpId; - } - if (custom != null) { - data['custom'] = custom?.toJson(); - } - if (wolCfg != null) { - data['wolCfg'] = wolCfg?.toJson(); - } - if (envs != null) { - data['envs'] = envs; - } - return data; - } + factory ServerPrivateInfo.fromJson(Map json) => + _$ServerPrivateInfoFromJson(json); + + Map toJson() => _$ServerPrivateInfoToJson(this); String toJsonString() => json.encode(toJson()); diff --git a/lib/data/model/server/server_private_info.g.dart b/lib/data/model/server/server_private_info.g.dart index 0ca3d1aac..97594438a 100644 --- a/lib/data/model/server/server_private_info.g.dart +++ b/lib/data/model/server/server_private_info.g.dart @@ -75,3 +75,47 @@ class ServerPrivateInfoAdapter extends TypeAdapter { runtimeType == other.runtimeType && typeId == other.typeId; } + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +ServerPrivateInfo _$ServerPrivateInfoFromJson(Map json) => + ServerPrivateInfo( + name: json['name'] as String, + ip: json['ip'] as String, + port: (json['port'] as num).toInt(), + user: json['user'] as String, + pwd: json['pwd'] as String?, + keyId: json['pubKeyId'] as String?, + tags: (json['tags'] as List?)?.map((e) => e as String).toList(), + alterUrl: json['alterUrl'] as String?, + autoConnect: json['autoConnect'] as bool? ?? true, + jumpId: json['jumpId'] as String?, + custom: json['custom'] == null + ? null + : ServerCustom.fromJson(json['custom'] as Map), + wolCfg: json['wolCfg'] == null + ? null + : WakeOnLanCfg.fromJson(json['wolCfg'] as Map), + envs: (json['envs'] as Map?)?.map( + (k, e) => MapEntry(k, e as String), + ), + ); + +Map _$ServerPrivateInfoToJson(ServerPrivateInfo instance) => + { + 'name': instance.name, + 'ip': instance.ip, + 'port': instance.port, + 'user': instance.user, + 'pwd': instance.pwd, + 'pubKeyId': instance.keyId, + 'tags': instance.tags, + 'alterUrl': instance.alterUrl, + 'autoConnect': instance.autoConnect, + 'jumpId': instance.jumpId, + 'custom': instance.custom, + 'wolCfg': instance.wolCfg, + 'envs': instance.envs, + }; diff --git a/lib/data/model/server/snippet.dart b/lib/data/model/server/snippet.dart index 667d2f60f..6b27183cc 100644 --- a/lib/data/model/server/snippet.dart +++ b/lib/data/model/server/snippet.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:fl_lib/fl_lib.dart'; import 'package:hive_flutter/hive_flutter.dart'; +import 'package:json_annotation/json_annotation.dart'; import 'package:server_box/data/model/server/server_private_info.dart'; import 'package:xterm/core.dart'; @@ -9,6 +10,7 @@ import 'package:server_box/data/model/app/tag_pickable.dart'; part 'snippet.g.dart'; +@JsonSerializable() @HiveType(typeId: 2) class Snippet implements TagPickable { @HiveField(0) @@ -32,22 +34,9 @@ class Snippet implements TagPickable { this.autoRunOn, }); - Snippet.fromJson(Map json) - : name = json['name'].toString(), - script = json['script'].toString(), - tags = json['tags']?.cast(), - note = json['note']?.toString(), - autoRunOn = json['autoRunOn']?.cast(); - - Map toJson() { - final data = {}; - data['name'] = name; - data['script'] = script; - data['tags'] = tags; - data['note'] = note; - data['autoRunOn'] = autoRunOn; - return data; - } + factory Snippet.fromJson(Map json) => _$SnippetFromJson(json); + + Map toJson() => _$SnippetToJson(this); @override bool containsTag(String tag) { diff --git a/lib/data/model/server/snippet.g.dart b/lib/data/model/server/snippet.g.dart index 5f03d1aee..755ef4230 100644 --- a/lib/data/model/server/snippet.g.dart +++ b/lib/data/model/server/snippet.g.dart @@ -51,3 +51,25 @@ class SnippetAdapter extends TypeAdapter { runtimeType == other.runtimeType && typeId == other.typeId; } + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Snippet _$SnippetFromJson(Map json) => Snippet( + name: json['name'] as String, + script: json['script'] as String, + tags: (json['tags'] as List?)?.map((e) => e as String).toList(), + note: json['note'] as String?, + autoRunOn: (json['autoRunOn'] as List?) + ?.map((e) => e as String) + .toList(), + ); + +Map _$SnippetToJson(Snippet instance) => { + 'name': instance.name, + 'script': instance.script, + 'tags': instance.tags, + 'note': instance.note, + 'autoRunOn': instance.autoRunOn, + }; diff --git a/lib/data/model/server/wol_cfg.dart b/lib/data/model/server/wol_cfg.dart index de4524aff..4f29b7127 100644 --- a/lib/data/model/server/wol_cfg.dart +++ b/lib/data/model/server/wol_cfg.dart @@ -1,10 +1,12 @@ import 'dart:io'; import 'package:hive_flutter/hive_flutter.dart'; +import 'package:json_annotation/json_annotation.dart'; import 'package:wake_on_lan/wake_on_lan.dart'; part 'wol_cfg.g.dart'; +@JsonSerializable() @HiveType(typeId: 8) final class WakeOnLanCfg { @HiveField(0) @@ -54,22 +56,8 @@ final class WakeOnLanCfg { ); } - static WakeOnLanCfg fromJson(Map json) { - return WakeOnLanCfg( - mac: json['mac'] as String, - ip: json['ip'] as String, - pwd: json['pwd'] as String?, - ); - } + factory WakeOnLanCfg.fromJson(Map json) => + _$WakeOnLanCfgFromJson(json); - Map toJson() { - final map = { - 'mac': mac, - 'ip': ip, - }; - if (pwd != null) { - map['pwd'] = pwd; - } - return map; - } + Map toJson() => _$WakeOnLanCfgToJson(this); } diff --git a/lib/data/model/server/wol_cfg.g.dart b/lib/data/model/server/wol_cfg.g.dart index e1dd9a5b2..615d8b740 100644 --- a/lib/data/model/server/wol_cfg.g.dart +++ b/lib/data/model/server/wol_cfg.g.dart @@ -45,3 +45,20 @@ class WakeOnLanCfgAdapter extends TypeAdapter { runtimeType == other.runtimeType && typeId == other.typeId; } + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +WakeOnLanCfg _$WakeOnLanCfgFromJson(Map json) => WakeOnLanCfg( + mac: json['mac'] as String, + ip: json['ip'] as String, + pwd: json['pwd'] as String?, + ); + +Map _$WakeOnLanCfgToJson(WakeOnLanCfg instance) => + { + 'mac': instance.mac, + 'ip': instance.ip, + 'pwd': instance.pwd, + }; diff --git a/lib/view/page/server/detail/view.dart b/lib/view/page/server/detail/view.dart index c561fa2c8..e13a88eec 100644 --- a/lib/view/page/server/detail/view.dart +++ b/lib/view/page/server/detail/view.dart @@ -711,7 +711,12 @@ class _ServerDetailPageState extends State child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text(key, style: UIs.text15), + Text(key, style: UIs.text15).paddingSymmetric(horizontal: 5).tap( + onTap: () { + Pfs.copy(key); + context.showSnackBar('${libL10n.copy} ${libL10n.success}'); + }, + ), Text('${val?.toStringAsFixed(1)}°C', style: UIs.text13Grey), ], ), diff --git a/lib/view/page/systemd.dart b/lib/view/page/systemd.dart index 9c7457d78..2a47f95ee 100644 --- a/lib/view/page/systemd.dart +++ b/lib/view/page/systemd.dart @@ -38,11 +38,11 @@ final class _SystemdPageState extends State { return Scaffold( appBar: CustomAppBar( title: const Text('Systemd'), - actions: [ - Btn.icon(icon: const Icon(Icons.refresh), onTap: _pro.getUnits), - ], + actions: isDesktop + ? [Btn.icon(icon: const Icon(Icons.refresh), onTap: _pro.getUnits)] + : null, ), - body: _buildBody(), + body: RefreshIndicator(onRefresh: _pro.getUnits, child: _buildBody()), ); } @@ -71,8 +71,8 @@ final class _SystemdPageState extends State { (units) { if (units.isEmpty) { return SliverToBoxAdapter( - child: CenterGreyTitle(libL10n.empty) - .paddingSymmetric(horizontal: 13), + child: + CenterGreyTitle(libL10n.empty).paddingSymmetric(horizontal: 13), ); } return SliverList( @@ -155,10 +155,8 @@ final class _SystemdPageState extends State { color: color?.withOpacity(0.7) ?? UIs.halfAlpha, borderRadius: BorderRadius.circular(5), ), - child: Text( - tag, - style: UIs.text11Grey, - ).paddingSymmetric(horizontal: 5, vertical: 1), + child: Text(tag, style: UIs.text11) + .paddingSymmetric(horizontal: 5, vertical: 1), ).paddingOnly(right: noPad ? 0 : 5); } } diff --git a/pubspec.lock b/pubspec.lock index 29751bd1f..83eea9af4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -672,13 +672,21 @@ packages: source: hosted version: "0.7.1" json_annotation: - dependency: transitive + dependency: "direct main" description: name: json_annotation sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" url: "https://pub.dev" source: hosted version: "4.9.0" + json_serializable: + dependency: "direct dev" + description: + name: json_serializable + sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b + url: "https://pub.dev" + source: hosted + version: "6.8.0" leak_tracker: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ed939fa6a..abe1ade97 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -29,6 +29,7 @@ dependencies: wake_on_lan: ^4.1.1+3 flutter_adaptive_scaffold: ^0.1.10+2 extended_image: ^8.2.1 + json_annotation: ^4.9.0 dartssh2: git: url: https://github.com/lollipopkit/dartssh2 @@ -77,6 +78,7 @@ dev_dependencies: hive_generator: ^2.0.0 build_runner: ^2.3.2 flutter_lints: ^3.0.0 + json_serializable: ^6.8.0 flutter_test: sdk: flutter fl_build: