Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
366 changes: 311 additions & 55 deletions lib/core/utils/server.dart

Large diffs are not rendered by default.

20 changes: 16 additions & 4 deletions lib/data/model/server/server_private_info.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:convert';

import 'package:fl_lib/fl_lib.dart';
import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:server_box/data/model/app/error.dart';
import 'package:server_box/data/model/server/custom.dart';
Expand Down Expand Up @@ -35,8 +36,15 @@ abstract class Spi with _$Spi {
String? alterUrl,
@Default(true) bool autoConnect,

/// [id] of the jump server
/// [id] of the jump server (legacy, single hop)
///
/// Migrated to [jumpChainIds].
String? jumpId,

/// Jump chain hop ids (nearest -> farthest)
///
/// Preferred over [jumpId].
@JsonKey(includeIfNull: false) List<String>? jumpChainIds,
ServerCustom? custom,
WakeOnLanCfg? wolCfg,

Expand Down Expand Up @@ -79,7 +87,10 @@ extension Spix on Spi {
String? migrateId() {
if (id.isNotEmpty) return null;
ServerStore.instance.delete(oldId);
final newSpi = copyWith(id: ShortId.generate());
final newSpi = copyWith(
id: ShortId.generate(),
jumpChainIds: jumpChainIds ?? (jumpId == null ? null : [jumpId!]),
);
newSpi.save();
return newSpi.id;
}
Expand All @@ -94,7 +105,8 @@ extension Spix on Spi {
port == other.port &&
pwd == other.pwd &&
keyId == other.keyId &&
jumpId == other.jumpId;
jumpId == other.jumpId &&
listEquals(jumpChainIds, other.jumpChainIds);
}

/// Returns true if the connection should be re-established.
Expand Down Expand Up @@ -137,7 +149,7 @@ extension Spix on Spi {
tags: ['tag1', 'tag2'],
alterUrl: 'user@ip:port',
autoConnect: true,
jumpId: 'jump_server_id',
jumpChainIds: ['jump_server_id'],
custom: ServerCustom(
pveAddr: 'http://localhost:8006',
pveIgnoreCert: false,
Expand Down
64 changes: 44 additions & 20 deletions lib/data/model/server/server_private_info.freezed.dart

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions lib/data/model/server/server_private_info.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 14 additions & 6 deletions lib/data/model/sftp/req.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,26 @@ class SftpReq {
final String localPath;
final SftpReqType type;
String? privateKey;
Spi? jumpSpi;
String? jumpPrivateKey;
List<Spi>? jumpChain;
List<String?>? jumpPrivateKeys;
Map<String, String>? knownHostFingerprints;

SftpReq(this.spi, this.remotePath, this.localPath, this.type) {
final keyId = spi.keyId;
if (keyId != null) {
privateKey = getPrivateKey(keyId);
}
if (spi.jumpId != null) {
jumpSpi = Stores.server.box.get(spi.jumpId);
jumpPrivateKey = Stores.key.fetchOne(jumpSpi?.keyId)?.key;
if (spi.jumpChainIds != null || spi.jumpId != null) {
// Use resolveMergedJumpChain to recursively expand nested hop chains
final chain = resolveMergedJumpChain(spi);
final keys = <String?>[];
for (final hop in chain) {
keys.add(hop.keyId != null ? getPrivateKey(hop.keyId!) : null);
}

// Always set when a jump is configured so the isolate won't fallback to Stores.
jumpChain = chain;
jumpPrivateKeys = keys;
}
try {
knownHostFingerprints = Map<String, String>.from(Stores.setting.sshKnownHostFingerprints.get());
Expand Down Expand Up @@ -90,4 +98,4 @@ class SftpReqStatus {
}
}

enum SftpWorkerStatus { preparing, sshConnectted, loading, finished }
enum SftpWorkerStatus { preparing, sshConnected, loading, finished }
12 changes: 6 additions & 6 deletions lib/data/model/sftp/worker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ Future<void> _download(SftpReq req, SendPort mainSendPort, SendErrorFunction sen
final client = await genClient(
req.spi,
privateKey: req.privateKey,
jumpSpi: req.jumpSpi,
jumpPrivateKey: req.jumpPrivateKey,
jumpChain: req.jumpChain,
jumpPrivateKeys: req.jumpPrivateKeys,
knownHostFingerprints: req.knownHostFingerprints,
);
mainSendPort.send(SftpWorkerStatus.sshConnectted);
mainSendPort.send(SftpWorkerStatus.sshConnected);

/// Create the directory if not exists
final dirPath = req.localPath.substring(0, req.localPath.lastIndexOf(Pfs.seperator));
Expand Down Expand Up @@ -120,11 +120,11 @@ Future<void> _upload(SftpReq req, SendPort mainSendPort, SendErrorFunction sendE
final client = await genClient(
req.spi,
privateKey: req.privateKey,
jumpSpi: req.jumpSpi,
jumpPrivateKey: req.jumpPrivateKey,
jumpChain: req.jumpChain,
jumpPrivateKeys: req.jumpPrivateKeys,
knownHostFingerprints: req.knownHostFingerprints,
);
mainSendPort.send(SftpWorkerStatus.sshConnectted);
mainSendPort.send(SftpWorkerStatus.sshConnected);

final local = File(req.localPath);
if (!await local.exists()) {
Expand Down
2 changes: 1 addition & 1 deletion lib/data/provider/server/all.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/data/provider/server/single.dart
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ class ServerNotifier extends _$ServerNotifier {

final time2 = DateTime.now();
final spentTime = time2.difference(time1).inMilliseconds;
if (spi.jumpId == null) {
if (spi.jumpChainIds == null && spi.jumpId == null) {
Loggers.app.info('Connected to ${spi.name} in $spentTime ms.');
} else {
Loggers.app.info('Jump to ${spi.name} in $spentTime ms.');
Expand Down
2 changes: 1 addition & 1 deletion lib/data/provider/server/single.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 6 additions & 9 deletions lib/data/store/server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,12 @@ class ServerStore extends HiveStore {
// 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);
}
}
final jumpChainIds = spi.jumpChainIds ?? (spi.jumpId == null ? null : [spi.jumpId!]);
if (jumpChainIds == null || jumpChainIds.isEmpty) continue;

final newChain = jumpChainIds.map((e) => idMap[e] ?? e).toList();
final newSpi = spi.copyWith(jumpId: null, jumpChainIds: newChain);
update(spi, newSpi);
}

// Replace ids in [Snippet]
Expand Down
7 changes: 5 additions & 2 deletions lib/hive/hive_adapters.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion lib/hive/hive_adapters.g.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ types:
index: 4
Spi:
typeId: 3
nextIndex: 16
nextIndex: 17
fields:
name:
index: 0
Expand Down Expand Up @@ -61,6 +61,8 @@ types:
index: 14
disabledCmdTypes:
index: 15
jumpChainIds:
index: 16
VirtKey:
typeId: 4
nextIndex: 45
Expand Down
29 changes: 27 additions & 2 deletions lib/view/page/server/edit/actions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,30 @@ extension _Actions on _ServerEditPageState {
return;
}

final oldSpi = this.spi;
if (oldSpi != null) {
final originalJumpChain = oldSpi.jumpChainIds ?? (oldSpi.jumpId == null ? const <String>[] : [oldSpi.jumpId!]);
final currentJumpChain = _jumpChain.value;

final jumpChainChanged = () {
if (originalJumpChain.isEmpty && currentJumpChain.isEmpty) return false;
if (originalJumpChain.length != currentJumpChain.length) return true;
for (var i = 0; i < originalJumpChain.length; i++) {
if (originalJumpChain[i] != currentJumpChain[i]) return true;
}
return false;
}();

if (jumpChainChanged) {
final ok = await context.showRoundDialog<bool>(
title: libL10n.attention,
child: Text(libL10n.askContinue('${l10n.jumpServer} ${libL10n.setting}')),
actions: Btnx.cancelOk,
);
if (ok != true) return;
}
}

if (_keyIdx.value == null && _passwordController.text.isEmpty) {
final ok = await context.showRoundDialog<bool>(
title: libL10n.attention,
Expand Down Expand Up @@ -277,7 +301,8 @@ extension _Actions on _ServerEditPageState {
tags: _tags.value.isEmpty ? null : _tags.value.toList(),
alterUrl: _altUrlController.text.selfNotEmptyOrNull,
autoConnect: _autoConnect.value,
jumpId: _jumpServer.value,
jumpId: null,
jumpChainIds: _jumpChain.value.isEmpty ? null : _jumpChain.value,
custom: custom,
wolCfg: wol,
envs: _env.value.isEmpty ? null : _env.value,
Expand Down Expand Up @@ -421,7 +446,7 @@ extension _Utils on _ServerEditPageState {

_altUrlController.text = spi.alterUrl ?? '';
_autoConnect.value = spi.autoConnect;
_jumpServer.value = spi.jumpId;
_jumpChain.value = spi.jumpChainIds ?? (spi.jumpId == null ? const <String>[] : [spi.jumpId!]);

final custom = spi.custom;
if (custom != null) {
Expand Down
6 changes: 3 additions & 3 deletions lib/view/page/server/edit/edit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import 'package:server_box/view/page/private_key/edit.dart';
import 'package:server_box/view/page/server/discovery/discovery.dart';

part 'actions.dart';
part 'jump_chain.dart';
part 'widget.dart';

class ServerEditPage extends ConsumerStatefulWidget {
Expand Down Expand Up @@ -66,7 +67,7 @@ class _ServerEditPageState extends ConsumerState<ServerEditPage> with AfterLayou
/// -1: non selected, null: password, others: index of private key
final _keyIdx = ValueNotifier<int?>(null);
final _autoConnect = ValueNotifier(true);
final _jumpServer = nvn<String?>();
final _jumpChain = <String>[].vn;
final _pveIgnoreCert = ValueNotifier(false);
final _env = <String, String>{}.vn;
final _customCmds = <String, String>{}.vn;
Expand Down Expand Up @@ -100,7 +101,7 @@ class _ServerEditPageState extends ConsumerState<ServerEditPage> with AfterLayou

_keyIdx.dispose();
_autoConnect.dispose();
_jumpServer.dispose();
_jumpChain.dispose();
_pveIgnoreCert.dispose();
_env.dispose();
_customCmds.dispose();
Expand Down Expand Up @@ -199,7 +200,6 @@ class _ServerEditPageState extends ConsumerState<ServerEditPage> with AfterLayou
),
_buildAuth(),
_buildSystemType(),
_buildJumpServer(),
_buildMore(),
];
return AutoMultiList(children: children);
Expand Down
Loading