new: use generated ids for servers (#765)

* new: use generated ids for servers
Fixes #743

* fix: deps.

* fix: migrate related settings

* fix: restore servers from json
This commit is contained in:
lollipopkit🏳️‍⚧️
2025-05-16 21:50:44 +08:00
committed by GitHub
parent d29bd1d806
commit d88e97e699
16 changed files with 1028 additions and 155 deletions

View File

@@ -1,17 +1,18 @@
import 'dart:convert';
import 'package:equatable/equatable.dart';
import 'package:fl_lib/fl_lib.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
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';
import 'package:server_box/data/provider/server.dart';
import 'package:server_box/data/model/app/error.dart';
import 'package:server_box/data/store/server.dart';
part 'server_private_info.g.dart';
part 'server_private_info.freezed.dart';
/// In the first version, it's called `ServerPrivateInfo` which was designed to
/// store the private information of a server.
@@ -19,76 +20,66 @@ part 'server_private_info.g.dart';
/// Some params named as `spi` in the codebase which is the abbreviation of `ServerPrivateInfo`.
///
/// Nowaday, more fields are added to this class, and it's renamed to `Spi`.
@JsonSerializable()
@freezed
@HiveType(typeId: 3)
class Spi with EquatableMixin {
@HiveField(0)
final String name;
@HiveField(1)
final String ip;
@HiveField(2)
final int port;
@HiveField(3)
final String user;
@HiveField(4)
final String? pwd;
class Spi with _$Spi {
const Spi._();
/// [id] of private key
@JsonKey(name: 'pubKeyId')
@HiveField(5)
final String? keyId;
@HiveField(6)
final List<String>? tags;
@HiveField(7)
final String? alterUrl;
@HiveField(8, defaultValue: true)
final bool autoConnect;
const factory Spi({
@HiveField(0) required String name,
@HiveField(1) required String ip,
@HiveField(2) required int port,
@HiveField(3) required String user,
@HiveField(4) String? pwd,
/// [id] of the jump server
@HiveField(9)
final String? jumpId;
/// [id] of private key
@JsonKey(name: 'pubKeyId') @HiveField(5) String? keyId,
@HiveField(6) List<String>? tags,
@HiveField(7) String? alterUrl,
@HiveField(8, defaultValue: true) @Default(true) bool autoConnect,
@HiveField(10)
final ServerCustom? custom;
/// [id] of the jump server
@HiveField(9) String? jumpId,
@HiveField(10) ServerCustom? custom,
@HiveField(11) WakeOnLanCfg? wolCfg,
@HiveField(11)
final WakeOnLanCfg? wolCfg;
/// It only applies to SSH terminal.
@HiveField(12)
final Map<String, String>? envs;
final String id;
const Spi({
required this.name,
required this.ip,
required this.port,
required this.user,
required this.pwd,
this.keyId,
this.tags,
this.alterUrl,
this.autoConnect = true,
this.jumpId,
this.custom,
this.wolCfg,
this.envs,
}) : id = '$user@$ip:$port';
/// It only applies to SSH terminal.
@HiveField(12) Map<String, String>? envs,
@JsonKey(fromJson: Spi.parseId) @HiveField(13, defaultValue: '') required String id,
}) = _Spi;
factory Spi.fromJson(Map<String, dynamic> json) => _$SpiFromJson(json);
Map<String, dynamic> toJson() => _$SpiToJson(this);
@override
String toString() => id;
String toString() => 'Spi<$oldId>';
@override
List<Object?> get props =>
[name, ip, port, user, pwd, keyId, tags, alterUrl, autoConnect, jumpId, custom, wolCfg, envs];
static String parseId(Object? id) {
if (id == null || id is! String || id.isEmpty) return ShortId.generate();
return id;
}
}
extension Spix on Spi {
/// After upgrading to >= 1155, this field is only recommended to be used
/// for displaying the server name.
String get oldId => '$user@$ip:$port';
/// Save the [Spi] to the local storage.
void save() => ServerStore.instance.put(this);
/// Migrate the [oldId] to the new generated [id] by [ShortId.generate].
///
/// Returns:
/// - `null` if the [id] is not empty.
/// - The new [id] if the [id] is empty.
String? migrateId() {
if (id.isNotEmpty) return null;
ServerStore.instance.delete(oldId);
final newSpi = copyWith(id: ShortId.generate());
newSpi.save();
return newSpi.id;
}
String toJsonString() => json.encode(toJson());
VNode<Server>? get server => ServerProvider.pick(spi: this);
@@ -127,27 +118,27 @@ extension Spix on Spi {
/// Just for showing the struct of the class.
///
/// **NOT** the default value.
static const example = Spi(
name: 'name',
ip: 'ip',
port: 22,
user: 'root',
pwd: 'pwd',
keyId: 'private_key_id',
tags: ['tag1', 'tag2'],
alterUrl: 'user@ip:port',
autoConnect: true,
jumpId: 'jump_server_id',
custom: ServerCustom(
pveAddr: 'http://localhost:8006',
pveIgnoreCert: false,
cmds: {
'echo': 'echo hello',
},
preferTempDev: 'nvme-pci-0400',
logoUrl: 'https://example.com/logo.png',
),
);
static final example = Spi(
name: 'name',
ip: 'ip',
port: 22,
user: 'root',
pwd: 'pwd',
keyId: 'private_key_id',
tags: ['tag1', 'tag2'],
alterUrl: 'user@ip:port',
autoConnect: true,
jumpId: 'jump_server_id',
custom: ServerCustom(
pveAddr: 'http://localhost:8006',
pveIgnoreCert: false,
cmds: {
'echo': 'echo hello',
},
preferTempDev: 'nvme-pci-0400',
logoUrl: 'https://example.com/logo.png',
),
id: 'id');
bool get isRoot => user == 'root';
}

View File

@@ -0,0 +1,529 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'server_private_info.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
Spi _$SpiFromJson(Map<String, dynamic> json) {
return _Spi.fromJson(json);
}
/// @nodoc
mixin _$Spi {
@HiveField(0)
String get name => throw _privateConstructorUsedError;
@HiveField(1)
String get ip => throw _privateConstructorUsedError;
@HiveField(2)
int get port => throw _privateConstructorUsedError;
@HiveField(3)
String get user => throw _privateConstructorUsedError;
@HiveField(4)
String? get pwd => throw _privateConstructorUsedError;
/// [id] of private key
@JsonKey(name: 'pubKeyId')
@HiveField(5)
String? get keyId => throw _privateConstructorUsedError;
@HiveField(6)
List<String>? get tags => throw _privateConstructorUsedError;
@HiveField(7)
String? get alterUrl => throw _privateConstructorUsedError;
@HiveField(8, defaultValue: true)
bool get autoConnect => throw _privateConstructorUsedError;
/// [id] of the jump server
@HiveField(9)
String? get jumpId => throw _privateConstructorUsedError;
@HiveField(10)
ServerCustom? get custom => throw _privateConstructorUsedError;
@HiveField(11)
WakeOnLanCfg? get wolCfg => throw _privateConstructorUsedError;
/// It only applies to SSH terminal.
@HiveField(12)
Map<String, String>? get envs => throw _privateConstructorUsedError;
@JsonKey(fromJson: Spi.parseId)
@HiveField(13, defaultValue: '')
String get id => throw _privateConstructorUsedError;
/// Serializes this Spi to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of Spi
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$SpiCopyWith<Spi> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $SpiCopyWith<$Res> {
factory $SpiCopyWith(Spi value, $Res Function(Spi) then) =
_$SpiCopyWithImpl<$Res, Spi>;
@useResult
$Res call(
{@HiveField(0) String name,
@HiveField(1) String ip,
@HiveField(2) int port,
@HiveField(3) String user,
@HiveField(4) String? pwd,
@JsonKey(name: 'pubKeyId') @HiveField(5) String? keyId,
@HiveField(6) List<String>? tags,
@HiveField(7) String? alterUrl,
@HiveField(8, defaultValue: true) bool autoConnect,
@HiveField(9) String? jumpId,
@HiveField(10) ServerCustom? custom,
@HiveField(11) WakeOnLanCfg? wolCfg,
@HiveField(12) Map<String, String>? envs,
@JsonKey(fromJson: Spi.parseId)
@HiveField(13, defaultValue: '')
String id});
}
/// @nodoc
class _$SpiCopyWithImpl<$Res, $Val extends Spi> implements $SpiCopyWith<$Res> {
_$SpiCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of Spi
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? name = null,
Object? ip = null,
Object? port = null,
Object? user = null,
Object? pwd = freezed,
Object? keyId = freezed,
Object? tags = freezed,
Object? alterUrl = freezed,
Object? autoConnect = null,
Object? jumpId = freezed,
Object? custom = freezed,
Object? wolCfg = freezed,
Object? envs = freezed,
Object? id = null,
}) {
return _then(_value.copyWith(
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
ip: null == ip
? _value.ip
: ip // ignore: cast_nullable_to_non_nullable
as String,
port: null == port
? _value.port
: port // ignore: cast_nullable_to_non_nullable
as int,
user: null == user
? _value.user
: user // ignore: cast_nullable_to_non_nullable
as String,
pwd: freezed == pwd
? _value.pwd
: pwd // ignore: cast_nullable_to_non_nullable
as String?,
keyId: freezed == keyId
? _value.keyId
: keyId // ignore: cast_nullable_to_non_nullable
as String?,
tags: freezed == tags
? _value.tags
: tags // ignore: cast_nullable_to_non_nullable
as List<String>?,
alterUrl: freezed == alterUrl
? _value.alterUrl
: alterUrl // ignore: cast_nullable_to_non_nullable
as String?,
autoConnect: null == autoConnect
? _value.autoConnect
: autoConnect // ignore: cast_nullable_to_non_nullable
as bool,
jumpId: freezed == jumpId
? _value.jumpId
: jumpId // ignore: cast_nullable_to_non_nullable
as String?,
custom: freezed == custom
? _value.custom
: custom // ignore: cast_nullable_to_non_nullable
as ServerCustom?,
wolCfg: freezed == wolCfg
? _value.wolCfg
: wolCfg // ignore: cast_nullable_to_non_nullable
as WakeOnLanCfg?,
envs: freezed == envs
? _value.envs
: envs // ignore: cast_nullable_to_non_nullable
as Map<String, String>?,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
abstract class _$$SpiImplCopyWith<$Res> implements $SpiCopyWith<$Res> {
factory _$$SpiImplCopyWith(_$SpiImpl value, $Res Function(_$SpiImpl) then) =
__$$SpiImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{@HiveField(0) String name,
@HiveField(1) String ip,
@HiveField(2) int port,
@HiveField(3) String user,
@HiveField(4) String? pwd,
@JsonKey(name: 'pubKeyId') @HiveField(5) String? keyId,
@HiveField(6) List<String>? tags,
@HiveField(7) String? alterUrl,
@HiveField(8, defaultValue: true) bool autoConnect,
@HiveField(9) String? jumpId,
@HiveField(10) ServerCustom? custom,
@HiveField(11) WakeOnLanCfg? wolCfg,
@HiveField(12) Map<String, String>? envs,
@JsonKey(fromJson: Spi.parseId)
@HiveField(13, defaultValue: '')
String id});
}
/// @nodoc
class __$$SpiImplCopyWithImpl<$Res> extends _$SpiCopyWithImpl<$Res, _$SpiImpl>
implements _$$SpiImplCopyWith<$Res> {
__$$SpiImplCopyWithImpl(_$SpiImpl _value, $Res Function(_$SpiImpl) _then)
: super(_value, _then);
/// Create a copy of Spi
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? name = null,
Object? ip = null,
Object? port = null,
Object? user = null,
Object? pwd = freezed,
Object? keyId = freezed,
Object? tags = freezed,
Object? alterUrl = freezed,
Object? autoConnect = null,
Object? jumpId = freezed,
Object? custom = freezed,
Object? wolCfg = freezed,
Object? envs = freezed,
Object? id = null,
}) {
return _then(_$SpiImpl(
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
ip: null == ip
? _value.ip
: ip // ignore: cast_nullable_to_non_nullable
as String,
port: null == port
? _value.port
: port // ignore: cast_nullable_to_non_nullable
as int,
user: null == user
? _value.user
: user // ignore: cast_nullable_to_non_nullable
as String,
pwd: freezed == pwd
? _value.pwd
: pwd // ignore: cast_nullable_to_non_nullable
as String?,
keyId: freezed == keyId
? _value.keyId
: keyId // ignore: cast_nullable_to_non_nullable
as String?,
tags: freezed == tags
? _value._tags
: tags // ignore: cast_nullable_to_non_nullable
as List<String>?,
alterUrl: freezed == alterUrl
? _value.alterUrl
: alterUrl // ignore: cast_nullable_to_non_nullable
as String?,
autoConnect: null == autoConnect
? _value.autoConnect
: autoConnect // ignore: cast_nullable_to_non_nullable
as bool,
jumpId: freezed == jumpId
? _value.jumpId
: jumpId // ignore: cast_nullable_to_non_nullable
as String?,
custom: freezed == custom
? _value.custom
: custom // ignore: cast_nullable_to_non_nullable
as ServerCustom?,
wolCfg: freezed == wolCfg
? _value.wolCfg
: wolCfg // ignore: cast_nullable_to_non_nullable
as WakeOnLanCfg?,
envs: freezed == envs
? _value._envs
: envs // ignore: cast_nullable_to_non_nullable
as Map<String, String>?,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
@JsonSerializable()
class _$SpiImpl extends _Spi {
const _$SpiImpl(
{@HiveField(0) required this.name,
@HiveField(1) required this.ip,
@HiveField(2) required this.port,
@HiveField(3) required this.user,
@HiveField(4) this.pwd,
@JsonKey(name: 'pubKeyId') @HiveField(5) this.keyId,
@HiveField(6) final List<String>? tags,
@HiveField(7) this.alterUrl,
@HiveField(8, defaultValue: true) this.autoConnect = true,
@HiveField(9) this.jumpId,
@HiveField(10) this.custom,
@HiveField(11) this.wolCfg,
@HiveField(12) final Map<String, String>? envs,
@JsonKey(fromJson: Spi.parseId)
@HiveField(13, defaultValue: '')
required this.id})
: _tags = tags,
_envs = envs,
super._();
factory _$SpiImpl.fromJson(Map<String, dynamic> json) =>
_$$SpiImplFromJson(json);
@override
@HiveField(0)
final String name;
@override
@HiveField(1)
final String ip;
@override
@HiveField(2)
final int port;
@override
@HiveField(3)
final String user;
@override
@HiveField(4)
final String? pwd;
/// [id] of private key
@override
@JsonKey(name: 'pubKeyId')
@HiveField(5)
final String? keyId;
final List<String>? _tags;
@override
@HiveField(6)
List<String>? get tags {
final value = _tags;
if (value == null) return null;
if (_tags is EqualUnmodifiableListView) return _tags;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}
@override
@HiveField(7)
final String? alterUrl;
@override
@JsonKey()
@HiveField(8, defaultValue: true)
final bool autoConnect;
/// [id] of the jump server
@override
@HiveField(9)
final String? jumpId;
@override
@HiveField(10)
final ServerCustom? custom;
@override
@HiveField(11)
final WakeOnLanCfg? wolCfg;
/// It only applies to SSH terminal.
final Map<String, String>? _envs;
/// It only applies to SSH terminal.
@override
@HiveField(12)
Map<String, String>? get envs {
final value = _envs;
if (value == null) return null;
if (_envs is EqualUnmodifiableMapView) return _envs;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(value);
}
@override
@JsonKey(fromJson: Spi.parseId)
@HiveField(13, defaultValue: '')
final String id;
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$SpiImpl &&
(identical(other.name, name) || other.name == name) &&
(identical(other.ip, ip) || other.ip == ip) &&
(identical(other.port, port) || other.port == port) &&
(identical(other.user, user) || other.user == user) &&
(identical(other.pwd, pwd) || other.pwd == pwd) &&
(identical(other.keyId, keyId) || other.keyId == keyId) &&
const DeepCollectionEquality().equals(other._tags, _tags) &&
(identical(other.alterUrl, alterUrl) ||
other.alterUrl == alterUrl) &&
(identical(other.autoConnect, autoConnect) ||
other.autoConnect == autoConnect) &&
(identical(other.jumpId, jumpId) || other.jumpId == jumpId) &&
(identical(other.custom, custom) || other.custom == custom) &&
(identical(other.wolCfg, wolCfg) || other.wolCfg == wolCfg) &&
const DeepCollectionEquality().equals(other._envs, _envs) &&
(identical(other.id, id) || other.id == id));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(
runtimeType,
name,
ip,
port,
user,
pwd,
keyId,
const DeepCollectionEquality().hash(_tags),
alterUrl,
autoConnect,
jumpId,
custom,
wolCfg,
const DeepCollectionEquality().hash(_envs),
id);
/// Create a copy of Spi
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$SpiImplCopyWith<_$SpiImpl> get copyWith =>
__$$SpiImplCopyWithImpl<_$SpiImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$SpiImplToJson(
this,
);
}
}
abstract class _Spi extends Spi {
const factory _Spi(
{@HiveField(0) required final String name,
@HiveField(1) required final String ip,
@HiveField(2) required final int port,
@HiveField(3) required final String user,
@HiveField(4) final String? pwd,
@JsonKey(name: 'pubKeyId') @HiveField(5) final String? keyId,
@HiveField(6) final List<String>? tags,
@HiveField(7) final String? alterUrl,
@HiveField(8, defaultValue: true) final bool autoConnect,
@HiveField(9) final String? jumpId,
@HiveField(10) final ServerCustom? custom,
@HiveField(11) final WakeOnLanCfg? wolCfg,
@HiveField(12) final Map<String, String>? envs,
@JsonKey(fromJson: Spi.parseId)
@HiveField(13, defaultValue: '')
required final String id}) = _$SpiImpl;
const _Spi._() : super._();
factory _Spi.fromJson(Map<String, dynamic> json) = _$SpiImpl.fromJson;
@override
@HiveField(0)
String get name;
@override
@HiveField(1)
String get ip;
@override
@HiveField(2)
int get port;
@override
@HiveField(3)
String get user;
@override
@HiveField(4)
String? get pwd;
/// [id] of private key
@override
@JsonKey(name: 'pubKeyId')
@HiveField(5)
String? get keyId;
@override
@HiveField(6)
List<String>? get tags;
@override
@HiveField(7)
String? get alterUrl;
@override
@HiveField(8, defaultValue: true)
bool get autoConnect;
/// [id] of the jump server
@override
@HiveField(9)
String? get jumpId;
@override
@HiveField(10)
ServerCustom? get custom;
@override
@HiveField(11)
WakeOnLanCfg? get wolCfg;
/// It only applies to SSH terminal.
@override
@HiveField(12)
Map<String, String>? get envs;
@override
@JsonKey(fromJson: Spi.parseId)
@HiveField(13, defaultValue: '')
String get id;
/// Create a copy of Spi
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$SpiImplCopyWith<_$SpiImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -30,13 +30,14 @@ class SpiAdapter extends TypeAdapter<Spi> {
custom: fields[10] as ServerCustom?,
wolCfg: fields[11] as WakeOnLanCfg?,
envs: (fields[12] as Map?)?.cast<String, String>(),
id: fields[13] == null ? '' : fields[13] as String,
);
}
@override
void write(BinaryWriter writer, Spi obj) {
writer
..writeByte(13)
..writeByte(14)
..writeByte(0)
..write(obj.name)
..writeByte(1)
@@ -62,7 +63,9 @@ class SpiAdapter extends TypeAdapter<Spi> {
..writeByte(11)
..write(obj.wolCfg)
..writeByte(12)
..write(obj.envs);
..write(obj.envs)
..writeByte(13)
..write(obj.id);
}
@override
@@ -80,7 +83,7 @@ class SpiAdapter extends TypeAdapter<Spi> {
// JsonSerializableGenerator
// **************************************************************************
Spi _$SpiFromJson(Map<String, dynamic> json) => Spi(
_$SpiImpl _$$SpiImplFromJson(Map<String, dynamic> json) => _$SpiImpl(
name: json['name'] as String,
ip: json['ip'] as String,
port: (json['port'] as num).toInt(),
@@ -100,9 +103,10 @@ Spi _$SpiFromJson(Map<String, dynamic> json) => Spi(
envs: (json['envs'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as String),
),
id: Spi.parseId(json['id']),
);
Map<String, dynamic> _$SpiToJson(Spi instance) => <String, dynamic>{
Map<String, dynamic> _$$SpiImplToJson(_$SpiImpl instance) => <String, dynamic>{
'name': instance.name,
'ip': instance.ip,
'port': instance.port,
@@ -116,4 +120,5 @@ Map<String, dynamic> _$SpiToJson(Spi instance) => <String, dynamic>{
'custom': instance.custom,
'wolCfg': instance.wolCfg,
'envs': instance.envs,
'id': instance.id,
};

View File

@@ -1,50 +1,36 @@
import 'dart:async';
import 'package:equatable/equatable.dart';
import 'package:fl_lib/fl_lib.dart';
import 'package:freezed_annotation/freezed_annotation.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';
part 'snippet.g.dart';
part 'snippet.freezed.dart';
@JsonSerializable()
@freezed
@HiveType(typeId: 2)
class Snippet with EquatableMixin {
@HiveField(0)
final String name;
@HiveField(1)
final String script;
@HiveField(2)
final List<String>? tags;
@HiveField(3)
final String? note;
/// List of server id that this snippet should be auto run on
@HiveField(4)
final List<String>? autoRunOn;
const Snippet({
required this.name,
required this.script,
this.tags,
this.note,
this.autoRunOn,
});
class Snippet with _$Snippet {
const factory Snippet({
@HiveField(0) required String name,
@HiveField(1) required String script,
@HiveField(2) List<String>? tags,
@HiveField(3) String? note,
/// List of server id that this snippet should be auto run on
@HiveField(4) List<String>? autoRunOn,
}) = _Snippet;
factory Snippet.fromJson(Map<String, dynamic> json) => _$SnippetFromJson(json);
Map<String, dynamic> toJson() => _$SnippetToJson(this);
@override
List<Object?> get props => [
name,
script,
tags,
note,
autoRunOn,
];
static const example = Snippet(
name: 'example',
script: 'echo hello',
tags: ['tag'],
note: 'note',
autoRunOn: ['server_id'],
);
}
extension SnippetX on Snippet {
@@ -73,7 +59,7 @@ extension SnippetX on Snippet {
/// There is no [TerminalKey] in the script
if (matches.isEmpty) {
terminal.textInput(argsFmted);
terminal.textInput(argsFmted);
if (autoEnter) terminal.keyInput(TerminalKey.enter);
return;
}
@@ -186,14 +172,6 @@ extension SnippetX on Snippet {
r'${ctrl': TerminalKey.control,
r'${alt': TerminalKey.alt,
};
static const example = Snippet(
name: 'example',
script: 'echo hello',
tags: ['tag'],
note: 'note',
autoRunOn: ['server_id'],
);
}
class SnippetResult {

View File

@@ -0,0 +1,291 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'snippet.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
Snippet _$SnippetFromJson(Map<String, dynamic> json) {
return _Snippet.fromJson(json);
}
/// @nodoc
mixin _$Snippet {
@HiveField(0)
String get name => throw _privateConstructorUsedError;
@HiveField(1)
String get script => throw _privateConstructorUsedError;
@HiveField(2)
List<String>? get tags => throw _privateConstructorUsedError;
@HiveField(3)
String? get note => throw _privateConstructorUsedError;
/// List of server id that this snippet should be auto run on
@HiveField(4)
List<String>? get autoRunOn => throw _privateConstructorUsedError;
/// Serializes this Snippet to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of Snippet
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$SnippetCopyWith<Snippet> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $SnippetCopyWith<$Res> {
factory $SnippetCopyWith(Snippet value, $Res Function(Snippet) then) =
_$SnippetCopyWithImpl<$Res, Snippet>;
@useResult
$Res call(
{@HiveField(0) String name,
@HiveField(1) String script,
@HiveField(2) List<String>? tags,
@HiveField(3) String? note,
@HiveField(4) List<String>? autoRunOn});
}
/// @nodoc
class _$SnippetCopyWithImpl<$Res, $Val extends Snippet>
implements $SnippetCopyWith<$Res> {
_$SnippetCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of Snippet
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? name = null,
Object? script = null,
Object? tags = freezed,
Object? note = freezed,
Object? autoRunOn = freezed,
}) {
return _then(_value.copyWith(
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
script: null == script
? _value.script
: script // ignore: cast_nullable_to_non_nullable
as String,
tags: freezed == tags
? _value.tags
: tags // ignore: cast_nullable_to_non_nullable
as List<String>?,
note: freezed == note
? _value.note
: note // ignore: cast_nullable_to_non_nullable
as String?,
autoRunOn: freezed == autoRunOn
? _value.autoRunOn
: autoRunOn // ignore: cast_nullable_to_non_nullable
as List<String>?,
) as $Val);
}
}
/// @nodoc
abstract class _$$SnippetImplCopyWith<$Res> implements $SnippetCopyWith<$Res> {
factory _$$SnippetImplCopyWith(
_$SnippetImpl value, $Res Function(_$SnippetImpl) then) =
__$$SnippetImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{@HiveField(0) String name,
@HiveField(1) String script,
@HiveField(2) List<String>? tags,
@HiveField(3) String? note,
@HiveField(4) List<String>? autoRunOn});
}
/// @nodoc
class __$$SnippetImplCopyWithImpl<$Res>
extends _$SnippetCopyWithImpl<$Res, _$SnippetImpl>
implements _$$SnippetImplCopyWith<$Res> {
__$$SnippetImplCopyWithImpl(
_$SnippetImpl _value, $Res Function(_$SnippetImpl) _then)
: super(_value, _then);
/// Create a copy of Snippet
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? name = null,
Object? script = null,
Object? tags = freezed,
Object? note = freezed,
Object? autoRunOn = freezed,
}) {
return _then(_$SnippetImpl(
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
script: null == script
? _value.script
: script // ignore: cast_nullable_to_non_nullable
as String,
tags: freezed == tags
? _value._tags
: tags // ignore: cast_nullable_to_non_nullable
as List<String>?,
note: freezed == note
? _value.note
: note // ignore: cast_nullable_to_non_nullable
as String?,
autoRunOn: freezed == autoRunOn
? _value._autoRunOn
: autoRunOn // ignore: cast_nullable_to_non_nullable
as List<String>?,
));
}
}
/// @nodoc
@JsonSerializable()
class _$SnippetImpl implements _Snippet {
const _$SnippetImpl(
{@HiveField(0) required this.name,
@HiveField(1) required this.script,
@HiveField(2) final List<String>? tags,
@HiveField(3) this.note,
@HiveField(4) final List<String>? autoRunOn})
: _tags = tags,
_autoRunOn = autoRunOn;
factory _$SnippetImpl.fromJson(Map<String, dynamic> json) =>
_$$SnippetImplFromJson(json);
@override
@HiveField(0)
final String name;
@override
@HiveField(1)
final String script;
final List<String>? _tags;
@override
@HiveField(2)
List<String>? get tags {
final value = _tags;
if (value == null) return null;
if (_tags is EqualUnmodifiableListView) return _tags;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}
@override
@HiveField(3)
final String? note;
/// List of server id that this snippet should be auto run on
final List<String>? _autoRunOn;
/// List of server id that this snippet should be auto run on
@override
@HiveField(4)
List<String>? get autoRunOn {
final value = _autoRunOn;
if (value == null) return null;
if (_autoRunOn is EqualUnmodifiableListView) return _autoRunOn;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}
@override
String toString() {
return 'Snippet(name: $name, script: $script, tags: $tags, note: $note, autoRunOn: $autoRunOn)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$SnippetImpl &&
(identical(other.name, name) || other.name == name) &&
(identical(other.script, script) || other.script == script) &&
const DeepCollectionEquality().equals(other._tags, _tags) &&
(identical(other.note, note) || other.note == note) &&
const DeepCollectionEquality()
.equals(other._autoRunOn, _autoRunOn));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(
runtimeType,
name,
script,
const DeepCollectionEquality().hash(_tags),
note,
const DeepCollectionEquality().hash(_autoRunOn));
/// Create a copy of Snippet
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$SnippetImplCopyWith<_$SnippetImpl> get copyWith =>
__$$SnippetImplCopyWithImpl<_$SnippetImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$SnippetImplToJson(
this,
);
}
}
abstract class _Snippet implements Snippet {
const factory _Snippet(
{@HiveField(0) required final String name,
@HiveField(1) required final String script,
@HiveField(2) final List<String>? tags,
@HiveField(3) final String? note,
@HiveField(4) final List<String>? autoRunOn}) = _$SnippetImpl;
factory _Snippet.fromJson(Map<String, dynamic> json) = _$SnippetImpl.fromJson;
@override
@HiveField(0)
String get name;
@override
@HiveField(1)
String get script;
@override
@HiveField(2)
List<String>? get tags;
@override
@HiveField(3)
String? get note;
/// List of server id that this snippet should be auto run on
@override
@HiveField(4)
List<String>? get autoRunOn;
/// Create a copy of Snippet
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$SnippetImplCopyWith<_$SnippetImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -56,7 +56,8 @@ class SnippetAdapter extends TypeAdapter<Snippet> {
// JsonSerializableGenerator
// **************************************************************************
Snippet _$SnippetFromJson(Map<String, dynamic> json) => Snippet(
_$SnippetImpl _$$SnippetImplFromJson(Map<String, dynamic> json) =>
_$SnippetImpl(
name: json['name'] as String,
script: json['script'] as String,
tags: (json['tags'] as List<dynamic>?)?.map((e) => e as String).toList(),
@@ -66,7 +67,8 @@ Snippet _$SnippetFromJson(Map<String, dynamic> json) => Snippet(
.toList(),
);
Map<String, dynamic> _$SnippetToJson(Snippet instance) => <String, dynamic>{
Map<String, dynamic> _$$SnippetImplToJson(_$SnippetImpl instance) =>
<String, dynamic>{
'name': instance.name,
'script': instance.script,
'tags': instance.tags,

View File

@@ -208,7 +208,7 @@ class ServerProvider extends Provider {
serverOrder.value.clear();
serverOrder.notify();
Stores.setting.serverOrder.put(serverOrder.value);
Stores.server.deleteAll();
Stores.server.clear();
_updateTags();
bakSync.sync(milliDelay: 1000);
}

View File

@@ -1,6 +1,9 @@
import 'package:fl_lib/fl_lib.dart';
import 'package:server_box/data/model/server/server_private_info.dart';
import 'package:server_box/data/store/container.dart';
import 'package:server_box/data/store/setting.dart';
import 'package:server_box/data/store/snippet.dart';
class ServerStore extends HiveStore {
ServerStore._() : super('server');
@@ -8,15 +11,12 @@ class ServerStore extends HiveStore {
static final instance = ServerStore._();
void put(Spi info) {
// box.put(info.id, info);
// box.updateLastModified();
set(info.id, info);
}
List<Spi> fetch() {
final ids = box.keys;
final List<Spi> ss = [];
for (final id in ids) {
for (final id in keys()) {
final s = box.get(id);
if (s != null && s is Spi) {
ss.add(s);
@@ -29,10 +29,6 @@ class ServerStore extends HiveStore {
remove(id);
}
void deleteAll() {
clear();
}
void update(Spi old, Spi newInfo) {
if (!have(old)) {
throw Exception('Old spi: $old not found');
@@ -41,5 +37,74 @@ class ServerStore extends HiveStore {
put(newInfo);
}
bool have(Spi s) => box.get(s.id) != null;
bool have(Spi s) => get(s.id) != null;
void migrateIds() {
final ss = fetch();
final idMap = <String, String>{};
// Collect all old to new ID mappings
for (final s in ss) {
final newId = s.migrateId();
if (newId == null) continue;
// Use s.oldId as the key, because s.id would be empty for a server being migrated.
// s.oldId represents the identifier used before migration.
idMap[s.oldId] = newId;
}
final srvOrder = SettingStore.instance.serverOrder.fetch();
final snippets = SnippetStore.instance.fetch();
final container = ContainerStore.instance;
bool srvOrderChanged = false;
// Update all references to the servers
for (final e in idMap.entries) {
final oldId = e.key;
final newId = e.value;
// Replace ids in ordering settings.
final srvIdx = srvOrder.indexOf(oldId);
if (srvIdx != -1) {
srvOrder[srvIdx] = newId;
srvOrderChanged = true;
}
// Replace ids in jump server settings.
final spi = get<Spi>(newId);
if (spi != null) {
final jumpId = spi.jumpId; // This could be an oldId.
// Check if this jumpId corresponds to a server that was also migrated.
if (jumpId != null && idMap.containsKey(jumpId)) {
final newJumpId = idMap[jumpId];
if (spi.jumpId != newJumpId) {
final newSpi = spi.copyWith(jumpId: newJumpId);
update(spi, newSpi);
}
}
}
// Replace ids in [Snippet]
for (final snippet in snippets) {
final autoRunsOn = snippet.autoRunOn;
final idx = autoRunsOn?.indexOf(oldId);
if (idx != null && idx != -1) {
final newAutoRunsOn = List<String>.from(autoRunsOn ?? []);
newAutoRunsOn[idx] = newId;
final newSnippet = snippet.copyWith(autoRunOn: newAutoRunsOn);
SnippetStore.instance.update(snippet, newSnippet);
}
}
// Replace ids in [Container]
final dockerHost = container.fetch(oldId);
if (dockerHost != null) {
container.remove(oldId);
container.set(newId, dockerHost);
}
}
if (srvOrderChanged) {
SettingStore.instance.serverOrder.put(srvOrder);
}
}
}

View File

@@ -8,8 +8,6 @@ class SnippetStore extends HiveStore {
static final instance = SnippetStore._();
void put(Snippet snippet) {
// box.put(snippet.name, snippet);
// box.updateLastModified();
set(snippet.name, snippet);
}
@@ -25,8 +23,16 @@ class SnippetStore extends HiveStore {
}
void delete(Snippet s) {
// box.delete(s.name);
// box.updateLastModified();
remove(s.name);
}
void update(Snippet old, Snippet newInfo) {
if (!have(old)) {
throw Exception('Old snippet: $old not found');
}
delete(old);
put(newInfo);
}
bool have(Snippet s) => get(s.name) != null;
}