refactor: SSHClientX.exec

This commit is contained in:
lollipopkit🏳️‍⚧️
2024-08-15 11:27:22 +08:00
parent 267b0b0a69
commit 195ddd2bcc
16 changed files with 155 additions and 221 deletions

View File

@@ -690,7 +690,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 1058;
CURRENT_PROJECT_VERSION = 1060;
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -700,7 +700,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.1058;
MARKETING_VERSION = 1.0.1060;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -826,7 +826,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 1058;
CURRENT_PROJECT_VERSION = 1060;
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -836,7 +836,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.1058;
MARKETING_VERSION = 1.0.1060;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -854,7 +854,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 1058;
CURRENT_PROJECT_VERSION = 1060;
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -864,7 +864,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.1058;
MARKETING_VERSION = 1.0.1060;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -885,7 +885,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1058;
CURRENT_PROJECT_VERSION = 1060;
DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
@@ -898,7 +898,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0.1058;
MARKETING_VERSION = 1.0.1060;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
@@ -924,7 +924,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1058;
CURRENT_PROJECT_VERSION = 1060;
DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
@@ -937,7 +937,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0.1058;
MARKETING_VERSION = 1.0.1060;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -960,7 +960,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1058;
CURRENT_PROJECT_VERSION = 1060;
DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
@@ -973,7 +973,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0.1058;
MARKETING_VERSION = 1.0.1060;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -996,7 +996,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1058;
CURRENT_PROJECT_VERSION = 1060;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES;
@@ -1008,7 +1008,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.1058;
MARKETING_VERSION = 1.0.1060;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
@@ -1037,7 +1037,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1058;
CURRENT_PROJECT_VERSION = 1060;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES;
@@ -1049,7 +1049,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.1058;
MARKETING_VERSION = 1.0.1060;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
PRODUCT_NAME = ServerBox;
@@ -1075,7 +1075,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1058;
CURRENT_PROJECT_VERSION = 1060;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES;
@@ -1087,7 +1087,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.1058;
MARKETING_VERSION = 1.0.1060;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
PRODUCT_NAME = ServerBox;

View File

@@ -13,65 +13,66 @@ typedef OnStdin = void Function(SSHSession session);
typedef PwdRequestFunc = Future<String?> Function(String? user);
extension SSHClientX on SSHClient {
/// TODO: delete [exec]
Future<SSHSession> exec(
String cmd, {
OnStdout? onStderr,
Future<(SSHSession, String)> exec(
OnStdin onStdin, {
String? entry,
SSHPtyConfig? pty,
OnStdout? onStdout,
OnStdin? stdin,
bool redirectToBash = false, // not working yet. do not use
OnStdout? onStderr,
bool stdout = true,
bool stderr = true,
Map<String, String>? env,
}) async {
final session = await execute(redirectToBash ? 'head -1 | bash' : cmd);
if (redirectToBash) {
session.stdin.add('$cmd\n'.uint8List);
}
final session = await execute(
entry ?? 'cat | sh',
pty: pty,
environment: env,
);
final result = BytesBuilder(copy: false);
final stdoutDone = Completer<void>();
final stderrDone = Completer<void>();
if (onStdout != null) {
session.stdout.listen(
(e) => onStdout(e.string, session),
(e) {
onStdout?.call(e.string, session);
if (stdout) result.add(e);
},
onDone: stdoutDone.complete,
onError: stderrDone.completeError,
);
} else {
stdoutDone.complete();
}
if (onStderr != null) {
session.stderr.listen(
(e) => onStderr(e.string, session),
(e) {
onStderr?.call(e.string, session);
if (stderr) result.add(e);
},
onDone: stderrDone.complete,
onError: stderrDone.completeError,
);
} else {
stderrDone.complete();
}
if (stdin != null) {
stdin(session);
}
onStdin(session);
await stdoutDone.future;
await stderrDone.future;
session.close();
return session;
return (session, result.takeBytes().string);
}
Future<int?> execWithPwd(
String cmd, {
String script, {
String? entry,
BuildContext? context,
OnStdout? onStdout,
OnStdout? onStderr,
OnStdin? stdin,
bool redirectToBash = false, // not working yet. do not use
required String id,
}) async {
var isRequestingPwd = false;
final session = await exec(
cmd,
redirectToBash: redirectToBash,
final (session, _) = await exec(
(sess) {
sess.stdin.add('$script\n'.uint8List);
sess.stdin.close();
},
onStderr: (data, session) async {
onStderr?.call(data, session);
if (isRequestingPwd) return;
@@ -84,88 +85,38 @@ extension SSHClientX on SSHClient {
? await context.showPwdDialog(title: user, id: id)
: null;
if (pwd == null || pwd.isEmpty) {
session.kill(SSHSignal.TERM);
session.stdin.close();
} else {
session.stdin.add('$pwd\n'.uint8List);
}
isRequestingPwd = false;
}
},
onStdout: (data, sink) async {
onStdout?.call(data, sink);
},
stdin: stdin,
onStdout: onStdout,
entry: entry,
);
return session.exitCode;
}
Future<Uint8List> runForOutput(
String command, {
bool runInPty = false,
Future<String> execForOutput(
String script, {
SSHPtyConfig? pty,
bool stdout = true,
bool stderr = true,
Map<String, String>? environment,
Future<void> Function(SSHSession)? action,
String? entry,
Map<String, String>? env,
}) async {
final session = await execute(
command,
pty: runInPty ? const SSHPtyConfig() : null,
environment: environment,
);
final result = BytesBuilder(copy: false);
final stdoutDone = Completer<void>();
final stderrDone = Completer<void>();
session.stdout.listen(
stdout ? result.add : (_) {},
onDone: stdoutDone.complete,
onError: stderrDone.completeError,
);
session.stderr.listen(
stderr ? result.add : (_) {},
onDone: stderrDone.complete,
onError: stderrDone.completeError,
);
if (action != null) await action(session);
await stdoutDone.future;
await stderrDone.future;
return result.takeBytes();
}
Future<String> runScriptIn(
String cmd, {
String shell = '/bin/sh',
bool stdout = true,
bool stderr = true,
}) async {
final session = await execute('cat | $shell');
final result = BytesBuilder(copy: false);
final stdoutDone = Completer<void>();
final stderrDone = Completer<void>();
session.stdout.listen(
stdout ? result.add : (_) {},
onDone: stdoutDone.complete,
onError: stderrDone.completeError,
);
session.stderr.listen(
stderr ? result.add : (_) {},
onDone: stderrDone.complete,
onError: stderrDone.completeError,
);
session.stdin.add('$cmd\n'.uint8List);
final ret = await exec(
(session) {
session.stdin.add('$script\n'.uint8List);
session.stdin.close();
await stdoutDone.future;
await stderrDone.future;
return result.takeBytes().string;
},
pty: pty,
env: env,
stdout: stdout,
stderr: stderr,
entry: entry,
);
return ret.$2;
}
}

View File

@@ -95,8 +95,8 @@ Future<SSHClient> genClient(
try {
final ipPort = spi.fromStringUrl();
return await SSHSocket.connect(
ipPort.ip,
ipPort.port,
ipPort.$1,
ipPort.$2,
timeout: timeout,
);
} catch (e) {

View File

@@ -31,8 +31,8 @@ class ServerPrivateInfo {
final List<String>? tags;
@HiveField(7)
final String? alterUrl;
@HiveField(8)
final bool? autoConnect;
@HiveField(8, defaultValue: true)
final bool autoConnect;
/// [id] of the jump server
@HiveField(9)
@@ -59,7 +59,7 @@ class ServerPrivateInfo {
this.keyId,
this.tags,
this.alterUrl,
this.autoConnect,
this.autoConnect = true,
this.jumpId,
this.custom,
this.wolCfg,
@@ -75,7 +75,7 @@ class ServerPrivateInfo {
final keyId = json['pubKeyId'] as String?;
final tags = (json['tags'] as List?)?.cast<String>();
final alterUrl = json['alterUrl'] as String?;
final autoConnect = json['autoConnect'] as bool?;
final autoConnect = json['autoConnect'] as bool? ?? true;
final jumpId = json['jumpId'] as String?;
final custom = json['customCmd'] == null
? null
@@ -128,9 +128,7 @@ class ServerPrivateInfo {
if (alterUrl != null) {
data['alterUrl'] = alterUrl;
}
if (autoConnect != null) {
data['autoConnect'] = autoConnect;
}
if (jumpId != null) {
data['jumpId'] = jumpId;
}
@@ -160,7 +158,7 @@ class ServerPrivateInfo {
custom?.cmds != old.custom?.cmds;
}
IpPort fromStringUrl() {
(String, int) fromStringUrl() {
if (alterUrl == null) {
throw SSHErr(type: SSHErrType.connect, message: 'alterUrl is null');
}
@@ -177,7 +175,7 @@ class ServerPrivateInfo {
if (port <= 0 || port > 65535) {
throw SSHErr(type: SSHErrType.connect, message: 'alterUrl port error');
}
return IpPort(ip_, port_);
return (ip_, port_);
}
@override
@@ -206,11 +204,6 @@ class ServerPrivateInfo {
logoUrl: 'https://example.com/logo.png',
),
);
}
class IpPort {
final String ip;
final int port;
IpPort(this.ip, this.port);
bool get isRoot => user == 'root';
}

View File

@@ -25,7 +25,7 @@ class ServerPrivateInfoAdapter extends TypeAdapter<ServerPrivateInfo> {
keyId: fields[5] as String?,
tags: (fields[6] as List?)?.cast<String>(),
alterUrl: fields[7] as String?,
autoConnect: fields[8] as bool?,
autoConnect: fields[8] == null ? true : fields[8] as bool,
jumpId: fields[9] as String?,
custom: fields[10] as ServerCustom?,
wolCfg: fields[11] as WakeOnLanCfg?,

View File

@@ -34,7 +34,7 @@ class ServerProvider extends ChangeNotifier {
final _manualDisconnectedIds = <String>{};
Future<void> load() async {
// Issue #147
// #147
// Clear all servers because of restarting app will cause duplicate servers
final oldServers = Map<String, Server>.from(_servers);
_servers.clear();
@@ -46,7 +46,7 @@ class ServerProvider extends ChangeNotifier {
final originServer = oldServers[spi.id];
final newServer = genServer(spi);
/// Issues #258
/// #258
/// If not [shouldReconnect], then keep the old state.
if (originServer != null && !originServer.spi.shouldReconnect(spi)) {
newServer.conn = originServer.conn;
@@ -112,20 +112,20 @@ class ServerProvider extends ChangeNotifier {
return;
}
await Future.wait(_servers.values.map((s) => _connectFn(s, onlyFailed)));
}
Future<void> _connectFn(Server s, bool onlyFailed) async {
await Future.wait(_servers.values.map((s) async {
if (onlyFailed) {
if (s.conn != ServerConn.failed) return;
TryLimiter.reset(s.spi.id);
}
if (!(s.spi.autoConnect ?? true) && s.conn == ServerConn.disconnected ||
_manualDisconnectedIds.contains(s.spi.id)) {
if (_manualDisconnectedIds.contains(s.spi.id)) return;
if (s.conn == ServerConn.disconnected && !s.spi.autoConnect) {
return;
}
return await _getData(s.spi);
}));
}
Future<void> startAutoRefresh() async {
@@ -301,16 +301,16 @@ class ServerProvider extends ChangeNotifier {
final scriptRaw = ShellFunc.allScript(spi.custom?.cmds).uint8List;
try {
final writeScriptResult = await s.client!.runForOutput(
ShellFunc.getInstallShellCmd(spi.id),
action: (session) async {
final (_, writeScriptResult) = await s.client!.exec(
(session) async {
session.stdin.add(scriptRaw);
session.stdin.close();
},
entry: ShellFunc.getInstallShellCmd(spi.id),
);
if (writeScriptResult.isNotEmpty) {
ShellFunc.switchScriptDir(spi.id);
throw String.fromCharCodes(writeScriptResult);
throw writeScriptResult;
}
} on SSHAuthAbortError catch (e) {
TryLimiter.inc(sid);

View File

@@ -8,28 +8,27 @@ import 'package:server_box/data/res/provider.dart';
final class SystemdProvider {
late final SSHClient _client;
late final bool isRoot;
SystemdProvider.init(ServerPrivateInfo spi) {
isRoot = spi.isRoot;
_client = Pros.server.pick(spi: spi)!.client!;
getUnits();
}
final isBusy = false.vn;
final isRoot = false.vn;
final units = <SystemdUnit>[].vn;
Future<void> getUnits() async {
isBusy.value = true;
try {
final result = await _client.runScriptIn(_getUnitsCmd);
final result = await _client.execForOutput(_getUnitsCmd);
final units = result.split('\n');
final isRootRaw = units.firstOrNull;
isRoot.value = isRootRaw == '0';
final userUnits = <String>[];
final systemUnits = <String>[];
for (final unit in units.skip(1)) {
for (final unit in units) {
if (unit.startsWith('/etc/systemd/system')) {
systemUnits.add(unit);
} else if (unit.startsWith('~/.config/systemd/user')) {
@@ -64,7 +63,7 @@ for unit in ${unitNames_.join(' ')}; do
echo -n "${ShellFunc.seperator}\n\$state"
done
''';
final result = await _client.runScriptIn(script);
final result = await _client.execForOutput(script);
final units = result.split(ShellFunc.seperator);
final parsedUnits = <SystemdUnit>[];
@@ -124,17 +123,8 @@ done
});
return parsedUnits;
}
}
String _getIniVal(String line) {
return line.split('=').last;
}
const _getUnitsCmd = '''
# If root, get system & user units, otherwise get user units
uid=\$(id -u)
echo \$uid
late final _getUnitsCmd = '''
get_files() {
unit_type=\$1
base_dir=\$2
@@ -149,14 +139,12 @@ get_files() {
get_type_files() {
unit_type=\$1
base_dir=""
if [ "\$uid" -eq 0 ]; then
get_files \$unit_type /etc/systemd/system
get_files \$unit_type ~/.config/systemd/user
else
get_files \$unit_type ~/.config/systemd/user
fi
${isRoot ? """
get_files \$unit_type /etc/systemd/system
get_files \$unit_type ~/.config/systemd/user""" : """
get_files \$unit_type ~/.config/systemd/user"""}
}
types="service socket mount timer"
@@ -165,3 +153,8 @@ for type in \$types; do
get_type_files \$type
done
''';
}
String _getIniVal(String line) {
return line.split('=').last;
}

View File

@@ -1,7 +1,8 @@
// This file is generated by fl_build. Do not edit.
// ignore_for_file: prefer_single_quotes
class BuildData {
static const String name = 'ServerBox';
static const int build = 1058;
static const int script = 56;
static const String name = "ServerBox";
static const int build = 1060;
static const int script = 57;
}

View File

@@ -156,9 +156,8 @@ class BackupPage extends StatelessWidget {
trailing: ListenableBuilder(
listenable: webdavLoading,
builder: (_, __) {
if (webdavLoading.value) {
return UIs.centerSizedLoadingSmall;
}
if (webdavLoading.value) return SizedLoading.centerSmall;
return Row(
mainAxisSize: MainAxisSize.min,
children: [

View File

@@ -199,7 +199,7 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage> {
return;
}
FocusScope.of(context).unfocus();
_loading.value = UIs.centerSizedLoading;
_loading.value = SizedLoading.centerMedium;
try {
final decrypted = await Computer.shared.start(decyptPem, [key, pwd]);
final pki = PrivateKeyInfo(id: name, key: decrypted);

View File

@@ -606,7 +606,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
_tags.value = spi.tags?.toSet() ?? {};
_altUrlController.text = spi.alterUrl ?? '';
_autoConnect.value = spi.autoConnect ?? true;
_autoConnect.value = spi.autoConnect;
_jumpServer.value = spi.jumpId;
final custom = spi.custom;

View File

@@ -64,24 +64,22 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
}
Widget _buildSortMenu() {
final options = [
(_SortType.name, libL10n.name),
(_SortType.size, l10n.size),
(_SortType.time, l10n.time),
];
return ValBuilder(
listenable: _sortOption,
builder: (value) {
return PopupMenuButton<_SortType>(
icon: const Icon(Icons.sort),
itemBuilder: (context) {
final currentSelectedOption = _sortOption.value;
final options = [
(_SortType.name, libL10n.name),
(_SortType.size, l10n.size),
(_SortType.time, l10n.time),
];
return options.map((r) {
final (type, name) = r;
final selected = type == currentSelectedOption.sortBy;
final title = selected
? "$name (${currentSelectedOption.reversed ? '-' : '+'})"
: name;
final selected = type == value.sortBy;
final title =
selected ? "$name (${value.reversed ? '-' : '+'})" : name;
return PopupMenuItem(
value: type,
child: Text(
@@ -607,7 +605,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
/// Issue #97
/// In order to compatible with the Synology NAS
/// which not has '.' and '..' in listdir
if (fs.isNotEmpty && fs.firstOrNull?.filename == '.') {
if (fs.firstOrNull?.filename == '.') {
fs.removeAt(0);
}

View File

@@ -54,9 +54,9 @@ final class _SystemdPageState extends State<SystemdPage> {
(isBusy) => AnimatedContainer(
duration: Durations.medium1,
curve: Curves.fastEaseInToSlowEaseOut,
height: isBusy ? 50 : 0,
height: isBusy ? 30 : 0,
child: isBusy
? UIs.centerSizedLoadingSmall.paddingOnly(bottom: 7)
? SizedLoading.centerSmall.paddingOnly(bottom: 7)
: const SizedBox.shrink(),
),
),
@@ -71,8 +71,7 @@ final class _SystemdPageState extends State<SystemdPage> {
(units) {
if (units.isEmpty) {
return SliverToBoxAdapter(
child: ListTile(title: Text(libL10n.empty))
.cardx
child: CenterGreyTitle(libL10n.empty)
.paddingSymmetric(horizontal: 13),
);
}
@@ -103,7 +102,7 @@ final class _SystemdPageState extends State<SystemdPage> {
return PopupMenu(
items: unit.availableFuncs.map(_buildUnitFuncBtn).toList(),
onSelected: (val) async {
final cmd = unit.getCmd(func: val, isRoot: _pro.isRoot.value);
final cmd = unit.getCmd(func: val, isRoot: _pro.isRoot);
final sure = await context.showRoundDialog(
title: libL10n.attention,
child: SimpleMarkdown(data: '```shell\n$cmd\n```'),

View File

@@ -471,7 +471,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1058;
CURRENT_PROJECT_VERSION = 1060;
DEVELOPMENT_TEAM = BA88US33G6;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Server Box";
@@ -481,7 +481,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 1.0.1058;
MARKETING_VERSION = 1.0.1060;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "Server Box";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -608,7 +608,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1058;
CURRENT_PROJECT_VERSION = 1060;
DEVELOPMENT_TEAM = BA88US33G6;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Server Box";
@@ -618,7 +618,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 1.0.1058;
MARKETING_VERSION = 1.0.1060;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "Server Box";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -638,7 +638,7 @@
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "3rd Party Mac Developer Application";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1058;
CURRENT_PROJECT_VERSION = 1060;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=macosx*]" = BA88US33G6;
INFOPLIST_FILE = Runner/Info.plist;
@@ -649,7 +649,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 1.0.1058;
MARKETING_VERSION = 1.0.1060;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "Server Box";
PROVISIONING_PROFILE_SPECIFIER = "";

View File

@@ -373,8 +373,8 @@ packages:
dependency: "direct dev"
description:
path: "."
ref: "v1.0.41"
resolved-ref: "6f1f4e8c9fecb3a43725e19e6747d4e2a727ea89"
ref: "v1.0.42"
resolved-ref: "2cae3388a66581169638c3b3f5fc73dd7b5b4bfd"
url: "https://github.com/lppcg/fl_build.git"
source: git
version: "1.0.0"
@@ -390,8 +390,8 @@ packages:
dependency: "direct main"
description:
path: "."
ref: "v1.0.132"
resolved-ref: f68453c64cfa3b363bcd6b5e7d76cad7fa7724c6
ref: "v1.0.134"
resolved-ref: "24b9778e8fb482ee233b70bcc4a38587aa7f76b1"
url: "https://github.com/lppcg/fl_lib"
source: git
version: "0.0.1"

View File

@@ -1,7 +1,7 @@
name: server_box
description: server status & toolbox app.
publish_to: 'none'
version: 1.0.1058+1058
version: 1.0.1060+1060
environment:
sdk: ">=3.0.0"
@@ -60,7 +60,7 @@ dependencies:
fl_lib:
git:
url: https://github.com/lppcg/fl_lib
ref: v1.0.132
ref: v1.0.134
dependency_overrides:
# dartssh2:
@@ -82,7 +82,7 @@ dev_dependencies:
fl_build:
git:
url: https://github.com/lppcg/fl_build.git
ref: v1.0.41
ref: v1.0.42
flutter:
generate: true