mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
opt.: ssh page
This commit is contained in:
@@ -53,7 +53,7 @@ class ServerStatus {
|
|||||||
NetSpeed netSpeed;
|
NetSpeed netSpeed;
|
||||||
Temperatures temps;
|
Temperatures temps;
|
||||||
SystemType system;
|
SystemType system;
|
||||||
String? failedInfo;
|
String? err;
|
||||||
DiskIO diskIO;
|
DiskIO diskIO;
|
||||||
|
|
||||||
/// Whether is connectting, parsing and etc.
|
/// Whether is connectting, parsing and etc.
|
||||||
@@ -71,7 +71,7 @@ class ServerStatus {
|
|||||||
required this.temps,
|
required this.temps,
|
||||||
required this.system,
|
required this.system,
|
||||||
required this.diskIO,
|
required this.diskIO,
|
||||||
this.failedInfo,
|
this.err,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
s.status.failedInfo = null;
|
s.status.err = null;
|
||||||
|
|
||||||
/// If busy, it may be because of network reasons that the last request
|
/// If busy, it may be because of network reasons that the last request
|
||||||
/// has not been completed, and the request should not be made again at this time.
|
/// has not been completed, and the request should not be made again at this time.
|
||||||
@@ -286,7 +286,7 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
TryLimiter.inc(sid);
|
TryLimiter.inc(sid);
|
||||||
s.status.failedInfo = e.toString();
|
s.status.err = e.toString();
|
||||||
_setServerState(s, ServerState.failed);
|
_setServerState(s, ServerState.failed);
|
||||||
|
|
||||||
/// In order to keep privacy, print [spi.name] instead of [spi.id]
|
/// In order to keep privacy, print [spi.name] instead of [spi.id]
|
||||||
@@ -329,7 +329,7 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
TryLimiter.inc(sid);
|
TryLimiter.inc(sid);
|
||||||
s.status.failedInfo = e.toString();
|
s.status.err = e.toString();
|
||||||
_setServerState(s, ServerState.failed);
|
_setServerState(s, ServerState.failed);
|
||||||
Loggers.app.warning('Write script to ${spi.name} by sftp', e);
|
Loggers.app.warning('Write script to ${spi.name} by sftp', e);
|
||||||
return;
|
return;
|
||||||
@@ -354,13 +354,13 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
segments = raw?.split(seperator).map((e) => e.trim()).toList();
|
segments = raw?.split(seperator).map((e) => e.trim()).toList();
|
||||||
if (raw == null || raw.isEmpty || segments == null || segments.isEmpty) {
|
if (raw == null || raw.isEmpty || segments == null || segments.isEmpty) {
|
||||||
TryLimiter.inc(sid);
|
TryLimiter.inc(sid);
|
||||||
s.status.failedInfo = 'Seperate segments failed, raw:\n$raw';
|
s.status.err = 'Seperate segments failed, raw:\n$raw';
|
||||||
_setServerState(s, ServerState.failed);
|
_setServerState(s, ServerState.failed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
TryLimiter.inc(sid);
|
TryLimiter.inc(sid);
|
||||||
s.status.failedInfo = e.toString();
|
s.status.err = e.toString();
|
||||||
_setServerState(s, ServerState.failed);
|
_setServerState(s, ServerState.failed);
|
||||||
Loggers.app.warning('Get status from ${spi.name} failed', e);
|
Loggers.app.warning('Get status from ${spi.name} failed', e);
|
||||||
return;
|
return;
|
||||||
@@ -369,7 +369,7 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
final systemType = SystemType.parse(segments[0]);
|
final systemType = SystemType.parse(segments[0]);
|
||||||
if (!systemType.isSegmentsLenMatch(segments.length)) {
|
if (!systemType.isSegmentsLenMatch(segments.length)) {
|
||||||
TryLimiter.inc(sid);
|
TryLimiter.inc(sid);
|
||||||
s.status.failedInfo =
|
s.status.err =
|
||||||
'Segments not match: expect ${systemType.segmentsLen}, got ${segments.length}';
|
'Segments not match: expect ${systemType.segmentsLen}, got ${segments.length}';
|
||||||
_setServerState(s, ServerState.failed);
|
_setServerState(s, ServerState.failed);
|
||||||
return;
|
return;
|
||||||
@@ -385,7 +385,7 @@ class ServerProvider extends ChangeNotifier {
|
|||||||
s.status = await compute(getStatus, req);
|
s.status = await compute(getStatus, req);
|
||||||
} catch (e, trace) {
|
} catch (e, trace) {
|
||||||
TryLimiter.inc(sid);
|
TryLimiter.inc(sid);
|
||||||
s.status.failedInfo = 'Parse failed: $e\n\n$raw';
|
s.status.err = 'Parse failed: $e\n\n$raw';
|
||||||
_setServerState(s, ServerState.failed);
|
_setServerState(s, ServerState.failed);
|
||||||
Loggers.parse.warning('Server status', e, trace);
|
Loggers.parse.warning('Server status', e, trace);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ class _FullScreenPageState extends State<FullScreenPage> with AfterLayoutMixin {
|
|||||||
cs,
|
cs,
|
||||||
ss.temps.first,
|
ss.temps.first,
|
||||||
ss.uptime,
|
ss.uptime,
|
||||||
ss.failedInfo,
|
ss.err,
|
||||||
);
|
);
|
||||||
return Text(
|
return Text(
|
||||||
topRightStr,
|
topRightStr,
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
if (srv.canViewDetails) {
|
if (srv.canViewDetails) {
|
||||||
AppRoute.serverDetail(spi: srv.spi).go(context);
|
AppRoute.serverDetail(spi: srv.spi).go(context);
|
||||||
} else if (srv.status.failedInfo != null) {
|
} else if (srv.status.err != null) {
|
||||||
_showFailReason(srv.status);
|
_showFailReason(srv.status);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -398,9 +398,9 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
cs,
|
cs,
|
||||||
ss.temps.first,
|
ss.temps.first,
|
||||||
ss.uptime,
|
ss.uptime,
|
||||||
ss.failedInfo,
|
ss.err,
|
||||||
);
|
);
|
||||||
if (cs == ServerState.failed && ss.failedInfo != null) {
|
if (cs == ServerState.failed && ss.err != null) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => _showFailReason(ss),
|
onTap: () => _showFailReason(ss),
|
||||||
child: Text(
|
child: Text(
|
||||||
@@ -421,11 +421,11 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
context.showRoundDialog(
|
context.showRoundDialog(
|
||||||
title: Text(l10n.error),
|
title: Text(l10n.error),
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: Text(ss.failedInfo ?? l10n.unknownError),
|
child: Text(ss.err ?? l10n.unknownError),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Shares.copy(ss.failedInfo!),
|
onPressed: () => Shares.copy(ss.err!),
|
||||||
child: Text(l10n.copy),
|
child: Text(l10n.copy),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:toolbox/core/extension/context/common.dart';
|
import 'package:toolbox/core/extension/context/common.dart';
|
||||||
import 'package:toolbox/core/extension/context/dialog.dart';
|
import 'package:toolbox/core/extension/context/dialog.dart';
|
||||||
import 'package:toolbox/core/extension/context/locale.dart';
|
import 'package:toolbox/core/extension/context/locale.dart';
|
||||||
import 'package:toolbox/core/extension/context/snackbar.dart';
|
|
||||||
import 'package:toolbox/core/utils/platform/base.dart';
|
import 'package:toolbox/core/utils/platform/base.dart';
|
||||||
import 'package:toolbox/core/utils/share.dart';
|
import 'package:toolbox/core/utils/share.dart';
|
||||||
import 'package:toolbox/data/model/server/server.dart';
|
import 'package:toolbox/data/model/server/server.dart';
|
||||||
@@ -27,7 +26,7 @@ import '../../../data/model/ssh/virtual_key.dart';
|
|||||||
import '../../../data/res/color.dart';
|
import '../../../data/res/color.dart';
|
||||||
import '../../../data/res/terminal.dart';
|
import '../../../data/res/terminal.dart';
|
||||||
|
|
||||||
const echoPWD = 'echo \$PWD';
|
const _echoPWD = 'echo \$PWD';
|
||||||
|
|
||||||
class SSHPage extends StatefulWidget {
|
class SSHPage extends StatefulWidget {
|
||||||
final ServerPrivateInfo spi;
|
final ServerPrivateInfo spi;
|
||||||
@@ -55,7 +54,6 @@ class _SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin {
|
|||||||
Timer? _virtKeyLongPressTimer;
|
Timer? _virtKeyLongPressTimer;
|
||||||
late final Server? _server = widget.spi.server;
|
late final Server? _server = widget.spi.server;
|
||||||
late final SSHClient? _client = _server?.client;
|
late final SSHClient? _client = _server?.client;
|
||||||
late final SSHSession? _session;
|
|
||||||
Timer? _discontinuityTimer;
|
Timer? _discontinuityTimer;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -68,8 +66,11 @@ class _SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin {
|
|||||||
);
|
);
|
||||||
_terminalStyle = TerminalStyle.fromTextStyle(textStyle);
|
_terminalStyle = TerminalStyle.fromTextStyle(textStyle);
|
||||||
_keyboardType = TextInputType.values[Stores.setting.keyboardType.fetch()];
|
_keyboardType = TextInputType.values[Stores.setting.keyboardType.fetch()];
|
||||||
_initTerminal();
|
|
||||||
_initVirtKeys();
|
_initVirtKeys();
|
||||||
|
|
||||||
|
Future.delayed(const Duration(milliseconds: 77), () async {
|
||||||
|
await _initTerminal();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -270,13 +271,13 @@ class _SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin {
|
|||||||
break;
|
break;
|
||||||
case VirtualKeyFunc.file:
|
case VirtualKeyFunc.file:
|
||||||
// get $PWD from SSH session
|
// get $PWD from SSH session
|
||||||
_terminal.textInput(echoPWD);
|
_terminal.textInput(_echoPWD);
|
||||||
_terminal.keyInput(TerminalKey.enter);
|
_terminal.keyInput(TerminalKey.enter);
|
||||||
final cmds = _terminal.buffer.lines.toList();
|
final cmds = _terminal.buffer.lines.toList();
|
||||||
// wait for the command to finish
|
// wait for the command to finish
|
||||||
await Future.delayed(const Duration(milliseconds: 777));
|
await Future.delayed(const Duration(milliseconds: 777));
|
||||||
// the line below `echo $PWD` is the current path
|
// the line below `echo $PWD` is the current path
|
||||||
final idx = cmds.lastIndexWhere((e) => e.toString().contains(echoPWD));
|
final idx = cmds.lastIndexWhere((e) => e.toString().contains(_echoPWD));
|
||||||
final initPath = cmds[idx + 1].toString();
|
final initPath = cmds[idx + 1].toString();
|
||||||
if (initPath.isEmpty || !initPath.startsWith('/')) {
|
if (initPath.isEmpty || !initPath.startsWith('/')) {
|
||||||
context.showRoundDialog(
|
context.showRoundDialog(
|
||||||
@@ -323,27 +324,12 @@ class _SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin {
|
|||||||
|
|
||||||
Future<void> _initTerminal() async {
|
Future<void> _initTerminal() async {
|
||||||
_write('Connecting...\r\n');
|
_write('Connecting...\r\n');
|
||||||
|
if (_client == null) {
|
||||||
// _client = await genClient(
|
await Pros.server.refreshData(spi: widget.spi);
|
||||||
// widget.spi,
|
}
|
||||||
// onStatus: (p0) {
|
|
||||||
// switch (p0) {
|
|
||||||
// case GenSSHClientStatus.socket:
|
|
||||||
// _write('Destination: ${widget.spi.id}');
|
|
||||||
// return _write('Establishing socket...');
|
|
||||||
// case GenSSHClientStatus.key:
|
|
||||||
// return _write('Using private key to connect...');
|
|
||||||
// case GenSSHClientStatus.pwd:
|
|
||||||
// return _write('Sending password to auth...');
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// timeout: Stores.setting.timeoutD,
|
|
||||||
// );
|
|
||||||
// _write('Connected\r\n');
|
|
||||||
_write('Terminal size: ${_terminal.viewWidth}x${_terminal.viewHeight}\r\n');
|
|
||||||
_write('Starting shell...\r\n');
|
_write('Starting shell...\r\n');
|
||||||
|
|
||||||
_session = await _client?.shell(
|
final session = await _client?.shell(
|
||||||
pty: SSHPtyConfig(
|
pty: SSHPtyConfig(
|
||||||
width: _terminal.viewWidth,
|
width: _terminal.viewWidth,
|
||||||
height: _terminal.viewHeight,
|
height: _terminal.viewHeight,
|
||||||
@@ -352,30 +338,30 @@ class _SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin {
|
|||||||
|
|
||||||
_setupDiscontinuityTimer();
|
_setupDiscontinuityTimer();
|
||||||
|
|
||||||
if (_session == null) {
|
if (session == null) {
|
||||||
context.showSnackBar('Null session');
|
_write(_server?.status.err ?? 'Null session');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// _terminal.buffer.clear();
|
_terminal.buffer.clear();
|
||||||
// _terminal.buffer.setCursor(0, 0);
|
_terminal.buffer.setCursor(0, 0);
|
||||||
|
|
||||||
_terminal.onOutput = (data) {
|
_terminal.onOutput = (data) {
|
||||||
_session?.write(utf8.encode(data) as Uint8List);
|
session.write(utf8.encode(data) as Uint8List);
|
||||||
};
|
};
|
||||||
_terminal.onResize = (width, height, pixelWidth, pixelHeight) {
|
_terminal.onResize = (width, height, pixelWidth, pixelHeight) {
|
||||||
_session?.resizeTerminal(width, height);
|
session.resizeTerminal(width, height);
|
||||||
};
|
};
|
||||||
|
|
||||||
_listen(_session?.stdout);
|
_listen(session.stdout);
|
||||||
_listen(_session?.stderr);
|
_listen(session.stderr);
|
||||||
|
|
||||||
if (widget.initCmd != null) {
|
if (widget.initCmd != null) {
|
||||||
_terminal.textInput(widget.initCmd!);
|
_terminal.textInput(widget.initCmd!);
|
||||||
_terminal.keyInput(TerminalKey.enter);
|
_terminal.keyInput(TerminalKey.enter);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _session?.done;
|
await session.done;
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
context.pop();
|
context.pop();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user