Merge pull request #110 from lollipopkit/lollipopkit/issue109
Lollipopkit/issue109
This commit is contained in:
@@ -6,7 +6,6 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
|
|||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:r_upgrade/r_upgrade.dart';
|
import 'package:r_upgrade/r_upgrade.dart';
|
||||||
import 'package:toolbox/core/extension/navigator.dart';
|
import 'package:toolbox/core/extension/navigator.dart';
|
||||||
import 'package:toolbox/core/utils/misc.dart';
|
|
||||||
import 'package:toolbox/data/res/path.dart';
|
import 'package:toolbox/data/res/path.dart';
|
||||||
|
|
||||||
import '../data/provider/app.dart';
|
import '../data/provider/app.dart';
|
||||||
|
|||||||
@@ -73,16 +73,7 @@ String getTime(int? unixMill) {
|
|||||||
.replaceFirst('.000', '');
|
.replaceFirst('.000', '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Join two path with `/`
|
||||||
String pathJoin(String path1, String path2) {
|
String pathJoin(String path1, String path2) {
|
||||||
return path1 + (path1.endsWith('/') ? '' : '/') + path2;
|
return path1 + (path1.endsWith('/') ? '' : '/') + path2;
|
||||||
}
|
}
|
||||||
|
|
||||||
String? getHomeDir() {
|
|
||||||
final envVars = Platform.environment;
|
|
||||||
if (isMacOS || isLinux) {
|
|
||||||
return envVars['HOME'];
|
|
||||||
} else if (isWindows) {
|
|
||||||
return envVars['UserProfile'];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ enum PlatformType {
|
|||||||
windows,
|
windows,
|
||||||
web,
|
web,
|
||||||
fuchsia,
|
fuchsia,
|
||||||
unknown,
|
unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
final _p = () {
|
final _p = () {
|
||||||
@@ -38,7 +38,15 @@ final _p = () {
|
|||||||
return PlatformType.unknown;
|
return PlatformType.unknown;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
|
final _pathSep = () {
|
||||||
|
if (Platform.isWindows) {
|
||||||
|
return '\\';
|
||||||
|
}
|
||||||
|
return '/';
|
||||||
|
}();
|
||||||
|
|
||||||
PlatformType get platform => _p;
|
PlatformType get platform => _p;
|
||||||
|
String get pathSeparator => _pathSep;
|
||||||
|
|
||||||
bool get isAndroid => _p == PlatformType.android;
|
bool get isAndroid => _p == PlatformType.android;
|
||||||
bool get isIOS => _p == PlatformType.ios;
|
bool get isIOS => _p == PlatformType.ios;
|
||||||
@@ -51,3 +59,23 @@ bool get isDesktop =>
|
|||||||
_p == PlatformType.linux ||
|
_p == PlatformType.linux ||
|
||||||
_p == PlatformType.macos ||
|
_p == PlatformType.macos ||
|
||||||
_p == PlatformType.windows;
|
_p == PlatformType.windows;
|
||||||
|
|
||||||
|
/// Available only on desktop,
|
||||||
|
/// return null on mobile
|
||||||
|
String? getHomeDir() {
|
||||||
|
final envVars = Platform.environment;
|
||||||
|
if (isMacOS || isLinux) {
|
||||||
|
return envVars['HOME'];
|
||||||
|
} else if (isWindows) {
|
||||||
|
return envVars['UserProfile'];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Join two paths with platform specific separator
|
||||||
|
String pathJoin(String path1, String path2) {
|
||||||
|
if (isWindows) {
|
||||||
|
return path1 + (path1.endsWith('\\') ? '' : '\\') + path2;
|
||||||
|
}
|
||||||
|
return path1 + (path1.endsWith('/') ? '' : '/') + path2;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:toolbox/core/utils/misc.dart';
|
import '../../../core/utils/platform.dart';
|
||||||
|
|
||||||
class PathWithPrefix {
|
class PathWithPrefix {
|
||||||
final String _prefixPath;
|
final String _prefixPath;
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import 'package:toolbox/data/store/private_key.dart';
|
|||||||
import 'package:toolbox/locator.dart';
|
import 'package:toolbox/locator.dart';
|
||||||
|
|
||||||
import '../../../core/route.dart';
|
import '../../../core/route.dart';
|
||||||
import '../../../core/utils/misc.dart';
|
|
||||||
import '../../../core/utils/platform.dart';
|
import '../../../core/utils/platform.dart';
|
||||||
import '../../../data/model/server/private_key_info.dart';
|
import '../../../data/model/server/private_key_info.dart';
|
||||||
import '../../../data/provider/private_key.dart';
|
import '../../../data/provider/private_key.dart';
|
||||||
|
|||||||
@@ -43,8 +43,7 @@ class _ProcessPageState extends State<ProcessPage> {
|
|||||||
showSnackBar(context, Text(_s.noClient));
|
showSnackBar(context, Text(_s.noClient));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_timer =
|
_timer = Timer.periodic(const Duration(seconds: 3), (_) => _refresh());
|
||||||
Timer.periodic(const Duration(seconds: 3), (_) => _refresh());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _refresh() async {
|
Future<void> _refresh() async {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:io' show Directory, File, Platform, Process;
|
||||||
|
|
||||||
import 'package:after_layout/after_layout.dart';
|
import 'package:after_layout/after_layout.dart';
|
||||||
import 'package:circle_chart/circle_chart.dart';
|
import 'package:circle_chart/circle_chart.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -6,7 +8,6 @@ import 'package:get_it/get_it.dart';
|
|||||||
import 'package:nil/nil.dart';
|
import 'package:nil/nil.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:toolbox/core/extension/order.dart';
|
import 'package:toolbox/core/extension/order.dart';
|
||||||
import 'package:toolbox/core/utils/misc.dart';
|
|
||||||
import 'package:toolbox/data/model/app/net_view.dart';
|
import 'package:toolbox/data/model/app/net_view.dart';
|
||||||
import 'package:toolbox/data/model/server/snippet.dart';
|
import 'package:toolbox/data/model/server/snippet.dart';
|
||||||
import 'package:toolbox/data/provider/snippet.dart';
|
import 'package:toolbox/data/provider/snippet.dart';
|
||||||
@@ -15,6 +16,9 @@ import 'package:toolbox/view/widget/tag/picker.dart';
|
|||||||
import 'package:toolbox/view/widget/tag/switcher.dart';
|
import 'package:toolbox/view/widget/tag/switcher.dart';
|
||||||
|
|
||||||
import '../../../core/route.dart';
|
import '../../../core/route.dart';
|
||||||
|
import '../../../core/utils/misc.dart' hide pathJoin;
|
||||||
|
import '../../../core/utils/platform.dart';
|
||||||
|
import '../../../core/utils/server.dart';
|
||||||
import '../../../core/utils/ui.dart';
|
import '../../../core/utils/ui.dart';
|
||||||
import '../../../data/model/server/disk.dart';
|
import '../../../data/model/server/disk.dart';
|
||||||
import '../../../data/model/server/server.dart';
|
import '../../../data/model/server/server.dart';
|
||||||
@@ -225,7 +229,6 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
_buildTopRightText(ss, cs),
|
_buildTopRightText(ss, cs),
|
||||||
width13,
|
width13,
|
||||||
_buildSSHBtn(spi),
|
_buildSSHBtn(spi),
|
||||||
// SizedBox(width: 5,),
|
|
||||||
_buildMoreBtn(spi),
|
_buildMoreBtn(spi),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -271,12 +274,9 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSSHBtn(ServerPrivateInfo spi) {
|
Widget _buildSSHBtn(ServerPrivateInfo spi) {
|
||||||
return IconButton(
|
return GestureDetector(
|
||||||
icon: const Icon(
|
child: const Icon(Icons.terminal, size: 21),
|
||||||
Icons.terminal,
|
onTap: () => gotoSSH(spi),
|
||||||
size: 21,
|
|
||||||
),
|
|
||||||
onPressed: () => startSSH(spi, context),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,6 +447,53 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> gotoSSH(ServerPrivateInfo spi) async {
|
||||||
|
// as a `Mobile first` app -> handle mobile first
|
||||||
|
if (!isDesktop) {
|
||||||
|
AppRoute(SSHPage(spi: spi), 'ssh page').go(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final extraArgs = <String>[];
|
||||||
|
if (spi.port != 22) {
|
||||||
|
extraArgs.addAll(['-p', '${spi.port}']);
|
||||||
|
}
|
||||||
|
|
||||||
|
final path = () {
|
||||||
|
final tempKeyFileName = 'srvbox_pk_${spi.pubKeyId}';
|
||||||
|
return pathJoin(Directory.systemTemp.path, tempKeyFileName);
|
||||||
|
}();
|
||||||
|
final file = File(path);
|
||||||
|
final shouldGenKey = spi.pubKeyId != null;
|
||||||
|
if (shouldGenKey) {
|
||||||
|
await file.delete();
|
||||||
|
await file.writeAsString(getPrivateKey(spi.pubKeyId!));
|
||||||
|
extraArgs.addAll(["-i", path]);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> sshCommand = ["ssh", "${spi.user}@${spi.ip}"] + extraArgs;
|
||||||
|
final system = Platform.operatingSystem;
|
||||||
|
switch (system) {
|
||||||
|
case "windows":
|
||||||
|
await Process.start("cmd", ["/c", "start"] + sshCommand);
|
||||||
|
break;
|
||||||
|
case "linux":
|
||||||
|
await Process.start("x-terminal-emulator", ["-e"] + sshCommand);
|
||||||
|
break;
|
||||||
|
case "macos":
|
||||||
|
await Process.start("osascript", [
|
||||||
|
"-e",
|
||||||
|
'tell application "Terminal" to do script "${sshCommand.join(" ")}"'
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
showSnackBar(context, Text('Mismatch system: $system'));
|
||||||
|
}
|
||||||
|
// For security reason, delete the private key file after use
|
||||||
|
if (shouldGenKey) {
|
||||||
|
await Future.delayed(const Duration(seconds: 2), file.delete);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get wantKeepAlive => true;
|
bool get wantKeepAlive => true;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'dart:io' show Process, File;
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
@@ -26,33 +25,6 @@ import '../../../data/store/setting.dart';
|
|||||||
import '../../../locator.dart';
|
import '../../../locator.dart';
|
||||||
import '../storage/sftp.dart';
|
import '../storage/sftp.dart';
|
||||||
|
|
||||||
startSSH(ServerPrivateInfo spi, BuildContext context) {
|
|
||||||
if (isLinux || isMacOS) {
|
|
||||||
unawaited(() async {
|
|
||||||
List<String> extarArgs = [];
|
|
||||||
if (spi.pubKeyId != null) {
|
|
||||||
String path = "/tmp/.serverbox_pk_${spi.pubKeyId}";
|
|
||||||
File(path).openWrite().write(getPrivateKey(spi.pubKeyId!));
|
|
||||||
extarArgs += ["-i", path];
|
|
||||||
}
|
|
||||||
List<String> sshCommand = ["ssh", spi.user + "@" + spi.ip] + extarArgs;
|
|
||||||
if (isLinux) {
|
|
||||||
Process.start("x-terminal-emulator", ["-e"] + sshCommand);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isMacOS) {
|
|
||||||
Process.start("osascript", [
|
|
||||||
"-e",
|
|
||||||
'tell application "Terminal" to do script "${sshCommand.join(" ")}"'
|
|
||||||
]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
AppRoute(SSHPage(spi: spi), 'ssh page').go(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
class SSHPage extends StatefulWidget {
|
class SSHPage extends StatefulWidget {
|
||||||
final ServerPrivateInfo spi;
|
final ServerPrivateInfo spi;
|
||||||
final String? initCmd;
|
final String? initCmd;
|
||||||
|
|||||||
Reference in New Issue
Block a user